]> code.delx.au - gnu-emacs/blobdiff - lisp/info.el
(tex-uptodate-p): Accept [1{/var/foo}] as a page number.
[gnu-emacs] / lisp / info.el
index b274c8258a4fcf391c1ddd091e018b52115fdd1f..4a90b3e65bba4dd3a142b9fd7170cca9998bce8f 100644 (file)
@@ -1,7 +1,7 @@
 ;;; 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
 ;;   Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -34,7 +34,7 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'jka-compr))
+(eval-when-compile (require 'jka-compr) (require 'cl))
 
 (defgroup info nil
   "Info subsystem."
@@ -55,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,
@@ -139,19 +139,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)
@@ -203,7 +203,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]
@@ -218,10 +218,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)
@@ -230,15 +231,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,
@@ -247,7 +254,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
@@ -305,6 +312,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.")
 
@@ -449,13 +461,43 @@ Do the right thing if the file has been compressed or zipped."
          (insert-file-contents-literally fullname visit)
          (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))
+              (member file '("dir" apropos history toc)))
+           (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))
@@ -543,7 +585,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))
@@ -841,18 +885,8 @@ a case-insensitive match is tried."
                 (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))
+             (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))
@@ -962,6 +996,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))))
@@ -971,7 +1009,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)
@@ -1058,7 +1096,10 @@ a case-insensitive match is tried."
                      (or buffers
                          (message "Composing main Info directory..."))
                      (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)
@@ -1209,19 +1250,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.
@@ -1623,7 +1665,7 @@ If DIRECTION is `backward', search in the reverse direction."
                              (point-max)))
          (while (and (not give-up)
                      (or (null found)
-                         (not (funcall isearch-success-function beg-found found))))
+                         (not (funcall isearch-filter-predicate beg-found found))))
            (let ((search-spaces-regexp
                   (if (or (not isearch-mode) isearch-regexp)
                       Info-search-whitespace-regexp)))
@@ -1703,7 +1745,7 @@ If DIRECTION is `backward', search in the reverse direction."
                (setq give-up nil found nil)
                (while (and (not give-up)
                            (or (null found)
-                               (not (funcall isearch-success-function beg-found found))))
+                               (not (funcall isearch-filter-predicate beg-found found))))
                  (let ((search-spaces-regexp
                         (if (or (not isearch-mode) isearch-regexp)
                             Info-search-whitespace-regexp)))
@@ -1778,7 +1820,12 @@ 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))
@@ -1808,19 +1855,25 @@ 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))
-
-(defun Info-search-success-function (beg-found found)
-  "Skip invisible text, node header line and Tag Table node."
+  (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
-       (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)))
+       (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 "\^_"))
@@ -1991,14 +2044,14 @@ Table of contents is created from the tree structure of menus."
            p)
        (with-current-buffer (get-buffer-create " *info-toc*")
          (let ((inhibit-read-only t)
-               (node-list (Info-build-toc curr-file)))
+               (node-list (Info-toc-nodes 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
+            (nth 3 (assoc "Top" node-list)) ; get Top nodes
             node-list 0 curr-file))
          (if (not (bobp))
              (let ((Info-hide-note-references 'hide)
@@ -2022,11 +2075,11 @@ Table of contents is created from the tree structure of menus."
   (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)
+        (Info-insert-toc (nth 3 node) node-list (1+ level) curr-file)
         (setq nodes (cdr nodes))))))
 
 (defun Info-build-toc (file)
@@ -2040,17 +2093,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)
@@ -2078,7 +2136,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))
@@ -2096,6 +2154,32 @@ 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 (file)
+  "Return a node list of Info FILE with parent-children information.
+This information is cached in the variable `Info-toc-nodes' with the help
+of the function `Info-build-toc'."
+  (or file (setq file Info-current-file))
+  (or (assoc file Info-toc-nodes)
+      ;; Skip virtual Info files
+      (and (or (not (stringp file))
+              (member file '("dir" apropos history toc)))
+           (push (cons file nil) Info-toc-nodes))
+      ;; Scan the entire manual and cache the result in Info-toc-nodes
+      (let ((nodes (Info-build-toc file)))
+       (push (cons file nodes) Info-toc-nodes)
+       nodes)
+      ;; If there is an error, still add nil to the cache
+      (push (cons file nil) Info-toc-nodes))
+  (cdr (assoc file Info-toc-nodes)))
+
 \f
 (defun Info-follow-reference (footnotename &optional fork)
   "Follow cross reference named FOOTNOTENAME to the node it refers to.
@@ -2376,17 +2460,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))))))
 
