]> code.delx.au - gnu-emacs/blobdiff - lisp/info.el
(Abbrevs): A @node line without explicit Prev, Next, and Up links.
[gnu-emacs] / lisp / info.el
index c94e4121dc8a105ea3f06247cce14d7e30459936..163441893dbf798c238f4d50453fbc3f2274eb64 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 Free Software Foundation, Inc.
+;;   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help
@@ -129,21 +129,14 @@ The Lisp code is executed when the node is selected.")
 (put 'info-menu-5 'face-alias 'info-menu-star)
 
 (defface info-xref
-  '((((min-colors 88)
-      (class color) (background light)) :foreground "blue1" :underline t)
-    (((class color) (background light)) :foreground "blue" :underline t)
-    (((min-colors 88)
-      (class color) (background dark)) :foreground "cyan1" :underline t)
-    (((class color) (background dark)) :foreground "cyan" :underline t)
-    (t :underline t))
-  "Face for Info cross-references."
+  '((t :inherit link))
+  "Face for unvisited Info cross-references."
   :group 'info)
 
 (defface info-xref-visited
-  '((default :inherit info-xref)
-    (((class color) (background light)) :foreground "magenta4")
-    (((class color) (background dark)) :foreground "violet"))
+  '((t :inherit (link-visited info-xref)))
   "Face for visited Info cross-references."
+  :version "22.1"
   :group 'info)
 
 (defcustom Info-fontify-visited-nodes t
@@ -290,8 +283,7 @@ with wrapping around the current Info node."
 (defvar Info-current-file nil
   "Info file that Info is now looking at, or nil.
 This is the name that was specified in Info, not the actual file name.
-It doesn't contain directory names or file name extensions added by Info.
-Can also be t when using `Info-on-current-buffer'.")
+It doesn't contain directory names or file name extensions added by Info.")
 
 (defvar Info-current-subfile nil
   "Info subfile that is actually in the *info* buffer now.
@@ -310,6 +302,9 @@ Marker points nowhere if file has no tag table.")
 (defvar Info-current-file-completions nil
   "Cached completion list for current Info file.")
 
+(defvar Info-file-supports-index-cookies nil
+  "Non-nil if current Info file supports index cookies.")
+
 (defvar Info-index-alternatives nil
   "List of possible matches for last `Info-index' command.")
 
@@ -446,8 +441,7 @@ Do the right thing if the file has been compressed or zipped."
       (or tail
          (error "Can't find %s or any compressed version of it" filename)))
     ;; check for conflict with jka-compr
-    (if (and (featurep 'jka-compr)
-            (jka-compr-installed-p)
+    (if (and (jka-compr-installed-p)
             (jka-compr-get-compression-info fullname))
        (setq decoder nil))
     (if decoder
@@ -520,22 +514,22 @@ Do the right thing if the file has been compressed or zipped."
               (Info-default-dirs)))))))
 
 ;;;###autoload
-(defun info-other-window (&optional file)
+(defun info-other-window (&optional file-or-node)
   "Like `info' but show the Info buffer in another window."
   (interactive (if current-prefix-arg
                   (list (read-file-name "Info file name: " nil nil t))))
   (let (same-window-buffer-names same-window-regexps)
-    (info file)))
+    (info file-or-node)))
 
 ;;;###autoload (add-hook 'same-window-regexps "\\*info\\*\\(\\|<[0-9]+>\\)")
 
 ;;;###autoload (put 'info 'info-file "emacs")
 ;;;###autoload
