]> code.delx.au - gnu-emacs/blobdiff - lisp/info.el
* lisp/version.el (emacs-version): Change '*Step' to 'NS' for consistency with other...
[gnu-emacs] / lisp / info.el
index b274c8258a4fcf391c1ddd091e018b52115fdd1f..78e3f2ef70cdab33663d6857616284e812f6c479 100644 (file)
@@ -34,7 +34,7 @@
 
 ;;; Code:
 
-(eval-when-compile (require 'jka-compr))
+(eval-when-compile (require 'jka-compr) (require 'cl))
 
 (defgroup info nil
   "Info subsystem."
@@ -221,7 +221,8 @@ when you hit the end of the current node."
   "*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,11 +233,17 @@ If value is non-nil but not t or `hide', the reference section is still shown.
 (defcustom Info-refill-paragraphs nil
   "*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.
 This applies to Info search for regular expressions.
@@ -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.")
 
@@ -456,6 +468,34 @@ Do the right thing if the file has been compressed or zipped."
            (apply 'call-process-region (point-min) (point-max)
                   (car decoder) t t nil (cdr decoder))))
       (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 +583,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 +883,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 +994,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))))
@@ -1209,19 +1245,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.
@@ -1991,14 +2028,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 +2059,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 +2077,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 +2120,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 +2138,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 +2444,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 +2481,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 +2511,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 +2520,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 +2775,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 +2815,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 +2843,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 +3139,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 +3163,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\"`({<>})']+\\)")
@@ -3237,23 +3317,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 +3480,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 +3512,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)
@@ -3710,49 +3789,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 +3874,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 +3936,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 +4422,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)