Fixed up initial line
[bpt/emacs.git] / lisp / info.el
index adbc193..ec783d1 100644 (file)
@@ -1,6 +1,6 @@
 ;;; info.el --- info package for Emacs.
 
-;; Copyright (C) 1985, 1986, 1992, 1993 Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: help
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
-;;; Note that nowadays we expect info files to be made using makeinfo.
+;; Note that nowadays we expect info files to be made using makeinfo.
 
 ;;; Code:
 
@@ -38,37 +39,73 @@ However, we recommend that you not do this.
 It is better to write a Texinfo file and generate the Info file from that,
 because that gives you a printed manual as well.")
 
-(defvar Info-enable-active-nodes t
+(defvar Info-enable-active-nodes nil
   "Non-nil allows Info to execute Lisp code associated with nodes.
 The Lisp code is executed when the node is selected.")
-
-(defvar Info-default-directory-list nil
-  "List of default directories to search for Info documentation files.
-This value is used as the default for `Info-directory-list'.  It is set
-in paths.el.")
+(put 'Info-enable-active-nodes 'risky-local-variable t)
 
 (defvar Info-fontify t
   "*Non-nil enables highlighting and fonts in Info nodes.")
 
+(defvar Info-fontify-maximum-menu-size 30000
+  "*Maximum size of menu to fontify if `Info-fontify' is non-nil.")
+
 (defvar Info-directory-list
-  (let ((path (getenv "INFOPATH")))
+  (let ((path (getenv "INFOPATH"))
+       ;; This is for older Emacs versions
+       ;; which might get this info.el from the Texinfo distribution.
+       (path-separator (if (boundp 'path-separator) path-separator
+                         (if (eq system-type 'ms-dos) ";" ":")))
+       (source (expand-file-name "info/" source-directory))
+       (sibling (if installation-directory
+                    (expand-file-name "info/" installation-directory)))
+       alternative)
     (if path
        (let ((list nil)
              idx)
          (while (> (length path) 0)
-           (setq idx (or (string-match ":" path) (length path))
+           (setq idx (or (string-match path-separator path) (length path))
                  list (cons (substring path 0 idx) list)
                  path (substring path (min (1+ idx)
                                            (length path)))))
          (nreverse list))
-      Info-default-directory-list))
+      (if (and sibling (file-exists-p sibling))
+         (setq alternative sibling)
+       (setq alternative source))
+      (if (or (member alternative Info-default-directory-list)
+             (not (file-exists-p alternative))
+             ;; On DOS/NT, we use movable executables always,
+             ;; and we must always find the Info dir at run time.
+             (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
+                 nil
+               ;; Use invocation-directory for Info only if we used it for
+               ;; exec-directory also.
+               (not (string= exec-directory
+                             (expand-file-name "lib-src/"
+                                               installation-directory)))))
+         Info-default-directory-list
+       (reverse (cons alternative
+                      (cdr (reverse Info-default-directory-list)))))))
   "List of directories to search for Info documentation files.
 nil means not yet initialized.  In this case, Info uses the environment
 variable INFOPATH to initialize it, or `Info-default-directory-list'
-if there is no INFOPATH variable in the environment.")
+if there is no INFOPATH variable in the environment.
+The last element of `Info-default-directory-list' is the directory
+where Emacs installs the Info files that come with it.
+
+If you run the Emacs executable from the `src' directory in the Emacs
+source tree, the `info' directory in the source tree is used as the last
+element, in place of the installation Info directory.  This is useful
+when you run a version of Emacs without installing it.")
+
+(defvar Info-additional-directory-list nil
+  "List of additional directories to search for Info documentation files.
+These directories are not searched for merging the `dir' file.")
 
 (defvar Info-current-file nil
-  "Info file that Info is now looking at, or 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.")
 
 (defvar Info-current-subfile nil
   "Info subfile that is actually in the *info* buffer now,
@@ -90,39 +127,91 @@ Marker points nowhere if file has no tag table.")
 (defvar Info-standalone nil
   "Non-nil if Emacs was started solely as an Info browser.")
 
-(defvar Info-suffix-list '( (".info"    . nil)
-                           (""         . nil)
-                           (".Z"       . "uncompress")
-                           (".Y"       . "unyabba")
-                           (".gz"      . "gunzip")
-                           (".z"       . "gunzip")
-                           (".info.Z"  . "uncompress")
-                           (".info.Y"  . "unyabba")
-                           (".info.gz" . "gunzip")
-                           (".info.z"  . "gunzip"))
+(defvar Info-suffix-list
+  (if (eq system-type 'ms-dos)
+      '( (".gz"      . "gunzip")
+        (".z"       . "gunzip")
+        (".inf"     . nil)
+        (""         . nil))
+    '( (".info.Z"  . "uncompress")
+       (".info.Y"  . "unyabba")
+       (".info.gz" . "gunzip")
+       (".info.z"  . "gunzip")
+       (".info"    . nil)
+       (".Z"       . "uncompress")
+       (".Y"       . "unyabba")
+       (".gz"      . "gunzip")
+       (".z"       . "gunzip")
+       (""         . nil)))
   "List of file name suffixes and associated decoding commands.
 Each entry should be (SUFFIX . STRING); the file is given to
-the command as standard input.  If STRING is nil, no decoding is done.")
+the command as standard input.  If STRING is nil, no decoding is done.
+Because the SUFFIXes are tried in order, the empty string should
+be last in the list.")
+
+;; Concatenate SUFFIX onto FILENAME.  SUFFIX should start with a dot.
+;; First, on ms-dos, delete some of the extension in FILENAME
+;; to make room.
+(defun info-insert-file-contents-1 (filename suffix)
+  (if (not (eq system-type 'ms-dos))
+      (concat filename suffix)
+    (let* ((sans-exts (file-name-sans-extension filename))
+          ;; How long is the extension in FILENAME (not counting the dot).
+          (ext-len (max 0 (- (length filename) (length sans-exts) 1)))
+          ext-left)
+      ;; SUFFIX starts with a dot.  If FILENAME already has one,
+      ;; get rid of the one in SUFFIX (unless suffix is empty).
+      (or (and (<= ext-len 0)
+              (not (eq (aref filename (1- (length filename))) ?.)))
+         (= (length suffix) 0)
+         (setq suffix (substring suffix 1)))
+      ;; How many chars of that extension should we keep?
+      (setq ext-left (min ext-len (max 0 (- 3 (length suffix)))))
+      ;; Get rid of the rest of the extension, and add SUFFIX.
+      (concat (substring filename 0 (- (length filename)
+                                      (- ext-len ext-left)))
+             suffix))))
 
 (defun info-insert-file-contents (filename &optional visit)
   "Insert the contents of an info file in the current buffer.
 Do the right thing if the file has been compressed or zipped."
-  (if (null (catch 'ok
-             (mapcar
-              (function
-               (lambda (x)
-                 (let ((compressed (concat filename (car x))))
-                   (if (file-exists-p compressed)
-                       (progn
-                         (insert-file-contents compressed visit)
-                         (if (cdr x)
-                             (let ((buffer-read-only nil))
-                               (shell-command-on-region
-                                (point-min) (point-max) (cdr x) t)))
-                         (throw 'ok t))))))
-              Info-suffix-list)
-             nil))
-      (error "Can't find %s or any compressed version of it!" filename)))
+  (let ((tail Info-suffix-list)
+       fullname decoder)
+    (if (file-exists-p filename)
+       ;; FILENAME exists--see if that name contains a suffix.
+       ;; If so, set DECODE accordingly.
+       (progn
+         (while (and tail
+                     (not (string-match
+                           (concat (regexp-quote (car (car tail))) "$")
+                           filename)))
+           (setq tail (cdr tail)))
+         (setq fullname filename
+               decoder (cdr (car tail))))
+      ;; Try adding suffixes to FILENAME and see if we can find something.
+      (while (and tail
+                 (not (file-exists-p (info-insert-file-contents-1
+                                      filename (car (car tail))))))
+       (setq tail (cdr tail)))
+      ;; If we found a file with a suffix, set DECODER according to the suffix
+      ;; and set FULLNAME to the file's actual name.
+      (setq fullname (info-insert-file-contents-1 filename (car (car tail)))
+           decoder (cdr (car tail)))
+      (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)
+            (jka-compr-get-compression-info fullname))
+       (setq decoder nil))
+    (insert-file-contents fullname visit)
+    (if decoder
+       (let ((buffer-read-only nil)
+             (default-directory (or (file-name-directory fullname)
+                                    default-directory)))
+         (call-process-region (point-min) (point-max) decoder t t)))))
+
+;;;###autoload (add-hook 'same-window-buffer-names "*info*")
 
 ;;;###autoload
 (defun info (&optional file)
@@ -131,13 +220,17 @@ Optional argument FILE specifies the file to examine;
 the default is the top-level directory of Info.
 
 In interactive use, a prefix argument directs this command
-to read a file name from the minibuffer."
+to read a file name from the minibuffer.
+
+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."
   (interactive (if current-prefix-arg
                   (list (read-file-name "Info file name: " nil nil t))))
   (if file
       (Info-goto-node (concat "(" file ")"))
     (if (get-buffer "*info*")
-       (switch-to-buffer "*info*")
+       (pop-to-buffer "*info*")
       (Info-directory))))
 
 ;;;###autoload
@@ -167,32 +260,37 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
   (if filename
       (let (temp temp-downcase found)
        (setq filename (substitute-in-file-name filename))
-       (if (string= (downcase (file-name-nondirectory filename)) "dir")
+       (if (string= (downcase filename) "dir")
            (setq found t)
          (let ((dirs (if (string-match "^\\./" filename)
                          ;; If specified name starts with `./'
                          ;; then just try current directory.
                          '("./")
-                       Info-directory-list)))
+                       (if (file-name-absolute-p filename)
+                           ;; No point in searching for an
+                           ;; absolute file name
+                           '(nil)
+                         (if Info-additional-directory-list
+                             (append Info-directory-list
+                                     Info-additional-directory-list)
+                           Info-directory-list)))))
            ;; Search the directory list for file FILENAME.
            (while (and dirs (not found))
              (setq temp (expand-file-name filename (car dirs)))
              (setq temp-downcase
                    (expand-file-name (downcase filename) (car dirs)))
              ;; Try several variants of specified name.
-             (catch 'foundit
-               (mapcar
-                (function
-                 (lambda (x)
-                   (if (file-exists-p (concat temp (car x)))
-                       (progn
-                         (setq found temp)
-                         (throw 'foundit nil)))
-                   (if (file-exists-p (concat temp-downcase (car x)))
-                       (progn
-                         (setq found temp-downcase)
-                         (throw 'foundit nil)))))
-                Info-suffix-list))
+             (let ((suffix-list Info-suffix-list))
+               (while (and suffix-list (not found))
+                 (cond ((file-exists-p
+                         (info-insert-file-contents-1
+                          temp (car (car suffix-list))))
+                        (setq found temp))
+                       ((file-exists-p
+                         (info-insert-file-contents-1
+                          temp-downcase (car (car suffix-list))))
+                        (setq found temp-downcase)))
+                 (setq suffix-list (cdr suffix-list))))
              (setq dirs (cdr dirs)))))
        (if found
            (setq filename found)
@@ -230,7 +328,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
              (set-marker Info-tag-table-marker nil)
              (goto-char (point-max))
              (forward-line -8)
-             (or (equal nodename "*")
+             ;; Use string-equal, not equal, to ignore text props.
+             (or (string-equal nodename "*")
                  (not (search-forward "\^_\nEnd tag table\n" nil t))
                  (let (pos)
                    ;; We have a tag table.  Find its beginning.
@@ -253,9 +352,9 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                                        (match-end 0))))
                      (set-marker Info-tag-table-marker pos))))
              (setq Info-current-file
-                   (if (eq filename t) "dir"
-                     (file-name-sans-versions buffer-file-name)))))
-       (if (equal nodename "*")
+                   (if (eq filename t) "dir" filename))))
+       ;; Use string-equal, not equal, to ignore text props.
+       (if (string-equal nodename "*")
            (progn (setq Info-current-node nodename)
                   (Info-set-mode-line))
          ;; Search file for a suitable node.
@@ -277,7 +376,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                        (if (not (eq (current-buffer) (get-buffer "*info*")))
                            (setq guesspos
                                  (Info-read-subfile guesspos))))
-                   (error "No such node: \"%s\"" nodename))))
+                   (error "No such node: %s" nodename))))
            (goto-char (max (point-min) (- guesspos 1000)))
            ;; Now search from our advised position (or from beg of buffer)
            ;; to find the actual node.
