diff lisp/oobr/br.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4103f0995bd7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/oobr/br.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,2001 @@
+;;!emacs
+;;
+;; FILE:         br.el
+;; SUMMARY:      Browse object-oriented code.
+;; USAGE:        GNU Emacs Lisp Library
+;; KEYWORDS:     matching, oop, tools
+;;
+;; AUTHOR:       Bob Weiner
+;; ORG:          Motorola Inc.
+;;
+;; ORIG-DATE:    12-Dec-89
+;; LAST-MOD:     21-Sep-95 at 12:39:17 by Bob Weiner
+;;
+;; Copyright (C) 1989-1995  Free Software Foundation, Inc.
+;; See the file BR-COPY for license information.
+;;
+;; This file is part of the OO-Browser.
+;;
+;; DESCRIPTION:  
+;; DESCRIP-END.
+
+;;; ************************************************************************
+;;; Other required Elisp libraries
+;;; ************************************************************************
+
+(require 'br-lib)
+
+;;; ************************************************************************
+;;; Public variables
+;;; ************************************************************************
+
+(defvar br-c-tags-flag t
+  "*Non-nil means add C constructs when building C-based language Environments.")
+
+(defvar br-directory nil
+  "Directory in which OO-Browser executable and help files are kept.")
+
+(defconst br-feature-signature-regexp "[:|,]"
+  "Regular expression that matches a feature signature but not a class name.")
+
+(defvar br-inherited-features-flag t
+  "*If non-nil (the default), feature/element listings include all inherited features.
+If nil, only those features lexically included within a class are shown.")
+
+(defvar br-inhibit-version nil
+  "*Personal setting which if non-nil, skips version/credit information upon startup.
+The default should be left as nil, since new users may find this helpful.")
+
+(defvar br-invert-ancestors nil
+  "*Personal setting which if non-nil makes ancestors appear as do other inheritance listings.
+That is, parents appear above children, rather than the default, which is the
+reverse.")
+
+(defvar br-keep-viewed-classes nil
+  "*Personal setting which if non-nil means leave all viewed classes around for later selection.  
+Non-nil deletes last viewed class when a new one is displayed.   Note this
+does not affect classes displayed for editing, all such classes are left
+around.")
+
+(defconst br-min-width-window 25
+  "*Minimum width of a browser class list window.
+This together with the frame width determines the number of such windows.")
+
+;; -f treats upper and lower case the same in sorting, also makes 'a' sort
+;; list before '[a]', so default classes appear at the end of the list,
+;; typically.
+;; -u leaves only unique elements in the sorted list
+(defvar br-sort-options "-fu"
+  "*String of options to send to the operating system `sort' command.
+Use nil for none.  This is used by the OO-Browser (br-order) command only
+under Emacs 18.")
+
+;;; ************************************************************************
+;;; Public macros
+;;; ************************************************************************
+
+(if (fboundp 'window-highest-p)
+    (defun br-non-listing-window-p ()
+      "Is the selected window a non-OO-Browser listing window?"
+      ;; Top of window is not at top of frame.
+      (not (window-highest-p (selected-window))))
+  (defun br-non-listing-window-p ()
+    "Is the selected window a non-OO-Browser listing window?"
+    ;; Top of window is not at top of frame.
+    (/= (nth 1 (window-edges)) br-top-of-frame)))
+
+(if (fboundp 'window-highest-p)
+    (defun br-listing-window-p ()
+      "Is the selected window an OO-Browser listing window?"
+      (window-highest-p (selected-window)))
+  (defun br-listing-window-p ()
+    "Is the selected window an OO-Browser listing window?"
+    ;; Top of window is at top of frame.
+    (= (nth 1 (window-edges)) br-top-of-frame)))
+
+;;; ************************************************************************
+;;; Public functions
+;;; ************************************************************************
+
+(defun br-browse ()
+  "Internally invoke the OO-Browser, for browsing class hierarchies.
+Use \\[br-help] and \\[br-help-ms] for help on browser usage."
+  (interactive)
+  ;; If not already in the browser, save window config.
+  (if (br-in-browser)
+      nil
+    (setq *br-prev-wconfig* (current-window-configuration)
+	  br-in-browser (selected-frame))
+    ;; If were previously in the browser, restore its saved window config,
+    ;; otherwise, set up from scratch.
+    (if *br-save-wconfig*
+	(set-window-configuration *br-save-wconfig*)
+      (br-window-setup)
+      (if br-inhibit-version
+	  (br-top-classes t)
+	(br-version)
+	(message "Press {h} for for help.")
+	;; Display all classes.
+	(br-top-classes t)
+	(message "Press {h} for for help.")
+	;; Wait for 60 seconds or until a single key sequence is given.
+	(sit-for 60)
+	(message ""))
+      (br-help))
+    (run-hooks 'br-mode-hook
+	       (intern (concat "br-" br-lang-prefix "mode-hook")))))
+
+;;;###autoload
+(defun br-add-class-file (&optional class-path lib-table-p save-file)
+  "Add a file of classes to the current Environment.
+Interactively or when optional CLASS-PATH is nil, CLASS-PATH defaults to the
+current buffer file pathname.  If optional LIB-TABLE-P is non-nil, add to
+Library Environment, otherwise add to System Environment.  If optional
+SAVE-FILE is t, the Environment is then stored to the filename given by
+'br-env-file'.  If SAVE-FILE is non-nil and not t, its string value is used
+as the file to which to save the Environment."
+  (interactive
+    (list (read-file-name (concat "Class file name to add"
+				  (if buffer-file-name
+				      (concat " (default \""
+					      (file-name-nondirectory
+						buffer-file-name)
+					      "\")"))
+				  ": ")
+			  nil buffer-file-name t)
+	  (y-or-n-p "Add to Library, rather than System tables? ")
+	  (y-or-n-p
+	    (concat "Save tables after addition to " br-env-file "? "))))
+  (or class-path (setq class-path buffer-file-name))
+  (if (not (if class-path (file-readable-p class-path)))
+      (error "(br-add-class-file): %s is not readable" class-path))
+  (let* ((paths-parents-cons
+	   (let ((br-view-file-function 'br-insert-file-contents))
+	     (br-get-classes-from-source class-path)))
+	 (classes (car paths-parents-cons))
+	 (parents (cdr paths-parents-cons))
+	 (paths-key class-path)
+	 (path-htable (br-get-htable (if lib-table-p "lib-paths" "sys-paths")))
+	 (par-htable (br-get-htable
+		       (if lib-table-p "lib-parents" "sys-parents")))
+	 (child-htable (br-get-children-htable)))
+    (mapcar
+      (function
+	(lambda (class)
+	  (br-add-to-paths-htable class paths-key path-htable)))
+      classes)
+    (mapcar
+      (function
+	(lambda (parent-cons)
+	  (hash-add (car parent-cons) (cdr parent-cons) par-htable)))
+      parents)
+    (br-env-set-htables)
+    (let ((child) (par-list) children)
+      (mapcar
+	(function
+	  (lambda (parent-cons)
+	    (setq child (cdr parent-cons)
+		  par-list (car parent-cons))
+	    (mapcar
+	      (function
+		(lambda (parent)
+		  (setq children (hash-get parent child-htable))
+		  (or (br-member child children)
+		      (hash-add (cons child children) parent child-htable))))
+	      par-list)))
+	parents)))
+  (cond ((eq save-file nil))
+	((eq save-file t) (br-env-save))
+	((br-env-save save-file))))
+
+(defun br-ancestors (&optional arg features-flag)
+  "Display ancestor tree whose root is the current class.
+With optional prefix ARG, display all ancestor trees whose roots are in the
+current listing.  If ARG = -1 or 'br-invert-ancestors' is t, the current
+class ancestry tree is inverted.  That is, it shows branches going down
+towards the root class, so that parents appear above children.  If ARG < -1 or
+'br-invert-ancestors' is t and ARG > 1, then the ancestry trees of all
+classes in the current listing are inverted.
+
+Optional second argument, FEATURES-FLAG non-nil means display features under
+each ancestor class."
+  (interactive "p")
+  (or arg (setq arg 1))
+  (if br-invert-ancestors (setq arg (- arg)))
+  (let* ((class-list
+	  (if (and (/= arg 1) (/= arg -1))
+	      (br-this-level-classes)
+	    (list (br-find-class-name))))
+	 (parents (delq nil (mapcar (function
+				     (lambda (c) (br-get-parents c)))
+				    class-list))))
+    (cond ((or parents
+	       (and features-flag
+		    (if (/= 1 (length class-list))
+			t ;; Assume some class will have features.
+		      ;; This class must have features.
+		      (br-list-features (car class-list)))))
+	   (if (and (/= arg 1) (/= arg -1))
+	       (message "Computing %s..."
+			(if features-flag "features" "ancestors")))
+	   (if features-flag
+	       (progn
+		 (br-add-level-hist)
+		 (br-next-buffer))
+	     (let ((child-level (br-buffer-level)))
+	       (br-add-level-hist)
+	       (br-next-listing-window -1)
+	       (br-next-buffer (concat "p" child-level))))
+	   (let (buffer-read-only)
+	     (cond ((>= arg 0)
+		    (br-ancestor-trees-inverted class-list))
+		   (t
+		    (br-ancestor-trees class-list))))
+	   (goto-char (point-min))
+	   (if (and (/= arg 1) (/= arg -1))
+	       (message "Computing %s...Done"
+			(if features-flag "features" "ancestors")))
+	   t)
+	  (t
+	   (message "No %s." (if features-flag "features" "ancestors"))
+	   (beep)))))
+
+(defun br-at (&optional arg)
+  "Display current class location in the inheritance graph.
+The class is displayed among both its ancestors and descendants.
+With optional prefix ARG, display location for all classes in the current
+listing."
+  (interactive "P")
+  (let* ((parent)
+	 (parent-list
+	   (if arg
+	       (br-this-level-classes)
+	     (list (setq parent (br-find-class-name))))))
+    (if arg 
+	(message "Computing class locations...")
+      (br-narrow-to-class))
+    (br-add-level-hist)
+    (br-next-buffer)
+    (let (buffer-read-only)
+      (br-descendant-trees (br-ancestor-roots parent-list))
+      (goto-char (point-min))
+      (if arg
+	  (message "Computing class locations...Done")
+	(re-search-forward (concat "\\(^\\|[ \t]+\\)" parent "$"))
+	(goto-char (match-end 1))
+	(recenter '(4))))))
+
+(defun br-categories (&optional arg)
+  "Display categories directly associated with the current class.
+This does not include any categories which the class inherits.
+With optional prefix ARG, display categories of all classes in the current
+listing."
+  (interactive "P")
+  (let ((has-categories)
+	class-list categories class-and-categories)
+    (setq class-list (cond (arg
+			    (message "Computing class categories...")
+			    (br-this-level-classes))
+			   (t 
+			    (list (br-find-class-name))))
+	  categories
+	  (delq nil (mapcar
+		     (function
+		      (lambda (class)
+			(setq class-and-categories (br-list-categories class)
+			      has-categories (or has-categories
+						 class-and-categories))
+			(cons class class-and-categories)))
+		     class-list)))
+    (cond ((not class-list)
+	   (message "(OO-Browser):  Apply 'br-categories' to a class.") (beep))
+	  ((not has-categories)
+	   (message "No class categories.") (beep))
+	  (t
+	   (br-add-level-hist)
+	   (br-next-buffer nil)
+	   (let (buffer-read-only done-set class)
+	     (mapcar
+	      (function
+	       (lambda (class-and-categories)
+		 (setq class (car class-and-categories))
+		 (if (not (br-set-cons done-set class))
+		     (insert class " ...\n")
+		   ;; Class successfully added to set, so it has not been
+		   ;; listed before.
+		   (insert class "\n")
+		   (br-insert-features (cdr class-and-categories) 2))))
+	      categories))
+	   (message "Computing class categories...Done")
+	   (goto-char (point-min))
+	   t))))
+
+(defun br-children (&optional arg)
+  "Display children of current class.
+With optional prefix ARG, display children of all the classes in the current
+listing."
+  (interactive "P")
+  (let ((class-list (cond (arg
+			   (message "Computing children...")
+			   (br-this-level-classes))
+			  (t
+			   (list (br-find-class-name)))))
+	(has-children)
+	children children-list)
+    (setq children-list (delq nil (mapcar
+				   (function
+				    (lambda (parent)
+				      (setq children
+					    (br-get-children parent)
+					    has-children
+					    (or has-children children))
+				      (cons parent children)))
+				   class-list)))
+    (cond ((not children-list)
+	   (message "(OO-Browser):  Apply 'br-children' to a class.")
+	   (beep))
+	  ((not has-children)
+	   (message "No children.") (beep))
+	  (t
+	   (br-add-level-hist)
+	   (br-next-buffer nil)
+	   (let (buffer-read-only done-set parent)
+	     (mapcar
+	      (function
+	       (lambda (parent-children-cons)
+		 (setq parent (car parent-children-cons))
+		 (if (not (br-set-cons done-set parent))
+		     (insert parent " ...\n")
+		   ;; Class successfully added to set, so it has not been
+		   ;; listed before.
+		   (insert parent "\n")
+		   (br-insert-classes (cdr parent-children-cons) 2))))
+	      children-list))
+	   (if arg (message "Computing children...Done"))
+	   (goto-char (point-min))
+	   t))))
+
+(defun br-class-stats (&optional prompt)
+  "Display statistics summary for current class.
+Optional prefix arg PROMPT means prompt for class name."
+  (interactive "P")
+  (let ((class-name (if prompt (br-complete-class-name) (br-find-class-name))))
+    (if class-name
+	(message "Class %s:  Parents: %d; Children: %d"
+		 class-name (length (br-get-parents class-name))
+		 (length (br-get-children class-name)))
+      (error "No class name at point."))))
+
+(defun br-cmd-help (key &optional full)
+  "Show first line of doc for OO-Browser KEY in minibuffer.
+With optional FULL, display full documentation for command."
+  (interactive "kOO-Browser key binding: \nP")
+  (let* ((cmd (let ((cmd (if (eq major-mode 'br-mode)
+			     (lookup-key br-mode-map key)
+			   (key-binding key))))
+		(if (not (integerp cmd)) cmd)))
+	 (doc (and cmd (documentation cmd)))
+	 (end-line))
+    (if doc
+	(or full
+	    (setq end-line (string-match "[\n]" doc)
+		  doc (substitute-command-keys (substring doc 0 end-line))))
+      (setq doc (format "No documentation for {%s} %s" key (or cmd ""))))
+    (if (and cmd doc)
+	(if full
+	    (progn (br-to-view-window)
+		   (other-window -1)
+		   (describe-function cmd))
+	  (message doc)))))
+
+(defun br-count ()
+  "Count number of entries visible in current listing buffer.
+Print text result in minibuffer when called interactively."
+  (interactive)
+  (let ((cnt (count-lines (point-min) (point-max))))
+    (if (interactive-p)
+	(message "%s contains %d entries." (buffer-name) cnt)
+      cnt)))
+
+(defun br-copyright ()
+  "Display browser copyright information in viewer window."
+  (interactive)
+  (br-file-to-viewer "BR-COPY"))
+
+(defun br-delete (&optional prompt)
+  "Delete class from current Environment.
+Does not alter descendency relations.
+Optional prefix arg PROMPT means prompt for class name."
+  (interactive "P")
+  (let ((class (if prompt (br-complete-class-name) (br-find-class-name))))
+    (and class
+	 (if (interactive-p)
+	     (y-or-n-p (concat "Delete class " class " from Environment? "))
+	   t)
+	 (progn (br-real-delete-class class)
+		;; Delete class name at point in listing window
+		(or prompt (let (buffer-read-only)
+			     (progn (beginning-of-line)
+				    (delete-region
+				     (point) (progn (forward-line 1)
+						    (point))))))
+		(message "Class " class " deleted.")))))
+
+(defun br-descendants (&optional arg)
+  "Display descendant tree whose root is the current class.
+With optional prefix ARG, display all descendant trees whose roots are
+the classes in the current listing."
+  (interactive "P")
+  (let ((parent-list
+	 (if arg
+	     (br-this-level-classes)
+	   (list (br-find-class-name)))))
+    (cond ((delq nil (mapcar
+		      (function (lambda (parent)
+				  (br-get-children parent)))
+		      parent-list))
+	   (if arg (message "Computing descendants..."))
+	   (br-add-level-hist)
+	   (br-next-buffer)
+	   (let (buffer-read-only)
+	     (br-descendant-trees parent-list))
+	   (goto-char (point-min))
+	   (if arg (message "Computing descendants...Done"))
+	   t)
+	  (t
+	   (message "No descendants.") (beep)))))
+
+(defun br-edit-entry (&optional prompt)
+  "Edits source for any browser listing entry, such as a class or a feature.
+Optional prefix arg PROMPT means prompt for entry name."
+  (interactive "P")
+  (let ((entry) (sig))
+    (if prompt
+	(cond ((and (setq entry (br-complete-entry))
+		    (string-match br-feature-signature-regexp entry))
+	       (if (setq sig (car (br-feature-signature-and-file entry)))
+		   (br-feature nil nil sig)
+		 (error "(br-feature-signature-and-file): Couldn't find match for: '%s'" entry)))
+	      (entry  ;; class name
+		(br-edit nil entry))
+	      (t (error "(br-complete-entry): Exited without selecting a match")))
+      (cond ((br-find-feature-entry)
+	     (br-feature))
+	    ((and (setq entry (br-find-class-name))
+		  (br-class-in-table-p entry))
+	     (br-edit nil entry))
+	    (t (error "(OO-Browser): No entry for current line in current Environment"))))))
+
+(defun br-edit (&optional prompt class)
+  "Edit a class in the viewer window.
+Select viewer window.  With optional prefix arg PROMPT, prompt for class
+name.  Optional CLASS is the one to edit."
+  (interactive "P")
+  (or br-editor-cmd
+      (br-in-view-window-p)
+      (setq *br-prev-listing-window* (selected-window)))
+  (br-view prompt t class))
+
+(defun br-edit-ext (editor-cmd file)
+  "Invoke a non-standard EDITOR-CMD on FILE.
+See also 'br-editor-cmd'."
+  (interactive "fFile to edit: ")
+  (or editor-cmd (setq editor-cmd br-editor-cmd))
+  (if (not (stringp editor-cmd)) ;; must be a Lisp function that takes a
+      ;; single, file arg
+      (funcall editor-cmd file)
+    (setq delete-exited-processes t)
+    (let ((proc)
+	  (name (concat br-ed-name br-ed-num))
+	  )
+      (setq br-ed-num (1+ br-ed-num)
+	    proc (br-edit-ext-start editor-cmd name file))
+      (if proc
+	  (process-kill-without-query proc)
+	(beep)
+	(message "(OO-Browser):  Could not start external edit process: %s"
+		 editor-cmd)))))
+
+(defun br-editor-kill ()
+  "Kill all current external editor sub-processes."
+  (interactive)
+  (if (br-kill-process-group br-ed-name br-ed-num "external editors")
+      (setq br-ed-num 0)))
+
+(defun br-entry-info ()
+  "Display attributes of the current entry in the viewer window."
+  (interactive)
+  (if (fboundp 'br-insert-class-info)
+      (let ((class-name (br-find-class-name)))
+	(if class-name
+	    (progn
+	      (message "Building '%s' class info..." class-name)
+	      ; (sit-for 2)   ; Why should we pause here?
+	      (br-store-class-info class-name)
+	      (message "Building '%s' class info...Done" class-name)
+	      (br-funcall-in-view-window
+	       (concat br-buffer-prefix-info "Info")
+	       'br-insert-class-info))
+	  (error "Move point to a class name line.")))
+    (beep)
+    (message "No class information function for this language.")))
+
+(defun br-exit-level (arg)
+  "Return to prefix ARGth previous inheritance level listing.
+The command is ignored with ARG < 1."
+  (interactive "p")
+  (setq arg (or arg 1))
+  (let ((prev-wind-buf-line))
+    (if (null *br-level-hist*)
+	(and (> arg 0)
+	     (message "No previous level to which to exit.")
+	     (beep))
+      (while (and (> arg 0) *br-level-hist*)
+	(br-next-buffer (br-listing-window-num) br-buffer-prefix-blank)
+	(setq prev-wind-buf-line (car *br-level-hist*)
+	      *br-level-hist* (cdr *br-level-hist*)
+	      arg (1- arg))
+	(select-window (car prev-wind-buf-line))
+	(switch-to-buffer (car (cdr prev-wind-buf-line))))
+      (widen)
+      ;; Position window lines exactly as before.
+      (recenter (car (cdr (cdr prev-wind-buf-line)))))))
+
+(defun br-feature (&optional arg view-only ftr-sig)
+  "Edit a feature in the viewer window.  Select viewer window.
+With optional prefix ARG, prompt for feature name.
+Optional VIEW-ONLY non-nil means view rather than edit feature.
+Optional FTR-SIG is signature of feature to edit."
+  (interactive "P")
+  (or ftr-sig
+      (setq ftr-sig (if arg
+			(br-feature-complete 'must-match)
+		      ;; Get current feature signature
+		      (br-feature-get-signature))))
+  (if (null ftr-sig)
+      (error "(br-feature): No definition for this entry")
+    (br-to-view-window)
+    (if (br-feature-found-p (br-feature-file ftr-sig) ftr-sig)
+	(if view-only
+	    (progn (setq buffer-read-only t)
+		   (br-to-from-viewer))
+	  (if (file-writable-p (buffer-file-name))
+	      (setq buffer-read-only nil)))
+      ;; Feature not found.  Return to original window and signal an error.
+      (br-to-from-viewer)
+      (error "(br-feature): Can't find definition of: '%s'" ftr-sig))))
+
+(defun br-features (arg)
+  "Display features/elements of the current class (prefix ARG = 1) or of the current listing if ARG is other than 0 or 1.
+
+With ARG = 0, the value of the variable, 'br-inherited-features-flag', is
+toggled and no other action is taken.
+
+If 'br-inherited-features-flag' is t, all features of each class are shown.
+If nil, only lexically included features are shown and if the features of a
+single class are requested and none are defined, the class definition is
+displayed so that its feature declarations may be browsed."
+  (interactive "p")
+  (cond ((and (integerp arg) (= arg 0))
+	 (setq br-inherited-features-flag
+	       (not br-inherited-features-flag))
+	 (message "Inherited features/elements will %sbe shown."
+		  (if br-inherited-features-flag "" "not ")))
+	(br-inherited-features-flag
+	 (br-inherited-features arg))
+	(t (br-lexical-features arg))))
+
+(defun br-find (element)
+  "Interactively complete class or ELEMENT name and jump to its definition.
+Return ELEMENT or signal an error."
+  (interactive (list (br-complete-entry)))
+  (if (and element
+	   (progn
+	     (if (not (br-in-view-window-p)) (br-to-from-viewer))
+	     (if (string-match br-feature-signature-regexp element)
+		 (br-find-feature element)
+	       (br-find-class element))))
+      element
+    (error "(OO-Browser): '%s' definition not found." element)))
+
+(defun br-help (&optional file)
+  "Display browser operation help information in viewer window."
+  (interactive)
+  (or file (setq file "br-help"))
+  (br-file-to-viewer file)
+  (save-window-excursion
+    (br-to-view-window)
+    (br-mode)
+    (use-local-map nil))
+  (message ""))
+
+(defun br-help-ms ()
+  "Display browser mouse usage help information in viewer window."
+  (interactive)
+  (br-help "br-help-ms"))
+
+(defun br-implementors (&optional arg)
+  "Display hierarchy of classes that define current element.
+Ignore inherited elements.  With optional prefix ARG, display implementors of
+all elements in the current listing."
+  (interactive "P")
+  (let
+      ((child-level (br-buffer-level))
+       (ftr-list (if arg (br-set-of-strings
+			  (sort (br-this-level-features) 'string-lessp))
+		   ;; Need this check to avoid trying to find implementors of
+		   ;; a class which happens to have an attached element tag,
+		   ;; e.g. in an implementors listing buffer.
+		   (save-excursion
+		     (beginning-of-line)
+		     (skip-chars-forward " \t")
+		     (if (looking-at br-feature-entry)
+			 (list (br-find-feature-entry)))))))
+    (if (or (null ftr-list) (null (car ftr-list)))
+	(error
+	  "(OO-Browser):  'br-implementors' must be applied to a feature.")
+      (message "Computing implementors...")
+      (br-add-level-hist)
+      (br-next-listing-window -1)
+      (br-next-buffer (concat "p" child-level))
+      (let ((buffer-read-only) (implementor-tags) (classes)
+	    start)
+	(widen)
+	(erase-buffer)
+	(mapcar (function
+		  (lambda (ftr-entry)
+		    (setq implementor-tags
+			  (sort
+			   (br-feature-implementors
+			    (br-feature-name ftr-entry))
+			   'string-lessp)
+			  classes (mapcar 'br-feature-tag-class
+					  implementor-tags))
+		    (insert ftr-entry "\n")
+		    (setq start (point))
+		    (br-insert-classes classes 4)
+		    (save-excursion
+		      (goto-char start)
+		      (br-feature-put-signatures implementor-tags))))
+		ftr-list))
+      (goto-char 1)
+      (message "Computing implementors...Done"))))
+
+(defun br-inherited-features (arg)
+  "Display class features, including those from ancestors.
+With optional prefix ARG, display features of all classes in the current
+listing."
+  (interactive "p")
+  (let ((br-ancestor-function
+	 (function
+	  (lambda (class repeated-class indent)
+	    (if repeated-class
+		nil
+	      (br-insert-features (br-list-features class indent) indent))))))
+    (br-ancestors arg t)))
+
+(defun br-kill ()
+  "Kill buffer in viewer window and redisplay help text."
+  (interactive)
+  (br-do-in-view-window '(progn (kill-buffer nil) (br-help))))
+
+(defun br-lexical-features (arg)
+  "Display class features lexically defined within current class.
+With numeric prefix ARG, display features of all classes in the current
+listing.
+
+If the features of a single class are requested and there are no feature
+definitions for the class, display the class definition so that its feature
+declarations may be browsed."
+  (interactive "p")
+  (let ((has-features)
+	class-list features class-and-features)
+    (setq class-list (cond ((and (integerp arg) (/= arg 1))
+			    (message "Computing class features...")
+			    (br-this-level-classes))
+			   (t 
+			    (list (br-find-class-name))))
+	  features
+	  (delq nil (mapcar
+		     (function
+		      (lambda (class)
+			(setq class-and-features (br-list-features class)
+			      has-features (or has-features
+					       class-and-features))
+			(cons class class-and-features)))
+		     class-list)))
+    (cond ((not class-list)
+	   (beep)
+	   (message "(OO-Browser):  Apply 'br-features' to a class."))
+	  ((not has-features)
+	   (if (and (= (length class-list) 1)
+		    (br-class-path (car class-list)))
+	       (if (br-view nil nil (car class-list))
+		   (message
+		    "No feature definitions, browse declarations instead."))
+	     (message "No class features.") (beep)))
+	  (t
+	   (br-add-level-hist)
+	   (br-next-buffer nil)
+	   (let (buffer-read-only done-set class)
+	     (mapcar
+	      (function
+	       (lambda (class-and-features)
+		 (setq class (car class-and-features))
+		 (if (not (br-set-cons done-set class))
+		     (insert class " ...\n")
+		   ;; Class successfully added to set, so it has not been
+		   ;; listed before.
+		   (insert class "\n")
+		   (br-insert-features (cdr class-and-features) 2))))
+	      features)
+	     (message "Computing class features...Done")
+	     (goto-char (point-min)))))))
+
+(defun br-lib-rebuild ()
+  "Rescan Library components of the current Environment."
+  (interactive)
+  (if (call-interactively 'br-build-lib-htable)
+      (br-top-classes t)))
+
+(defun br-lib-top-classes (&optional arg)
+  "Display list of top level Library classes.
+With prefix ARG, display all Library classes."
+  (interactive "P")
+  (and (or (not (interactive-p))
+	   (br-in-top-buffer-p)
+	   (y-or-n-p "Exit to top-level class listing buffer? "))
+       (cond (arg
+	      (br-show-top-classes
+	       (function (lambda () (br-all-classes "lib")))
+	       'uniq)
+	      (message "Listing of all Library classes"))
+	     (t
+	      (br-show-top-classes 'br-get-lib-top-classes 'uniq)
+	      (message "Listing of top-level Library classes")))
+       (setq *br-level-hist* nil)))
+
+(defun br-match (&optional expr arg again matched)
+  "Show all class names in current Environment that contain optional EXPR.
+Nil value of EXPR means prompt for a value.  With optional prefix ARG, EXPR
+is treated as a string.  By default, it is treated as a regular expresion.
+AGAIN non-nil shows the number of classes MATCHED from the last search,
+allowing repeated narrowing of the search set.  Empty EXPR when AGAIN is nil
+matches to all classes in the Environment."
+  (interactive (list nil current-prefix-arg))
+  (or expr (setq expr (read-string
+		       (concat (if again (format "(%s matches)  " matched))
+			       (if arg
+				   "Find Environment class string matches"
+				 "Find Environment class regular expression matches")
+			       (if again " (RTN to end): " ": ")))))
+  (if (and again (equal expr ""))
+      nil
+    (let* ((match-expr (if arg (regexp-quote expr) expr))
+	   (classes
+	    (delq nil (mapcar
+		       (function
+			(lambda (cl)
+			  (if (string-match match-expr cl) cl)))
+		       (if again
+			   (sort (br-this-level-classes) 'string-lessp)
+			 (br-all-classes))))))
+      (setq classes (br-class-list-filter classes))
+      (if classes
+	  (progn (let (buffer-read-only)
+		   (br-feature-clear-signatures)
+		   (erase-buffer)
+		   (br-insert-classes classes 0))
+		 (goto-char (point-min))
+		 (br-match nil arg t (br-count)))
+	(beep)
+	(message "No matches for \"%s\"." expr)))))
+
+(defun br-match-entries (&optional expr arg again matched)
+  "Show all entries in current listing that contain optional EXPR.
+Nil value of EXPR means prompt for a value.  With optional prefix ARG, EXPR
+is treated as a string.  By default, it is treated as a regular expresion.
+AGAIN non-nil means show the number of entries MATCHED from last search,
+allowing repeated narrowing of the search set.  Empty EXPR when AGAIN is nil
+matches to all entries in the listing."
+  (interactive (list nil current-prefix-arg))
+  (or expr (setq expr (read-string
+			(concat (if again (format "(%s matches)  " matched))
+				(if arg
+				    "Find string matches in listing"
+				  "Find regular expression matches in listing")
+				(if again " (RTN to end): " ": ")))))
+  (if (and again (equal expr ""))
+      nil
+    (let* ((match-expr (if arg (regexp-quote expr) expr))
+	   (buffer-read-only))
+      (goto-char (point-min))
+      (if (not (re-search-forward match-expr nil t))
+	  (progn (beep)
+		 (message "No matches for \"%s\"." expr))
+	(goto-char (point-min))
+	(delete-non-matching-lines match-expr)
+	(goto-char (point-min))
+	(br-match-entries nil arg t (br-count))))))
+
+(defun br-next-entry (arg)
+  "Move point vertically down prefix ARG number of lines in listing buffer."
+  (interactive "p")
+  (let ((end))
+    (setq end (= (forward-line arg) arg))
+    (and (looking-at "^$") (forward-line -1) (setq end t))
+    (and end (message "No next entry.") (beep))))
+
+(defun br-order (arg)
+  "Order current browser listing window entries.
+With prefix ARG other than 1 (the default), don't remove leading space from
+entry lines before ordering.  Negative ARG means order in descending Ascii
+sequence, otherwise order in ascending sequence."
+  (interactive "p")
+  (setq arg (or arg 1))
+  (message "Ordering entries...")
+  (let ((buffer-read-only)
+	sort-args)
+    (and (= arg 1) (progn (goto-char (point-min))
+			  (while (re-search-forward "^[ \t]+" nil t)
+			    (replace-match ""))))
+    (if (string-match "^19\\." emacs-version)
+	(progn
+	  ;; Emacs 19: This slower than calling an external sort but it
+	  ;; maintains the element tags in a listing, allowing further browsing
+	  ;; from this buffer.
+	  (sort-lines (< arg 0) (point-min) (point-max))
+	  ;; Move [default] classes to the end of the sorted list.
+	  (goto-char (point-min))
+	  (if (re-search-forward "^[ \t]*\\[" nil t)
+	      (let (start end)
+		(beginning-of-line)
+		(setq start (point))
+		(goto-char (point-max))
+		(re-search-backward "^[ \t]*\\[" nil t)
+		(forward-line 1)
+		(setq end (point))
+		(goto-char (point-max))
+		(append-to-buffer (current-buffer) start end)
+		(delete-region start end))))
+      ;;
+      ;; Emacs 18: We can't maintain the buffer tags, so we just use a fast
+      ;; external sort.
+      (setq sort-args (list (point-min) (point-max) "sort" t t nil)
+	    sort-args (if (< arg 0)
+			  (if (stringp br-sort-options)
+			      (nconc sort-args (list "-r" br-sort-options))
+			    (nconc sort-args (list "-r")))
+			(if (stringp br-sort-options)
+			    (nconc sort-args (list br-sort-options))
+			  sort-args)))
+      (apply 'call-process-region sort-args)))
+  (goto-char (point-min))
+  (message "Ordering entries...Done"))
+
+(defun br-parents (&optional arg)
+  "Display parents of current class.
+With optional prefix ARG, display parents of all the classes in the current
+listing."
+  (interactive "P")
+  (let ((class-list (cond (arg
+			   (message "Computing parents...")
+			   (br-this-level-classes))
+			  (t
+			   (list (br-find-class-name)))))
+	(has-parents)
+	parents parents-list)
+    (setq parents-list
+	  (delq nil (mapcar (function
+			     (lambda (class)
+			       (setq parents (br-get-parents class)
+				     has-parents (or has-parents parents))
+			       (cons class parents)))
+			    class-list)))
+    (cond ((not parents-list)
+	   (message "(OO-Browser):  Apply 'br-parents' to a class.") (beep))
+	  ((not has-parents)
+	   (message "No parents.") (beep))
+	  (t
+	   (let ((child-level (br-buffer-level)))
+	     (br-add-level-hist)
+	     (br-next-listing-window -1)
+	     (br-next-buffer (concat "p" child-level)))
+	   (let (buffer-read-only done-set class)
+	     (mapcar
+	      (function
+	       (lambda (class-parents-cons)
+		 (setq class (car class-parents-cons))
+		 (if (not (br-set-cons done-set class))
+		     (insert class " ...\n")
+		   ;; Class successfully added to set, so it has not been
+		   ;; listed before.
+		   (insert class "\n")
+		   (br-insert-classes (cdr class-parents-cons) 2))))
+	      parents-list))
+	   (if arg (message "Computing parents...Done"))
+	   (goto-char (point-min))
+	   t))))
+
+(defun br-prev-entry (arg)
+  "Move point vertically up prefix ARG number of lines in listing buffer."
+  (interactive "p")
+  (setq arg (- arg))
+  (and (= (forward-line arg) arg)
+       (message "No previous entry.")
+       (beep)))
+
+(defun br-protocols (&optional arg)
+  "Display protocols to which the current class conforms.
+This does not include any protocols which the class inherits from its
+ancestors but it does include protocols which conform to other protocols.
+With optional prefix ARG, display protocols of all classes in the current
+listing."
+  (interactive "P")
+  (let ((has-protocols)
+	class-list protocols class-and-protocols)
+    (setq class-list (cond (arg
+			    (message "Computing class protocols...")
+			    (br-this-level-classes))
+			   (t 
+			    (list (br-find-class-name)))))
+    (if (and (= (length class-list) 1)
+	     (br-protocol-entry-p))
+	;; If on a protocol entry, display its definition.
+	(br-view-protocol (car class-list))
+      ;; Otherwise, list protocols for all elements of class-list.
+      (setq protocols
+	    (delq nil (mapcar
+		       (function
+			(lambda (class)
+			  (setq class-and-protocols (br-list-protocols class)
+				has-protocols (or has-protocols
+						  class-and-protocols))
+			  (cons class class-and-protocols)))
+		       class-list)))
+      (cond ((not class-list)
+	     (beep)
+	     (message "(OO-Browser):  Apply 'br-protocols' to a class."))
+	    ((not has-protocols)
+	     (message "No class protocols.") (beep))
+	    (t
+	     (br-add-level-hist)
+	     (br-next-buffer nil)
+	     (let (buffer-read-only done-set class)
+	       (mapcar
+		(function
+		 (lambda (class-and-protocols)
+		   (setq class (car class-and-protocols))
+		   (if (not (br-set-cons done-set class))
+		       (insert class " ...\n")
+		     ;; Class successfully added to set, so it has not been
+		     ;; listed before.
+		     (insert class "\n")
+		     (br-insert-features (cdr class-and-protocols) 2))))
+		protocols))
+	     (message "Computing class protocols...Done")
+	     (goto-char (point-min)))))))
+
+(defun br-quit (&optional arg)
+  "Quit browser.
+With optional prefix ARG, delete window configurations and listing
+buffers associated with the browser."
+  (interactive "P")
+  (if (not (br-in-browser))
+      (br-interrupt arg)
+    (if (null arg)
+	(setq *br-save-wconfig* (current-window-configuration))
+      (if (featurep 'br-tree) (br-tree-kill))
+      (br-viewer-kill)
+      ;; Too dangerous to include (br-editor-kill) here.
+      ;; The user can invoke it manually if desired.
+      )
+    (and *br-prev-wconfig* (set-window-configuration *br-prev-wconfig*))
+    (br-interrupt arg)))
+
+(defun br-refresh ()
+  "Restore OO-Browser to its state upon startup."
+  (interactive)
+  (br-window-setup)
+  (br-top-classes t)
+  (br-help)
+  (setq br-in-browser (selected-frame)))
+
+(defun br-report-bug ()
+  "Send a message to the OO-Browser discussion list."
+  (interactive)
+  (if (br-in-browser) (br-to-view-window))
+  (hmail:compose "oo-browser@hub.ucsb.edu" '(hypb:configuration)))
+
+(defun br-sys-rebuild ()
+  "Rescan System components of the current Environment."
+  (interactive)
+  (if (call-interactively 'br-build-sys-htable)
+      (br-top-classes t)))
+
+(defun br-sys-top-classes (&optional arg)
+  "Display list of top level System classes.
+With prefix ARG, display all System classes."
+  (interactive "P")
+  (and (or (not (interactive-p))
+	   (br-in-top-buffer-p)
+	   (y-or-n-p "Exit to top-level class listing buffer? "))
+       (cond (arg
+	      (br-show-top-classes
+	       (function (lambda () (br-all-classes "sys")))
+	       'uniq)
+	      (message "Listing of all System classes"))
+	     (t
+	      (br-show-top-classes 'br-get-sys-top-classes 'uniq)
+	      (message "Listing of top-level System classes")))
+       (setq *br-level-hist* nil)))
+
+;;;###autoload
+(defun br-to-from-viewer ()
+  "Move point to viewer window or back to last recorded listing window."
+  (interactive)
+  (if (br-in-view-window-p)
+      (progn (if *br-prev-listing-window*
+		 (select-window *br-prev-listing-window*)
+	       (other-window 1))
+	     (setq *br-prev-listing-window* nil))
+    (br-to-view-window)))
+
+(defun br-toggle-c-tags ()
+  "Toggle the value of the 'br-c-tags-flag' flag."
+  (interactive)
+  (setq br-c-tags-flag (not br-c-tags-flag))
+  (message "C constructs will %sbe added to C-based language Environments."
+	   (if br-c-tags-flag "" "not ")))
+
+(defun br-toggle-keep-viewed ()
+  "Toggle the value of the 'br-keep-viewed-classes' flag."
+  (interactive)
+  (setq br-keep-viewed-classes (not br-keep-viewed-classes))
+  (message "Viewed classes will no%s be kept after use."
+	   (if br-keep-viewed-classes "w" "t")))
+
+(defun br-top-classes (&optional arg)
+  "Display list of top level classes.
+With prefix ARG, display all Environment classes."
+  (interactive "P")
+  (and (or (not (interactive-p))
+	   (br-in-top-buffer-p)
+	   (y-or-n-p "Exit to top-level class listing buffer? "))
+       (cond (arg
+	      (br-show-top-classes 'br-all-classes 'uniq)
+	      (message "Listing of all Environment classes"))
+	     (t
+	      (br-show-top-classes 'br-get-top-classes 'uniq)
+	      (message "Listing of top-level classes")))
+       (setq *br-level-hist* nil)))
+
+(defun br-unique ()
+  "Eliminate adjacent duplicate entry names from the current listing window.
+If two adjacent entries look the same one is eliminated, even if they refer
+to different class elements."
+  (interactive)
+  (let ((buffer-read-only)
+	(again t)
+	first second)
+    (goto-char (point-min))
+    (setq first (br-feature-current))
+    (while again
+      (setq again (= (forward-line 1) 0)
+	    second (br-feature-current))
+      (if (not (string-equal first second))
+	  (setq first second)
+	(beginning-of-line)
+	(delete-region (point) (progn (forward-line 1) (point)))
+	;; back up to first line again
+	(forward-line -1)))
+    (goto-char (point-min))))
+
+(defun br-version ()
+  "Display browser version number and credits."
+  (interactive)
+  (br-file-to-viewer "BR-VERSION")
+  (br-funcall-in-view-window
+   (concat br-buffer-prefix-info "Help")
+   (function (lambda ()
+	       (if (re-search-forward "<VERSION>" nil t)
+		   (replace-match br-version t t))
+	       (center-line)
+	       (set-buffer-modified-p nil)))
+   t))
+
+(defun br-view-entry (&optional prompt)
+  "Displays source for any browser listing entry.
+Optional prefix arg PROMPT means prompt for entry name."
+  (interactive "P")
+  (let ((entry) (sig))
+    (if prompt
+	(cond ((and (setq entry (br-complete-entry))
+		    (string-match br-feature-signature-regexp entry))
+	       (if (setq sig (car (br-feature-signature-and-file entry)))
+		   (br-feature nil 'view sig)
+		 (error "(br-feature-signature-and-file): Couldn't find match for: '%s'" entry)))
+	      (entry ;; class name
+	       (br-view nil nil entry))
+	      (t (error "(br-complete-entry): Exited without selecting a match")))
+      (cond ((br-find-feature-entry)
+	     (br-feature nil 'view))
+	    ((and (setq entry (br-find-class-name))
+		  (br-class-in-table-p entry))
+	     (br-view nil nil entry))
+	    (t (error "(OO-Browser): Entry may be referenced but not defined in the Environment."))))))
+
+(defun br-view (&optional prompt writable class)
+  "Displays class file in viewer window.
+Optional prefix arg PROMPT means prompt for class name.  Non-nil WRITABLE means
+allow editing, otherwise display in read-only mode.  Non-nil CLASS is class to
+display.
+
+Return t if class is displayed or sent to an external viewer, else nil."
+  (interactive "P")
+  (or class (setq class (if prompt (br-complete-class-name)
+			  (br-find-class-name))))
+  (cond ((null class)
+	 (beep)
+	 (message "(OO-Browser):  Select a class to view.")
+	 nil)
+	((not (br-class-defined-p class)) nil)
+	((and hyperb:window-system
+	      (cond ((and br-editor-cmd writable)
+		    (br-edit-ext br-editor-cmd (br-class-path class))
+		    t)
+		   (br-viewer-cmd
+		    (br-view-ext br-viewer-cmd (br-class-path class))
+		    t))))
+	;; Support custom Lisp-based edit/view cmds on any display type
+	((and br-editor-cmd writable (not (stringp br-editor-cmd)))
+	 (br-edit-ext br-editor-cmd (br-class-path class))
+	 t)
+	((and br-viewer-cmd (not (stringp br-viewer-cmd)))
+	 (br-view-ext br-viewer-cmd (br-class-path class))
+	 t)
+	(t (let ((owind (selected-window)))
+	     (unwind-protect
+		 (progn (br-to-view-window)
+			(if (and (not br-keep-viewed-classes) buffer-read-only
+				 (null (buffer-modified-p)))
+			    (kill-buffer (current-buffer)))
+			(if (br-find-class class (not writable))
+			    (progn (br-major-mode)
+				   (if writable
+				       (if (file-writable-p (buffer-file-name))
+					   (setq buffer-read-only nil))
+				     (setq buffer-read-only t)
+				     (select-window owind))
+				   t)))
+	       (or writable (select-window owind)))))))
+
+(defun br-view-ext (viewer-cmd file)
+  "Invoke a non-standard VIEWER-CMD on FILE.
+See also 'br-viewer-cmd'."
+  (interactive "fFile to view: ")
+  (or viewer-cmd (setq viewer-cmd br-viewer-cmd))
+  (if (not (stringp viewer-cmd)) ;; must be a Lisp function that takes a
+      ;; single, file arg
+      (funcall viewer-cmd file)
+    (setq delete-exited-processes t)
+    (let ((proc)
+	  (name (concat br-vw-name br-vw-num))
+	  )
+      (setq br-vw-num (1+ br-vw-num)
+	    proc (br-view-ext-start viewer-cmd name file))
+      (if proc
+	  (process-kill-without-query proc)
+	(beep)
+	(message "(OO-Browser):  Could not start external view process: %s"
+		  viewer-cmd)))))
+
+(defun br-view-full-frame ()
+  "Delete all windows in the selected frame except for the viewer window."
+  (interactive)
+  (setq *br-save-wconfig* (current-window-configuration))
+  (br-to-view-window)
+  (let ((buf (current-buffer)))
+    (br-interrupt)
+    (delete-other-windows)
+    (switch-to-buffer buf))
+  (let* ((cmd (concat br-lang-prefix "browse"))
+	 (key (car (where-is-internal (intern-soft cmd)))))
+    (message "Recall OO-Browser with: {%s}"
+	     (if key
+		 (key-description key)
+	       (concat (key-description
+			(or (car (where-is-internal
+				  'execute-extended-command))
+			    "\M-x"))
+		       " " cmd)))))
+
+(defun br-viewer-kill ()
+  "Kill all current external viewer sub-processes."
+  (interactive)
+  (if (br-kill-process-group br-vw-name br-vw-num "external viewers")
+      (setq br-vw-num 0)))
+
+(defun br-viewer-scroll-down (&optional arg)
+  "Scroll viewer window downward ARG lines or a windowful if no ARG."
+  (interactive "P")
+  (let ((owind (selected-window)))
+    (unwind-protect
+	(progn (br-to-view-window)
+	       (scroll-down arg))
+      (select-window owind))))
+
+(defun br-viewer-scroll-up (&optional arg)
+  "Scroll viewer window upward ARG lines or a windowful if no ARG."
+  (interactive "P")
+  (let ((owind (selected-window)))
+    (unwind-protect
+	(progn (br-to-view-window)
+	       (scroll-up arg))
+      (select-window owind))))
+
+(defun br-where (&optional prompt)
+  "Display in minibuffer and return full path of a browser listing entry.
+Optional prefix arg PROMPT means prompt for entry name."
+  (interactive "P")
+  (let ((entry) (path))
+    (if prompt
+	(cond ((and (setq entry (br-complete-entry))
+		    (string-match br-feature-signature-regexp entry))
+	       (setq path (cdr (br-feature-signature-and-file entry))))
+	      (entry ;; class name
+	       (setq path (br-class-defined-p entry)))
+	      (t (error "(br-complete-entry): Exited without selecting a match")))
+      (cond ((setq entry (br-find-feature-entry))
+	     (setq path (cdr (br-feature-signature-and-file entry))))
+	    ((setq entry (br-find-class-name))
+	     (or (setq path (br-class-path entry))
+		 (error "(OO-Browser): No path for this class in current Environment")))
+	    (t (error "(OO-Browser): No entry for current line in current Environment"))))
+    (and path (message (concat entry ":  " "\"" path "\""))
+	 path)))
+
+(defun br-write-buffer (file)
+  "Write narrowed portion of current browser buffer to a file."
+  (interactive "FFile to write buffer to: ")
+  (write-region (point-min) (point-max) file))
+
+;;; ************************************************************************
+;;; Private functions
+;;; ************************************************************************
+
+(defun br-add-level-hist ()
+  ;; Even though this next line looks useless, it cures a problem with
+  ;; window buffer correspondences when the OO-Browser is started, so don't
+  ;; remove it.
+  (set-buffer (window-buffer (selected-window)))
+  (setq *br-level-hist*
+	(cons (list (selected-window) (buffer-name) (br-wind-line-at-point))
+	      *br-level-hist*)))
+
+(defun br-ancestor-roots (class-list)
+  "Return list of CLASS-LIST's unique ancestors which do not inherit from any other class.
+This list may include elements from CLASS-LIST itself."
+  (let ((rtn) (parents) func)
+    (setq func (function
+		(lambda (class-list)
+		  (mapcar
+		   (function
+		    (lambda (class)
+		      (if (not (setq parents (br-get-parents class)))
+			  (setq rtn (cons class rtn))
+			(funcall func parents))))
+		   class-list))))
+    (funcall func class-list)
+    (br-set-of-strings (sort rtn 'string-lessp))))
+
+(defun br-ancestor-trees-inverted (class-list &optional depth offset)
+  "Insert ancestor trees starting with classes from CLASS-LIST.
+Ancestor trees are inverted, i.e. parents appear below children, not above.
+Indent each class in CLASS-LIST by optional DEPTH spaces (default is 0 in
+order to ensure proper initialization).  Offset each child level by optional
+OFFSET spaces from its parent (which must be greater than zero, default 2)."
+  (or offset (setq offset 2))
+  (or depth (setq depth 0))
+  (if (= depth 0) (setq br-tmp-class-set nil))
+  (let ((prev-expansion-str " ...")
+	parents expand-subtree)
+    (mapcar
+      (function
+	(lambda (class)
+	  (setq expand-subtree (br-set-cons br-tmp-class-set class)
+		parents (if expand-subtree (br-get-parents class)))
+	  (indent-to depth)
+	  (insert class)
+	  (and (not expand-subtree) (br-has-children-p class)
+	       (insert prev-expansion-str))
+	  (insert "\n")
+	  (if br-ancestor-function
+	      (funcall br-ancestor-function
+		       class (not expand-subtree) (+ depth offset)))
+	  (if parents
+	      (br-ancestor-trees-inverted parents (+ depth offset) offset))))
+      class-list))
+  (if (= depth 0) (setq br-tmp-class-set nil)))
+
+(defun br-ancestor-trees (class-list &optional depth offset)
+  "Insert ancestor trees starting with classes from CLASS-LIST.
+Ancestor trees are not inverted, parents appear above children as in other
+browser listing windows.  Indent each class in CLASS-LIST by optional DEPTH
+spaces (default is 0 in order to ensure proper initialization).  Offset each
+child level by optional OFFSET spaces from its parent (which must be greater
+than zero, default 2)."
+  (or offset (setq offset 2))
+  (or depth (setq depth 0 br-tmp-depth 0))
+  (if (= depth 0) (setq br-tmp-class-set nil))
+  (let ((prev-expansion-str " ...")
+	parents expand-subtree)
+    (mapcar (function
+	      (lambda (class)
+		(setq expand-subtree (br-set-cons br-tmp-class-set class)
+		      parents (if expand-subtree (br-get-parents class)))
+		(if parents
+		    (progn (setq br-tmp-depth
+				 (max (+ depth offset) br-tmp-depth))
+			   (br-ancestor-trees
+			    parents (+ depth offset) offset)))
+		(indent-to (- br-tmp-depth depth))
+		(insert class)
+		(and (not expand-subtree) (br-has-parents-p class)
+		     (insert prev-expansion-str))
+		(insert "\n")
+		(if br-ancestor-function
+		    (funcall br-ancestor-function
+			     class (not expand-subtree) (+ depth offset)))
+		(if (= depth 0) (setq br-tmp-depth 0))))
+	    class-list))
+  (if (= depth 0) (setq br-tmp-class-set nil)))
+
+(defun br-browser-buffer-p (&optional buffer)
+  "Returns t iff optional BUFFER or current buffer is an OO-Browser specific buffer."
+  (equal 0 (string-match (concat br-buffer-prefix-inher
+				 "\\|" br-buffer-prefix-categ
+				 "\\|" br-buffer-prefix-blank
+				 "\\|" br-buffer-prefix-info)
+			 (buffer-name buffer))))
+
+(defun br-buffer-level ()
+  "Returns current listing buffer level as a string."
+  (let* ((name (buffer-name))
+	 (pos (string-match "-[p]*[0-9]+$" name)))
+    (and pos (substring name (1+ pos)))))
+
+(defun br-class-level ()
+  "Returns current class hierarchy level as an integer.
+1 is the top level."
+  (let* ((name (buffer-name))
+	 (pos (string-match "[0-9]" name)))
+    (and pos (string-to-int (substring name pos)))))
+
+(defun br-listing-window-num ()
+  "Return listing window number, lefmost is 1, non-listing window = 0."
+  (let ((wind (selected-window))
+	(ctr 0))
+    (br-to-view-window)
+    (while (not (eq wind (selected-window)))
+      (other-window 1)
+      (setq ctr (1+ ctr)))
+    ctr))
+
+(defun br-cleanup ()
+  "Cleanup and free browser Environment data structures."
+  (setq br-lang-prefix nil
+	br-sys-paths-htable nil
+	br-lib-paths-htable nil
+	br-paths-htable nil
+	br-sys-parents-htable nil
+	br-lib-parents-htable nil
+	br-parents-htable nil
+	br-children-htable nil
+	br-lib-prev-search-dirs nil
+	br-sys-prev-search-dirs nil
+	))
+
+(defun br-clear ()
+  "Re-initialize all browser listing buffer displays.
+Leave point in browser top-level class listing buffer."
+  (let ((n (max 1 (/ (frame-width) br-min-width-window))))
+    (br-to-view-window)
+    (other-window 1)
+    (br-next-buffer 1)
+    (while (> n 1)
+      (setq n (1- n))
+      (br-next-buffer nil br-buffer-prefix-blank))
+    (br-to-view-window)
+    (other-window 1)))
+
+(defun br-descendant-trees (class-list &optional indent offset)
+  "Insert descendant trees starting with classes from CLASS-LIST.
+Indent each class in CLASS-LIST by optional INDENT spaces (default is 0 in
+order to ensure proper initialization).  Offset each child level by optional
+OFFSET spaces from its parent (which must be greater than zero, default 2)."
+  (or indent (setq indent 0))
+  (or offset (setq offset 2))
+  (if (= indent 0) (setq br-tmp-class-set nil))
+  (let ((prev-expansion-str " ...")
+	children expand-subtree)
+    (mapcar (function
+	      (lambda (class)
+		(setq expand-subtree (br-set-cons br-tmp-class-set class)
+		      children (if expand-subtree (br-get-children class)))
+		(indent-to indent)
+		(insert class)
+		(and (not expand-subtree) (br-has-children-p class)
+		     (insert prev-expansion-str))
+		(insert "\n")
+		(if children
+		    (br-descendant-trees children (+ indent offset) offset))))
+	    class-list))
+  (if (= indent 0) (setq br-tmp-class-set nil)))
+
+(defun br-display-buffer (suffix)
+  "Displays browser buffer ending in SUFFIX in current window."
+  (let ((buf (get-buffer (concat br-buffer-prefix suffix))))
+    (if buf (progn (set-window-buffer (selected-window) buf)))
+    buf))
+
+(defun br-do-in-view-window (form)
+  "Evaluate FORM in viewer window and then return to current window."
+  (interactive)
+  (let ((wind (selected-window)))
+    (unwind-protect
+	(progn (br-to-view-window)
+	       (eval form))
+      (select-window wind))))
+
+(defun br-edit-ext-start (editor-cmd name file)
+  "Start an external viewer given by EDITOR-CMD using NAME applied to FILE."
+  ;; Conditionalized code is necessary because of silly (start-process) calling
+  ;; protocol.
+  (cond (br-ed9
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			br-ed5 br-ed6 br-ed7 br-ed8 br-ed9 file))
+	(br-ed8
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			br-ed5 br-ed6 br-ed7 br-ed8 file))
+	(br-ed7
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			br-ed5 br-ed6 br-ed7 file))
+	(br-ed6
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			br-ed5 br-ed6 file))
+	(br-ed5
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			br-ed5 file))
+	(br-ed4
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 br-ed4
+			file))
+	(br-ed3
+	 (start-process name name editor-cmd br-ed1 br-ed2 br-ed3 file))
+	(br-ed2
+	 (start-process name name editor-cmd br-ed1 br-ed2 file))
+	(br-ed1
+	 (start-process name name editor-cmd br-ed1 file))
+	(t
+	 (start-process name name editor-cmd file))
+	))
+
+(defun br-funcall-in-view-window (buffer function &optional no-erase)
+  "Clear out BUFFER and display return value from invocation of FUNCTION in viewer window.
+Move point to beginning of buffer and then return to current window.  BUFFER
+may be a buffer name.
+With optional NO-ERASE, buffer is not erased before function is called."
+  (interactive)
+  (let ((wind (selected-window)))
+    (unwind-protect
+	(progn (br-to-view-window)
+	       (set-window-buffer (selected-window) (get-buffer-create buffer))
+	       (let (buffer-read-only)
+		 (if no-erase
+		     (goto-char (point-min))
+		   (erase-buffer))
+		 (funcall function))
+	       (goto-char (point-min)))
+      (select-window wind))))
+
+(defun br-file-to-viewer (filename)
+  "Display FILENAME from OO-Browser source directory in browser viewer window.
+FILENAME should not contain any path information."
+  (br-funcall-in-view-window
+   (concat br-buffer-prefix-info "Help")
+   (function (lambda ()
+	       (insert-file-contents (br-pathname filename))
+	       (set-buffer-modified-p nil)))))
+
+(defun br-in-browser ()
+  "Return selected frame if the OO-Browser is active in it, else return nil."
+  (cond ((not (eq br-in-browser (selected-frame))) nil)
+	((one-window-p 'nomini)
+	 (setq br-in-browser nil))
+	(t br-in-browser)))
+
+
+(defun br-in-top-buffer-p ()
+  "Return t if point is in the top class listing buffer, else nil."
+  (string-equal (br-buffer-level) "1"))
+
+(defun br-in-view-window-p ()
+  "Is point in a viewer window?"
+  (br-non-listing-window-p))
+
+(defun br-init ()
+  "Initialization common to all OO-Browser invocations."
+  (br-feature-tags-init))
+
+(defun br-insert-classes (class-list &optional indent)
+  "Insert CLASS-LIST in current buffer indented INDENT columns."
+  (mapcar (function
+	    (lambda (class-name)
+	      (and indent (indent-to indent))
+	      (and class-name (insert class-name "\n"))))
+	  class-list))
+
+(defun br-interrupt (&optional arg)
+  (if (null arg)
+      (mapcar
+       (function
+	(lambda (buf)
+	  (set-buffer buf)
+	  (if (or (eq major-mode 'br-mode) (br-browser-buffer-p))
+	      (bury-buffer nil))))
+       (buffer-list))
+    (setq *br-save-wconfig* nil
+	  *br-prev-wconfig* nil
+	  *br-prev-listing-window* nil)
+    (mapcar
+     (function
+      (lambda (buf)
+	(set-buffer buf)
+	(if (or (eq major-mode 'br-mode)
+		(br-browser-buffer-p))
+	    (progn (br-feature-clear-signatures)
+		   (set-buffer-modified-p nil)
+		   (kill-buffer (current-buffer))))))
+     (buffer-list))
+    (br-cleanup))
+  (setq br-in-browser nil))
+
+(defun br-mode ()
+  "The major mode used by OO-Browser listing windows.
+See the file \"br-help\" for browser usage information.
+It provides the following keys: \\{br-mode-map}"
+  (interactive)
+  (use-local-map br-mode-map)
+  (setq major-mode 'br-mode)
+  (setq mode-name "OO-Browse")
+  (set-syntax-table text-mode-syntax-table)
+  (setq local-abbrev-table text-mode-abbrev-table)
+  (setq case-fold-search t)
+  (setq buffer-read-only t)
+  (run-hooks 'br-class-list-hook)
+  (run-hooks 'br-mode-hook))
+
+(defun br-narrow-to-class ()
+  (cond ((= (point-min) (point-max)) nil)
+	((br-find-class-name)
+	 (narrow-to-region (match-beginning 0) (match-end 0)))
+	(t (error
+	    "(OO-Browser):  'br-narrow-to-class', current entry is not a class"))))
+
+(defun br-narrow-to-feature ()
+  "Narrow buffer to current feature entry."
+  (if (br-feature-at-p)
+      (narrow-to-region (match-beginning 0) (match-end 0))
+    (error
+     "(OO-Browser):  'br-narrow-to-feature' no current feature.")))
+
+(defun br-feature-at-p ()
+  "Returns t iff point is on a feature listing line."
+  (save-excursion
+    (beginning-of-line)
+    (looking-at (concat "[ \t]*" br-feature-entry))))
+
+(defun br-next-buffer (&optional special alt-prefix)
+  "Returns next sequential browser buffer or special one if optional SPECIAL is non-nil.
+Non-nil ALT-PREFIX is used as prefix in buffer name."
+  (let* ((suffix (or special (1+ (or (br-class-level) 0))))
+	 (buf (get-buffer-create
+	       (concat (or alt-prefix br-buffer-prefix)
+		      (if (integerp suffix)
+			  (int-to-string suffix)
+			suffix)))))
+    (if buf (progn
+	      (or special (br-next-listing-window))
+	      (set-window-buffer (selected-window) buf)
+	      (let (buffer-read-only)
+		(erase-buffer)
+		(kill-all-local-variables)
+		;; Clear out any feature tags that may have been associated
+		;; with this buffer, so we don't mistakenly reference them.
+		(br-feature-clear-signatures))
+	      (setq mode-line-format (list "  %17b --" '(-3 . "%p") "-%-"))
+	      (br-mode)
+	      (br-set-mode-line)
+	      (set-buffer-modified-p nil)))
+    buf))
+
+(defun br-next-listing-window (&optional prev)
+  "Move to next browser listing window (non-viewer window).
+Optional PREV means to previous window."
+  (let ((owind (selected-window)))
+    (while (progn (other-window (if prev -1 1))
+		  (if (br-non-listing-window-p)
+		      (not (eq (selected-window) owind)))))))
+
+(defun br-pathname (filename)
+  "Return full pathname for FILENAME in browser Elisp directory."
+  (if br-directory
+      (expand-file-name filename br-directory)
+    (error "The 'br-directory' variable must be set to a string value.")))
+
+(defun br-protocol-entry-p ()
+  "Return non-nil if point is within a protocol listing entry line."
+  (and (string-equal br-lang-prefix "objc-")
+       (save-excursion
+	 (beginning-of-line)
+	 (looking-at "[ \t]*@ <[^\>]*>"))))
+
+(defun br-resize (min-width)
+  "Resize browser listing windows to have MIN-WIDTH."
+  (interactive)
+  (let* ((window-min-width 3)
+	 (oldn (1- (length (br-window-list))))
+	 (n (max 1 (/ (frame-width) min-width)))
+	 (numw n)
+	 (diff (- numw oldn))
+	 (width (/ (frame-width) numw))
+	 (obuf (current-buffer)))
+    (br-to-first-list-window)
+    (cond ((= diff 0)
+	   (br-resize-windows numw width))
+	  ((> diff 0)
+	   (setq n oldn)
+	   (while (> n 1)
+	     (setq n (1- n))
+	     (shrink-window-horizontally (max 0 (- (window-width)
+						   min-width)))
+	     (br-next-listing-window))
+	   (setq n diff)
+	   (while (> n 0)
+	     (setq n (1- n))
+	     (split-window-horizontally (max window-min-width
+					     (- (window-width)
+						min-width)))) 
+	   (setq n oldn)
+	   (while (< n numw)
+	     (setq n (1+ n))
+	     (br-next-listing-window)
+	     (br-next-buffer n br-buffer-prefix-blank))
+	   (br-to-first-list-window)
+	   (br-resize-windows numw width)
+	   )
+	  (t  ;; (< diff 0)
+	   (while (> n 0)
+	     (setq n (1- n))
+	     (br-next-listing-window))
+	   (setq n (- diff))
+	   (while (> n 0)
+	     (setq n (1- n))
+	     (delete-window))
+	   (br-to-first-list-window)
+	   (br-resize-windows numw width)
+	   ))
+    (setq br-min-width-window min-width)
+    (let ((owind (get-buffer-window obuf)))
+      (if owind
+	  (select-window owind)
+	(br-to-view-window)
+	(br-next-listing-window)))))
+
+(defun br-resize-narrow ()
+  "Resize listing windows so are narrower by 10 characters."
+  (interactive)
+  (if (<= window-min-width (- br-min-width-window 10))
+      (br-resize (max window-min-width (- br-min-width-window 10)))
+    (beep)))
+
+(defun br-resize-widen ()
+  "Resize listing windows so are wider by 10 characters."
+  (interactive)
+  (if (and (>= (frame-width) (+ br-min-width-window 10))
+	   (> (length (br-window-list)) 2))
+      (br-resize (min (frame-width) (+ br-min-width-window 10)))
+    (beep)))
+
+(defun br-resize-windows (n width)
+  (while (> n 1)
+    (setq n (1- n))
+    (shrink-window-horizontally (- (window-width) width))
+    (br-next-listing-window)))
+
+(defun br-set-mode-line ()
+  "Set mode line string."
+  (setq mode-line-buffer-identification (list (buffer-name)))
+  (set-buffer-modified-p t))
+
+(defun br-show-top-classes (func &optional uniq)
+  "Display list of top level classes generated by calling FUNC.
+Optional UNIQ means sort and eliminate duplicates."
+  (message "Ordering classes...")
+  (let ((classes (funcall func)))
+    (setq classes (br-class-list-filter classes))
+    (br-clear)
+    (let (buffer-read-only)
+      (erase-buffer)
+      (br-insert-classes classes)
+      (if uniq
+	  (progn
+	    (if (stringp br-sort-options)
+		(call-process-region (point-min) (point-max) "sort" t t nil
+				     br-sort-options)
+	      (call-process-region (point-min) (point-max) "sort" t t nil))
+	    (if (and (stringp br-sort-options)
+		     (string-match "u" br-sort-options))
+		;; Then sort made the list of elements unique, so do nothing.
+		nil
+	      (call-process-region (point-min) (point-max) "uniq" t t))))))
+  (goto-char (point-min))
+  (message "Ordering classes...Done"))
+
+(defun br-this-level-classes (&optional keep-indent)
+  "Return list of the classes in the current listing.
+Optional KEEP-INDENT non-nil means keep indentation preceding class name."
+  (let ((classes))
+    (save-excursion
+      (goto-char (point-min))
+      (while (and (not (looking-at "^[ \t]*$"))
+		  (if (looking-at (format "^[ \t]*%s "
+					  br-feature-type-regexp)) ;; a feature
+		      t ;; skip this entry
+		    ;; assume is a class
+		    (setq classes (cons (br-find-class-name keep-indent)
+					classes)))
+		  (= (forward-line 1) 0))))
+    (nreverse (delq nil classes))))
+
+(defun br-this-level-entries ()
+  "Return list of all entries in the current listing."
+  (let ((entries))
+    (save-excursion
+      (goto-char (point-min))
+      (while (and (not (looking-at "^[ \t]*$"))
+		  (if (looking-at (format "^[ \t]*%s "
+					  br-feature-type-regexp)) ;; a feature
+		      (setq entries
+			    (cons (br-find-feature-entry) entries))
+		    ;; assume is a class
+		    (setq entries (cons (br-find-class-name) entries)))
+		  (= (forward-line 1) 0))))
+    (nreverse (delq nil entries))))
+
+(defun br-this-level-features ()
+  "Return list of features in the current listing."
+  (let ((feature-regexp (concat "[ \t]*" br-feature-entry))
+	(features))
+    (save-excursion
+      (goto-char (point-min))
+      (while (progn (if (looking-at feature-regexp)
+			(setq features
+			      (cons (br-find-feature-entry) features)))
+		    (= (forward-line 1) 0))))
+    (nreverse (delq nil features))))
+
+(defun br-to-first-list-window ()
+  (br-to-view-window)
+  (br-next-listing-window))
+
+(defun br-to-tree ()
+  "If point is within ... move to inher/ancestry expansion for the current class."
+  (if (save-excursion
+	(skip-chars-backward ".")
+	(looking-at "\\.\\.\\."))
+      (progn (beginning-of-line)
+	     (let ((class-expr (concat "^[ \t]*"
+				       (br-find-class-name)
+				       "$")))
+	       (if (re-search-backward class-expr nil t)
+		   (progn (skip-chars-forward " \t")
+			  (recenter '(4))
+			  t))))))
+
+(defun br-to-view-window ()
+  "Move to viewer window."
+  (if (br-in-view-window-p)
+      nil
+    (setq *br-prev-listing-window* (selected-window))
+    (while (and (not (br-in-view-window-p))
+		(progn (other-window 1)
+		       (not (eq (selected-window)
+				*br-prev-listing-window*)))))))
+
+(defun br-window-setup ()
+  (and (fboundp 'modify-frame-parameters)
+       (cdr (assq 'unsplittable (frame-parameters)))
+       (modify-frame-parameters (selected-frame) '((unsplittable))))
+  (delete-other-windows)
+  ;; Set top of frame line in case it is not 0.
+  (or (fboundp 'window-highest-p)
+      (setq br-top-of-frame (nth 1 (window-edges))))
+  (split-window-vertically nil)
+  (let* ((n (max 1 (/ (frame-width) br-min-width-window)))
+	 (width (/ (frame-width) n)))
+    (br-next-buffer 1)
+    (while (> n 1)
+      (setq n (1- n))
+      (split-window-horizontally width)
+      (br-next-buffer nil br-buffer-prefix-blank))))
+
+(defun br-view-ext-start (viewer-cmd name file)
+  "Start an external viewer given by VIEWER-CMD using NAME applied to FILE."
+  ;; Conditionalized code is necessary because of silly (start-process) calling
+  ;; protocol.
+  (cond (br-vw9
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			br-vw5 br-vw6 br-vw7 br-vw8 br-vw9 file))
+	(br-vw8
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			br-vw5 br-vw6 br-vw7 br-vw8 file))
+	(br-vw7
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			br-vw5 br-vw6 br-vw7 file))
+	(br-vw6
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			br-vw5 br-vw6 file))
+	(br-vw5
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			br-vw5 file))
+	(br-vw4
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 br-vw4
+			file))
+	(br-vw3
+	 (start-process name name viewer-cmd br-vw1 br-vw2 br-vw3 file))
+	(br-vw2
+	 (start-process name name viewer-cmd br-vw1 br-vw2 file))
+	(br-vw1
+	 (start-process name name viewer-cmd br-vw1 file))
+	(t
+	 (start-process name name viewer-cmd file))
+	))
+
+;;; ************************************************************************
+;;; Private variables
+;;; ************************************************************************
+
+(defvar br-ancestor-function nil
+  "If non-nil, a function of 3 arguments called after each ancestor class is inserted into an ancestry listing.
+First argument is the class just inserted, second argument is a flag
+indicating whether class has previously been displayed within the listing and
+third argument is the number of spaces to indent each feature entry for this
+class.")
+
+(defvar br-top-of-frame 0
+  "Frame line number of windows at top of the OO-Browser frame.")
+
+(defvar br-ed-num 0)
+(defvar br-ed-name "extEd")
+(defvar br-vw-num 0)
+(defvar br-vw-name "extVw")
+
+(defvar br-in-browser nil
+  "Equal to the frame displaying the OO-Browser when in use, else nil.")
+
+(defvar br-lib-search-dirs nil
+  "List of directories below which OO source files and other library
+directories are found.  A library is a stable group of OO classes.  Do not
+set this variable directly.  Each OO language library which invokes
+'br-browse' should set it.")
+
+(defvar br-sys-search-dirs nil
+  "List of directories below which OO source files and other system
+directories are found.  A system is a group of OO classes that are likely to
+change.  Do not set this variable directly.  Each OO language library which
+invokes 'br-browse' should set it.")
+
+(defvar *br-level-hist* nil
+  "Internal history of visited listing windows and buffers.")
+
+(defvar *br-prev-listing-window* nil
+  "Saves listing window used prior to viewer window entry.
+Allows return to previous listing window when done with the viewer.")
+
+(defvar *br-prev-wconfig* nil
+  "Saves window configuration prior to browser entry.")
+
+(defvar *br-save-wconfig* nil
+  "Saves window configuration between invocations of the browser.")
+
+(defconst br-buffer-prefix-categ "Categ-Lvl-")
+(defconst br-buffer-prefix-inher "Inher-Lvl-")
+(defconst br-buffer-prefix-blank "Blank-")
+(defconst br-buffer-prefix-info "OO-Browser ")
+(defvar br-buffer-prefix br-buffer-prefix-inher
+  "Browser buffer name prefix.")
+
+
+(defvar br-mode-map nil
+  "Keymap containing OO-Browser commands.")
+(if br-mode-map
+    nil
+  (setq br-mode-map (make-keymap))
+  (suppress-keymap br-mode-map)
+  (define-key br-mode-map "@"        'br-at)
+  (define-key br-mode-map "1"        'br-view-full-frame)
+  (define-key br-mode-map "\C-c^"    'br-add-class-file)
+  (define-key br-mode-map "a"        'br-ancestors)
+  (define-key br-mode-map "b"        'br-buffer-menu)
+  (define-key br-mode-map "\C-c\C-b" 'br-report-bug)
+  (define-key br-mode-map "c"        'br-children)
+  (define-key br-mode-map "C"        'br-categories)
+  (define-key br-mode-map "\M-c"     'br-class-stats)
+  (define-key br-mode-map "\C-c\C-c" 'br-env-create)
+  (define-key br-mode-map "d"        'br-descendants)
+  (define-key br-mode-map "\C-c\C-d" 'br-delete)
+  ;; {M-d} is used down below for 'br-tree'
+  (define-key br-mode-map "e"        'br-edit-entry)
+  (define-key br-mode-map "\M-e"     'br-env-stats)
+  (define-key br-mode-map "\C-c\C-e" 'br-env-rebuild)
+  (define-key br-mode-map "f"        'br-features)
+  (define-key br-mode-map "F"        'br-feature-signature)
+  ;; {M-f} is used down below for 'br-tree-features-toggle'
+  ;; {M-g} is used down below for 'br-tree-graph'
+  (define-key br-mode-map "?"        'br-help)
+  (define-key br-mode-map "h"        'br-help)
+  (define-key br-mode-map "H"        'br-help-ms) ;; mouse help
+  (define-key br-mode-map "i"        'br-entry-info)
+  (define-key br-mode-map "I"        'br-implementors)
+  (define-key br-mode-map "\C-c\C-k" 'br-kill)
+  ;; {M-k} is used down below for 'br-tree-kill'
+  (define-key br-mode-map "l"        'br-lib-top-classes)
+  (define-key br-mode-map "L"        'br-lib-rebuild)
+  (define-key br-mode-map "\C-c\C-l" 'br-env-load)
+  (define-key br-mode-map "m"        'br-match)
+  (define-key br-mode-map "M"        'br-match-entries)
+  ;; "\C-c\C-m" is reserved for future use.
+  (define-key br-mode-map "\C-n"     'br-next-entry)
+  (define-key br-mode-map "o"        'br-order)
+  (define-key br-mode-map "p"        'br-parents)
+  (define-key br-mode-map "P"        'br-protocols)
+  (define-key br-mode-map "\C-p"     'br-prev-entry)
+  (define-key br-mode-map "q"        'br-quit)
+  ;; {r} does the same thing as {f} and is for backward compatibility
+  ;; with prior OO-Browser releases.  It may be rebound in the future, so
+  ;; learn to use {f} instead.
+  (define-key br-mode-map "r"        'br-features)
+  (define-key br-mode-map "\C-c\C-r" 'br-refresh)
+  (define-key br-mode-map "s"        'br-sys-top-classes)
+  (define-key br-mode-map "S"        'br-sys-rebuild)
+  (define-key br-mode-map "\C-c\C-s" 'br-env-save)
+  (define-key br-mode-map "t"        'br-top-classes)
+  (define-key br-mode-map "u"        'br-unique)
+  (define-key br-mode-map "v"        'br-view-entry)
+  (define-key br-mode-map "V"        'br-view-friend)
+  (define-key br-mode-map "\C-c\C-v" 'br-to-from-viewer)
+  (define-key br-mode-map "\C-c\C-w" 'br-write-buffer)
+  (define-key br-mode-map "w"        'br-where)
+  (define-key br-mode-map "x"        'br-exit-level)
+  (define-key br-mode-map "\C-x-"    'br-resize-narrow)
+  (define-key br-mode-map "\C-x+"    'br-resize-widen)
+  (define-key br-mode-map "#"        'br-count)
+  (define-key br-mode-map "\C-c#"    'br-version)
+  (define-key br-mode-map " "        'br-viewer-scroll-up)
+  (define-key br-mode-map "\177"     'br-viewer-scroll-down)
+  ;;
+  ;; Define graphical browser keys if a window system is available.
+  (if hyperb:window-system
+      (progn (require 'br-tree)
+	     (define-key br-mode-map "\M-d" 'br-tree)
+	     (define-key br-mode-map "\M-f" 'br-tree-features-toggle)
+	     (define-key br-mode-map "\M-g" 'br-tree-graph)
+	     (define-key br-mode-map "\M-k" 'br-tree-kill))))
+
+(defvar br-tmp-class-set nil
+  "Set of classes created for temporary use by br-*-trees functions.")
+(defvar br-tmp-depth 0
+  "Temporary variable indicating inheritance depth of class in 'br-ancestor-trees'.")
+
+(provide 'br)