-(defun info (&optional file buffer)
+(defun info (&optional file-or-node buffer)
   "Enter Info, the documentation browser.
-Optional argument FILE specifies the file to examine;
+Optional argument FILE-OR-NODE specifies the file to examine;
 the default is the top-level directory of Info.
-Called from a program, FILE may specify an Info node of the form
+Called from a program, FILE-OR-NODE may specify an Info node of the form
 `(FILENAME)NODENAME'.
 Optional argument BUFFER specifies the Info buffer name;
 the default buffer name is *info*.  If BUFFER exists,
@@ -558,17 +552,19 @@ in all the directories in that path."
   (pop-to-buffer (or buffer "*info*"))
   (if (and buffer (not (eq major-mode 'Info-mode)))
       (Info-mode))
-  (if file
+  (if file-or-node
       ;; If argument already contains parentheses, don't add another set
       ;; since the argument will then be parsed improperly.  This also
       ;; has the added benefit of allowing node names to be included
       ;; following the parenthesized filename.
       (Info-goto-node
-       (if (and (stringp file) (string-match "(.*)" file))
-           file
-         (concat "(" file ")")))
-    (if (zerop (buffer-size))
-        (Info-directory))))
+       (if (and (stringp file-or-node) (string-match "(.*)" file-or-node))
+           file-or-node
+         (concat "(" file-or-node ")")))
+    (if (and (zerop (buffer-size))
+            (null Info-history))
+       ;; If we just created the Info buffer, go to the directory.
+       (Info-directory))))
 
 ;;;###autoload
 (defun info-emacs-manual ()
@@ -626,12 +622,6 @@ just return nil (no error)."
        (cond
         ((string= (downcase filename) "dir")
          (setq found t))
-        ((string= filename "apropos")
-         (setq found 'apropos))
-        ((string= filename "history")
-         (setq found 'history))
-        ((string= filename "toc")
-         (setq found 'toc))
         (t
          (let ((dirs (if (string-match "^\\./" filename)
                           ;; If specified name starts with `./'
@@ -677,7 +667,8 @@ just return nil (no error)."
           (if noerror
               (setq filename nil)
             (error "Info file %s does not exist" filename)))
-        filename)))
+        filename)
+    (and (member filename '(apropos history toc)) filename)))
 
 (defun Info-find-node (filename nodename &optional no-going-back)
   "Go to an Info node specified as separate FILENAME and NODENAME.
@@ -687,17 +678,19 @@ it says do not attempt further (recursive) error recovery."
   (setq filename (Info-find-file filename))
   ;; Go into Info buffer.
   (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
-  ;; Record the node we are leaving.
-  (if (and Info-current-file (not no-going-back))
-      (setq Info-history
-            (cons (list Info-current-file Info-current-node (point))
-                  Info-history)))
+  ;; 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)))
   (Info-find-node-2 filename nodename no-going-back))
 