@@ -292,7 +391,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
          (Info-select-node)))
     ;; If we did not finish finding the specified node,
     ;; go back to the previous one.
-    (or Info-current-node no-going-back
+    (or Info-current-node no-going-back (null Info-history)
        (let ((hist (car Info-history)))
          (setq Info-history (cdr Info-history))
          (Info-find-node (nth 0 hist) (nth 1 hist) t)
@@ -321,47 +420,54 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
           ;; since we used it.
           (eval (cons 'and
                       (mapcar '(lambda (elt)
-                                 (equal (cdr elt)
-                                        (file-attributes (car elt))))
+                                 (let ((curr (file-attributes (car elt))))
+                                   ;; Don't compare the access time.
+                                   (if curr (setcar (nthcdr 4 curr) 0))
+                                   (setcar (nthcdr 4 (cdr elt)) 0)
+                                   (equal (cdr elt) curr)))
                               Info-dir-file-attributes))))
       (insert Info-dir-contents)
     (let ((dirs Info-directory-list)
          buffers buffer others nodes dirs-done)
 
+      (setq Info-dir-file-attributes nil)
+
       ;; Search the directory list for the directory file.
       (while dirs
-       (or (member (file-truename (expand-file-name (car dirs))) dirs-done)
-           (member (directory-file-name (file-truename (expand-file-name (car dirs))))
-                   dirs-done)
-           ;; Try several variants of specified name.
-           ;; Try upcasing, appending `.info', or both.
-           (let* (temp
-                  (buffer
-                   (cond
-                    ((progn (setq temp (expand-file-name "DIR" (car dirs)))
-                            (file-exists-p temp))
-                     (find-file-noselect temp))
-                    ((progn (setq temp (expand-file-name "dir" (car dirs)))
-                            (file-exists-p temp))
-                     (find-file-noselect temp))
-                    ((progn (setq temp (expand-file-name "DIR.INFO" (car dirs)))
-                            (file-exists-p temp))
-                     (find-file-noselect temp))
-                    ((progn (setq temp (expand-file-name "dir.info" (car dirs)))
-                            (file-exists-p temp))
-                     (find-file-noselect temp)))))
-             (setq dirs-done
-                   (cons (file-truename (expand-file-name (car dirs)))
-                         (cons (directory-file-name
-                                (file-truename (expand-file-name (car dirs))))
-                               dirs-done)))
-             (if buffer (setq buffers (cons buffer buffers)
-                              Info-dir-file-attributes
-                              (cons (cons (buffer-file-name buffer)
-                                          (file-attributes (buffer-file-name buffer)))
-                                    Info-dir-file-attributes)))))
-       (setq dirs (cdr dirs)))
-
+       (let ((truename (file-truename (expand-file-name (car dirs)))))
+         (or (member truename dirs-done)
+             (member (directory-file-name truename) dirs-done)
+             ;; Try several variants of specified name.
+             ;; Try upcasing, appending `.info', or both.
+             (let* (file
+                    (attrs
+                     (or
+                      (progn (setq file (expand-file-name "dir" truename))
+                             (file-attributes file))
+                      (progn (setq file (expand-file-name "DIR" truename))
+                             (file-attributes file))
+                      (progn (setq file (expand-file-name "dir.info" truename))
+                             (file-attributes file))
+                      (progn (setq file (expand-file-name "DIR.INFO" truename))
+                             (file-attributes file)))))
+               (setq dirs-done
+                     (cons truename
+                           (cons (directory-file-name truename)
+                                 dirs-done)))
+               (if attrs
+                   (save-excursion
+                     (or buffers
+                         (message "Composing main Info directory..."))
+                     (set-buffer (generate-new-buffer "info dir"))
+                     (insert-file-contents file)
+                     (setq buffers (cons (current-buffer) buffers)
+                           Info-dir-file-attributes
+                           (cons (cons file attrs)
+                                 Info-dir-file-attributes))))))
+         (setq dirs (cdr dirs))))
+      
+      (or buffers
+         (error "Can't find the Info directory node"))
       ;; Distinguish the dir file that comes with Emacs from all the
       ;; others.  Yes, that is really what this is supposed to do.
       ;; If it doesn't work, fix it.
