Add 2010 to copyright years.
[bpt/emacs.git] / lisp / info.el
index 5bbbd60..4789d67 100644 (file)
@@ -1,7 +1,7 @@
-;;; info.el --- info package for Emacs
+;; info.el --- info package for Emacs
 
 ;; Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-;;   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+;;   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
 ;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -9,10 +9,10 @@
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, 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
@@ -20,9 +20,7 @@
 ;; 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, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -36,7 +34,7 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'jka-compr))
+(eval-when-compile (require 'jka-compr) (require 'cl))
 
 (defgroup info nil
   "Info subsystem."
@@ -57,7 +55,7 @@ Each element of the stack is a list (FILENAME NODENAME BUFFERPOS).")
 Each element of the list is a list (FILENAME NODENAME).")
 
 (defcustom Info-enable-edit nil
-  "*Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
+  "Non-nil means the \\<Info-mode-map>\\[Info-edit] command in Info can edit the current node.
 This is convenient if you want to write Info files by hand.
 However, we recommend that you not do this.
 It is better to write a Texinfo file and generate the Info file from that,
@@ -85,32 +83,28 @@ The Lisp code is executed when the node is selected.")
     (t :height 1.2 :inherit info-title-2))
   "Face for info titles at level 1."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-1-face 'face-alias 'info-title-1)
+(define-obsolete-face-alias 'Info-title-1-face 'info-title-1 "22.1")
 
 (defface info-title-2
   '((((type tty pc) (class color)) :foreground "lightblue" :weight bold)
     (t :height 1.2 :inherit info-title-3))
   "Face for info titles at level 2."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-2-face 'face-alias 'info-title-2)
+(define-obsolete-face-alias 'Info-title-2-face 'info-title-2 "22.1")
 
 (defface info-title-3
   '((((type tty pc) (class color)) :weight bold)
     (t :height 1.2 :inherit info-title-4))
   "Face for info titles at level 3."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-3-face 'face-alias 'info-title-3)
+(define-obsolete-face-alias 'Info-title-3-face 'info-title-3 "22.1")
 
 (defface info-title-4
   '((((type tty pc) (class color)) :weight bold)
     (t :weight bold :inherit variable-pitch))
   "Face for info titles at level 4."
   :group 'info)