@@ -2409,17 +2497,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))
@@ -2437,7 +2527,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")))
@@ -2445,7 +2536,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")))))
@@ -2700,10 +2791,10 @@ 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))
+              (member file '("dir" apropos history toc)))
            (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
@@ -2740,7 +2831,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
@@ -2768,12 +2859,13 @@ 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."
+  (or file (setq file Info-current-file))
   (if (or (and node (not (equal node Info-current-node)))
-          (assoc (or file Info-current-file) Info-index-nodes))
+          (assoc file Info-index-nodes))
       (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
+    (if (Info-file-supports-index-cookies file)
        (save-excursion
          (goto-char (+ (or (save-excursion
                              (search-backward "\n\^_" nil t))
@@ -3063,7 +3155,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)))
 
@@ -3087,12 +3179,16 @@ 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\"`({<>})']+\\)")
@@ -3161,8 +3257,6 @@ If FORK is non-nil, it i spassed to `Info-goto-node'."
     (define-key map "r" 'Info-history-forward)
     (define-key map "s" 'Info-search)
     (define-key map "S" 'Info-search-case-sensitively)
-    ;; For consistency with Rmail.
-    (define-key map "\M-s" 'Info-search)
     (define-key map "\M-n" 'clone-buffer)
     (define-key map "t" 'Info-top-node)
     (define-key map "T" 'Info-toc)
@@ -3237,23 +3331,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.
@@ -3401,6 +3494,7 @@ Advanced commands:
 \\[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-apropos]       Look for a string in the indices of all manuals.
@@ -3432,10 +3526,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)
@@ -3454,8 +3547,8 @@ Advanced commands:
        'Info-isearch-wrap)
   (set (make-local-variable 'isearch-push-state-function)
        'Info-isearch-push-state)
-  (set (make-local-variable 'isearch-success-function)
-       'Info-search-success-function)
+  (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)
@@ -3710,49 +3803,46 @@ the variable `Info-file-list-for-emacs'."
     keymap)
   "Keymap to put on the Up link in the text or the header line.")
 
-(defcustom Info-breadcrumbs-depth 3
-  "Depth of breadcrumbs to display.
-0 means do not display breadcrumbs."
-  :type 'integer)
-
 (defun Info-insert-breadcrumbs ()
-  (let ((onode Info-current-node)
+  (let ((nodes (Info-toc-nodes Info-current-file))
+       (node Info-current-node)
         (crumbs ())
-        (depth Info-breadcrumbs-depth)
-        (Info-fontify-maximum-menu-size nil)) ; Prevent infinite recursion.
-    (unwind-protect
-        (while (and (not (equal "Top" Info-current-node)) (> depth 0))
-          (let ((up (Info-extract-pointer "up")))
-            (push up crumbs)
-            (setq depth (1- depth))
-            (Info-find-node Info-current-file up 'no-going-back)))
-      (if crumbs                  ;Do bother going back if we haven't moved.
-          (Info-find-node Info-current-file onode 'no-going-back))
-      ;; 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"
-                             (file-name-nondirectory 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")))))
+        (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)
+                          ;; Can be `toc', `apropos', or even `history'.
+                          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."
@@ -3798,10 +3888,10 @@ 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))
@@ -3860,7 +3950,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)))))
 
@@ -4346,62 +4436,30 @@ 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" ())
+(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 ()
   `(,Info-current-node
-    (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))
+    ,@(bookmark-make-record-default 'point-only)
+    (filename . ,Info-current-file)
     (info-node . ,Info-current-node)
     (handler . Info-bookmark-jump)))
 
-
-(defvar bookmark-current-bookmark)
-(declare-function bookmark-prop-get                  "bookmark" (bookmark prop))
-(declare-function bookmark-file-or-variation-thereof "bookmark" (file))
-(declare-function bookmark-jump-noselect             "bookmark" (str))
-(declare-function bookmark-get-bookmark-record       "bookmark" (bookmark))
-
 ;;;###autoload
 (defun Info-bookmark-jump (bmk)
   ;; This implements the `handler' function interface for record type returned
   ;; by `Info-bookmark-make-record', which see.
-  (let* ((file (expand-file-name (bookmark-prop-get bmk 'filename)))
-         (forward-str            (bookmark-prop-get bmk 'front-context-string))
-         (behind-str             (bookmark-prop-get bmk 'rear-context-string))
-        (info-node              (bookmark-prop-get bmk 'info-node)))
-    (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))))
-           `((buffer ,(current-buffer)) (position ,(point))))))))
+  (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)