@@ -388,10 +494,10 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
              (let (beg nodename end)
                (forward-line 1)
                (setq beg (point))
-               (search-backward "\n\1f")
+               (search-backward "\n\^_")
                (search-forward "Node: ")
                (setq nodename (Info-following-node-name))
-               (search-forward "\n\1f" nil 'move)
+               (search-forward "\n\^_" nil 'move)
                (beginning-of-line)
                (setq end (point))
                (setq nodes (cons (list nodename other beg end) nodes))))))
@@ -402,15 +508,18 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
       (let ((menu-items '("top"))
            (nodes nodes)
            (case-fold-search t)
-           (end (save-excursion (search-forward "\1f" nil t) (point))))
+           (end (save-excursion (search-forward "\^_" nil t) (point))))
        (while nodes
          (let ((nodename (car (car nodes))))
-           (or (member (downcase nodename) menu-items)
-               (re-search-forward (concat "^\\* " (regexp-quote nodename) ":")
-                                  end t)
-               (progn
-                 (insert "* " nodename "\n")
-                 (setq menu-items (cons nodename menu-items)))))
+           (save-excursion
+             (or (member (downcase nodename) menu-items)
+                 (re-search-forward (concat "^\\* "
+                                            (regexp-quote nodename)
+                                            "::")
+                                    end t)
+                 (progn
+                   (insert "* " nodename "::" "\n")
+                   (setq menu-items (cons nodename menu-items))))))
          (setq nodes (cdr nodes))))
       ;; Now take each node of each of the other buffers
       ;; and merge it into the main buffer.
@@ -418,25 +527,26 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
        (let ((nodename (car (car nodes))))
          (goto-char (point-min))
          ;; Find the like-named node in the main buffer.
-         (if (re-search-forward (concat "\n\1f.*\n.*Node: "
+         (if (re-search-forward (concat "\n\^_.*\n.*Node: "
                                         (regexp-quote nodename)
                                         "[,\n\t]")
                                 nil t)
              (progn
-               (search-forward "\n\1f" nil 'move)
-               (beginning-of-line))
+               (search-forward "\n\^_" nil 'move)
+               (beginning-of-line)
+               (insert "\n"))
            ;; If none exists, add one.
            (goto-char (point-max))
-           (insert "\1f\nFile: dir\tnode: " nodename "\n\n* Menu:\n\n"))
+           (insert "\^_\nFile: dir\tNode: " nodename "\n\n* Menu:\n\n"))
          ;; Merge the text from the other buffer's menu
          ;; into the menu in the like-named node in the main buffer.
-         (apply 'insert-buffer-substring (cdr (car nodes)))
-         (insert "\n"))
+         (apply 'insert-buffer-substring (cdr (car nodes))))
        (setq nodes (cdr nodes)))
       ;; Kill all the buffers we just made.
       (while buffers
        (kill-buffer (car buffers))
-       (setq buffers (cdr buffers))))
+       (setq buffers (cdr buffers)))
+      (message "Composing main Info directory...done"))
     (setq Info-dir-contents (buffer-string)))
   (setq default-directory Info-dir-contents-directory))
 
@@ -485,10 +595,10 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
    ;; Get nodename spelled as it is in the node.
    (re-search-forward "Node:[ \t]*")
    (setq Info-current-node
-        (buffer-substring (point)
-                          (progn
-                           (skip-chars-forward "^,\t\n")
-                           (point))))
+        (buffer-substring-no-properties (point)
+                                        (progn
+                                         (skip-chars-forward "^,\t\n")
+                                         (point))))
    (Info-set-mode-line)
    ;; Find the end of it, and narrow.
    (beginning-of-line)
@@ -534,12 +644,32 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
       (if trim (setq filename (substring filename 0 trim))))
     (let ((trim (string-match "\\s *\\'" nodename)))
       (if trim (setq nodename (substring nodename 0 trim))))
+    (if transient-mark-mode (deactivate-mark))
     (Info-find-node (if (equal filename "") nil filename)
                    (if (equal nodename "") "Top" nodename))))
 