-;; backward-compatibility alias
-(put 'Info-title-4-face 'face-alias 'info-title-4)
+(define-obsolete-face-alias 'Info-title-4-face 'info-title-4 "22.1")
 
 (defface info-menu-header
   '((((type tty pc))
@@ -127,7 +121,7 @@ The Lisp code is executed when the node is selected.")
     (t :underline t))
   "Face for every third `*' in an Info menu."
   :group 'info)
-(put 'info-menu-5 'face-alias 'info-menu-star)
+(define-obsolete-face-alias 'info-menu-5 'info-menu-star "22.1")
 
 (defface info-xref
   '((t :inherit link))
@@ -141,19 +135,19 @@ The Lisp code is executed when the node is selected.")
   :group 'info)
 
 (defcustom Info-fontify-visited-nodes t
-  "*Non-nil to fontify references to visited nodes in `info-xref-visited' face."
+  "Non-nil to fontify references to visited nodes in `info-xref-visited' face."
   :version "22.1"
   :type 'boolean
   :group 'info)
 
 (defcustom Info-fontify-maximum-menu-size 100000
-  "*Maximum size of menu to fontify if `font-lock-mode' is non-nil.
+  "Maximum size of menu to fontify if `font-lock-mode' is non-nil.
 Set to nil to disable node fontification."
   :type 'integer
   :group 'info)
 
 (defcustom Info-use-header-line t
-  "*Non-nil means to put the beginning-of-node links in an Emacs header-line.
+  "Non-nil means to put the beginning-of-node links in an Emacs header-line.
 A header-line does not scroll with the rest of the buffer."
   :type 'boolean
   :group 'info)
@@ -205,7 +199,7 @@ These directories are searched after those in `Info-directory-list'."
   :group 'info)
 
 (defcustom Info-scroll-prefer-subnodes nil
-  "*If non-nil, \\<Info-mode-map>\\[Info-scroll-up] in a menu visits subnodes.
+  "If non-nil, \\<Info-mode-map>\\[Info-scroll-up] in a menu visits subnodes.
 
 If this is non-nil, and you scroll far enough in a node that its menu
 appears on the screen, the next \\<Info-mode-map>\\[Info-scroll-up]
@@ -220,10 +214,11 @@ when you hit the end of the current node."
   :group 'info)
 
 (defcustom Info-hide-note-references t
-  "*If non-nil, hide the tag and section reference in *note and * menu items.
+  "If non-nil, hide the tag and section reference in *note and * menu items.
 If value is non-nil but not `hide', also replaces the \"*note\" with \"see\".
 If value is non-nil but not t or `hide', the reference section is still shown.
-`nil' completely disables this feature."
+`nil' completely disables this feature.  If this is non-nil, you might
+want to set `Info-refill-paragraphs'."
   :version "22.1"
   :type '(choice (const :tag "No hiding" nil)
                 (const :tag "Replace tag and hide reference" t)
@@ -232,15 +227,21 @@ If value is non-nil but not t or `hide', the reference section is still shown.
   :group 'info)
 
 (defcustom Info-refill-paragraphs nil
-  "*If non-nil, attempt to refill paragraphs with hidden references.
+  "If non-nil, attempt to refill paragraphs with hidden references.
 This refilling may accidentally remove explicit line breaks in the Info
-file, so be prepared for a few surprises if you enable this feature."
+file, so be prepared for a few surprises if you enable this feature.
+This only has an effect if `Info-hide-note-references' is non-nil."
   :version "22.1"
   :type 'boolean
   :group 'info)
 
+(defcustom Info-breadcrumbs-depth 4
+  "Depth of breadcrumbs to display.
+0 means do not display breadcrumbs."
+  :type 'integer)
+
 (defcustom Info-search-whitespace-regexp "\\s-+"
-  "*If non-nil, regular expression to match a sequence of whitespace chars.
+  "If non-nil, regular expression to match a sequence of whitespace chars.
 This applies to Info search for regular expressions.
 You might want to use something like \"[ \\t\\r\\n]+\" instead.
 In the Customization buffer, that is `[' followed by a space,
@@ -249,7 +250,7 @@ a tab, a carriage return (control-M), a newline, and `]+'."
   :group 'info)
 
 (defcustom Info-isearch-search t
-  "*If non-nil, isearch in Info searches through multiple nodes.
+  "If non-nil, isearch in Info searches through multiple nodes.
 Before leaving the initial Info node, where isearch was started,
 it fails once with the error message [initial node], and with
 subsequent C-s/C-r continues through other nodes without failing
@@ -307,6 +308,11 @@ Marker points nowhere if file has no tag table.")
 (defvar Info-file-supports-index-cookies nil
   "Non-nil if current Info file supports index cookies.")
 
+(defvar Info-file-supports-index-cookies-list nil
+  "List of Info files with information about index cookies support.
+Each element of the list is a list (FILENAME SUPPORTS-INDEX-COOKIES)
+where SUPPORTS-INDEX-COOKIES can be either t or nil.")
+
 (defvar Info-index-alternatives nil
   "List of possible matches for last `Info-index' command.")
 
@@ -318,6 +324,54 @@ If number, the point is moved to the corresponding line.")
 
 (defvar Info-standalone nil
   "Non-nil if Emacs was started solely as an Info browser.")
+
+(defvar Info-virtual-files nil
+  "List of definitions of virtual Info files.
+Each element of the list has the format (FILENAME (OPERATION . HANDLER) ...)
+where FILENAME is a regexp that matches a class of virtual Info file names.
+It should be carefully chosen to not cause file name clashes with
+existing file names.  OPERATION is one of the following operation
+symbols `find-file', `find-node', `toc-nodes' that define what HANDLER
+function to call instead of calling the default corresponding function
+to override it.")
+
+(defvar Info-virtual-nodes nil
+  "List of definitions of virtual Info nodes.
+Each element of the list has the format (NODENAME (OPERATION . HANDLER) ...)
+where NODENAME is a regexp that matches a class of virtual Info node names.
+It should be carefully chosen to not cause node name clashes with
+existing node names.  OPERATION is one of the following operation
+symbols `find-node' that define what HANDLER
+function to call instead of calling the default corresponding function
+to override it.")
+
+(defvar Info-current-node-virtual nil
+  "Non-nil if the current Info node is virtual.")
+
+(defun Info-virtual-file-p (filename)
+  "Check if Info file FILENAME is virtual."
+  (Info-virtual-fun 'find-file filename nil))
+
+(defun Info-virtual-fun (op filename nodename)
+  "Find a function that handles operations on virtual manuals.
+OP is an operation symbol (`find-file', `find-node' or `toc-nodes'),
+FILENAME is a virtual Info file name, NODENAME is a virtual Info
+node name.  Return a function found either in `Info-virtual-files'
+or `Info-virtual-nodes'."
+  (or (and (stringp filename) ; some legacy code can still use a symbol
+          (cdr-safe (assoc op (assoc-default filename
+                                             Info-virtual-files
+                                             'string-match))))
+      (and (stringp nodename) ; some legacy code can still use a symbol
+          (cdr-safe (assoc op (assoc-default nodename
+                                             Info-virtual-nodes
+                                             'string-match))))))
+
+(defun Info-virtual-call (virtual-fun &rest args)
+  "Call a function that handles operations on virtual manuals."
+  (when (functionp virtual-fun)
+    (or (apply virtual-fun args) t)))
+
 \f
 (defvar Info-suffix-list
   ;; The MS-DOS list should work both when long file names are
@@ -449,15 +503,45 @@ Do the right thing if the file has been compressed or zipped."
     (if decoder
        (progn
          (insert-file-contents-literally fullname visit)
-         (let ((buffer-read-only nil)
+         (let ((inhibit-read-only t)
                (coding-system-for-write 'no-conversion)
+               (inhibit-null-byte-detection t) ; Index nodes include null bytes
                (default-directory (or (file-name-directory fullname)
                                       default-directory)))
            (or (consp decoder)
                (setq decoder (list decoder)))
            (apply 'call-process-region (point-min) (point-max)
                   (car decoder) t t nil (cdr decoder))))
-      (insert-file-contents fullname visit))))
+      (let ((inhibit-null-byte-detection t)) ; Index nodes include null bytes
+       (insert-file-contents fullname visit)))))
+
+(defun Info-file-supports-index-cookies (&optional file)
+  "Return non-nil value if FILE supports Info index cookies.
+Info index cookies were first introduced in 4.7, and all later
+makeinfo versions output them in index nodes, so we can rely
+solely on the makeinfo version.  This function caches the information
+in `Info-file-supports-index-cookies-list'."
+  (or file (setq file Info-current-file))
+  (or (assoc file Info-file-supports-index-cookies-list)
+      ;; Skip virtual Info files
+      (and (or (not (stringp file))
+              (Info-virtual-file-p file))
+           (setq Info-file-supports-index-cookies-list
+                (cons (cons file nil) Info-file-supports-index-cookies-list)))
+      (save-excursion
+       (let ((found nil))
+         (goto-char (point-min))
+         (condition-case ()
+             (if (and (re-search-forward
+                       "makeinfo[ \n]version[ \n]\\([0-9]+.[0-9]+\\)"
+                       (line-beginning-position 3) t)
+                      (not (version< (match-string 1) "4.7")))
+                 (setq found t))
+           (error nil))
+         (setq Info-file-supports-index-cookies-list
+               (cons (cons file found) Info-file-supports-index-cookies-list)))))
+  (cdr (assoc file Info-file-supports-index-cookies-list)))
+
 \f
 (defun Info-default-dirs ()
   (let ((source (expand-file-name "info/" source-directory))
@@ -523,9 +607,9 @@ Do the right thing if the file has been compressed or zipped."
   (let (same-window-buffer-names same-window-regexps)
     (info file-or-node)))
 
-;;;###autoload (add-hook 'same-window-regexps "\\*info\\*\\(\\|<[0-9]+>\\)")
+;;;###autoload (add-hook 'same-window-regexps (purecopy "\\*info\\*\\(\\|<[0-9]+>\\)"))
 
-;;;###autoload (put 'info 'info-file "emacs")
+;;;###autoload (put 'info 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun info (&optional file-or-node buffer)
   "Enter Info, the documentation browser.
@@ -545,7 +629,9 @@ appended to the Info buffer name.
 
 The search path for Info files is in the variable `Info-directory-list'.
 The top-level Info directory is made by combining all the files named `dir'
-in all the directories in that path."
+in all the directories in that path.
+
+See a list of available Info commands in `Info-mode'."
   (interactive (list
                 (if (and current-prefix-arg (not (numberp current-prefix-arg)))
                     (read-file-name "Info file name: " nil nil t))
@@ -618,59 +704,58 @@ Optional second argument NOERROR, if t, means if file is not found
 just return nil (no error)."
   ;; Convert filename to lower case if not found as specified.
   ;; Expand it.
-  (if (stringp filename)
-      (let (temp temp-downcase found)
-        (setq filename (substitute-in-file-name filename))
-       (cond
-        ((string= (downcase filename) "dir")
-         (setq found t))
-        (t
-         (let ((dirs (if (string-match "^\\./" filename)
-                          ;; If specified name starts with `./'
-                          ;; then just try current directory.
-                          '("./")
-                        (if (file-name-absolute-p filename)
-                            ;; No point in searching for an
-                            ;; absolute file name
-                            '(nil)
-                          (if Info-additional-directory-list
-                              (append Info-directory-list
-                                      Info-additional-directory-list)
-                            Info-directory-list)))))
-            ;; Search the directory list for file FILENAME.
-            (while (and dirs (not found))
-              (setq temp (expand-file-name filename (car dirs)))
-              (setq temp-downcase
-                    (expand-file-name (downcase filename) (car dirs)))
-              ;; Try several variants of specified name.
-              (let ((suffix-list Info-suffix-list)
-                   (lfn (if (fboundp 'msdos-long-file-names)
-                            (msdos-long-file-names)
-                          t)))
-                (while (and suffix-list (not found))
-                  (cond ((info-file-exists-p
-                          (info-insert-file-contents-1
-                           temp (car (car suffix-list)) lfn))
-                         (setq found temp))
-                        ((info-file-exists-p
-                          (info-insert-file-contents-1
-                           temp-downcase (car (car suffix-list)) lfn))
-                         (setq found temp-downcase))
-                       ((and (fboundp 'msdos-long-file-names)
-                             lfn
-                             (info-file-exists-p
-                              (info-insert-file-contents-1
-                               temp (car (car suffix-list)) nil)))
-                        (setq found temp)))
-                  (setq suffix-list (cdr suffix-list))))
-              (setq dirs (cdr dirs))))))
-        (if found
-            (setq filename found)
-          (if noerror
-              (setq filename nil)
-            (error "Info file %s does not exist" filename)))
-        filename)
-    (and (member filename '(apropos history toc)) filename)))
+  (cond
+   ((Info-virtual-call
+     (Info-virtual-fun 'find-file filename nil)
+     filename noerror))
+   ((stringp filename)
+    (let (temp temp-downcase found)
+      (setq filename (substitute-in-file-name filename))
+      (let ((dirs (if (string-match "^\\./" filename)
+                     ;; If specified name starts with `./'
+                     ;; then just try current directory.
+                     '("./")
+                   (if (file-name-absolute-p filename)
+                       ;; No point in searching for an
+                       ;; absolute file name
+                       '(nil)
+                     (if Info-additional-directory-list
+                         (append Info-directory-list
+                                 Info-additional-directory-list)
+                       Info-directory-list)))))
+       ;; Search the directory list for file FILENAME.
+       (while (and dirs (not found))
+         (setq temp (expand-file-name filename (car dirs)))
+         (setq temp-downcase
+               (expand-file-name (downcase filename) (car dirs)))
+         ;; Try several variants of specified name.
+         (let ((suffix-list Info-suffix-list)
+               (lfn (if (fboundp 'msdos-long-file-names)
+                        (msdos-long-file-names)
+                      t)))
+           (while (and suffix-list (not found))
+             (cond ((info-file-exists-p
+                     (info-insert-file-contents-1
+                      temp (car (car suffix-list)) lfn))
+                    (setq found temp))
+                   ((info-file-exists-p
+                     (info-insert-file-contents-1
+                      temp-downcase (car (car suffix-list)) lfn))
+                    (setq found temp-downcase))
+                   ((and (fboundp 'msdos-long-file-names)
+                         lfn
+                         (info-file-exists-p
+                          (info-insert-file-contents-1
+                           temp (car (car suffix-list)) nil)))
+                    (setq found temp)))
+             (setq suffix-list (cdr suffix-list))))
+         (setq dirs (cdr dirs))))
+      (if found
+         (setq filename found)
+       (if noerror
+           (setq filename nil)
+         (error "Info file %s does not exist" filename)))
+      filename))))
 
 (defun Info-find-node (filename nodename &optional no-going-back)
   "Go to an Info node specified as separate FILENAME and NODENAME.
@@ -683,9 +768,8 @@ it says do not attempt further (recursive) error recovery."
   ;; Record the node we are leaving, if we were in one.
   (and (not no-going-back)
        Info-current-file
-       (setq Info-history
-            (cons (list Info-current-file Info-current-node (point))
-                  Info-history)))
+       (push (list Info-current-file Info-current-node (point))
+             Info-history))
   (Info-find-node-2 filename nodename no-going-back))
 
 ;;;###autoload
@@ -756,8 +840,7 @@ FOUND-ANCHOR is non-nil if a `Ref:' was matched, POS is the position
 where the match was found, and MODE is `major-mode' of the buffer in
 which the match was found."
   (let ((case-fold-search case-fold))
-    (save-excursion
-      (set-buffer (marker-buffer marker))
+    (with-current-buffer (marker-buffer marker)
       (goto-char marker)
 
       ;; Search tag table
@@ -822,79 +905,81 @@ a case-insensitive match is tried."
   (setq Info-current-node nil)
   (unwind-protect
       (let ((case-fold-search t)
+           (virtual-fun (Info-virtual-fun 'find-node
+                                          (or filename Info-current-file)
+                                          nodename))
            anchorpos)
-        ;; Switch files if necessary
-        (or (null filename)
-            (equal Info-current-file filename)
-            (let ((buffer-read-only nil))
-              (setq Info-current-file nil
-                    Info-current-subfile nil
-                    Info-current-file-completions nil
-                    buffer-file-name nil)
-              (erase-buffer)
-             (cond
-              ((eq filename t)
-               (Info-insert-dir))
-              ((eq filename 'apropos)
-               (insert-buffer-substring " *info-apropos*"))
-              ((eq filename 'history)
-               (insert-buffer-substring " *info-history*"))
-              ((eq filename 'toc)
-               (insert-buffer-substring " *info-toc*"))
-              (t
-                (info-insert-file-contents filename nil)
-                (setq default-directory (file-name-directory filename))))
-              (set-buffer-modified-p nil)
-
-             ;; Check makeinfo version for index cookie support
-             (let ((found nil))
-               (goto-char (point-min))
-               (condition-case ()
-                   (if (and (re-search-forward
-                             "makeinfo[ \n]version[ \n]\\([0-9]+.[0-9]+\\)"
-                             (line-beginning-position 3) t)
-                            (not (version< (match-string 1) "4.7")))
-                       (setq found t))
-                 (error nil))
-               (set (make-local-variable 'Info-file-supports-index-cookies) found))
-
-              ;; See whether file has a tag table.  Record the location if yes.
-              (goto-char (point-max))
-              (forward-line -8)
-              ;; Use string-equal, not equal, to ignore text props.
-              (if (not (or (string-equal nodename "*")
-                           (not
-                            (search-forward "\^_\nEnd tag table\n" nil t))))
-                  (let (pos)
-                    ;; We have a tag table.  Find its beginning.
-                    ;; Is this an indirect file?
-                    (search-backward "\nTag table:\n")
-                    (setq pos (point))
-                    (if (save-excursion
-                          (forward-line 2)
-                          (looking-at "(Indirect)\n"))
-                        ;; It is indirect.  Copy it to another buffer
-                        ;; and record that the tag table is in that buffer.
-                        (let ((buf (current-buffer))
-                              (tagbuf
-                               (or Info-tag-table-buffer
-                                   (generate-new-buffer " *info tag table*"))))
-                          (setq Info-tag-table-buffer tagbuf)
-                          (save-excursion
-                            (set-buffer tagbuf)
-                            (buffer-disable-undo (current-buffer))
-                            (setq case-fold-search t)
-                            (erase-buffer)
-                            (insert-buffer-substring buf))
-                          (set-marker Info-tag-table-marker
-                                      (match-end 0) tagbuf))
-                      (set-marker Info-tag-table-marker pos)))
-                (set-marker Info-tag-table-marker nil))
-              (setq Info-current-file
-                   (cond
-                    ((eq filename t) "dir")
-                    (t filename)))
-             ))
+       (cond
+        ((functionp virtual-fun)
+         (let ((filename (or filename Info-current-file)))
+           (setq buffer-read-only nil)
+           (setq Info-current-file filename
+                 Info-current-subfile nil
+                 Info-current-file-completions nil
+                 buffer-file-name nil)
+           (erase-buffer)
+           (Info-virtual-call virtual-fun filename nodename no-going-back)
+           (set-marker Info-tag-table-marker nil)
+           (setq buffer-read-only t)
+           (set-buffer-modified-p nil)
+           (set (make-local-variable 'Info-current-node-virtual) t)))
+        ((not (and
+               ;; Reread a file when moving from a virtual node.
+               (not Info-current-node-virtual)
+               (or (null filename)
+                   (equal Info-current-file filename))))
+         ;; Switch files if necessary
+         (let ((inhibit-read-only t))
+           (when Info-current-node-virtual
+             ;; When moving from a virtual node.
+             (set (make-local-variable 'Info-current-node-virtual) nil)
+             (if (null filename)
+                 (setq filename Info-current-file)))
+           (setq Info-current-file nil
+                 Info-current-subfile nil
+                 Info-current-file-completions nil
+                 buffer-file-name nil)
+           (erase-buffer)
+           (info-insert-file-contents filename nil)
+           (setq default-directory (file-name-directory filename))
+           (set-buffer-modified-p nil)
+           (set (make-local-variable 'Info-file-supports-index-cookies)
+                (Info-file-supports-index-cookies filename))
+
+           ;; See whether file has a tag table.  Record the location if yes.
+           (goto-char (point-max))
+           (forward-line -8)
+           ;; Use string-equal, not equal, to ignore text props.
+           (if (not (or (string-equal nodename "*")
+                        (not
+                         (search-forward "\^_\nEnd tag table\n" nil t))))
+               (let (pos)
+                 ;; We have a tag table.  Find its beginning.
+                 ;; Is this an indirect file?
+                 (search-backward "\nTag table:\n")
+                 (setq pos (point))
+                 (if (save-excursion
+                       (forward-line 2)
+                       (looking-at "(Indirect)\n"))
+                     ;; It is indirect.  Copy it to another buffer
+                     ;; and record that the tag table is in that buffer.
+                     (let ((buf (current-buffer))
+                           (tagbuf
+                            (or Info-tag-table-buffer
+                                (generate-new-buffer " *info tag table*"))))
+                       (setq Info-tag-table-buffer tagbuf)
+                       (with-current-buffer tagbuf
+                         (buffer-disable-undo (current-buffer))
+                         (setq case-fold-search t)
+                         (erase-buffer)
+                         (insert-buffer-substring buf))
+                       (set-marker Info-tag-table-marker
+                                   (match-end 0) tagbuf))
+                   (set-marker Info-tag-table-marker pos)))
+             (set-marker Info-tag-table-marker nil))
+           (setq Info-current-file filename)
+           )))
+
         ;; Use string-equal, not equal, to ignore text props.
         (if (string-equal nodename "*")
             (progn (setq Info-current-node nodename)
@@ -967,6 +1052,10 @@ a case-insensitive match is tried."
 
            (Info-select-node)
            (goto-char (point-min))
+           (forward-line 1)                   ; skip header line
+           (when (> Info-breadcrumbs-depth 0) ; skip breadcrumbs line
+             (forward-line 1))
+
            (cond (anchorpos
                    (let ((new-history (list Info-current-file
                                             (substring-no-properties nodename))))
@@ -976,7 +1065,7 @@ a case-insensitive match is tried."
                                  (delete new-history Info-history-list))))
                    (goto-char anchorpos))
                   ((numberp Info-point-loc)
-                   (forward-line (1- Info-point-loc))
+                   (forward-line (- Info-point-loc 2))
                    (setq Info-point-loc nil))
                  ((stringp Info-point-loc)
                   (Info-find-index-name Info-point-loc)
@@ -1059,12 +1148,14 @@ a case-insensitive match is tried."
                            (cons (directory-file-name truename)
                                  dirs-done)))
                (if attrs
-                   (save-excursion
+                   (with-current-buffer (generate-new-buffer " info dir")
                      (or buffers
                          (message "Composing main Info directory..."))
-                     (set-buffer (generate-new-buffer " info dir"))
                      (condition-case nil
-                         (progn
+                         ;; Index nodes include null bytes.  DIR
+                         ;; files should not have indices, but who
+                         ;; knows...
+                         (let ((inhibit-null-byte-detection t))
                            (insert-file-contents file)
                            (set (make-local-variable 'Info-dir-file-name)
                                 file)
@@ -1215,19 +1306,20 @@ a case-insensitive match is tried."
              (delete-region (1- (point)) (point))))
 
          ;; Now remove duplicate entries under the same heading.
-         (let ((seen nil)
-               (limit (point-marker)))
-           (goto-char start)
-           (while (and (> limit (point))
-                       (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)"
-                                          limit 'move))
-             ;; Fold case straight away; `member-ignore-case' here wasteful.
-             (let ((x (downcase (match-string 1))))
-               (if (member x seen)
-                   (delete-region (match-beginning 0)
-                                  (progn (re-search-forward "^[^ \t]" nil t)
-                                         (match-beginning 0)))
-                 (push x seen))))))))))
+         (let (seen)
+           (save-restriction
+             (narrow-to-region start (point))
+             (goto-char (point-min))
+             (while (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)" nil 'move)
+               ;; Fold case straight away; `member-ignore-case' here wasteful.
+               (let ((x (downcase (match-string 1))))
+                 (if (member x seen)
+                     (delete-region
+                      (match-beginning 0)
+                      (if (re-search-forward "^[^ \t]" nil 'move)
+                          (goto-char (match-beginning 0))
+                        (point-max)))
+                   (push x seen)))))))))))
 
 ;; Note that on entry to this function the current-buffer must be the
 ;; *info* buffer; not the info tags buffer.
@@ -1237,8 +1329,7 @@ a case-insensitive match is tried."
   (let (lastfilepos
        lastfilename)
     (if (numberp nodepos)
-       (save-excursion
-         (set-buffer (marker-buffer Info-tag-table-marker))
+       (with-current-buffer (marker-buffer Info-tag-table-marker)
          (goto-char (point-min))
          (or (looking-at "\^_")
              (search-forward "\n\^_"))
@@ -1264,7 +1355,7 @@ a case-insensitive match is tried."
     ;; Assume previous buffer is in Info-mode.
     ;; (set-buffer (get-buffer "*info*"))
     (or (equal Info-current-subfile lastfilename)
-       (let ((buffer-read-only nil))
+       (let ((inhibit-read-only t))
          (setq buffer-file-name nil)
          (widen)
          (erase-buffer)
@@ -1355,7 +1446,8 @@ any double quotes or backslashes must be escaped (\\\",\\\\)."
              "\\(\0[\0-\37][[][^\0]*\0[\0-\37][]]\n?\\)"
              nil t)
        (let* ((start (match-beginning 1)))
-         (if (not (get-text-property start 'invisible))
+         (if (and (not (get-text-property start 'invisible))
+                  (not (get-text-property start 'display)))
              (put-text-property start (point) 'invisible t)))))
     (set-buffer-modified-p nil)))
 
@@ -1469,17 +1561,15 @@ If FORK is a string, it is the name to use for the new buffer."
 
 (defvar Info-read-node-completion-table)
 
-(defun Info-read-node-name-2 (string path-and-suffixes action)
+(defun Info-read-node-name-2 (dirs suffixes string pred action)
   "Virtual completion table for file names input in Info node names.
 PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
-  (let* ((names nil)
-        (suffixes (remove "" (cdr path-and-suffixes)))
-        (suffix (concat (regexp-opt suffixes t) "\\'"))
-        (string-dir (file-name-directory string))
-        (dirs
-         (if (file-name-absolute-p string)
-             (list (file-name-directory string))
-           (car path-and-suffixes))))
+  (setq suffixes (remove "" suffixes))
+  (when (file-name-absolute-p string)
+    (setq dirs (list (file-name-directory string))))
+  (let ((names nil)
+        (suffix (concat (regexp-opt suffixes t) "\\'"))
+        (string-dir (file-name-directory string)))
     (dolist (dir dirs)
       (unless dir
        (setq dir default-directory))
@@ -1501,10 +1591,7 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
          (when (string-match suffix file)
            (setq file (substring file 0 (match-beginning 0)))
            (push (if string-dir (concat string-dir file) file) names)))))
-    (cond
-     ((eq action t) (all-completions string names))
-     ((null action) (try-completion string names))
-     (t (test-completion string names)))))
+    (complete-with-action action names string pred)))
 
 ;; This function is used as the "completion table" while reading a node name.
 ;; It does completion using the alist in Info-read-node-completion-table
@@ -1513,20 +1600,16 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
   (cond
    ;; First complete embedded file names.
    ((string-match "\\`([^)]*\\'" string)
-    (let ((file (substring string 1)))
-      (cond
-       ((eq code nil)
-       (let ((comp (try-completion file 'Info-read-node-name-2
-                                   (cons Info-directory-list
-                                         (mapcar 'car Info-suffix-list)))))
-         (cond
-          ((eq comp t) (concat string ")"))
-          (comp (concat "(" comp)))))
-       ((eq code t)
-       (all-completions file 'Info-read-node-name-2
-                        (cons Info-directory-list
-                              (mapcar 'car Info-suffix-list))))
-       (t nil))))
+    (completion-table-with-context
+     "("
+     (apply-partially 'completion-table-with-terminator ")"
+                      (apply-partially 'Info-read-node-name-2
+                                       Info-directory-list
+                                       (mapcar 'car Info-suffix-list)))
+     (substring string 1)
+     predicate
+     code))
+
    ;; If a file name was given, then any node is fair game.
    ((string-match "\\`(" string)
     (cond
@@ -1534,21 +1617,11 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
      ((eq code t) nil)
      (t t)))
    ;; Otherwise use Info-read-node-completion-table.
-   ((eq code nil)
-    (try-completion string Info-read-node-completion-table predicate))
-   ((eq code t)
-    (all-completions string Info-read-node-completion-table predicate))
-   (t
-    (test-completion string Info-read-node-completion-table predicate))))
+   (t (complete-with-action
+       code Info-read-node-completion-table string predicate))))
 
 ;; Arrange to highlight the proper letters in the completion list buffer.
-(put 'Info-read-node-name-1 'completion-base-size-function
-     (lambda ()
-       (if (string-match "\\`([^)]*\\'"
-                        (or completion-common-substring
-                            (minibuffer-completion-contents)))
-          1
-        0)))
+
 
 (defun Info-read-node-name (prompt)
   (let* ((completion-ignore-case t)
@@ -1622,8 +1695,7 @@ If DIRECTION is `backward', search in the reverse direction."
                        (format "Regexp search%s: "
                                (if case-fold-search "" " case-sensitively")))
                      nil 'Info-search-history)))
-  (when transient-mark-mode
-    (deactivate-mark))
+  (deactivate-mark)
   (when (equal regexp "")
     (setq regexp (car Info-search-history)))
   (when regexp
@@ -1648,20 +1720,8 @@ If DIRECTION is `backward', search in the reverse direction."
                                (1- (point)))
                              (point-max)))
          (while (and (not give-up)
-                     (save-match-data
-                       (or (null found)
-                           (if backward
-                               (isearch-range-invisible found beg-found)
-                             (isearch-range-invisible beg-found found))
-                           ;; Skip node header line
-                           (and (save-excursion (forward-line -1)
-                                                (looking-at "\^_"))
-                                (forward-line (if backward -1 1)))
-                           ;; Skip Tag Table node
-                           (save-excursion
-                             (and (search-backward "\^_" nil t)
-                                  (looking-at
-                                   "\^_\n\\(Tag Table\\|Local Variables\\)"))))))
+                     (or (null found)
+                         (not (funcall isearch-filter-predicate beg-found found))))
            (let ((search-spaces-regexp
                   (if (or (not isearch-mode) isearch-regexp)
                       Info-search-whitespace-regexp)))
@@ -1697,8 +1757,7 @@ If DIRECTION is `backward', search in the reverse direction."
        (unwind-protect
            ;; Try other subfiles.
            (let ((list ()))
-             (save-excursion
-               (set-buffer (marker-buffer Info-tag-table-marker))
+             (with-current-buffer (marker-buffer Info-tag-table-marker)
                (goto-char (point-min))
                (search-forward "\n\^_\nIndirect:")
                (save-restriction
@@ -1741,20 +1800,8 @@ If DIRECTION is `backward', search in the reverse direction."
                (setq list (cdr list))
                (setq give-up nil found nil)
                (while (and (not give-up)
-                           (save-match-data
-                             (or (null found)
-                                 (if backward
-                                     (isearch-range-invisible found beg-found)
-                                   (isearch-range-invisible beg-found found))
-                                 ;; Skip node header line
-                                 (and (save-excursion (forward-line -1)
-                                                      (looking-at "\^_"))
-                                      (forward-line (if backward -1 1)))
-                                 ;; Skip Tag Table node
-                                 (save-excursion
-                                   (and (search-backward "\^_" nil t)
-                                        (looking-at
-                                         "\^_\n\\(Tag Table\\|Local Variables\\)"))))))
+                           (or (null found)
+                               (not (funcall isearch-filter-predicate beg-found found))))
                  (let ((search-spaces-regexp
                         (if (or (not isearch-mode) isearch-regexp)
                             Info-search-whitespace-regexp)))
@@ -1829,13 +1876,18 @@ If DIRECTION is `backward', search in the reverse direction."
                                        (replace-regexp-in-string
                                         "^\\W+\\|\\W+$" "" string)
                                        nil t)
-                                "\\b")
+                                ;; Lax version of word search
+                                (if (or isearch-nonincremental
+                                        (eq (length string)
+                                            (length (isearch-string-state
+                                                     (car isearch-cmds)))))
+                                    "\\b"))
                         bound noerror count
                         (unless isearch-forward 'backward))
          (Info-search (if isearch-regexp string (regexp-quote string))
                       bound noerror count
-                      (unless isearch-forward 'backward))
-         (point)))
+                      (unless isearch-forward 'backward)))
+       (point))
     (let ((isearch-search-fun-function nil))
       (isearch-search-fun))))
 
@@ -1859,7 +1911,35 @@ If DIRECTION is `backward', search in the reverse direction."
       (progn (Info-find-node file node) (sit-for 0))))
 
 (defun Info-isearch-start ()
-  (setq Info-isearch-initial-node nil))
+  (setq Info-isearch-initial-node
+       ;; Don't stop at initial node for nonincremental search.
+       ;; Otherwise this variable is set after first search failure.
+       (and isearch-nonincremental Info-current-node)))
+
+(defun Info-isearch-filter (beg-found found)
+  "Test whether the current search hit is a visible useful text.
+Return non-nil if the text from BEG-FOUND to FOUND is visible
+and is not in the header line or a tag table."
+  (save-match-data
+    (let ((backward (< found beg-found)))
+      (not
+       (or
+       (and (not (eq search-invisible t))
+            (if backward
+                (or (text-property-not-all found beg-found 'invisible nil)
+                    (text-property-not-all found beg-found 'display nil))
+              (or (text-property-not-all beg-found found 'invisible nil)
+                  (text-property-not-all beg-found found 'display nil))))
+       ;; Skip node header line
+       (and (save-excursion (forward-line -1)
+                            (looking-at "\^_"))
+            (forward-line (if backward -1 1)))
+       ;; Skip Tag Table node
+       (save-excursion
+         (and (search-backward "\^_" nil t)
+              (looking-at
+               "\^_\n\\(Tag Table\\|Local Variables\\)"))))))))
+
 \f
 (defun Info-extract-pointer (name &optional errorname)
   "Extract the value of the node-pointer named NAME.
@@ -1974,6 +2054,26 @@ If SAME-FILE is non-nil, do not move to a different Info file."
     (Info-find-node filename nodename)
     (setq Info-history-forward history-forward)
     (goto-char opoint)))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`dir\\'"
+              (toc-nodes . Info-directory-toc-nodes)
+              (find-file . Info-directory-find-file)
+              (find-node . Info-directory-find-node)
+              ))
+
+(defun Info-directory-toc-nodes (filename)
+  "Directory-specific implementation of Info-directory-toc-nodes."
+  `(,filename
+    ("Top" nil nil nil)))
+
+(defun Info-directory-find-file (filename &optional noerror)
+  "Directory-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-directory-find-node (filename nodename &optional no-going-back)
+  "Directory-specific implementation of Info-find-node-2."
+  (Info-insert-dir))
 
 ;;;###autoload
 (defun Info-directory ()
@@ -1981,84 +2081,100 @@ If SAME-FILE is non-nil, do not move to a different Info file."
   (interactive)
   (Info-find-node "dir" "top"))
 \f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*History\\*\\'"
+              (toc-nodes . Info-history-toc-nodes)
+              (find-file . Info-history-find-file)
+              (find-node . Info-history-find-node)
+              ))
+
+(defun Info-history-toc-nodes (filename)
+  "History-specific implementation of Info-history-toc-nodes."
+  `(,filename
+    ("Top" nil nil nil)))
+
+(defun Info-history-find-file (filename &optional noerror)
+  "History-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-history-find-node (filename nodename &optional no-going-back)
+  "History-specific implementation of Info-find-node-2."
+  (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                 (or filename Info-current-file) nodename))
+  (insert "Recently Visited Nodes\n")
+  (insert "**********************\n\n")
+  (insert "* Menu:\n\n")
+  (let ((hl (delete '("*History*" "Top") Info-history-list)))
+    (while hl
+      (let ((file (nth 0 (car hl)))
+           (node (nth 1 (car hl))))
+       (if (stringp file)
+           (insert "* " node ": ("
+                   (propertize (or (file-name-directory file) "") 'invisible t)
+                   (file-name-nondirectory file)
+                   ")" node ".\n")))
+      (setq hl (cdr hl)))))
+
 (defun Info-history ()
   "Go to a node with a menu of visited nodes."
   (interactive)
-  (let ((curr-file Info-current-file)
-        (curr-node Info-current-node)
-        p)
-    (with-current-buffer (get-buffer-create " *info-history*")
-      (let ((inhibit-read-only t))
-        (erase-buffer)
-        (goto-char (point-min))
-        (insert "\n\^_\nFile: history,  Node: Top,  Up: (dir)\n\n")
-        (insert "Recently Visited Nodes\n**********************\n\n")
-        (insert "* Menu:\n\n")
-        (let ((hl (delete '("history" "Top") Info-history-list)))
-          (while hl
-            (let ((file (nth 0 (car hl)))
-                  (node (nth 1 (car hl))))
-              (if (and (equal file curr-file)
-                       (equal node curr-node))
-                  (setq p (point)))
-              (if (stringp file)
-                 (insert "* " node ": ("
-                         (propertize (or (file-name-directory file) "") 'invisible t)
-                         (file-name-nondirectory file)
-                         ")" node ".\n")))
-            (setq hl (cdr hl))))))
-    (Info-find-node 'history "Top")
-    (goto-char (or p (point-min)))))
+  (Info-find-node "*History*" "Top")
+  (Info-next-reference)
+  (Info-next-reference))
+\f
+(add-to-list 'Info-virtual-nodes
+            '("\\`\\*TOC\\*\\'"
+              (find-node . Info-toc-find-node)
+              ))
+
+(defun Info-toc-find-node (filename nodename &optional no-going-back)
+  "Toc-specific implementation of Info-find-node-2."
+  (let* ((curr-file (substring-no-properties (or filename Info-current-file)))
+        (curr-node (substring-no-properties (or nodename Info-current-node)))
+        (node-list (Info-toc-nodes curr-file)))
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   curr-file curr-node))
+    (insert "Table of Contents\n")
+    (insert "*****************\n\n")
+    (insert "*Note Top::\n")
+    (Info-toc-insert
+     (nth 3 (assoc "Top" node-list))   ; get Top nodes
+     node-list 0 curr-file)
+    (unless (bobp)
+      (let ((Info-hide-note-references 'hide)
+           (Info-fontify-visited-nodes nil))
+       (setq Info-current-file filename Info-current-node "*TOC*")
+       (goto-char (point-min))
+       (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
+                             (point-min))
+                         (point-max))
+       (Info-fontify-node)
+       (widen)))))
 
 (defun Info-toc ()
   "Go to a node with table of contents of the current Info file.
 Table of contents is created from the tree structure of menus."
   (interactive)
-  (if (stringp Info-current-file)
-      (let ((curr-file (substring-no-properties Info-current-file))
-           (curr-node (substring-no-properties Info-current-node))
-           p)
-       (with-current-buffer (get-buffer-create " *info-toc*")
-         (let ((inhibit-read-only t)
-               (node-list (Info-build-toc curr-file)))
-           (erase-buffer)
-           (goto-char (point-min))
-           (insert "\n\^_\nFile: toc,  Node: Top,  Up: (dir)\n\n")
-           (insert "Table of Contents\n*****************\n\n")
-           (insert "*Note Top: (" curr-file ")Top.\n")
-           (Info-insert-toc
-            (nth 2 (assoc "Top" node-list)) ; get Top nodes
-            node-list 0 curr-file))
-         (if (not (bobp))
-             (let ((Info-hide-note-references 'hide)
-                   (Info-fontify-visited-nodes nil))
-               (Info-mode)
-               (setq Info-current-file 'toc Info-current-node "Top")
-               (goto-char (point-min))
-               (narrow-to-region (or (re-search-forward "\n[\^_\f]\n" nil t)
-                                     (point-min))
-                                 (point-max))
-               (Info-fontify-node)
-               (widen)))
-         (goto-char (point-min))
-         (if (setq p (search-forward (concat "*Note " curr-node ":") nil t))
-             (setq p (- p (length curr-node) 2))))
-       (Info-find-node 'toc "Top")
-       (goto-char (or p (point-min))))))
+  (Info-find-node Info-current-file "*TOC*")
+  (let ((prev-node (nth 1 (car Info-history))) p)
+    (goto-char (point-min))
+    (if (setq p (search-forward (concat "*Note " prev-node ":") nil t))
+       (setq p (- p (length prev-node) 2)))
+    (goto-char (or p (point-min)))))
 
-(defun Info-insert-toc (nodes node-list level curr-file)
+(defun Info-toc-insert (nodes node-list level curr-file)
   "Insert table of contents with references to nodes."
   (let ((section "Top"))
     (while nodes
       (let ((node (assoc (car nodes) node-list)))
-        (unless (member (nth 1 node) (list nil section))
-          (insert (setq section (nth 1 node)) "\n"))
+        (unless (member (nth 2 node) (list nil section))
+          (insert (setq section (nth 2 node)) "\n"))
         (insert (make-string level ?\t))
-        (insert "*Note " (car nodes) ": (" curr-file ")" (car nodes) ".\n")
-        (Info-insert-toc (nth 2 node) node-list (1+ level) curr-file)
+        (insert "*Note " (car nodes) ":\n")
+        (Info-toc-insert (nth 3 node) node-list (1+ level) curr-file)
         (setq nodes (cdr nodes))))))
 
-(defun Info-build-toc (file)
+(defun Info-toc-build (file)
   "Build table of contents from menus of Info FILE and its subfiles."
   (with-temp-buffer
     (let* ((file (and (stringp file) (Info-find-file file)))
@@ -2069,17 +2185,22 @@ Table of contents is created from the tree structure of menus."
            (sections '(("Top" "Top")))
            nodes subfiles)
       (while (or main-file subfiles)
-        (or main-file (message "Searching subfile %s..." (car subfiles)))
+        ;; (or main-file (message "Searching subfile %s..." (car subfiles)))
         (erase-buffer)
         (info-insert-file-contents (or main-file (car subfiles)))
         (goto-char (point-min))
         (while (and (search-forward "\n\^_\nFile:" nil 'move)
                     (search-forward "Node: " nil 'move))
-          (let ((nodename (substring-no-properties (Info-following-node-name)))
-                (bound (- (or (save-excursion (search-forward "\n\^_" nil t))
-                              (point-max)) 2))
-                (section "Top")
-                menu-items)
+          (let* ((nodename (substring-no-properties (Info-following-node-name)))
+                (bound (- (or (save-excursion (search-forward "\n\^_" nil t))
+                              (point-max)) 2))
+                (upnode (and (re-search-forward
+                              (concat "Up:" (Info-following-node-name-re))
+                              bound t)
+                             (match-string-no-properties 1)))
+                (section "Top")
+                menu-items)
+           (when (string-match "(" upnode) (setq upnode nil))
             (when (and (not (Info-index-node nodename file))
                        (re-search-forward "^\\* Menu:" bound t))
               (forward-line 1)
@@ -2107,7 +2228,7 @@ Table of contents is created from the tree structure of menus."
                   (setq section (match-string-no-properties 1))))
                 (forward-line 1)
                 (beginning-of-line)))
-            (setq nodes (cons (list nodename
+            (setq nodes (cons (list nodename upnode
                                     (cadr (assoc nodename sections))
                                     (nreverse menu-items))
                               nodes))
@@ -2125,6 +2246,37 @@ Table of contents is created from the tree structure of menus."
           (setq subfiles (cdr subfiles))))
       (message "")
       (nreverse nodes))))
+
+(defvar Info-toc-nodes nil
+  "Alist of cached parent-children node information in visited Info files.
+Each element is (FILE (NODE-NAME PARENT SECTION CHILDREN) ...)
+where PARENT is the parent node extracted from the Up pointer,
+SECTION is the section name in the Top node where this node is placed,
+CHILDREN is a list of child nodes extracted from the node menu.")
+
+(defun Info-toc-nodes (filename)
+  "Return a node list of Info FILENAME with parent-children information.
+This information is cached in the variable `Info-toc-nodes' with the help
+of the function `Info-toc-build'."
+  (cond
+   ((Info-virtual-call
+     (Info-virtual-fun 'toc-nodes (or filename Info-current-file) nil)
+     filename))
+   (t
+    (or filename (setq filename Info-current-file))
+    (or (assoc filename Info-toc-nodes)
+       ;; Skip virtual Info files
+       (and (or (not (stringp filename))
+                (Info-virtual-file-p filename))
+            (push (cons filename nil) Info-toc-nodes))
+       ;; Scan the entire manual and cache the result in Info-toc-nodes
+       (let ((nodes (Info-toc-build filename)))
+         (push (cons filename nodes) Info-toc-nodes)
+         nodes)
+       ;; If there is an error, still add nil to the cache
+       (push (cons filename nil) Info-toc-nodes))
+    (cdr (assoc filename Info-toc-nodes)))))
+
 \f
 (defun Info-follow-reference (footnotename &optional fork)
   "Follow cross reference named FOOTNOTENAME to the node it refers to.
@@ -2286,57 +2438,59 @@ Because of ambiguities, this should be concatenated with something like
 
   ;; Note that `Info-complete-menu-buffer' could be current already,
   ;; so we want to save point.
-  (save-excursion
-    (set-buffer Info-complete-menu-buffer)
-    (let ((completion-ignore-case t)
-         (case-fold-search t)
-         (orignode Info-current-node)
-         nextnode)
-      (goto-char (point-min))
-      (search-forward "\n* Menu:")
-      (if (not (memq action '(nil t)))
-         (re-search-forward
-          (concat "\n\\* +" (regexp-quote string) ":") nil t)
-       (let ((pattern (concat "\n\\* +\\("
-                              (regexp-quote string)
-                              Info-menu-entry-name-re "\\):" Info-node-spec-re))
-             completions
-             (complete-nodes Info-complete-nodes))
-         ;; Check the cache.
-         (if (and (equal (nth 0 Info-complete-cache) Info-current-file)
-                  (equal (nth 1 Info-complete-cache) Info-current-node)
-                  (equal (nth 2 Info-complete-cache) Info-complete-next-re)
-                  (equal (nth 5 Info-complete-cache) Info-complete-nodes)
-                  (let ((prev (nth 3 Info-complete-cache)))
-                    (eq t (compare-strings string 0 (length prev)
-                                           prev 0 nil t))))
-             ;; We can reuse the previous list.
-             (setq completions (nth 4 Info-complete-cache))
-           ;; The cache can't be used.
-           (while
-               (progn
-                 (while (re-search-forward pattern nil t)
-                   (push (match-string-no-properties 1)
-                         completions))
-                 ;; Check subsequent nodes if applicable.
-                 (or (and Info-complete-next-re
-                          (setq nextnode (Info-extract-pointer "next" t))
-                          (string-match Info-complete-next-re nextnode))
-                     (and complete-nodes
-                          (setq complete-nodes (cdr complete-nodes)
-                                nextnode (car complete-nodes)))))
-             (Info-goto-node nextnode))
-           ;; Go back to the start node (for the next completion).
-           (unless (equal Info-current-node orignode)
-             (Info-goto-node orignode))
-           ;; Update the cache.
-           (set (make-local-variable 'Info-complete-cache)
-                (list Info-current-file Info-current-node
-                      Info-complete-next-re string completions
-                      Info-complete-nodes)))
-         (if action
-             (all-completions string completions predicate)
-           (try-completion string completions predicate)))))))
+  (with-current-buffer Info-complete-menu-buffer
+    (save-excursion
+      (let ((completion-ignore-case t)
+            (case-fold-search t)
+            (orignode Info-current-node)
+            nextnode)
+        (goto-char (point-min))
+        (search-forward "\n* Menu:")
+        (cond
+         ((eq (car-safe action) 'boundaries) nil)
+         ((eq action 'lambda)
+          (re-search-forward
+           (concat "\n\\* +" (regexp-quote string) ":") nil t))
+         (t
+          (let ((pattern (concat "\n\\* +\\("
+                                 (regexp-quote string)
+                                 Info-menu-entry-name-re "\\):"
+                                 Info-node-spec-re))
+                completions
+                (complete-nodes Info-complete-nodes))
+            ;; Check the cache.
+            (if (and (equal (nth 0 Info-complete-cache) Info-current-file)
+                     (equal (nth 1 Info-complete-cache) Info-current-node)
+                     (equal (nth 2 Info-complete-cache) Info-complete-next-re)
+                     (equal (nth 5 Info-complete-cache) Info-complete-nodes)
+                     (let ((prev (nth 3 Info-complete-cache)))
+                       (eq t (compare-strings string 0 (length prev)
+                                              prev 0 nil t))))
+                ;; We can reuse the previous list.
+                (setq completions (nth 4 Info-complete-cache))
+              ;; The cache can't be used.
+              (while
+                  (progn
+                    (while (re-search-forward pattern nil t)
+                      (push (match-string-no-properties 1)
+                            completions))
+                    ;; Check subsequent nodes if applicable.
+                    (or (and Info-complete-next-re
+                             (setq nextnode (Info-extract-pointer "next" t))
+                             (string-match Info-complete-next-re nextnode))
+                        (and complete-nodes
+                             (setq complete-nodes (cdr complete-nodes)
+                                   nextnode (car complete-nodes)))))
+                (Info-goto-node nextnode))
+              ;; Go back to the start node (for the next completion).
+              (unless (equal Info-current-node orignode)
+                (Info-goto-node orignode))
+              ;; Update the cache.
+              (set (make-local-variable 'Info-complete-cache)
+                  (list Info-current-file Info-current-node
+                        Info-complete-next-re string completions
+                        Info-complete-nodes)))
+            (complete-with-action action completions string predicate))))))))
 
 
 (defun Info-menu (menu-item &optional fork)
@@ -2347,12 +2501,10 @@ If FORK is non-nil (interactively with a prefix arg), show the node in
 a new Info buffer.  If FORK is a string, it is the name to use for the
 new buffer."
   (interactive
-   (let ((completions '())
-        ;; If point is within a menu item, use that item as the default
+   (let (;; If point is within a menu item, use that item as the default
         (default nil)
         (p (point))
         beg
-        (last nil)
         (case-fold-search t))
      (save-excursion
        (goto-char (point-min))
@@ -2405,17 +2557,21 @@ new buffer."
        (Info-extract-menu-node-name nil (Info-index-node))))))
 
 ;; If COUNT is nil, use the last item in the menu.
-(defun Info-extract-menu-counting (count)
+(defun Info-extract-menu-counting (count &optional no-detail)
   (let ((case-fold-search t))
     (save-excursion
-      (let ((case-fold-search t))
+      (let ((case-fold-search t)
+           (bound (when (and no-detail
+                             (re-search-forward
+                              "^[ \t-]*The Detailed Node Listing" nil t))
+                    (match-beginning 0))))
        (goto-char (point-min))
-       (or (search-forward "\n* menu:" nil t)
+       (or (search-forward "\n* menu:" bound t)
            (error "No menu in this node"))
        (if count
-           (or (search-forward "\n* " nil t count)
+           (or (search-forward "\n* " bound t count)
                (error "Too few items in menu"))
-         (while (search-forward "\n* " nil t)
+         (while (search-forward "\n* " bound t)
            nil))
        (Info-extract-menu-node-name nil (Info-index-node))))))
 
@@ -2438,17 +2594,19 @@ N is the digit argument used to invoke this command."
   (Info-goto-node "Top")
   (let ((Info-history nil)
        (case-fold-search t))
-    ;; Go to the last node in the menu of Top.
-    (Info-goto-node (Info-extract-menu-counting nil))
+    ;; Go to the last node in the menu of Top.  But don't delve into
+    ;; detailed node listings.
+    (Info-goto-node (Info-extract-menu-counting nil t))
     ;; If the last node in the menu is not last in pointer structure,
-    ;; move forward until we can't go any farther.
-    (while (Info-forward-node t t) nil)
+    ;; move forward (but not down- or upward - see bug#1116) until we
+    ;; can't go any farther.
+    (while (Info-forward-node t t t) nil)
     ;; Then keep moving down to last subnode, unless we reach an index.
     (while (and (not (Info-index-node))
                (save-excursion (search-forward "\n* Menu:" nil t)))
       (Info-goto-node (Info-extract-menu-counting nil)))))
 
-(defun Info-forward-node (&optional not-down no-error)
+(defun Info-forward-node (&optional not-down not-up no-error)
   "Go forward one node, considering all nodes as forming one sequence."
   (interactive)
   (goto-char (point-min))
@@ -2466,7 +2624,8 @@ N is the digit argument used to invoke this command."
          ((save-excursion (search-backward "next:" nil t))
           (Info-next)
           t)
-         ((and (save-excursion (search-backward "up:" nil t))
+         ((and (not not-up)
+               (save-excursion (search-backward "up:" nil t))
                ;; Use string-equal, not equal, to ignore text props.
                (not (string-equal (downcase (Info-extract-pointer "up"))
                                   "top")))
@@ -2474,7 +2633,7 @@ N is the digit argument used to invoke this command."
             (Info-up)
             (let (Info-history success)
               (unwind-protect
-                  (setq success (Info-forward-node t no-error))
+                  (setq success (Info-forward-node t nil no-error))
                 (or success (Info-goto-node old-node))))))
          (no-error nil)
          (t (error "No pointer forward from this node")))))
@@ -2554,8 +2713,10 @@ N is the digit argument used to invoke this command."
         ;; go up to the end of this node.
         (goto-char (point-max))
         ;; Since logically we are done with the node with that menu,
-        ;; move on from it.
-        (Info-next-preorder))
+        ;; move on from it.  But don't add intermediate nodes
+        ;; to the history on recursive calls.
+        (let (Info-history)
+          (Info-next-preorder)))
        (t
         (error "No more nodes"))))
 
@@ -2727,17 +2888,17 @@ following nodes whose names also contain the word \"Index\"."
   (or file (setq file Info-current-file))
   (or (assoc file Info-index-nodes)
       ;; Skip virtual Info files
-      (and (member file '("dir" apropos history toc))
+      (and (or (not (stringp file))
+              (Info-virtual-file-p file))
            (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
-      (not (stringp file))
-      (if Info-file-supports-index-cookies
+      (if (Info-file-supports-index-cookies file)
          ;; Find nodes with index cookie
          (let* ((default-directory (or (and (stringp file)
                                             (file-name-directory
                                              (setq file (Info-find-file file))))
                                        default-directory))
                 Info-history Info-history-list Info-fontify-maximum-menu-size
-                (main-file file) subfiles nodes node)
+                (main-file file) subfiles nodes)
            (condition-case nil
                (with-temp-buffer
                  (while (or main-file subfiles)
@@ -2767,7 +2928,7 @@ following nodes whose names also contain the word \"Index\"."
            nodes)
        ;; Else find nodes with the word "Index" in the node name
        (let ((case-fold-search t)
-             Info-history Info-history-list Info-fontify-maximum-menu-size
+             Info-history Info-history-list Info-fontify-maximum-menu-size Info-point-loc
              nodes node)
          (condition-case nil
              (with-temp-buffer
@@ -2780,7 +2941,7 @@ following nodes whose names also contain the word \"Index\"."
                  (Info-goto-node (car nodes))
                  (while (and (setq node (Info-extract-pointer "next" t))
                              (string-match "\\<Index\\>" node))
-                   (setq nodes (cons node nodes))
+                   (push node nodes)
                    (Info-goto-node node))))
            (error nil))
          (if nodes
@@ -2795,21 +2956,23 @@ following nodes whose names also contain the word \"Index\"."
   "Return non-nil value if NODE is an index node.
 If NODE is nil, check the current Info node.
 If FILE is nil, check the current Info file."
-  (if (or (and node (not (equal node Info-current-node)))
-          (assoc (or file Info-current-file) Info-index-nodes))
+  (or file (setq file Info-current-file))
+  (if (and (or (and node (not (equal node Info-current-node)))
+              (assoc file Info-index-nodes))
+          (not Info-current-node-virtual))
       (member (or node Info-current-node) (Info-index-nodes file))
     ;; Don't search all index nodes if request is only for the current node
     ;; and file is not in the cache of index nodes
-    (if Info-file-supports-index-cookies
-       (save-excursion
-         (goto-char (+ (or (save-excursion
-                             (search-backward "\n\^_" nil t))
-                           (point-min)) 2))
-         (search-forward "\0\b[index\0\b]"
-                         (or (save-excursion
-                               (search-forward "\n\^_" nil t))
-                             (point-max)) t))
-      (save-match-data
+    (save-match-data
+      (if (Info-file-supports-index-cookies file)
+         (save-excursion
+           (goto-char (+ (or (save-excursion
+                               (search-backward "\n\^_" nil t))
+                             (point-min)) 2))
+           (search-forward "\0\b[index\0\b]"
+                           (or (save-excursion
+                                 (search-forward "\n\^_" nil t))
+                               (point-max)) t))
        (string-match "\\<Index\\>" (or node Info-current-node ""))))))
 
 (defun Info-goto-index ()
@@ -2935,11 +3098,163 @@ Give an empty topic name to go to the Index node itself."
                  (Info-find-index-name (match-string 1 name))))
        (progn (beginning-of-line) t)  ;; non-nil for recursive call
       (goto-char (point-min)))))
-
-;;;###autoload
-(defun info-apropos (string)
-  "Grovel indices of all known Info files on your system for STRING.
-Build a menu of the possible matches."
+\f
+(add-to-list 'Info-virtual-nodes
+            '("\\`\\*Index.*\\*\\'"
+              (find-node . Info-virtual-index-find-node)
+              ))
+
+(defvar Info-virtual-index-nodes nil
+  "Alist of cached matched index search nodes.
+Each element is ((FILENAME . TOPIC) MATCHES) where
+FILENAME is the file name of the manual,
+TOPIC is the search string given as an argument to `Info-virtual-index',
+MATCHES is a list of index matches found by `Info-index'.")
+
+(defun Info-virtual-index-find-node (filename nodename &optional no-going-back)
+  "Index-specific implementation of Info-find-node-2."
+  ;; Generate Index-like menu of matches
+  (if (string-match "^\\*Index for `\\(.+\\)'\\*$" nodename)
+      ;; Generate Index-like menu of matches
+      (let* ((topic (match-string 1 nodename))
+            (matches (cdr (assoc (cons (or filename Info-current-file) topic)
+                                 Info-virtual-index-nodes))))
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: *Index*\n\n"
+                       (or filename Info-current-file) nodename))
+       (insert "Info Virtual Index\n")
+       (insert "******************\n\n")
+       (insert "Index entries that match `" topic "':\n\n")
+       (insert "\0\b[index\0\b]\n")
+       (if (null matches)
+           (insert "No matches found.\n")
+         (insert "* Menu:\n\n")
+         (dolist (entry matches)
+           (insert (format "* %-38s %s.%s\n"
+                           (format "%s [%s]:" (nth 0 entry) (nth 2 entry))
+                           (nth 1 entry)
+                           (if (nth 3 entry)
+                               (format " (line %s)" (nth 3 entry))
+                             ""))))))
+    ;; Else, Generate a list of previous search results
+    (let ((nodes (reverse Info-virtual-index-nodes)))
+      (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                     (or filename Info-current-file) nodename))
+      (insert "Info Virtual Index\n")
+      (insert "******************\n\n")
+      (insert "This is a list of search results produced by\n"
+             "`Info-virtual-index' for the current manual.\n\n")
+      (insert "* Menu:\n\n")
+      (dolist (nodeinfo nodes)
+       (when (equal (car (nth 0 nodeinfo)) (or filename Info-current-file))
+         (insert
+          (format "* %-20s %s.\n"
+                  (format "*Index for `%s'*::" (cdr (nth 0 nodeinfo)))
+                  (cdr (nth 0 nodeinfo)))))))))
+
+(defun Info-virtual-index (topic)
+  "Show a node with all lines in the index containing a string TOPIC.
+Like `Info-index' but displays a node with index search results.
+Give an empty topic name to go to the node with links to previous
+search results."
+  ;; `interactive' is a copy from `Info-index'
+  (interactive
+   (list
+    (let ((completion-ignore-case t)
+         (Info-complete-menu-buffer (clone-buffer))
+         (Info-complete-nodes (Info-index-nodes))
+         (Info-history-list nil))
+      (if (equal Info-current-file "dir")
+         (error "The Info directory node has no index; use m to select a manual"))
+      (unwind-protect
+         (with-current-buffer Info-complete-menu-buffer
+           (Info-goto-index)
+           (completing-read "Index topic: " 'Info-complete-menu-item))
+       (kill-buffer Info-complete-menu-buffer)))))
+  (if (equal topic "")
+      (Info-find-node Info-current-file "*Index*")
+    (unless (assoc (cons Info-current-file topic) Info-virtual-index-nodes)
+      (let ((orignode Info-current-node)
+           (ohist-list Info-history-list)
+           nodename)
+       ;; Reuse `Info-index' to set `Info-index-alternatives'.
+       (Info-index topic)
+       (push (cons (cons Info-current-file topic) Info-index-alternatives)
+             Info-virtual-index-nodes)
+       ;; Clean up unneccessary side-effects of `Info-index'.
+       (setq Info-history-list ohist-list)
+       (Info-goto-node orignode)
+       (message "")))
+    (Info-find-node Info-current-file (format "*Index for `%s'*" topic))))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*Apropos\\*\\'"
+              (toc-nodes . Info-apropos-toc-nodes)
+              (find-file . Info-apropos-find-file)
+              (find-node . Info-apropos-find-node)
+              ))
+
+(defvar Info-apropos-file "*Apropos*"
+  "Info file name of the virtual manual for matches of `info-apropos'.")
+
+(defvar Info-apropos-nodes nil
+  "Alist of cached apropos matched nodes.
+Each element is (NODENAME STRING MATCHES) where
+NODENAME is the name of the node that holds the search result,
+STRING is the search string given as an argument to `info-apropos',
+MATCHES is a list of index matches found by `Info-apropos-matches'.")
+
+(defun Info-apropos-toc-nodes (filename)
+  "Apropos-specific implementation of Info-apropos-toc-nodes."
+  (let ((nodes (mapcar 'car (reverse Info-apropos-nodes))))
+    `(,filename
+      ("Top" nil nil ,nodes)
+      ,@(mapcar (lambda (node) `(,node "Top" nil nil)) nodes))))
+
+(defun Info-apropos-find-file (filename &optional noerror)
+  "Apropos-specific implementation of Info-find-file."
+  filename)
+
+(defun Info-apropos-find-node (filename nodename &optional no-going-back)
+  "Apropos-specific implementation of Info-find-node-2."
+  (if (equal nodename "Top")
+      ;; Generate Top menu
+      (let ((nodes (reverse Info-apropos-nodes)))
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                       Info-apropos-file nodename))
+       (insert "Apropos Index\n")
+       (insert "*************\n\n")
+       (insert "This is a list of search results produced by `info-apropos'.\n\n")
+       (insert "* Menu:\n\n")
+       (dolist (nodeinfo nodes)
+         (insert (format "* %-20s %s.\n"
+                         (format "%s::" (nth 0 nodeinfo))
+                         (nth 1 nodeinfo)))))
+    ;; Else, Generate Index-like menu of matches
+    (let* ((nodeinfo (assoc nodename Info-apropos-nodes))
+          (matches (nth 2 nodeinfo)))
+      (when matches
+       (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                       Info-apropos-file nodename))
+       (insert "Apropos Index\n")
+       (insert "*************\n\n")
+       (insert "Index entries that match `" (nth 1 nodeinfo) "':\n\n")
+       (insert "\0\b[index\0\b]\n")
+       (if (eq matches t)
+           (insert "No matches found.\n")
+         (insert "* Menu:\n\n")
+         (dolist (entry matches)
+           (insert (format "* %-38s (%s)%s.%s\n"
+                           (format "%s [%s]:" (nth 1 entry) (nth 0 entry))
+                           (nth 0 entry)
+                           (nth 2 entry)
+                           (if (nth 3 entry)
+                               (format " (line %s)" (nth 3 entry))
+                             "")))))))))
+
+(defun Info-apropos-matches (string)
+  "Collect STRING matches from all known Info files on your system.
+Return a list of matches where each element is in the format
+\((FILENAME INDEXTEXT NODENAME LINENUMBER))."
   (interactive "sIndex apropos: ")
   (unless (string= string "")
     (let ((pattern (format "\n\\* +\\([^\n]*%s[^\n]*\\):[ \t]+\\([^\n]+\\)\\.\\(?:[ \t\n]*(line +\\([0-9]+\\))\\)?"
@@ -2991,24 +3306,107 @@ Build a menu of the possible matches."
       (setq Info-history ohist
            Info-history-list ohist-list)
       (message "Searching indices...done")
-      (if (null matches)
-         (message "No matches found")
-       (with-current-buffer (get-buffer-create " *info-apropos*")
-         (erase-buffer)
-         (insert "\n\^_\nFile: apropos, Node: Index, Up: (dir)\n")
-         (insert "* Menu: \nNodes whose indices contain `" string "':\n\n")
-         (dolist (entry (nreverse matches))
-           (insert
-            (format "* %-38s (%s)%s.%s\n"
-                    (concat (nth 1 entry) " [" (nth 0 entry) "]:")
-                    (nth 0 entry)
-                    (nth 2 entry)
-                    (if (nth 3 entry)
-                        (concat " (line " (nth 3 entry) ")")
-                      "")))))
-       (Info-find-node 'apropos "Index")
-       (setq Info-complete-cache nil)))))
+      (or (nreverse matches) t))))
+
+;;;###autoload
+(defun info-apropos (string)
+  "Grovel indices of all known Info files on your system for STRING.
+Build a menu of the possible matches."
+  (interactive "sIndex apropos: ")
+  (if (equal string "")
+      (Info-find-node Info-apropos-file "Top")
+    (let* ((nodes Info-apropos-nodes) nodename)
+      (while (and nodes (not (equal string (nth 1 (car nodes)))))
+       (setq nodes (cdr nodes)))
+      (if nodes
+         (Info-find-node Info-apropos-file (car (car nodes)))
+       (setq nodename (format "Index for `%s'" string))
+       (push (list nodename string (Info-apropos-matches string))
+             Info-apropos-nodes)
+       (Info-find-node Info-apropos-file nodename)))))
+\f
+(add-to-list 'Info-virtual-files
+            '("\\`\\*Finder.*\\*\\'"
+              (find-file . Info-finder-find-file)
+              (find-node . Info-finder-find-node)
+              ))
+
+(defvar Info-finder-file "*Finder*"
+  "Info file name of the virtual Info keyword finder manual.")
+
+(defun Info-finder-find-file (filename &optional noerror)
+  "Finder-specific implementation of Info-find-file."
+  filename)
+
+(defvar finder-known-keywords)
+(defvar finder-package-info)
+(declare-function find-library-name "find-func" (library))
+(declare-function lm-commentary "lisp-mnt" (&optional file))
+
+(defun Info-finder-find-node (filename nodename &optional no-going-back)
+  "Finder-specific implementation of Info-find-node-2."
+  (cond
+   ((equal nodename "Top")
+    ;; Display Top menu with descriptions of the keywords
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: (dir)\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Keywords\n")
+    (insert "***************\n\n")
+    (insert "* Menu:\n\n")
+    (mapc
+     (lambda (assoc)
+       (let ((keyword (car assoc)))
+        (insert (format "* %-14s %s.\n"
+                        (concat (symbol-name keyword) "::")
+                        (cdr assoc)))))
+     finder-known-keywords))
+   ((string-match-p "\\.el\\'" nodename)
+    ;; Display commentary section
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Commentary\n")
+    (insert "*****************\n\n")
+    (insert
+     "Commentary section of the package `" nodename "':\n\n")
+    (let ((str (lm-commentary (find-library-name nodename))))
+      (if (null str)
+         (insert "Can't find any Commentary section\n\n")
+       (insert
+        (with-temp-buffer
+          (insert str)
+          (goto-char (point-min))
+          (delete-blank-lines)
+          (goto-char (point-max))
+          (delete-blank-lines)
+          (goto-char (point-min))
+          (while (re-search-forward "^;+ ?" nil t)
+            (replace-match "" nil nil))
+          (buffer-string))))))
+   (t
+    ;; Display packages that match the keyword
+    (insert (format "\n\^_\nFile: %s,  Node: %s,  Up: Top\n\n"
+                   Info-finder-file nodename))
+    (insert "Finder Packages\n")
+    (insert "***************\n\n")
+    (insert
+     "The following packages match the keyword `" nodename "':\n\n")
+    (insert "* Menu:\n\n")
+    (let ((id (intern nodename)))
+      (mapc
+       (lambda (x)
+        (when (memq id (cadr (cdr x)))
+          (insert (format "* %-16s %s.\n"
+                          (concat (car x) "::")
+                          (cadr x)))))
+       finder-package-info)))))
 
+;;;###autoload
+(defun info-finder ()
+  "Display descriptions of the keywords in the Finder virtual manual."
+  (interactive)
+  (require 'finder)
+  (Info-find-node Info-finder-file "Top"))
+\f
 (defun Info-undefined ()
   "Make command be undefined in Info."
   (interactive)
@@ -3090,7 +3488,7 @@ Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \
 At end of the node's text, moves to the next node, or up if none."
   (interactive "e")
   (mouse-set-point click)
-  (and (not (Info-try-follow-nearest-node))
+  (and (not (Info-follow-nearest-node))
        (save-excursion (forward-line 1) (eobp))
        (Info-next-preorder)))
 
@@ -3114,17 +3512,22 @@ If FORK is a string, it is the name to use for the new buffer."
            (Info-goto-node
             (Info-extract-menu-item (match-string-no-properties 1)) fork)
            t)))
+      (and (eq this-command 'Info-mouse-follow-nearest-node)
+          ;; Don't raise an error when mouse-1 is bound to this - it's
+          ;; often used to simply select the window or frame.
+          (eq 'mouse-1 (event-basic-type last-input-event)))
       (error "Point neither on reference nor in menu item description")))
 
 ;; Common subroutine.
 (defun Info-try-follow-nearest-node (&optional fork)
   "Follow a node reference near point.  Return non-nil if successful.
-If FORK is non-nil, it i spassed to `Info-goto-node'."
+If FORK is non-nil, it ipassed to `Info-goto-node'."
   (let (node)
     (cond
-     ((Info-get-token (point) "[hf]t?tps?://" "[hf]t?tps?://\\([^ \t\n\"`({<>})']+\\)")
-      (setq node t)
-      (browse-url (browse-url-url-at-point)))
+     ((setq node (Info-get-token (point) "[hf]t?tps?://"
+                                "\\([hf]t?tps?://[^ \t\n\"`({<>})']+\\)"))
+      (browse-url node)
+      (setq node t))
      ((setq node (Info-get-token (point) "\\*note[ \n\t]+"
                                 "\\*note[ \n\t]+\\([^:]*\\):\\(:\\|[ \n\t]*(\\)?"))
       (Info-follow-reference node fork))
@@ -3147,66 +3550,64 @@ If FORK is non-nil, it i spassed to `Info-goto-node'."
       (Info-goto-node node fork)))
     node))
 \f
-(defvar Info-mode-map nil
+(defvar Info-mode-map
+  (let ((map (make-keymap)))
+    (suppress-keymap map)
+    (define-key map "." 'beginning-of-buffer)
+    (define-key map " " 'Info-scroll-up)
+    (define-key map "\C-m" 'Info-follow-nearest-node)
+    (define-key map "\t" 'Info-next-reference)
+    (define-key map "\e\t" 'Info-prev-reference)
+    (define-key map [(shift tab)] 'Info-prev-reference)
+    (define-key map [backtab] 'Info-prev-reference)
+    (define-key map "1" 'Info-nth-menu-item)
+    (define-key map "2" 'Info-nth-menu-item)
+    (define-key map "3" 'Info-nth-menu-item)
+    (define-key map "4" 'Info-nth-menu-item)
+    (define-key map "5" 'Info-nth-menu-item)
+    (define-key map "6" 'Info-nth-menu-item)
+    (define-key map "7" 'Info-nth-menu-item)
+    (define-key map "8" 'Info-nth-menu-item)
+    (define-key map "9" 'Info-nth-menu-item)
+    (define-key map "0" 'undefined)
+    (define-key map "?" 'Info-summary)
+    (define-key map "]" 'Info-forward-node)
+    (define-key map "[" 'Info-backward-node)
+    (define-key map "<" 'Info-top-node)
+    (define-key map ">" 'Info-final-node)
+    (define-key map "b" 'beginning-of-buffer)
+    (define-key map "d" 'Info-directory)
+    (define-key map "e" 'Info-edit)
+    (define-key map "f" 'Info-follow-reference)
+    (define-key map "g" 'Info-goto-node)
+    (define-key map "h" 'Info-help)
+    (define-key map "i" 'Info-index)
+    (define-key map "I" 'Info-virtual-index)
+    (define-key map "l" 'Info-history-back)
+    (define-key map "L" 'Info-history)
+    (define-key map "m" 'Info-menu)
+    (define-key map "n" 'Info-next)
+    (define-key map "p" 'Info-prev)
+    (define-key map "q" 'Info-exit)
+    (define-key map "r" 'Info-history-forward)
+    (define-key map "s" 'Info-search)
+    (define-key map "S" 'Info-search-case-sensitively)
+    (define-key map "\M-n" 'clone-buffer)
+    (define-key map "t" 'Info-top-node)
+    (define-key map "T" 'Info-toc)
+    (define-key map "u" 'Info-up)
+    ;; `w' for consistency with `dired-copy-filename-as-kill'.
+    (define-key map "w" 'Info-copy-current-node-name)
+    (define-key map "c" 'Info-copy-current-node-name)
+    ;; `^' for consistency with `dired-up-directory'.
+    (define-key map "^" 'Info-up)
+    (define-key map "," 'Info-index-next)
+    (define-key map "\177" 'Info-scroll-down)
+    (define-key map [mouse-2] 'Info-mouse-follow-nearest-node)
+    (define-key map [follow-link] 'mouse-face)
+    map)
   "Keymap containing Info commands.")
-(if Info-mode-map
-    nil
-  (setq Info-mode-map (make-keymap))
-  (suppress-keymap Info-mode-map)
-  (define-key Info-mode-map "." 'beginning-of-buffer)
-  (define-key Info-mode-map " " 'Info-scroll-up)
-  (define-key Info-mode-map "\C-m" 'Info-follow-nearest-node)
-  (define-key Info-mode-map "\t" 'Info-next-reference)
-  (define-key Info-mode-map "\e\t" 'Info-prev-reference)
-  (define-key Info-mode-map [(shift tab)] 'Info-prev-reference)
-  (define-key Info-mode-map [backtab] 'Info-prev-reference)
-  (define-key Info-mode-map "1" 'Info-nth-menu-item)
-  (define-key Info-mode-map "2" 'Info-nth-menu-item)
-  (define-key Info-mode-map "3" 'Info-nth-menu-item)
-  (define-key Info-mode-map "4" 'Info-nth-menu-item)
-  (define-key Info-mode-map "5" 'Info-nth-menu-item)
-  (define-key Info-mode-map "6" 'Info-nth-menu-item)
-  (define-key Info-mode-map "7" 'Info-nth-menu-item)
-  (define-key Info-mode-map "8" 'Info-nth-menu-item)
-  (define-key Info-mode-map "9" 'Info-nth-menu-item)
-  (define-key Info-mode-map "0" 'undefined)
-  (define-key Info-mode-map "?" 'Info-summary)
-  (define-key Info-mode-map "]" 'Info-forward-node)
-  (define-key Info-mode-map "[" 'Info-backward-node)
-  (define-key Info-mode-map "<" 'Info-top-node)
-  (define-key Info-mode-map ">" 'Info-final-node)
-  (define-key Info-mode-map "b" 'beginning-of-buffer)
-  (define-key Info-mode-map "d" 'Info-directory)
-  (define-key Info-mode-map "e" 'Info-edit)
-  (define-key Info-mode-map "f" 'Info-follow-reference)
-  (define-key Info-mode-map "g" 'Info-goto-node)
-  (define-key Info-mode-map "h" 'Info-help)
-  (define-key Info-mode-map "i" 'Info-index)
-  (define-key Info-mode-map "l" 'Info-history-back)
-  (define-key Info-mode-map "L" 'Info-history)
-  (define-key Info-mode-map "m" 'Info-menu)
-  (define-key Info-mode-map "n" 'Info-next)
-  (define-key Info-mode-map "p" 'Info-prev)
-  (define-key Info-mode-map "q" 'Info-exit)
-  (define-key Info-mode-map "r" 'Info-history-forward)
-  (define-key Info-mode-map "s" 'Info-search)
-  (define-key Info-mode-map "S" 'Info-search-case-sensitively)
-  ;; For consistency with Rmail.
-  (define-key Info-mode-map "\M-s" 'Info-search)
-  (define-key Info-mode-map "\M-n" 'clone-buffer)
-  (define-key Info-mode-map "t" 'Info-top-node)
-  (define-key Info-mode-map "T" 'Info-toc)
-  (define-key Info-mode-map "u" 'Info-up)
-  ;; `w' for consistency with `dired-copy-filename-as-kill'.
-  (define-key Info-mode-map "w" 'Info-copy-current-node-name)
-  (define-key Info-mode-map "c" 'Info-copy-current-node-name)
-  ;; `^' for consistency with `dired-up-directory'.
-  (define-key Info-mode-map "^" 'Info-up)
-  (define-key Info-mode-map "," 'Info-index-next)
-  (define-key Info-mode-map "\177" 'Info-scroll-down)
-  (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
-  (define-key Info-mode-map [follow-link] 'mouse-face)
-  )
+
 
 (defun Info-check-pointer (item)
   "Non-nil if ITEM is present in this node."
@@ -3255,6 +3656,8 @@ If FORK is non-nil, it i spassed to `Info-goto-node'."
      :help "Look for a string in the index items"]
     ["Next Matching Item" Info-index-next :active Info-index-alternatives
      :help "Look for another occurrence of previous item"]
+    ["Lookup a string and display index of results..." Info-virtual-index
+     :help "Look for a string in the index items and display node with results"]
     ["Lookup a string in all indices..." info-apropos
      :help "Look for a string in the indices of all manuals"])
    ["Copy Node Name" Info-copy-current-node-name
@@ -3265,23 +3668,22 @@ If FORK is non-nil, it i spassed to `Info-goto-node'."
 
 
 (defvar info-tool-bar-map
-  (if (display-graphic-p)
-      (let ((map (make-sparse-keymap)))
-       (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map
-                                      :rtl "right-arrow")
-       (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map
-                                      :rtl "left-arrow")
-       (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map
-                                      :rtl "next-node")
-       (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map
-                                      :rtl "prev-node")
-       (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map)
-       map)))
+  (let ((map (make-sparse-keymap)))
+    (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map
+                                  :rtl "right-arrow")
+    (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map
+                                  :rtl "left-arrow")
+    (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map
+                                  :rtl "next-node")
+    (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map
+                                  :rtl "prev-node")
+    (tool-bar-local-item-from-menu 'Info-up "up-node" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-top-node "home" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-goto-node "jump-to" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-index "index" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-search "search" map Info-mode-map)
+    (tool-bar-local-item-from-menu 'Info-exit "exit" map Info-mode-map)
+    map))
 
 (defvar Info-menu-last-node nil)
 ;; Last node the menu was created for.
@@ -3374,7 +3776,7 @@ With a zero prefix arg, put the name inside a function call to `info'."
 (put 'Info-mode 'no-clone-indirect t)
 
 (defvar tool-bar-map)
-(defvar bookmark-make-cell-function)
+(defvar bookmark-make-record-function)
 
 ;; Autoload cookie needed by desktop.el
 ;;;###autoload
@@ -3427,10 +3829,10 @@ Advanced commands:
 \\[Info-search]        Search through this Info file for specified regexp,
          and select the node in which the next occurrence is found.
 \\[Info-search-case-sensitively]       Search through this Info file for specified regexp case-sensitively.
-\\[Info-search-next]   Search for another occurrence of regexp
-         from a previous \\<Info-mode-map>\\[Info-search] command.
+\\[isearch-forward], \\[isearch-forward-regexp]        Use Isearch to search through multiple Info nodes.
 \\[Info-index] Search for a topic in this manual's Index and go to index entry.
 \\[Info-index-next]    (comma) Move to the next match from a previous \\<Info-mode-map>\\[Info-index] command.
+\\[Info-virtual-index] Look for a string and display the index node with results.
 \\[info-apropos]       Look for a string in the indices of all manuals.
 \\[Info-goto-node]     Move to node specified by name.
          You may include a filename as well, as (FILENAME)NODENAME.
@@ -3460,10 +3862,9 @@ Advanced commands:
   (make-local-variable 'Info-history)
   (make-local-variable 'Info-history-forward)
   (make-local-variable 'Info-index-alternatives)
-  (setq header-line-format
-       (if Info-use-header-line
-           '(:eval (get-text-property (point-min) 'header-line))
-         nil)) ; so the header line isn't displayed
+  (if Info-use-header-line    ; do not override global header lines
+      (setq header-line-format
+           '(:eval (get-text-property (point-min) 'header-line))))
   (set (make-local-variable 'tool-bar-map) info-tool-bar-map)
   ;; This is for the sake of the invisible text we use handling titles.
   (make-local-variable 'line-move-ignore-invisible)
@@ -3482,13 +3883,15 @@ Advanced commands:
        'Info-isearch-wrap)
   (set (make-local-variable 'isearch-push-state-function)
        'Info-isearch-push-state)
+  (set (make-local-variable 'isearch-filter-predicate)
+       'Info-isearch-filter)
   (set (make-local-variable 'search-whitespace-regexp)
        Info-search-whitespace-regexp)
   (set (make-local-variable 'revert-buffer-function)
        'Info-revert-buffer-function)
   (Info-set-mode-line)
-  (set (make-local-variable 'bookmark-make-cell-function)
-       'Info-bookmark-make-cell)
+  (set (make-local-variable 'bookmark-make-record-function)
+       'Info-bookmark-make-record)
   (run-mode-hooks 'Info-mode-hook))
 
 ;; When an Info buffer is killed, make sure the associated tags buffer
@@ -3644,7 +4047,7 @@ in the first element of the returned list (which is treated specially in
              (cdr where))
       where)))
 
-;;;###autoload (put 'Info-goto-emacs-command-node 'info-file "emacs")
+;;;###autoload (put 'Info-goto-emacs-command-node 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun Info-goto-emacs-command-node (command)
   "Go to the Info node in the Emacs manual for command COMMAND.
@@ -3686,7 +4089,7 @@ COMMAND must be a symbol or string."
                         (if (> num-matches 2) "them" "it")))))
       (error "Couldn't find documentation for %s" command))))
 
-;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file "emacs")
+;;;###autoload (put 'Info-goto-emacs-key-command-node 'info-file (purecopy "emacs"))
 ;;;###autoload
 (defun Info-goto-emacs-key-command-node (key)
   "Go to the node in the Emacs manual which describes the command bound to KEY.
@@ -3699,7 +4102,7 @@ the variable `Info-file-list-for-emacs'."
   (let ((command (key-binding key)))
     (cond ((null command)
           (message "%s is undefined" (key-description key)))
-         ((and (interactive-p)
+         ((and (called-interactively-p 'interactive)
                (eq command 'execute-extended-command))
           (Info-goto-emacs-command-node
            (read-command "Find documentation for command: ")))
@@ -3736,6 +4139,47 @@ the variable `Info-file-list-for-emacs'."
     keymap)
   "Keymap to put on the Up link in the text or the header line.")
 
+(defun Info-insert-breadcrumbs ()
+  (let ((nodes (Info-toc-nodes Info-current-file))
+       (node Info-current-node)
+        (crumbs ())
+        (depth Info-breadcrumbs-depth))
+
+    ;; Get ancestors from the cached parent-children node info
+    (while (and (not (equal "Top" node)) (> depth 0))
+      (setq node (nth 1 (assoc node nodes)))
+      (if node (push node crumbs))
+      (setq depth (1- depth)))
+
+    ;; Add bottom node.
+    (when Info-use-header-line
+      ;; Let it disappear if crumbs is nil.
+      (nconc crumbs (list Info-current-node)))
+    (when (or Info-use-header-line crumbs)
+      ;; Add top node (and continuation if needed).
+      (setq crumbs
+           (cons "Top" (if (member (pop crumbs) '(nil "Top"))
+                           crumbs (cons nil crumbs))))
+      ;; Eliminate duplicate.
+      (forward-line 1)
+      (dolist (node crumbs)
+       (let ((text
+              (if (not (equal node "Top")) node
+                (format "(%s)Top"
+                        (if (stringp Info-current-file)
+                            (file-name-nondirectory Info-current-file)
+                          ;; Some legacy code can still use a symbol.
+                          Info-current-file)))))
+         (insert (if (bolp) "" " > ")
+                 (cond
+                  ((null node) "...")
+                  ((equal node Info-current-node)
+                   ;; No point linking to ourselves.
+                   (propertize text 'font-lock-face 'info-header-node))
+                  (t
+                   (concat "*Note " text "::"))))))
+      (insert "\n"))))
+
 (defun Info-fontify-node ()
   "Fontify the node."
   (save-excursion
@@ -3743,7 +4187,7 @@ the variable `Info-file-list-for-emacs'."
            (case-fold-search t)
            paragraph-markers
            (not-fontified-p ; the node hasn't already been fontified
-            (not (let ((where (next-single-property-change (point-min) 
+            (not (let ((where (next-single-property-change (point-min)
                                                           'font-lock-face)))
                    (and where (not (= where (point-max)))))))
            (fontify-visited-p ; visited nodes need to be re-fontified
@@ -3780,6 +4224,11 @@ the variable `Info-file-list-for-emacs'."
                ((string-equal (downcase tag) "prev") Info-prev-link-keymap)
                ((string-equal (downcase tag) "next") Info-next-link-keymap)
                ((string-equal (downcase tag) "up"  ) Info-up-link-keymap))))))
+
+        (when (> Info-breadcrumbs-depth 0)
+          (Info-insert-breadcrumbs))
+
+        ;; Treat header line.
         (when Info-use-header-line
           (goto-char (point-min))
           (let* ((header-end (line-end-position))
@@ -3807,10 +4256,13 @@ the variable `Info-file-list-for-emacs'."
                                (lambda (s) (concat s s)) header))
             ;; Hide the part of the first line
             ;; that is in the header, if it is just part.
-            (unless (bobp)
+            (cond
+             ((> Info-breadcrumbs-depth 0)
+              (put-text-property (point-min) (1+ header-end) 'invisible t))
+             ((not (bobp))
               ;; Hide the punctuation at the end, too.
               (skip-chars-backward " \t,")
-              (put-text-property (point) header-end 'invisible t)))))
+              (put-text-property (point) header-end 'invisible t))))))
 
       ;; Fontify titles
       (goto-char (point-min))
@@ -3834,7 +4286,7 @@ the variable `Info-file-list-for-emacs'."
           ;; This is a serious problem for trying to handle multiple
           ;; frame types at once.  We want this text to be invisible
           ;; on frames that can display the font above.
-          (when (memq (framep (selected-frame)) '(x pc w32 mac))
+          (when (memq (framep (selected-frame)) '(x pc w32 ns))
             (add-text-properties (1- (match-beginning 2)) (match-end 2)
                                  '(invisible t front-sticky nil rear-nonsticky t)))))
 
@@ -3847,7 +4299,8 @@ the variable `Info-file-list-for-emacs'."
                 other-tag)
             (when not-fontified-p
               (when Info-hide-note-references
-                (when (not (eq Info-hide-note-references 'hide))
+                (when (and (not (eq Info-hide-note-references 'hide))
+                           (> (line-number-at-pos) 4)) ; Skip breadcrumbs
                   ;; *Note is often used where *note should have been
                   (goto-char start)
                   (skip-syntax-backward " ")
@@ -4094,8 +4547,8 @@ the variable `Info-file-list-for-emacs'."
                                   nil t)
           (add-text-properties (match-beginning 0) (match-end 0)
                                '(font-lock-face info-xref
-                                                mouse-face highlight
-                                                help-echo "mouse-2: go to this URL"))))
+                                 mouse-face highlight
+                                 help-echo "mouse-2: go to this URL"))))
 
       (set-buffer-modified-p nil))))
 \f
@@ -4234,9 +4687,8 @@ INDENT is the current indentation depth."
 (defun Info-speedbar-fetch-file-nodes (nodespec)
   "Fetch the subnodes from the info NODESPEC.
 NODESPEC is a string of the form: (file)node."
-  (save-excursion
-    ;; Set up a buffer we can use to fake-out Info.
-    (set-buffer (get-buffer-create " *info-browse-tmp*"))
+  ;; Set up a buffer we can use to fake-out Info.
+  (with-current-buffer (get-buffer-create " *info-browse-tmp*")
     (if (not (equal major-mode 'Info-mode))
        (Info-mode))
     ;; Get the node into this buffer
@@ -4300,7 +4752,7 @@ BUFFER is the buffer speedbar is requesting buttons for."
 
 (defun Info-desktop-buffer-misc-data (desktop-dirname)
   "Auxiliary information to be saved in desktop file."
-  (unless (member Info-current-file '(apropos history toc nil))
+  (unless (Info-virtual-file-p Info-current-file)
     (list Info-current-file Info-current-node)))
 
 (defun Info-restore-desktop-buffer (desktop-buffer-file-name
@@ -4320,95 +4772,32 @@ BUFFER is the buffer speedbar is requesting buttons for."
             '(Info-mode . Info-restore-desktop-buffer))
 
 ;;;; Bookmark support
-
-(defvar bookmark-search-size)
-
-;; This is only called from bookmark.el.
-(declare-function bookmark-buffer-file-name "bookmark" ())
-
-(defun Info-bookmark-make-cell (annotation &optional info-node)
-  (let ((the-record
-         `((filename . ,(bookmark-buffer-file-name))
-           (front-context-string
-            . ,(if (>= (- (point-max) (point)) bookmark-search-size)
-                   (buffer-substring-no-properties
-                    (point)
-                    (+ (point) bookmark-search-size))
-                nil))
-           (rear-context-string
-            . ,(if (>= (- (point) (point-min)) bookmark-search-size)
-                   (buffer-substring-no-properties
-                    (point)
-                    (- (point) bookmark-search-size))
-                nil))
-           (position . ,(point))
-          (info-node . ,info-node)
-          (handler . Info-bookmark-jump))))
-
-    ;; Now fill in the optional parts:
-
-    ;; Take no chances with text properties
-    (set-text-properties 0 (length annotation) nil annotation)
-
-    (if annotation
-        (nconc the-record (list (cons 'annotation annotation))))
-
-    ;; Finally, return the completed record.
-    the-record))
-
-(defvar bookmark-current-bookmark)
-(declare-function bookmark-get-filename              "bookmark" (bookmark))
-(declare-function bookmark-get-front-context-string  "bookmark" (bookmark))
-(declare-function bookmark-get-rear-context-string   "bookmark" (bookmark))
-(declare-function bookmark-get-position              "bookmark" (bookmark))
-(declare-function bookmark-get-info-node             "bookmark" (bookmark))
-(declare-function bookmark-file-or-variation-thereof "bookmark" (file))
-(declare-function bookmark-jump-noselect             "bookmark" (str))
+(declare-function bookmark-make-record-default "bookmark" (&optional pos-only))
+(declare-function bookmark-prop-get "bookmark" (bookmark prop))
+(declare-function bookmark-default-handler "bookmark" (bmk))
+(declare-function bookmark-get-bookmark-record "bookmark" (bmk))
+
+(defun Info-bookmark-make-record ()
+  "This implements the `bookmark-make-record-function' type (which see)
+for Info nodes."
+  `(,Info-current-node
+    ,@(bookmark-make-record-default 'point-only)
+    (filename . ,Info-current-file)
+    (info-node . ,Info-current-node)
+    (handler . Info-bookmark-jump)))
 
 ;;;###autoload
 (defun Info-bookmark-jump (bmk)
-  ;; This implements the `handler' function interface for record type returned
-  ;; by `Info-make-cell-function', which see.
-  (let* ((file (expand-file-name (bookmark-get-filename bmk)))
-         (forward-str            (bookmark-get-front-context-string bmk))
-         (behind-str             (bookmark-get-rear-context-string bmk))
-         (place                  (bookmark-get-position bmk))
-        (info-node              (bookmark-get-info-node bmk))
-        (orig-file              file))
-    (if (setq file (bookmark-file-or-variation-thereof file))
-        (save-excursion
-          (save-window-excursion
-           (with-no-warnings
-             (Info-find-node file info-node))
-           ;; Go searching forward first.  Then, if forward-str exists and was
-            ;; found in the file, we can search backward for behind-str.
-            ;; Rationale is that if text was inserted between the two in the
-            ;; file, it's better to be put before it so you can read it, rather
-            ;; than after and remain perhaps unaware of the changes.
-            (if forward-str
-                (if (search-forward forward-str (point-max) t)
-                    (goto-char (match-beginning 0))))
-            (if behind-str
-                (if (search-backward behind-str (point-min) t)
-                    (goto-char (match-end 0))))
-            ;; added by db
-            (setq bookmark-current-bookmark bmk)
-           `((buffer ,(current-buffer)) (position ,(point)))))
-
-      ;; Else unable to find the marked file, so ask if user wants to
-      ;; relocate the bookmark, else remind them to consider deletion.
-      (ding)
-      (if (y-or-n-p (concat (file-name-nondirectory orig-file)
-                            " nonexistent.  Relocate \""
-                            bmk
-                            "\"? "))
-          (progn
-            (bookmark-relocate bmk)
-            ;; gasp!  It's a recursive function call in Emacs Lisp!
-            (bookmark-jump-noselect bmk))
-        (message
-         "Bookmark not relocated; consider removing it \(%s\)." bmk)
-        nil))))
+  "This implements the `handler' function interface for the record
+type returned by `Info-bookmark-make-record', which see."
+  (let* ((file                   (bookmark-prop-get bmk 'filename))
+         (info-node              (bookmark-prop-get bmk 'info-node))
+         (buf (save-window-excursion    ;FIXME: doesn't work with frames!
+                (Info-find-node file info-node) (current-buffer))))
+    ;; Use bookmark-default-handler to move to the appropriate location
+    ;; within the node.
+    (bookmark-default-handler
+     (list* "" `(buffer . ,buf) (bookmark-get-bookmark-record bmk)))))
 
 (provide 'info)