+;;;###autoload
 (defun Info-on-current-buffer (&optional nodename)
-  "Use the `Info-mode' to browse the current Info buffer.
-If a prefix arg is provided, it queries for the NODENAME which
-else defaults to \"Top\"."
+  "Use Info mode to browse the current Info buffer.
+With a prefix arg, this queries for the node name to visit first;
+otherwise, that defaults to `Top'."
   (interactive
    (list (if current-prefix-arg
             (completing-read "Node name: " (Info-build-node-completions)
@@ -705,7 +698,10 @@ else defaults to \"Top\"."
   (unless nodename (setq nodename "Top"))
   (info-initialize)
   (Info-mode)
-  (set (make-local-variable 'Info-current-file) t)
+  (set (make-local-variable 'Info-current-file)
+       (or buffer-file-name
+          ;; If called on a non-file buffer, make a fake file name.
+          (concat default-directory (buffer-name))))
   (Info-find-node-2 nil nodename))
 
 ;; It's perhaps a bit nasty to kill the *info* buffer to force a re-read,
@@ -723,8 +719,8 @@ is preserved, if possible."
        (pline        (count-lines (point-min) (line-beginning-position)))
        (wline        (count-lines (point-min) (window-start)))
        (old-history  Info-history)
-       (new-history (and Info-current-file
-                         (list Info-current-file Info-current-node (point)))))
+       (new-history  (and Info-current-file
+                          (list Info-current-file Info-current-node (point)))))
     (kill-buffer (current-buffer))
     (Info-find-node filename nodename)
     (setq Info-history old-history)
@@ -842,6 +838,19 @@ 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 version \\([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)
@@ -877,9 +886,6 @@ a case-insensitive match is tried."
               (setq Info-current-file
                    (cond
                     ((eq filename t) "dir")
-                    ((eq filename 'apropos) "apropos")
-                    ((eq filename 'history) "history")
-                    ((eq filename 'toc) "toc")
                     (t filename)))
              ))
         ;; Use string-equal, not equal, to ignore text props.
@@ -1190,7 +1196,9 @@ a case-insensitive match is tried."
                  (point)
                  (if (re-search-forward "^[^* \n\t]" nil t)
                      (match-beginning 0)
-                   (or limit (point-max)))) entries))))
+                   (or limit (point-max))))
+                entries)
+               (forward-line 0))))
          ;; Insert the entries just found.
          (while (= (line-beginning-position 0) (1- (point)))
            (backward-char))
@@ -1201,10 +1209,11 @@ a case-insensitive match is tried."
 
          ;; Now remove duplicate entries under the same heading.
          (let ((seen nil)
-               (limit (point)))
+               (limit (point-marker)))
            (goto-char start)
-           (while (re-search-forward "^* \\([^:\n]+:\\(:\\|[^.\n]+\\).\\)"
-                                     limit 'move)
+           (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)
@@ -1379,17 +1388,33 @@ any double quotes or backslashes must be escaped (\\\",\\\\)."
        (Info-hide-cookies-node)
        (run-hooks 'Info-selection-hook)))))
 
+(defvar Info-mode-line-node-keymap
+  (let ((map (make-sparse-keymap)))
+    (define-key map [mode-line mouse-1] 'Info-scroll-up)
+    (define-key map [mode-line mouse-3] 'Info-scroll-down)
+    map)
+  "Keymap to put on the Info node name in the mode line.")
+
 (defun Info-set-mode-line ()
   (setq mode-line-buffer-identification
        (nconc (propertized-buffer-identification "%b")
               (list
-               (concat " ("
-                       (file-name-nondirectory
-                        (if (stringp Info-current-file)
-                            Info-current-file
-                          (or buffer-file-name "")))
-                       ") "
-                       (or Info-current-node ""))))))
+               (concat
+                " ("
+                (if (stringp Info-current-file)
+                    (replace-regexp-in-string
+                     "%" "%%" (file-name-nondirectory Info-current-file))
+                  (format "*%S*" Info-current-file))
+                ") "
+                (if Info-current-node
+                    (propertize (replace-regexp-in-string
+                                 "%" "%%" Info-current-node)
+                                'face 'mode-line-buffer-id
+                                'help-echo
+                                "mouse-1: scroll forward, mouse-3: scroll back"
+                                'mouse-face 'mode-line-highlight
+                                'local-map Info-mode-line-node-keymap)
+                  ""))))))
 \f
 ;; Go to an Info node specified with a filename-and-nodename string
 ;; of the sort that is found in pointers in nodes.
@@ -1502,7 +1527,12 @@ PATH-AND-SUFFIXES is a pair of lists, (DIRECTORIES . SUFFIXES)."
 
 ;; Arrange to highlight the proper letters in the completion list buffer.
 (put 'Info-read-node-name-1 'completion-base-size-function
-     (lambda () 1))
+     (lambda ()
+       (if (string-match "\\`([^)]*\\'"
+                        (or completion-common-substring
+                            (minibuffer-completion-contents)))
+          1
+        0)))
 
 (defun Info-read-node-name (prompt &optional default)
   (let* ((completion-ignore-case t)
@@ -1613,8 +1643,11 @@ If DIRECTION is `backward', search in the reverse direction."
                            ;; Skip Tag Table node
                            (save-excursion
                              (and (search-backward "\^_" nil t)
-                                  (looking-at "\^_\nTag Table"))))))
-           (let ((search-spaces-regexp Info-search-whitespace-regexp))
+                                  (looking-at
+                                   "\^_\n\\(Tag Table\\|Local Variables\\)"))))))
+           (let ((search-spaces-regexp
+                  (if (or (not isearch-mode) isearch-regexp)
+                      Info-search-whitespace-regexp)))
              (if (if backward
                      (re-search-backward regexp bound t)
                    (re-search-forward regexp bound t))
@@ -1632,7 +1665,9 @@ If DIRECTION is `backward', search in the reverse direction."
       ;; If no subfiles, give error now.
       (if give-up
          (if (null Info-current-subfile)
-             (let ((search-spaces-regexp Info-search-whitespace-regexp))
+             (let ((search-spaces-regexp
+                    (if (or (not isearch-mode) isearch-regexp)
+                        Info-search-whitespace-regexp)))
                (if backward
                    (re-search-backward regexp)
                  (re-search-forward regexp)))
@@ -1701,8 +1736,11 @@ If DIRECTION is `backward', search in the reverse direction."
                                  ;; Skip Tag Table node
                                  (save-excursion
                                    (and (search-backward "\^_" nil t)
-                                        (looking-at "\^_\nTag Table"))))))
-                 (let ((search-spaces-regexp Info-search-whitespace-regexp))
+                                        (looking-at
+                                         "\^_\n\\(Tag Table\\|Local Variables\\)"))))))
+                 (let ((search-spaces-regexp
+                        (if (or (not isearch-mode) isearch-regexp)
+                            Info-search-whitespace-regexp)))
                    (if (if backward
                            (re-search-backward regexp nil t)
                          (re-search-forward regexp nil t))
@@ -1796,11 +1834,11 @@ If DIRECTION is `backward', search in the reverse direction."
 
 (defun Info-isearch-push-state ()
   `(lambda (cmd)
-     (Info-isearch-pop-state cmd ,Info-current-file ,Info-current-node)))
+     (Info-isearch-pop-state cmd ',Info-current-file ',Info-current-node)))
 
 (defun Info-isearch-pop-state (cmd file node)
-  (or (and (string= Info-current-file file)
-           (string= Info-current-node node))
+  (or (and (equal Info-current-file file)
+           (equal Info-current-node node))
       (progn (Info-find-node file node) (sit-for 0))))
 
 (defun Info-isearch-start ()
@@ -1818,7 +1856,7 @@ if ERRORNAME is nil, just return nil."
        (forward-line 1)
        (cond ((re-search-backward
                (concat name ":" (Info-following-node-name-re)) bound t)
-              (match-string 1))
+              (match-string-no-properties 1))
              ((not (eq errorname t))
               (error "Node has no %s"
                      (capitalize (or errorname name)))))))))
@@ -1840,7 +1878,7 @@ End of submatch 0, 1, and 3 are the same, so you can safely concat."
 ;;; For compatibility; other files have used this name.
 (defun Info-following-node-name ()
   (and (looking-at (Info-following-node-name-re))
-       (match-string 1)))
+       (match-string-no-properties 1)))
 
 (defun Info-next ()
   "Go to the next node of this node."
@@ -1868,13 +1906,14 @@ If SAME-FILE is non-nil, do not move to a different Info file."
     (let ((old-node Info-current-node)
          (old-file Info-current-file)
          (node (Info-extract-pointer "up")) p)
-      (and (or same-file (not (stringp Info-current-file)))
+      (and same-file
           (string-match "^(" node)
           (error "Up node is in another Info file"))
       (Info-goto-node node)
       (setq p (point))
       (goto-char (point-min))
-      (if (and (search-forward "\n* Menu:" nil t)
+      (if (and (stringp old-file)
+              (search-forward "\n* Menu:" nil t)
               (re-search-forward
                (if (string-equal old-node "Top")
                    (concat "\n\\*[^:]+: +(" (file-name-nondirectory old-file) ")")
@@ -1942,49 +1981,53 @@ If SAME-FILE is non-nil, do not move to a different Info file."
           (while hl
             (let ((file (nth 0 (car hl)))
                   (node (nth 1 (car hl))))
-              (if (and (string-equal file curr-file)
-                       (string-equal node curr-node))
+              (if (and (equal file curr-file)
+                       (equal node curr-node))
                   (setq p (point)))
-              (insert "* " node ": (" (file-name-nondirectory file)
-                      ")" node ".\n"))
+              (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")
+    (Info-find-node 'history "Top")
     (goto-char (or p (point-min)))))
 
 (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)
-  (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)))))
+  (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))))))
 
 (defun Info-insert-toc (nodes node-list level curr-file)
   "Insert table of contents with references to nodes."
@@ -2184,16 +2227,18 @@ Because of ambiguities, this should be concatenated with something like
         (setq Info-point-loc
               (if (match-beginning 5)
                   (string-to-number (match-string 5))
-                (buffer-substring (match-beginning 0) (1- (match-beginning 1)))))
+                (buffer-substring-no-properties
+                (match-beginning 0) (1- (match-beginning 1)))))
 ;;; Uncomment next line to use names of cross-references in non-index nodes:
 ;;;       (setq Info-point-loc
 ;;;             (buffer-substring (match-beginning 0) (1- (match-beginning 1))))
       )
     (replace-regexp-in-string
      "[ \n]+" " "
-     (or (match-string 2)
+     (or (match-string-no-properties 2)
         ;; If the node name is the menu entry name (using `entry::').
-        (buffer-substring (match-beginning 0) (1- (match-beginning 1)))))))
+        (buffer-substring-no-properties
+         (match-beginning 0) (1- (match-beginning 1)))))))
 
 ;; No one calls this.
 ;;(defun Info-menu-item-sequence (list)
@@ -2238,7 +2283,8 @@ Because of ambiguities, this should be concatenated with something like
        (let ((pattern (concat "\n\\* +\\("
                               (regexp-quote string)
                               Info-menu-entry-name-re "\\):" Info-node-spec-re))
-             completions)
+             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)
@@ -2259,9 +2305,9 @@ Because of ambiguities, this should be concatenated with something like
                  (or (and Info-complete-next-re
                           (setq nextnode (Info-extract-pointer "next" t))
                           (string-match Info-complete-next-re nextnode))
-                     (and Info-complete-nodes
-                          (setq Info-complete-nodes (cdr Info-complete-nodes)
-                                nextnode (car Info-complete-nodes)))))
+                     (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)
@@ -2646,65 +2692,66 @@ 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" "history" "toc" "apropos"))
+      (and (member file '("dir" apropos history toc))
            (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
       (not (stringp 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)
-        (condition-case nil
-            (with-temp-buffer
-              (while (or main-file subfiles)
-                (erase-buffer)
-                (info-insert-file-contents (or main-file (car subfiles)))
-                (goto-char (point-min))
-                (while (search-forward "\0\b[index\0\b]" nil 'move)
-                  (save-excursion
-                    (re-search-backward "^\^_")
-                    (search-forward "Node: ")
-                    (setq nodes (cons (Info-following-node-name) nodes))))
-                (if main-file
-                    (save-excursion
-                      (goto-char (point-min))
-                      (if (search-forward "\n\^_\nIndirect:" nil t)
-                          (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
-                            (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
-                              (setq subfiles (cons (match-string-no-properties 1)
-                                                   subfiles)))))
-                      (setq subfiles (nreverse subfiles)
-                            main-file nil))
-                  (setq subfiles (cdr subfiles)))))
-          (error nil))
-        (if nodes
-            (setq nodes (nreverse nodes)
-                  Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
-        nodes)
-      ;; 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
-            nodes node)
-        (condition-case nil
-            (with-temp-buffer
-              (Info-mode)
-              (Info-find-node file "Top")
-              (when (and (search-forward "\n* menu:" nil t)
-                         (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
-                (goto-char (match-beginning 1))
-                (setq nodes (list (Info-extract-menu-node-name)))
-                (Info-goto-node (car nodes))
-                (while (and (setq node (Info-extract-pointer "next" t))
-                            (string-match "\\<Index\\>" node))
-                  (setq nodes (cons node nodes))
-                  (Info-goto-node node))))
-          (error nil))
-        (if nodes
-            (setq nodes (nreverse nodes)
-                  Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
-        nodes)
+      (if Info-file-supports-index-cookies
+         ;; 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)
+           (condition-case nil
+               (with-temp-buffer
+                 (while (or main-file subfiles)
+                   (erase-buffer)
+                   (info-insert-file-contents (or main-file (car subfiles)))
+                   (goto-char (point-min))
+                   (while (search-forward "\0\b[index\0\b]" nil 'move)
+                     (save-excursion
+                       (re-search-backward "^\^_")
+                       (search-forward "Node: ")
+                       (setq nodes (cons (Info-following-node-name) nodes))))
+                   (if main-file
+                       (save-excursion
+                         (goto-char (point-min))
+                         (if (search-forward "\n\^_\nIndirect:" nil t)
+                             (let ((bound (save-excursion (search-forward "\n\^_" nil t))))
+                               (while (re-search-forward "^\\(.*\\): [0-9]+$" bound t)
+                                 (setq subfiles (cons (match-string-no-properties 1)
+                                                      subfiles)))))
+                         (setq subfiles (nreverse subfiles)
+                               main-file nil))
+                     (setq subfiles (cdr subfiles)))))
+             (error nil))
+           (if nodes
+               (setq nodes (nreverse nodes)
+                     Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
+           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
+             nodes node)
+         (condition-case nil
+             (with-temp-buffer
+               (Info-mode)
+               (Info-find-node file "Top")
+               (when (and (search-forward "\n* menu:" nil t)
+                          (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
+                 (goto-char (match-beginning 1))
+                 (setq nodes (list (Info-extract-menu-node-name)))
+                 (Info-goto-node (car nodes))
+                 (while (and (setq node (Info-extract-pointer "next" t))
+                             (string-match "\\<Index\\>" node))
+                   (setq nodes (cons node nodes))
+                   (Info-goto-node node))))
+           (error nil))
+         (if nodes
+             (setq nodes (nreverse nodes)
+                   Info-index-nodes (cons (cons file nodes) Info-index-nodes)))
+         nodes))
       ;; If file has no index nodes, still add it to the cache
       (setq Info-index-nodes (cons (cons file nil) Info-index-nodes)))
   (cdr (assoc file Info-index-nodes)))
@@ -2718,17 +2765,17 @@ If FILE is nil, check the current Info file."
       (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
-    (or
-     (save-match-data
-       (string-match "\\<Index\\>" (or node Info-current-node "")))
-     (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)))))
+    (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
+       (string-match "\\<Index\\>" (or node Info-current-node ""))))))
 
 (defun Info-goto-index ()
   "Go to the first index node."
@@ -2738,11 +2785,11 @@ If FILE is nil, check the current Info file."
 
 ;;;###autoload
 (defun Info-index (topic)
-  "Look up a string TOPIC in the index for this file.
+  "Look up a string TOPIC in the index for this manual and go to that entry.
 If there are no exact matches to the specified topic, this chooses
 the first match which is a case-insensitive substring of a topic.
 Use the \\<Info-mode-map>\\[Info-index-next] command to see the other matches.
-Give a blank topic name to go to the Index node itself."
+Give an empty topic name to go to the Index node itself."
   (interactive
    (list
     (let ((Info-complete-menu-buffer (clone-buffer))
@@ -2887,7 +2934,7 @@ Build a menu of the possible matches."
             (message "%s" (if (eq (car-safe err) 'error)
                               (nth 1 err) err))
             (sit-for 1 t)))))
-      (Info-goto-node (concat "(" current-file ")" current-node))
+      (Info-find-node current-file current-node)
       (setq Info-history ohist
            Info-history-list ohist-list)
       (message "Searching indices...done")
@@ -2906,7 +2953,7 @@ Build a menu of the possible matches."
                     (if (nth 3 entry)
                         (concat " (line " (nth 3 entry) ")")
                       "")))))
-       (Info-find-node "apropos" "Index")
+       (Info-find-node 'apropos "Index")
        (setq Info-complete-cache nil)))))
 
 (defun Info-undefined ()
@@ -2997,7 +3044,11 @@ At end of the node's text, moves to the next node, or up if none."
 (defun Info-follow-nearest-node (&optional fork)
   "Follow a node reference near point.
 If point is on a reference, follow that reference.  Otherwise,
-if point is in a menu item description, follow that menu item."
+if point is in a menu item description, follow that menu item.
+
+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 "P")
   (or (Info-try-follow-nearest-node fork)
       (when (save-excursion
@@ -3052,6 +3103,7 @@ if point is in a menu item description, follow that menu item."
   (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)
@@ -3162,14 +3214,14 @@ if point is in a menu item description, follow that menu item."
   (if (display-graphic-p)
       (let ((map (make-sparse-keymap)))
        (tool-bar-local-item-from-menu 'Info-exit "close" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-prev "left-arrow" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-next "right-arrow" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-up "up-arrow" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-history-back "back-arrow" map Info-mode-map)
-       (tool-bar-local-item-from-menu 'Info-history-forward "fwd-arrow" map Info-mode-map)
+       (tool-bar-local-item-from-menu 'Info-history-back "left-arrow" map Info-mode-map)
+       (tool-bar-local-item-from-menu 'Info-history-forward "right-arrow" map Info-mode-map)
+       (tool-bar-local-item-from-menu 'Info-prev "prev-node" map Info-mode-map)
+       (tool-bar-local-item-from-menu 'Info-next "next-node" map Info-mode-map)
+       (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-index "index" 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)
        map)))
 
@@ -3247,14 +3299,14 @@ With a zero prefix arg, put the name inside a function call to `info'."
   (interactive "P")
   (unless Info-current-node
     (error "No current Info node"))
-  (let ((node (concat "(" (file-name-nondirectory
-                           (or (and (stringp Info-current-file)
-                                    Info-current-file)
-                               buffer-file-name
-                               ""))
-                      ")" Info-current-node)))
+  (let ((node (if (stringp Info-current-file)
+                 (concat "(" (file-name-nondirectory Info-current-file) ")"
+                         Info-current-node))))
     (if (zerop (prefix-numeric-value arg))
         (setq node (concat "(info \"" node "\")")))
+    (unless (stringp Info-current-file)
+      (setq node (format "(Info-find-node '%S '%S)"
+                        Info-current-file Info-current-node)))
     (kill-new node)
     (message "%s" node)))
 
@@ -3318,7 +3370,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.
-\\[Info-index] Look up a topic in this file's Index and move to that node.
+\\[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.
 \\[Info-goto-node]     Move to node specified by name.
@@ -3504,10 +3556,6 @@ in the first element of the returned list (which is treated specially in
                (setq info-file file file-list nil))
            (setq file-list (cdr file-list))))))
     (Info-find-node info-file "Top")
-    (or (and (search-forward "\n* menu:" nil t)
-            (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t))
-       (error "Info file `%s' appears to lack an index" info-file))
-    (goto-char (match-beginning 1))
     ;; Bind Info-history to nil, to prevent the index nodes from
     ;; getting into the node history.
     (let ((Info-history nil)
@@ -3649,7 +3697,7 @@ the variable `Info-file-list-for-emacs'."
                  (nend (match-end 2))
                  (tbeg (match-beginning 1))
                  (tag (match-string 1)))
-            (if (string-equal tag "Node")
+            (if (string-equal (downcase tag) "node")
                 (put-text-property nbeg nend 'font-lock-face 'info-header-node)
               (put-text-property nbeg nend 'font-lock-face 'info-header-xref)
               (put-text-property tbeg nend 'mouse-face 'highlight)
@@ -3660,11 +3708,12 @@ the variable `Info-file-list-for-emacs'."
               ;; Always set up the text property keymap.
               ;; It will either be used in the buffer
               ;; or copied in the header line.
-              (put-text-property tbeg nend 'keymap
-                                 (cond
-                                  ((equal tag "Prev") Info-prev-link-keymap)
-                                  ((equal tag "Next") Info-next-link-keymap)
-                                  ((equal tag "Up") Info-up-link-keymap))))))
+              (put-text-property
+              tbeg nend 'keymap
+              (cond
+               ((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-use-header-line
           (goto-char (point-min))
           (let* ((header-end (line-end-position))
@@ -3706,8 +3755,8 @@ the variable `Info-file-list-for-emacs'."
                     ;; underline has the same size as the text.  A typical
                     ;; counter example is when a continuation "..." is alone
                     ;; on a line.
-                    (= (- (match-end 1) (match-beginning 1))
-                       (- (match-end 2) (match-beginning 2))))
+                    (= (string-width (match-string 1))
+                       (string-width (match-string 2))))
           (let* ((c (preceding-char))
                  (face
                   (cond ((= c ?*) 'info-title-1)
@@ -3736,20 +3785,20 @@ the variable `Info-file-list-for-emacs'."
                   ;; *Note is often used where *note should have been
                   (goto-char start)
                   (skip-syntax-backward " ")
+                 (when (memq (char-before) '(?\( ?\[ ?\{))
+                   ;; Check whether the paren is preceded by
+                   ;; an end of sentence
+                   (skip-syntax-backward " ("))
                   (setq other-tag
-                        (cond ((memq (char-before) '(nil ?\. ?! ??))
+                       (cond ((save-match-data (looking-back "\\<see"))
+                              "")
+                             ((memq (char-before) '(nil ?\. ?! ??))
                                "See ")
-                              ((memq (char-before) '(?\, ?\; ?\: ?-))
-                               "see ")
-                              ((memq (char-before) '(?\( ?\[ ?\{))
-                               ;; Check whether the paren is preceded by
-                               ;; an end of sentence
-                               (skip-syntax-backward " (")
-                               (if (memq (char-before) '(nil ?\. ?! ??))
-                                   "See "
-                                 "see "))
-                              ((save-match-data (looking-at "\n\n"))
-                               "See "))))
+                             ((save-match-data
+                                (save-excursion
+                                  (search-forward "\n\n" start t)))
+                              "See ")
+                             (t "see "))))
                 (goto-char next)
                 (add-text-properties
                  (match-beginning 1)
@@ -3784,27 +3833,32 @@ the variable `Info-file-list-for-emacs'."
                                         "^[ \t]+" ""
                                         (replace-regexp-in-string
                                          "[ \t\n]+" " "
-                                         (or (match-string 5)
+                                         (or (match-string-no-properties 5)
                                              (and (not (equal (match-string 4) ""))
-                                                  (match-string 4))
-                                             (match-string 2)))))
-                                 (file (file-name-nondirectory
-                                        Info-current-file))
+                                                  (match-string-no-properties 4))
+                                             (match-string-no-properties 2)))))
+                                (external-link-p
+                                 (string-match "(\\([^)]+\\))\\([^)]*\\)" node))
+                                 (file (if external-link-p
+                                          (file-name-nondirectory
+                                           (match-string-no-properties 1 node))
+                                        Info-current-file))
                                  (hl Info-history-list)
                                  res)
-                            (if (string-match "(\\([^)]+\\))\\([^)]*\\)" node)
-                                (setq file (file-name-nondirectory
-                                            (match-string 1 node))
-                                      node (if (equal (match-string 2 node) "")
+                            (if external-link-p
+                               (setq node (if (equal (match-string 2 node) "")
                                                "Top"
-                                             (match-string 2 node))))
-                            (while hl
-                              (if (and (string-equal node (nth 1 (car hl)))
-                                       (string-equal file
-                                                     (file-name-nondirectory
-                                                      (nth 0 (car hl)))))
-                                  (setq res (car hl) hl nil)
-                                (setq hl (cdr hl))))
+                                             (match-string-no-properties 2 node))))
+                           (while hl
+                             (if (and (string-equal node (nth 1 (car hl)))
+                                      (equal file
+                                             (if (and external-link-p
+                                                      (stringp (caar hl)))
+                                                 (file-name-nondirectory
+                                                  (caar hl))
+                                               (caar hl))))
+                                 (setq res (car hl) hl nil)
+                               (setq hl (cdr hl))))
                             res))) 'info-xref-visited 'info-xref))
               ;; For multiline ref, unfontify newline and surrounding whitespace
               (save-excursion
@@ -3894,25 +3948,31 @@ the variable `Info-file-list-for-emacs'."
                  ;; Display visited menu items in a different face
                  (if (and Info-fontify-visited-nodes
                           (save-match-data
-                            (let ((node (if (equal (match-string 3) "")
-                                            (match-string 1)
-                                          (match-string 3)))
-                                  (file (file-name-nondirectory Info-current-file))
-                                  (hl Info-history-list)
-                                  res)
-                              (if (string-match "(\\([^)]+\\))\\([^)]*\\)" node)
-                                  (setq file (file-name-nondirectory
-                                              (match-string 1 node))
-                                        node (if (equal (match-string 2 node) "")
+                            (let* ((node (if (equal (match-string 3) "")
+                                            (match-string-no-properties 1)
+                                          (match-string-no-properties 3)))
+                                  (external-link-p
+                                   (string-match "(\\([^)]+\\))\\([^)]*\\)" node))
+                                  (file (if external-link-p
+                                            (file-name-nondirectory
+                                             (match-string-no-properties 1 node))
+                                          Info-current-file))
+                                  (hl Info-history-list)
+                                  res)
+                              (if external-link-p
+                                  (setq node (if (equal (match-string 2 node) "")
                                                  "Top"
-                                               (match-string 2 node))))
-                              (while hl
-                                (if (and (string-equal node (nth 1 (car hl)))
-                                         (string-equal file
-                                                       (file-name-nondirectory
-                                                        (nth 0 (car hl)))))
-                                    (setq res (car hl) hl nil)
-                                  (setq hl (cdr hl))))
+                                               (match-string-no-properties 2 node))))
+                             (while hl
+                               (if (and (string-equal node (nth 1 (car hl)))
+                                        (equal file
+                                               (if (and external-link-p
+                                                        (stringp (caar hl)))
+                                                   (file-name-nondirectory
+                                                    (caar hl))
+                                                 (caar hl))))
+                                   (setq res (car hl) hl nil)
+                                 (setq hl (cdr hl))))
                               res))) 'info-xref-visited 'info-xref)))
              (when (and not-fontified-p (memq Info-hide-note-references '(t hide)))
                (put-text-property (match-beginning 2) (1- (match-end 6))
@@ -4105,7 +4165,7 @@ INDENT is the current indentation depth."
 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-buffer (get-buffer-create " *info-browse-tmp*"))
     (if (not (equal major-mode 'Info-mode))
        (Info-mode))
     ;; Get the node into this buffer
@@ -4169,8 +4229,8 @@ 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."
-  (if (not (member Info-current-file '("apropos" "history" "toc")))
-      (list Info-current-file Info-current-node)))
+  (unless (member Info-current-file '(apropos history toc nil))
+    (list Info-current-file Info-current-node)))
 
 (defun Info-restore-desktop-buffer (desktop-buffer-file-name
                                     desktop-buffer-name