+;; This function is used as the "completion table" while reading a node name.
+;; It does completion using the alist in completion-table
+;; unless STRING starts with an open-paren.
+(defun Info-read-node-name-1 (string predicate code)
+  (let ((no-completion (and (> (length string) 0) (eq (aref string 0) ?\())))
+    (cond ((eq code nil)
+          (if no-completion
+              string
+            (try-completion string completion-table predicate)))
+         ((eq code t)
+          (if no-completion
+              nil
+            (all-completions string completion-table predicate)))
+         ((eq code 'lambda)
+          (if no-completion
+              t
+            (assoc string completion-table))))))
+
 (defun Info-read-node-name (prompt &optional default)
   (let* ((completion-ignore-case t)
-        (nodename (completing-read prompt (Info-build-node-completions))))
+        (completion-table (Info-build-node-completions))
+        (nodename (completing-read prompt 'Info-read-node-name-1)))
     (if (equal nodename "")
        (or default
            (Info-read-node-name prompt))
@@ -553,6 +683,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
            (if (marker-buffer Info-tag-table-marker)
                (progn
                  (set-buffer (marker-buffer Info-tag-table-marker))
+                 (widen)
                  (goto-char Info-tag-table-marker)
                  (while (re-search-forward "\nNode: \\(.*\\)\177" nil t)
                    (setq compl
@@ -575,11 +706,14 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 \f
 (defun Info-restore-point (hl)
   "If this node has been visited, restore the point value when we left."
-  (if hl
-      (if (and (equal (nth 0 (car hl)) Info-current-file)
-              (equal (nth 1 (car hl)) Info-current-node))
+  (while hl
+    (if (and (equal (nth 0 (car hl)) Info-current-file)
+            ;; Use string-equal, not equal, to ignore text props.
+            (string-equal (nth 1 (car hl)) Info-current-node))
+       (progn
          (goto-char (nth 2 (car hl)))
-       (Info-restore-point (cdr hl)))))
+         (setq hl nil))                ;terminate the while at next iter
+      (setq hl (cdr hl)))))
 \f
 (defvar Info-last-search nil
   "Default regexp for \\<Info-mode-map>\\[Info-search] command to search for.")
@@ -587,6 +721,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 (defun Info-search (regexp)
   "Search for REGEXP, starting from point, and select node it's found in."
   (interactive "sSearch (regexp): ")
+  (if transient-mark-mode (deactivate-mark))
   (if (equal regexp "")
       (setq regexp Info-last-search)
     (setq Info-last-search regexp))
@@ -631,7 +766,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                (message "Searching subfile %s..." (cdr (car list)))
                (Info-read-subfile (car (car list)))
                (setq list (cdr list))
-               (goto-char (point-min))
+;;             (goto-char (point-min))
                (if (re-search-forward regexp nil t)
                    (setq found (point) list ())))
              (if found
@@ -644,7 +779,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
     (widen)
     (goto-char found)
     (Info-select-node)
-    (or (and (equal onode Info-current-node)
+    ;; Use string-equal, not equal, to ignore text props.
+    (or (and (string-equal onode Info-current-node)
             (equal ofile Info-current-file))
        (setq Info-history (cons (list ofile onode opoint)
                                 Info-history)))))
@@ -662,14 +798,14 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
         (Info-following-node-name))
      (if (eq errorname t)
         nil
-       (error (concat "Node has no " (capitalize (or errorname name))))))))
+       (error "Node has no %s" (capitalize (or errorname name)))))))
 
 ;; Return the node name in the buffer following point.
 ;; ALLOWEDCHARS, if non-nil, goes within [...] to make a regexp
 ;; saying which chas may appear in the node name.
 (defun Info-following-node-name (&optional allowedchars)
   (skip-chars-forward " \t")
-  (buffer-substring
+  (buffer-substring-no-properties
    (point)
    (progn
      (while (looking-at (concat "[" (or allowedchars "^,\t\n") "]"))
@@ -719,8 +855,14 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 NAME may be an abbreviation of the reference name."
   (interactive
    (let ((completion-ignore-case t)
-        completions default (start-point (point)) str i)
+        completions default alt-default (start-point (point)) str i bol eol)
      (save-excursion
+       ;; Store end and beginning of line.
+       (end-of-line)
+       (setq eol (point))
+       (beginning-of-line)
+       (setq bol (point))
+
        (goto-char (point-min))
        (while (re-search-forward "\\*note[ \n\t]*\\([^:]*\\):" nil t)
         (setq str (buffer-substring
@@ -728,9 +870,14 @@ NAME may be an abbreviation of the reference name."
                    (1- (point))))
         ;; See if this one should be the default.
         (and (null default)
-             (< (match-beginning 0) start-point)
+             (<= (match-beginning 0) start-point)
              (<= start-point (point))
              (setq default t))
+        ;; See if this one should be the alternate default.
+        (and (null alt-default)
+             (and (<= bol (match-beginning 0))
+                  (<= (point) eol))
+             (setq alt-default t))
         (setq i 0)
         (while (setq i (string-match "[ \n\t]+" str i))
           (setq str (concat (substring str 0 i) " "
@@ -738,9 +885,16 @@ NAME may be an abbreviation of the reference name."
           (setq i (1+ i)))
         ;; Record as a completion and perhaps as default.
         (if (eq default t) (setq default str))
+        (if (eq alt-default t) (setq alt-default str))
         (setq completions
               (cons (cons str nil)
                     completions))))
+     ;; If no good default was found, try an alternate.
+     (or default
+        (setq default alt-default))
+     ;; If only one cross-reference found, then make it default.
+     (if (eq (length completions) 1)
+         (setq default (car (car completions))))
      (if completions
         (let ((input (completing-read (if default
                                           (concat "Follow reference named: ("
@@ -750,7 +904,7 @@ NAME may be an abbreviation of the reference name."
           (list (if (equal input "")
                     default input)))
        (error "No cross-references in this node"))))
-  (let (target beg i (str (concat "\\*note " footnotename)))
+  (let (target beg i (str (concat "\\*note " (regexp-quote footnotename))))
     (while (setq i (string-match " " str i))
       (setq str (concat (substring str 0 i) "[ \t\n]+" (substring str (1+ i))))
       (setq i (+ i 6)))
@@ -775,19 +929,69 @@ NAME may be an abbreviation of the reference name."
     (forward-char 1)
     (setq str
          (if (looking-at ":")
-             (buffer-substring beg (1- (point)))
+             (buffer-substring-no-properties beg (1- (point)))
            (skip-chars-forward " \t\n")
            (Info-following-node-name (if multi-line "^.,\t" "^.,\t\n"))))
     (while (setq i (string-match "\n" str i))
       (aset str i ?\ ))
+    ;; Collapse multiple spaces.
+    (while (string-match "  +" str)
+      (setq str (replace-match " " t t str)))
     str))
 
-;; No one calls this and Info-menu-item doesn't exist.
+;; No one calls this.
 ;;(defun Info-menu-item-sequence (list)
 ;;  (while list
-;;    (Info-menu-item (car list))
+;;    (Info-menu (car list))
 ;;    (setq list (cdr list))))
 
+(defun Info-complete-menu-item (string predicate action)
+  (let ((case-fold-search t))
+    (cond ((eq action nil)
+          (let (completions
+                (pattern (concat "\n\\* \\("
+                                 (regexp-quote string)
+                                 "[^:\t\n]*\\):")))
+            (save-excursion
+              (set-buffer Info-complete-menu-buffer)
+              (goto-char (point-min))
+              (search-forward "\n* Menu:")
+              (while (re-search-forward pattern nil t)
+                (setq completions (cons (cons (format "%s"
+                                                      (buffer-substring
+                                                       (match-beginning 1)
+                                                       (match-end 1)))
+                                              (match-beginning 1))
+                                        completions))))
+            (try-completion string completions predicate)))
+         ((eq action t)
+          (let (completions
+                (pattern (concat "\n\\* \\("
+                                 (regexp-quote string)
+                                 "[^:\t\n]*\\):")))
+            (save-excursion
+              (set-buffer Info-complete-menu-buffer)
+              (goto-char (point-min))
+              (search-forward "\n* Menu:")
+              (while (re-search-forward pattern nil t)
+                (setq completions (cons (cons (format "%s"
+                                                      (buffer-substring
+                                                       (match-beginning 1)
+                                                       (match-end 1)))
+                                              (match-beginning 1))
+                                        completions))))
+            (all-completions string completions predicate)))
+         (t
+          (save-excursion
+            (set-buffer Info-complete-menu-buffer)
+            (goto-char (point-min))
+            (search-forward "\n* Menu:")
+            (re-search-forward (concat "\n\\* "
+                                       (regexp-quote string)
+                                       ":")
+                               nil t))))))
+
+
 (defun Info-menu (menu-item)
   "Go to node for menu item named (or abbreviated) NAME.
 Completion is allowed, and the menu item point is on is the default."
@@ -796,35 +1000,30 @@ Completion is allowed, and the menu item point is on is the default."
         ;; If point is within a menu item, use that item as the default
         (default nil)
         (p (point))
+        beg
         (last nil))
      (save-excursion
        (goto-char (point-min))
        (if (not (search-forward "\n* menu:" nil t))
           (error "No menu in this node"))
-       (while (re-search-forward
-               "\n\\* \\([^:\t\n]*\\):" nil t)
-        (if (and (null default)
-                 (prog1 (if last (< last p) nil)
-                   (setq last (match-beginning 0)))
-                 (<= p last))
-            (setq default (car (car completions))))
-        (setq completions (cons (cons (buffer-substring
-                                        (match-beginning 1)
-                                        (match-end 1))
-                                      (match-beginning 1))
-                                completions)))
-       (if (and (null default) last
-               (< last p)
-               (<= p (progn (end-of-line) (point))))
-          (setq default (car (car completions)))))
+       (setq beg (point))
+       (and (< (point) p)
+           (save-excursion
+             (goto-char p)
+             (end-of-line)
+             (re-search-backward "\n\\* \\([^:\t\n]*\\):" beg t)
+             (setq default (format "%s" (buffer-substring
+                                         (match-beginning 1)
+                                         (match-end 1)))))))
      (let ((item nil))
        (while (null item)
-        (setq item (let ((completion-ignore-case t))
+        (setq item (let ((completion-ignore-case t)
+                         (Info-complete-menu-buffer (current-buffer)))
                      (completing-read (if default
                                           (format "Menu item (default %s): "
                                                   default)
                                           "Menu item: ")
-                                      completions nil t)))
+                                      'Info-complete-menu-item nil t)))
         ;; we rely on the fact that completing-read accepts an input
         ;; of "" even when the require-match argument is true and ""
         ;; is not a valid possibility
@@ -844,8 +1043,8 @@ Completion is allowed, and the menu item point is on is the default."
     (goto-char (point-min))
     (or (search-forward "\n* menu:" nil t)
        (error "No menu in this node"))
-    (or (re-search-forward (concat "\n* " menu-item ":") nil t)
-       (re-search-forward (concat "\n* " menu-item) nil t)
+    (or (re-search-forward (concat "\n\\* " menu-item ":") nil t)
+       (re-search-forward (concat "\n\\* " menu-item) nil t)
        (error "No such item in menu"))
     (beginning-of-line)
     (forward-char 2)
@@ -910,7 +1109,9 @@ N is the digit argument used to invoke this command."
          (Info-next)
          t)
         ((and (save-excursion (search-backward "up:" nil t))
-             (not (equal (downcase (Info-extract-pointer "up")) "top")))
+             ;; Use string-equal, not equal, to ignore text props.
+             (not (string-equal (downcase (Info-extract-pointer "up"))
+                                "top")))
          (let ((old-node Info-current-node))
            (Info-up)
            (let (Info-history success)
@@ -928,7 +1129,10 @@ N is the digit argument used to invoke this command."
     (cond ((and upnode (string-match "(" upnode))
           (error "First node in file"))
          ((and upnode (or (null prevnode)
-                          (equal (downcase prevnode) (downcase upnode))))
+                          ;; Use string-equal, not equal,
+                          ;; to ignore text properties.
+                          (string-equal (downcase prevnode)
+                                        (downcase upnode))))
           (Info-up))
          (prevnode
           ;; If we move back at the same level,
@@ -962,48 +1166,84 @@ N is the digit argument used to invoke this command."
   (interactive)
   (save-excursion
     (forward-line 1)
-    (search-backward "\n* menu:" nil t)
-    (or (search-backward "\n* " nil t)
-       (error "No previous items in menu"))
-    (Info-goto-node (Info-extract-menu-node-name))))
+    (let ((beg (save-excursion
+                (and (search-backward "\n* menu:" nil t)
+                     (point)))))
+      (or (and beg (search-backward "\n* " beg t))
+         (error "No previous items in menu")))
+    (Info-goto-node (save-excursion
+                     (goto-char (match-end 0))
+                     (Info-extract-menu-node-name)))))
 
 (defmacro Info-no-error (&rest body)
   (list 'condition-case nil (cons 'progn (append body '(t))) '(error nil)))
 
 (defun Info-next-preorder ()
-  "Go to the next node, popping up a level if there is none."
+  "Go to the next subnode or the next node, or go up a level."
   (interactive)
-  (cond ((looking-at "\\*note[ \n]*\\([^:]*\\):")
-        (Info-follow-reference
-         (buffer-substring (match-beginning 1) (match-end 1))))
-       ((Info-no-error (Info-next-menu-item))  )
-       ((Info-no-error (Info-up))              (forward-line 1))
-       (t                                      (error "No more nodes"))))
+  (cond ((Info-no-error (Info-next-menu-item)))
+       ((Info-no-error (Info-next)))
+       ((Info-no-error (Info-up))
+        ;; Since we have already gone thru all the items in this menu,
+        ;; go up to the end of this node.
+        (goto-char (point-max)))
+       (t
+        (error "No more nodes"))))
 
 (defun Info-last-preorder ()
   "Go to the last node, popping up a level if there is none."
   (interactive)
-  (cond ((Info-no-error (Info-last-menu-item)) )
-       ((Info-no-error (Info-up))              (forward-line -1))
-       (t                                      (error "No previous nodes"))))
+  (cond ((Info-no-error
+         (Info-last-menu-item)
+         ;; If we go down a menu item, go to the end of the node
+         ;; so we can scroll back through it.
+         (goto-char (point-max)))
+        (recenter -1))
+       ((Info-no-error (Info-prev))
+        (goto-char (point-max))
+        (recenter -1))
+       ((Info-no-error (Info-up))
+        (goto-char (point-min))
+        (or (search-forward "\n* Menu:" nil t)
+            (goto-char (point-max))))
+       (t (error "No previous nodes"))))
 
 (defun Info-scroll-up ()
-  "Read the next screen.  If end of buffer is visible, go to next entry."
+  "Scroll one screenful forward in Info, considering all nodes as one sequence.
+Once you scroll far enough in a node that its menu appears on the screen,
+the next scroll moves into its first subnode.  When you scroll past
+the end of a node, that goes to the next node or back up to the parent node."
   (interactive)
-  (if (pos-visible-in-window-p (point-max))
-      (Info-next-preorder)
-      (scroll-up))
-  )
+  (if (or (< (window-start) (point-min))
+         (> (window-start) (point-max)))
+      (set-window-start (selected-window) (point)))
+  (let ((virtual-end (save-excursion
+                      (goto-char (point-min))
+                      (if (search-forward "\n* Menu:" nil t)
+                          (point)
+                        (point-max)))))
+    (if (or (< virtual-end (window-start))
+           (pos-visible-in-window-p virtual-end))
+       (Info-next-preorder)
+      (scroll-up))))
 
 (defun Info-scroll-down ()
-  "Read the previous screen.  If start of buffer is visible, go to last entry."
+  "Scroll one screenful back in Info, considering all nodes as one sequence.
+Within the menu of a node, this goes to its last subnode.
+When you scroll past the beginning of a node, that goes to the
+previous node or back up to the parent node."
   (interactive)
-  (if (pos-visible-in-window-p (point-min))
-      (Info-last-preorder)
-      (scroll-down))
-  )
-
-(defun Info-next-reference ()
+  (if (or (< (window-start) (point-min))
+         (> (window-start) (point-max)))
+      (set-window-start (selected-window) (point)))
+  (let ((virtual-end (save-excursion
+                      (goto-char (point-min))
+                      (search-forward "\n* Menu:" nil t))))
+    (if (or virtual-end (pos-visible-in-window-p (point-min)))
+       (Info-last-preorder)
+      (scroll-down))))
+
+(defun Info-next-reference (&optional recur)
   "Move cursor to the next cross-reference or menu item in the node."
   (interactive)
   (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
@@ -1018,9 +1258,11 @@ N is the digit argument used to invoke this command."
                (error "No cross references in this node")))))
     (goto-char (match-beginning 0))
     (if (looking-at "\\* Menu:")
-       (Info-next-reference))))
+       (if recur
+           (error "No cross references in this node")
+         (Info-next-reference t)))))
 
-(defun Info-prev-reference ()
+(defun Info-prev-reference (&optional recur)
   "Move cursor to the previous cross-reference or menu item in the node."
   (interactive)
   (let ((pat "\\*note[ \n\t]*\\([^:]*\\):\\|^\\* .*:")
@@ -1034,7 +1276,9 @@ N is the digit argument used to invoke this command."
                (error "No cross references in this node")))))
     (goto-char (match-beginning 0))
     (if (looking-at "\\* Menu:")
-       (Info-prev-reference))))
+       (if recur
+           (error "No cross references in this node")
+         (Info-prev-reference t)))))
 
 (defun Info-index (topic)
   "Look up a string in the index for this file.
@@ -1048,7 +1292,7 @@ Give a blank topic name to go to the Index node itself."
   (interactive "sIndex topic: ")
   (let ((orignode Info-current-node)
        (rnode nil)
-       (pattern (format "\n\\* \\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ t]*\\([0-9]*\\)"
+       (pattern (format "\n\\* \\([^\n:]*%s[^\n:]*\\):[ \t]*\\([^.\n]*\\)\\.[ \t]*\\([0-9]*\\)"
                         (regexp-quote topic)))
        node)
     (Info-goto-node "Top")
@@ -1057,12 +1301,17 @@ Give a blank topic name to go to the Index node itself."
     (or (re-search-forward "\n\\* \\(.*\\<Index\\>\\)" nil t)
        (error "No index"))
     (goto-char (match-beginning 1))
-    (let ((Info-keeping-history nil))
+    ;; Here, and subsequently in this function,
+    ;; we bind Info-history to nil for internal node-switches
+    ;; so that we don't put junk in the history.
+    ;; In the first Info-goto-node call, above, we do update the history
+    ;; because that is what the user's previous node choice into it.
+    (let ((Info-history nil))
       (Info-goto-node (Info-extract-menu-node-name)))
     (or (equal topic "")
        (let ((matches nil)
              (exact nil)
-             (Info-keeping-history nil)
+             (Info-history nil)
              found)
          (while
              (progn
@@ -1084,8 +1333,8 @@ Give a blank topic name to go to the Index node itself."
            (Info-goto-node node))
          (or matches
              (progn
-               (Info-last)
-               (error "No \"%s\" in index" topic)))
+               (Info-goto-node orignode)
+               (error "No `%s' in index" topic)))
          ;; Here it is a feature that assoc is case-sensitive.
          (while (setq found (assoc topic matches))
            (setq exact (cons found exact)
@@ -1110,24 +1359,28 @@ Give a blank topic name to go to the Index node itself."
       (forward-line (nth 3 (car Info-index-alternatives)))
     (forward-line 3)  ; don't search in headers
     (let ((name (car (car Info-index-alternatives))))
-      (if (or (re-search-forward (format
-                                 "\\(Function\\|Command\\): %s\\( \\|$\\)"
-                                 (regexp-quote name)) nil t)
-             (search-forward (format "`%s'" name) nil t)
-             (and (string-match "\\`.*\\( (.*)\\)\\'" name)
-                  (search-forward
-                   (format "`%s'" (substring name 0 (match-beginning 1)))
-                   nil t))
-             (search-forward name nil t))
-         (beginning-of-line)
-       (goto-char (point-min)))))
-  (message "Found \"%s\" in %s.  %s"
+      (Info-find-index-name name)))
+  (message "Found `%s' in %s.  %s"
           (car (car Info-index-alternatives))
           (nth 2 (car Info-index-alternatives))
           (if (cdr Info-index-alternatives)
               "(Press `,' for more)"
             "(Only match)")))
 
+(defun Info-find-index-name (name)
+  "Move point to the place within the current node where NAME is defined."
+  (if (or (re-search-forward (format
+                             "[a-zA-Z]+: %s\\( \\|$\\)"
+                             (regexp-quote name)) nil t)
+         (search-forward (format "`%s'" name) nil t)
+         (and (string-match "\\`.*\\( (.*)\\)\\'" name)
+              (search-forward
+               (format "`%s'" (substring name 0 (match-beginning 1)))
+               nil t))
+         (search-forward name nil t))
+      (beginning-of-line)
+    (goto-char (point-min))))
+
 (defun Info-undefined ()
   "Make command be undefined in Info."
   (interactive)
@@ -1149,6 +1402,7 @@ Give a blank topic name to go to the Index node itself."
     (switch-to-buffer "*Help*")
     (erase-buffer)
     (insert (documentation 'Info-mode))
+    (help-mode)
     (goto-char (point-min))
     (let (ch flag)
       (while (progn (setq flag (not (pos-visible-in-window-p (point-max))))
@@ -1166,7 +1420,7 @@ POS must be somewhere inside the token
 START is a regular expression which will match the
     beginning of the tokens delimited string
 ALL is a regular expression with a single
-    parenthized subpattern which is the token to be
+    parenthesized subpattern which is the token to be
     returned. E.g. '{\(.*\)}' would return any string
     enclosed in braces around POS.
 SIG optional fourth argument, controls action on no match
@@ -1191,19 +1445,35 @@ SIG optional fourth argument, controls action on no match
              (t
               (error "No %s around position %d" errorstring pos)))))))
 
-(defun Info-follow-nearest-node (click)
+(defun Info-mouse-follow-nearest-node (click)
   "\\<Info-mode-map>Follow a node reference near point.
 Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where you click.
-At end of the node's text, moves to the next node."
+At end of the node's text, moves to the next node, or up if none."
   (interactive "e")
   (let* ((start (event-start click))
         (window (car start))
         (pos (car (cdr start))))
     (select-window window)
     (goto-char pos))
+  (and (not (Info-try-follow-nearest-node))
+       (save-excursion (forward-line 1) (eobp))
+       (Info-next-preorder)))
+
+(defun Info-follow-nearest-node ()
+  "\\<Info-mode-map>Follow a node reference near point.
+Like \\[Info-menu], \\[Info-follow-reference], \\[Info-next], \\[Info-prev] or \\[Info-up] command, depending on where point is.
+If no reference to follow, moves to the next node, or up if none."
+  (interactive)
+  (or (Info-try-follow-nearest-node)
+      (Info-next-preorder)))
+
+;; Common subroutine.
+(defun Info-try-follow-nearest-node ()
+  "Follow a node reference near point.  Return non-nil if successful."
   (let (node)
     (cond
-     ((setq node (Info-get-token (point) "\\*note[ \n]" "\\*note[ \n]\\([^:]*\\):"))
+     ((setq node (Info-get-token (point) "\\*note[ \n]"
+                                "\\*note[ \n]\\([^:]*\\):"))
       (Info-follow-reference node))
      ((setq node (Info-get-token (point) "\\* " "\\* \\([^:]*\\)::"))
       (Info-goto-node node))
@@ -1216,10 +1486,8 @@ At end of the node's text, moves to the next node."
      ((setq node (Info-get-token (point) "File: " "File: \\([^,\n\t]*\\)"))
       (Info-goto-node "Top"))
      ((setq node (Info-get-token (point) "Prev: " "Prev: \\([^,\n\t]*\\)"))
-      (Info-goto-node node))
-     ((save-excursion (forward-line 1) (eobp))
-      (Info-next)))
-    ))
+      (Info-goto-node node)))
+    node))
 \f
 (defvar Info-mode-map nil
   "Keymap containing Info commands.")
@@ -1229,7 +1497,7 @@ At end of the node's text, moves to the next node."
   (suppress-keymap Info-mode-map)
   (define-key Info-mode-map "." 'beginning-of-buffer)
   (define-key Info-mode-map " " 'Info-scroll-up)
-  (define-key Info-mode-map "\C-m" 'Info-next-preorder)
+  (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 "1" 'Info-nth-menu-item)
@@ -1260,11 +1528,13 @@ At end of the node's text, moves to the next node."
   (define-key Info-mode-map "p" 'Info-prev)
   (define-key Info-mode-map "q" 'Info-exit)
   (define-key Info-mode-map "s" 'Info-search)
+  ;; For consistency with Rmail.
+  (define-key Info-mode-map "\M-s" 'Info-search)
   (define-key Info-mode-map "t" 'Info-top-node)
   (define-key Info-mode-map "u" 'Info-up)
   (define-key Info-mode-map "," 'Info-index-next)
   (define-key Info-mode-map "\177" 'Info-scroll-down)
-  (define-key Info-mode-map [mouse-2] 'Info-follow-nearest-node)
+  (define-key Info-mode-map [mouse-2] 'Info-mouse-follow-nearest-node)
   )
 \f
 ;; Info mode is suitable only for specially formatted data.
@@ -1280,6 +1550,10 @@ topics.  Info has commands to follow the references and show you other nodes.
 \\[Info-help]  Invoke the Info tutorial.
 
 Selecting other nodes:
+\\[Info-mouse-follow-nearest-node]
+       Follow a node reference you click on.
+         This works with menu items, cross references, and
+         the \"next\", \"previous\" and \"up\", depending on where you click.
 \\[Info-next]  Move to the \"next\" node of this node.
 \\[Info-prev]  Move to the \"previous\" node of this node.
 \\[Info-up]    Move \"up\" from this node.
@@ -1292,9 +1566,9 @@ Selecting other nodes:
 \\[Info-index-next]    (comma) Move to the next match from a previous `i' command.
 
 Moving within a node:
-\\[scroll-up]  Normally, scroll forward a full screen.  If the end of the buffer is
+\\[Info-scroll-up]     Normally, scroll forward a full screen.  If the end of the buffer is
 already visible, try to go to the next menu entry, or up if there is none.
-\\[scroll-down]  Normally, scroll backward.  If the beginning of the buffer is
+\\[Info-scroll-down]  Normally, scroll backward.  If the beginning of the buffer is
 already visible, try to go to the previous menu entry, or up if there is none.
 \\[beginning-of-buffer]        Go to beginning of node.  
 
@@ -1308,9 +1582,6 @@ Advanced commands:
 \\[universal-argument] \\[info]        Move to new Info file with completion.
 \\[Info-search]        Search through this Info file for specified regexp,
        and select the node in which the next occurrence is found.
-\\[Info-next-preorder] Next-preorder; that is, try to go to the next menu item,
-       and if that fails try to move up, and if that fails, tell user
-       he/she is done reading.
 \\[Info-next-reference]        Move cursor to next cross-reference or menu item.
 \\[Info-prev-reference]        Move cursor to previous cross-reference or menu item."
   (kill-all-local-variables)
@@ -1327,7 +1598,7 @@ Advanced commands:
   (make-local-variable 'Info-tag-table-marker)
   (make-local-variable 'Info-history)
   (make-local-variable 'Info-index-alternatives)
-  (if (fboundp 'make-face)
+  (if (memq (framep (selected-frame)) '(x pc))
       (progn
        (make-face 'info-node)
        (make-face 'info-menu-5)
@@ -1359,7 +1630,14 @@ Advanced commands:
 Like text mode with the addition of `Info-cease-edit'
 which returns to Info mode for browsing.
 \\{Info-edit-map}"
-  )
+  (use-local-map Info-edit-map)
+  (setq major-mode 'Info-edit-mode)
+  (setq mode-name "Info Edit")
+  (kill-local-variable 'mode-line-buffer-identification)
+  (setq buffer-read-only nil)
+  (force-mode-line-update)
+  (buffer-enable-undo (current-buffer))
+  (run-hooks 'Info-edit-mode-hook))
 
 (defun Info-edit ()
   "Edit the contents of this Info node.
@@ -1367,15 +1645,9 @@ Allowed only if variable `Info-enable-edit' is non-nil."
   (interactive)
   (or Info-enable-edit
       (error "Editing info nodes is not enabled"))
-  (use-local-map Info-edit-map)
-  (setq major-mode 'Info-edit-mode)
-  (setq mode-name "Info Edit")
-  (kill-local-variable 'mode-line-buffer-identification)
-  (setq buffer-read-only nil)
-  ;; Make mode line update.
-  (set-buffer-modified-p (buffer-modified-p))
-  (message (substitute-command-keys
-            "Editing: Type \\<Info-mode-map>\\[Info-cease-edit] to return to info")))
+  (Info-edit-mode)
+  (message "%s" (substitute-command-keys
+           "Editing: Type \\<Info-edit-map>\\[Info-cease-edit] to return to info")))
 
 (defun Info-cease-edit ()
   "Finish editing Info node; switch back to Info proper."
@@ -1389,22 +1661,53 @@ Allowed only if variable `Info-enable-edit' is non-nil."
   (setq mode-name "Info")
   (Info-set-mode-line)
   (setq buffer-read-only t)
-  ;; Make mode line update.
-  (set-buffer-modified-p (buffer-modified-p))
+  (force-mode-line-update)
   (and (marker-position Info-tag-table-marker)
        (buffer-modified-p)
        (message "Tags may have changed.  Use Info-tagify if necessary")))
 \f
+(defvar Info-file-list-for-emacs
+  '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc")
+  "List of Info files that describe Emacs commands.
+An element can be a file name, or a list of the form (PREFIX . FILE)
+where PREFIX is a name prefix and FILE is the file to look in.
+If the element is just a file name, the file name also serves as the prefix.")
+
 (defun Info-find-emacs-command-nodes (command)
-  "Return a list of locations documenting COMMAND in the Emacs Info manual.
+  "Return a list of locations documenting COMMAND.
+The `info-file' property of COMMAND says which Info manual to search.
+If COMMAND has no property, the variable `Info-file-list-for-emacs'
+defines heuristics for which Info manual to try.
 The locations are of the format used in Info-history, i.e.
 \(FILENAME NODENAME BUFFERPOS\)."
-  (require 'info)
   (let ((where '())
        (cmd-desc (concat "^\\* " (regexp-quote (symbol-name command))
-                         ":\\s *\\(.*\\)\\.$")))
+                         ":\\s *\\(.*\\)\\.$"))
+       (info-file "emacs"))            ;default
+    ;; Determine which info file this command is documented in.
+    (if (get command 'info-file)
+       (setq info-file (get command 'info-file))
+      ;; If it doesn't say explicitly, test its name against
+      ;; various prefixes that we know.
+      (let ((file-list Info-file-list-for-emacs))
+       (while file-list
+         (let* ((elt (car file-list))
+                (name (if (consp elt)
+                          (car elt)
+                        elt))
+                (file (if (consp elt) (cdr elt) elt))
+                (regexp (concat "\\`" (regexp-quote name)
+                                "\\(\\'\\|-\\)")))
+           (if (string-match regexp (symbol-name command))
+               (setq info-file file file-list nil))
+           (setq file-list (cdr file-list))))))
     (save-excursion
-      (Info-find-node "emacs" "Command Index")
+      (condition-case nil
+         (Info-find-node info-file "Command Index")
+       ;; Some manuals may not have a separate Command Index node,
+       ;; so try just Index instead.
+       (error
+        (Info-find-node info-file "Index")))
       ;; Take the index node off the Info history.
       (setq Info-history (cdr Info-history))
       (goto-char (point-max))
@@ -1420,7 +1723,9 @@ The locations are of the format used in Info-history, i.e.
 ;;;###autoload
 (defun Info-goto-emacs-command-node (command)
   "Go to the Info node in the Emacs manual for command COMMAND.
-The command is found by looking up in Emacs manual's Command Index."
+The command is found by looking up in Emacs manual's Command Index
+or in another manual found via COMMAND's `info-file' property or
+the variable `Info-file-list-for-emacs'."
   (interactive "CFind documentation for command: ")
   (or (commandp command)
       (signal 'wrong-type-argument (list 'commandp command)))
@@ -1439,18 +1744,20 @@ The command is found by looking up in Emacs manual's Command Index."
                ;; Info-history.  Put the other nodes that were found on
                ;; the history.
                (setq Info-history (nconc (cdr where) Info-history))
-               (message (substitute-command-keys
-                         "Found %d other entr%.  Use \\[Info-last] to see %s."
-                       (1- num-matches)
-                       (if (> num-matches 2) "ies" "y")
-                       (if (> num-matches 2) "them" "it"))))))
-      (error "Couldn't find documentation for %s." command))))
+               (message "Found %d other entr%s.  Use %s to see %s."
+                        (1- num-matches)
+                        (if (> num-matches 2) "ies" "y")
+                        (substitute-command-keys "\\[Info-last]")
+                        (if (> num-matches 2) "them" "it")))))
+      (error "Couldn't find documentation for %s" command))))
 
 ;;;###autoload
 (defun Info-goto-emacs-key-command-node (key)
   "Go to the Info node in the Emacs manual the command bound to KEY, a string.
 Interactively, if the binding is execute-extended-command, a command is read.
-The command is found by looking up in Emacs manual's Command Index."
+The command is found by looking up in Emacs manual's Command Index
+or in another manual found via COMMAND's `info-file' property or
+the variable `Info-file-list-for-emacs'."
   (interactive "kFind documentation for key:")
   (let ((command (key-binding key)))
     (cond ((null command)
@@ -1462,6 +1769,13 @@ The command is found by looking up in Emacs manual's Command Index."
          (t
           (Info-goto-emacs-command-node command)))))
 \f
+(defvar Info-title-face-alist
+  '((?* bold underline)
+    (?= bold-italic underline)
+    (?- italic underline))
+  "*Alist of face or list of faces to use for pseudo-underlined titles.
+The alist key is the character the title is underlined with (?*, ?= or ?-).")
+
 (defun Info-fontify-node ()
   (save-excursion
     (let ((buffer-read-only nil))
@@ -1473,18 +1787,30 @@ The command is found by looking up in Emacs manual's Command Index."
                (looking-at "[ \t]*[^:, \t\n]+:[ \t]+\\([^:,\t\n]+\\),?")
              (goto-char (match-end 0))
              (put-text-property (match-beginning 1) (match-end 1)
-                                'face 'info-xref))))
+                                'face 'info-xref)
+             (put-text-property (match-beginning 1) (match-end 1)
+                                'mouse-face 'highlight))))
+      (goto-char (point-min))
+      (while (re-search-forward "\n\\([^ \t\n].+\\)\n\\(\\*+\\|=+\\|-+\\)$"
+                                nil t)
+       (put-text-property (match-beginning 1) (match-end 1)
+                          'face
+                          (cdr (assq (preceding-char) Info-title-face-alist)))
+       (put-text-property (match-end 1) (match-end 2)
+                          'invisible t))
       (goto-char (point-min))
-      (while (re-search-forward "\\*Note[ \n\t]*\\([^:]*\\):" nil t)
+      (while (re-search-forward "\\*Note[ \n\t]+\\([^:]*\\):" nil t)
        (if (= (char-after (1- (match-beginning 0))) ?\") ; hack
            nil
          (put-text-property (match-beginning 1) (match-end 1)
-                            'face 'info-xref)))
+                            'face 'info-xref)
+         (put-text-property (match-beginning 1) (match-end 1)
+                            'mouse-face 'highlight)))
       (goto-char (point-min))
       (if (and (search-forward "\n* Menu:" nil t)
               (not (string-match "\\<Index\\>" Info-current-node))
               ;; Don't take time to annotate huge menus
-              (< (- (point-max) (point)) 10000))
+              (< (- (point-max) (point)) Info-fontify-maximum-menu-size))
          (let ((n 0))
            (while (re-search-forward "^\\* \\([^:\t\n]*\\):" nil t)
              (setq n (1+ n))
@@ -1493,7 +1819,9 @@ The command is found by looking up in Emacs manual's Command Index."
                                     (1+ (match-beginning 0))
                                     'face 'info-menu-5))
              (put-text-property (match-beginning 1) (match-end 1)
-                                'face 'info-node))))
+                                'face 'info-node)
+             (put-text-property (match-beginning 1) (match-end 1)
+                                'mouse-face 'highlight))))
       (set-buffer-modified-p nil))))
 
 (provide 'info)