]> code.delx.au - gnu-emacs/blobdiff - lisp/info.el
(fortran-comment-line-start): Renamed from comment-line-start.
[gnu-emacs] / lisp / info.el
index 6149c570b148dc137ac21e246ae97efe2ef743d8..c48e2800cf3a9085328d4cb74febd15048a3f896 100644 (file)
@@ -1,6 +1,6 @@
 ;;; info.el --- info package for Emacs.
 
-;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 97, 98 Free Software
+;; Copyright (C) 1985, 86, 92, 93, 94, 95, 96, 97, 98, 99 Free Software
 ;; Foundation, Inc.
 
 ;; Maintainer: FSF
@@ -29,6 +29,8 @@
 
 ;;; Code:
 
+(eval-when-compile (require 'jka-compr))
+
 (defgroup info nil
   "Info subsystem"
   :group 'help
@@ -78,42 +80,7 @@ The Lisp code is executed when the node is selected.")
   :type 'integer
   :group 'info)
 
-(defvar Info-directory-list
-  (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-separator path) (length path))
-                 list (cons (substring path 0 idx) list)
-                 path (substring path (min (1+ idx)
-                                           (length path)))))
-         (nreverse 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)))))))
+(defvar Info-directory-list nil
   "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'
@@ -135,7 +102,8 @@ 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.
 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.")
+It doesn't contain directory names or file name extensions added by Info.
+Can also be t when using `Info-on-current-buffer'.")
 
 (defvar Info-current-subfile nil
   "Info subfile that is actually in the *info* buffer now,
@@ -159,13 +127,14 @@ Marker points nowhere if file has no tag table.")
 
 (defvar Info-standalone nil
   "Non-nil if Emacs was started solely as an Info browser.")
-
+\f
 (defvar Info-suffix-list
   ;; The MS-DOS list should work both when long file names are
   ;; supported (Windows 9X), and when only 8+3 file names are available.
   (if (eq system-type 'ms-dos)
       '( (".gz"      . "gunzip")
         (".z"       . "gunzip")
+        (".bz2"     . "bzip2 -dc")
         (".inz"     . "gunzip")
         (".igz"     . "gunzip")
         (".info.Z"  . "gunzip")
@@ -183,21 +152,25 @@ Marker points nowhere if file has no tag table.")
        (".info.Y".    "unyabba")
        (".info.gz".   "gunzip")
        (".info.z".    "gunzip")
+       (".info.bz2" . "bzip2 -dc")
        (".info".      nil)
        ("-info.Z".   "uncompress")
        ("-info.Y".   "unyabba")
        ("-info.gz".  "gunzip")
+       ("-info.bz2" . "bzip2 -dc")
        ("-info.z".   "gunzip")
        ("-info".     nil)
        ("/index.Z".   "uncompress")
        ("/index.Y".   "unyabba")
        ("/index.gz".  "gunzip")
        ("/index.z".   "gunzip")
+       ("/index.bz2". "bzip2 -dc")
        ("/index".     nil)
        (".Z".         "uncompress")
        (".Y".         "unyabba")
        (".gz".        "gunzip")
        (".z".         "gunzip")
+       (".bz2" .      "bzip2 -dc")
        ("".           nil)))
   "List of file name suffixes and associated decoding commands.
 Each entry should be (SUFFIX . STRING); the file is given to
@@ -273,6 +246,40 @@ Do the right thing if the file has been compressed or zipped."
                                       default-directory)))
            (call-process-region (point-min) (point-max) decoder t t)))
       (insert-file-contents fullname visit))))
+\f
+;; Initialize Info-directory-list, if that hasn't been done yet.
+(defun info-initialize ()
+  (unless Info-directory-list
+    (let ((path (getenv "INFOPATH"))
+         (source (expand-file-name "info/" source-directory))
+         (sibling (if installation-directory
+                      (expand-file-name "info/" installation-directory)))
+         alternative)
+      (setq Info-directory-list
+           (if path
+               (split-string path (regexp-quote path-separator))
+             (if (and sibling (file-exists-p sibling))
+                 ;; Uninstalled, Emacs builddir != srcdir.
+                 (setq alternative sibling)
+               ;; Uninstalled, builddir == srcdir
+               (setq alternative source))
+             (if (or (member alternative Info-default-directory-list)
+                     ;; On DOS/NT, we use movable executables always,
+                     ;; and we must always find the Info dir at run time.
+                     (if (memq system-type '(ms-dos 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))))
+                     (not (file-exists-p alternative)))
+                 Info-default-directory-list
+               ;; `alternative' contains the Info files that came with this
+               ;; version, so we should look there first.  `Info-insert-dir'
+               ;; currently expects to find `alternative' first on the list.
+               (cons alternative
+                     (reverse (cdr (reverse Info-default-directory-list))))))))))
 
 ;;;###autoload
 (defun info-other-window (&optional file)
@@ -289,6 +296,8 @@ Do the right thing if the file has been compressed or zipped."
   "Enter Info, the documentation browser.
 Optional argument FILE 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
+`(FILENAME)NODENAME'.
 
 In interactive use, a prefix argument directs this command
 to read a file name from the minibuffer.
@@ -329,14 +338,35 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                                   (nth 1 err) err)))
               (save-buffers-kill-emacs)))
     (info)))
+\f
+;; See if the the accessible portion of the buffer begins with a node
+;; delimiter, and the node header line which follows matches REGEXP.
+;; Typically, this test will be followed by a loop that examines the
+;; rest of the buffer with (search-forward "\n\^_"), and it's a pity
+;; to have the overhead of this special test inside the loop.
+
+;; This function changes match-data, but supposedly the caller might
+;; want to use the results of re-search-backward.
+
+;; The return value is the value of point at the beginning of matching
+;; REGERXP, if the function succeeds, nil otherwise.
+(defun Info-node-at-bob-matching (regexp)
+  (and (bobp)                  ; are we at beginning of buffer?
+       (looking-at "\^_")      ; does it begin with node delimiter?
+       (let (beg)
+        (forward-line 1)
+        (setq beg (point))
+        (forward-line 1)       ; does the line after delimiter match REGEXP?
+        (re-search-backward regexp beg t))))
 
 ;; Go to an info node specified as separate filename and nodename.
 ;; no-going-back is non-nil if recovering from an error in this function;
 ;; it says do not attempt further (recursive) error recovery.
 (defun Info-find-node (filename nodename &optional no-going-back)
+  (info-initialize)
   ;; Convert filename to lower case if not found as specified.
   ;; Expand it.
-  (if filename
+  (if (stringp filename)
       (let (temp temp-downcase found)
         (setq filename (substitute-in-file-name filename))
         (if (string= (downcase filename) "dir")
@@ -381,6 +411,22 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                   Info-history)))
   ;; Go into info buffer.
   (or (eq major-mode 'Info-mode) (pop-to-buffer "*info*"))
+  (Info-find-node-2 filename nodename no-going-back))
+
+(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'."
+  (interactive
+   (list (if current-prefix-arg
+            (completing-read "Node name: " (Info-build-node-completions)
+                             nil t "Top")
+          "Top")))
+  (Info-mode)
+  (set (make-local-variable 'Info-current-file) t)
+  (Info-find-node-2 nil nodename))
+
+(defun Info-find-node-2 (filename nodename &optional no-going-back)
   (buffer-disable-undo (current-buffer))
   (or (eq major-mode 'Info-mode)
       (Info-mode))
@@ -388,7 +434,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
   (setq Info-current-node nil)
   (unwind-protect
       ;; Bind case-fold-search in case the user sets it to nil.
-      (let ((case-fold-search t))
+      (let ((case-fold-search t)
+           anchorpos)
         ;; Switch files if necessary
         (or (null filename)
             (equal Info-current-file filename)
@@ -453,28 +500,37 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
           ;; *Or* the same, but in an indirect subfile.
 
           ;; Search file for a suitable node.
-          (let ((guesspos (point-min))
-                (regexp
-                 (concat "\\(Node:\\|Ref:\\) *"
-                         (regexp-quote nodename)
-                         " *[,\t\n\177]")))
+         (let ((guesspos (point-min))
+               (regexp
+                (concat "\\(Node:\\|Ref:\\) *\\("
+                        (regexp-quote nodename)
+                        "\\) *[,\t\n\177]"))
+               (nodepos nil))
 
             ;; First, search a tag table, if any
             (if (marker-position Info-tag-table-marker)
-
-                (let (found-in-tag-table
-                      found-mode
-                      (m Info-tag-table-marker))
+               (let ((found-in-tag-table t)
+                     found-anchor
+                     found-mode
+                     (m Info-tag-table-marker))
                   (save-excursion
                     (set-buffer (marker-buffer m))
                     (goto-char m)
                     (beginning-of-line) ; so re-search will work.
 
                     ;; Search tag table
-                    (setq found-in-tag-table
-                          (re-search-forward regexp nil t))
+                   (catch 'foo
+                     (while (re-search-forward regexp nil t)
+                       (setq found-anchor
+                             (string-equal "Ref:" (match-string 1)))
+                       (or nodepos (setq nodepos (point))
+                       (if (string-equal (match-string 2) nodename)
+                           (throw 'foo t))))
+                     (if nodepos
+                         (goto-char nodepos)
+                       (setq found-in-tag-table nil)))
                     (if found-in-tag-table
-                        (setq guesspos (read (current-buffer))))
+                        (setq guesspos (1+ (read (current-buffer)))))
                     (setq found-mode major-mode))
 
                   ;; Indirect file among split files
@@ -491,31 +547,66 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                             (setq guesspos (Info-read-subfile guesspos)))))
 
                   ;; Handle anchor
-                  (if (and found-in-tag-table
-                           (string-equal "Ref:" (match-string 1)))
-                      (goto-char guesspos)
+                  (if found-anchor
+                     (goto-char (setq anchorpos guesspos))
 
                     ;; Else we may have a node, which we search for:
-                   (goto-char (max (point-min)
-                                   (- (byte-to-position guesspos) 1000)))
+                   (let ((guesschar
+                          (or (byte-to-position guesspos)
+                              (if (< (position-bytes (point-max)) guesspos)
+                                  (point-max)
+                                (point-min)))))
+                     (goto-char (max (point-min)
+                                     (- guesschar 1000))))
                     ;; Now search from our advised position
                     ;; (or from beg of buffer)
                     ;; to find the actual node.
-                    (catch 'foo
-                      (while (search-forward "\n\^_" nil t)
-                        (forward-line 1)
-                        (let ((beg (point)))
-                          (forward-line 1)
-                          (if (re-search-backward regexp beg t)
-                              (progn
-                                (beginning-of-line)
-                                (throw 'foo t)))))
-                      (error
-                       "No such anchor in tag table or node in tag table or file: %s"
-                       nodename))))))
-
+                   ;; First, check whether the node is right
+                   ;; where we are, in case the buffer begins
+                   ;; with a node.
+                   (setq nodepos nil)
+                   (or (Info-node-at-bob-matching regexp)
+                       (catch 'foo
+                         (while (search-forward "\n\^_" nil t)
+                           (forward-line 1)
+                           (let ((beg (point)))
+                             (forward-line 1)
+                             (if (re-search-backward regexp beg t)
+                                 (if (string-equal (match-string 2) nodename)
+                                     (progn
+                                       (beginning-of-line)
+                                       (throw 'foo t))
+                                   (or nodepos
+                                     (setq nodepos (point)))))))
+                         (if nodepos
+                             (progn
+                               (goto-char nodepos)
+                               (beginning-of-line))
+                           (error
+                            "No such anchor in tag table or node in tag table or file: %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.
+             ;; First, check whether the node is right where we are, in case
+             ;; the buffer begins with a node.
+             (setq nodepos nil)
+             (or (Info-node-at-bob-matching regexp)
+                 (catch 'foo
+                   (while (search-forward "\n\^_" nil t)
+                     (forward-line 1)
+                     (let ((beg (point)))
+                       (forward-line 1)
+                       (if (re-search-backward regexp beg t)
+                           (if (string-equal (match-string 2) nodename)
+                               (throw 'foo t)
+                             (or nodepos
+                                 (setq nodepos (point)))))))
+                   (if nodepos
+                       (goto-char nodepos)
+                     (error "No such node: %s" nodename))))))
           (Info-select-node)
-         (goto-char (point-min))))
+         (goto-char (or anchorpos (point-min)))))
     ;; If we did not finish finding the specified node,
     ;; go back to the previous one.
     (or Info-current-node no-going-back (null Info-history)
@@ -536,6 +627,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 ;; constructed Info-dir-contents.
 (defvar Info-dir-file-attributes nil)
 
+(defvar Info-dir-file-name nil)
+
 ;; Construct the Info directory node by merging the files named `dir'
 ;; from various directories.  Set the *info* buffer's
 ;; default-directory to the first directory we actually get any text
@@ -546,16 +639,23 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
           ;; since we used it.
           (eval (cons 'and
                       (mapcar '(lambda (elt)
-                                 (let ((curr (file-attributes (car elt))))
+                                 (let ((curr (file-attributes 
+                                              ;; Handle symlinks
+                                              (file-truename (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)
+      (progn
+       (insert Info-dir-contents)
+       (goto-char (point-min)))
     (let ((dirs Info-directory-list)
          ;; Bind this in case the user sets it to nil.
          (case-fold-search t)
+         ;; This is set non-nil if we find a problem in some input files.
+         problems
          buffers buffer others nodes dirs-done)
 
       (setq Info-dir-file-attributes nil)
@@ -590,6 +690,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                      (condition-case nil
                          (progn
                            (insert-file-contents file)
+                           (make-local-variable 'Info-dir-file-name)
+                           (setq Info-dir-file-name file)
                            (setq buffers (cons (current-buffer) buffers)
                                  Info-dir-file-attributes
                                  (cons (cons file attrs)
@@ -603,9 +705,10 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
          (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.
-      (setq buffer (car buffers)
-           others (cdr buffers))
+      ;; The definition of `Info-directory-list' puts it first on that
+      ;; list and so last in `buffers' at this point.
+      (setq buffer (car (last buffers))
+           others (delq buffer buffers))
 
       ;; Insert the entire original dir file as a start; note that we've
       ;; already saved its default directory to use as the default
@@ -614,7 +717,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 
       ;; Look at each of the other buffers one by one.
       (while others
-       (let ((other (car others)))
+       (let ((other (car others))
+             this-buffer-nodes)
          ;; In each, find all the menus.
          (save-excursion
            (set-buffer other)
@@ -624,13 +728,21 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
              (let (beg nodename end)
                (forward-line 1)
                (setq beg (point))
-               (search-backward "\n\^_")
+               (or (search-backward "\n\^_" nil 'move)
+                   (looking-at "\^_")
+                   (signal 'search-failed (list "\n\^_")))
                (search-forward "Node: ")
                (setq nodename (Info-following-node-name))
                (search-forward "\n\^_" nil 'move)
                (beginning-of-line)
                (setq end (point))
-               (setq nodes (cons (list nodename other beg end) nodes))))))
+               (setq this-buffer-nodes
+                     (cons (list nodename other beg end)
+                           this-buffer-nodes))))
+           (if (assoc-ignore-case "top" this-buffer-nodes)
+               (setq nodes (nconc this-buffer-nodes nodes))
+             (setq problems t)
+             (message "No `top' node in %s" Info-dir-file-name))))
        (setq others (cdr others)))
       ;; Add to the main menu a menu item for each other node.
       (re-search-forward "^\\* Menu:")
@@ -657,7 +769,7 @@ 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\^_.*\n.*Node: "
+         (if (re-search-forward (concat "^\^_.*\n.*Node: "
                                         (regexp-quote nodename)
                                         "[,\n\t]")
                                 nil t)
@@ -676,7 +788,10 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
       (while buffers
        (kill-buffer (car buffers))
        (setq buffers (cdr buffers)))
-      (message "Composing main Info directory...done"))
+      (goto-char (point-min))
+      (if problems
+         (message "Composing main Info directory...problems encountered, see `*Messages*'")
+       (message "Composing main Info directory...done")))
     (setq Info-dir-contents (buffer-string)))
   (setq default-directory Info-dir-contents-directory))
 
@@ -691,7 +806,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
        (save-excursion
          (set-buffer (marker-buffer Info-tag-table-marker))
          (goto-char (point-min))
-         (search-forward "\n\^_")
+         (or (looking-at "\^_")
+             (search-forward "\n\^_"))
          (forward-line 2)
          (catch 'foo
            (while (not (looking-at "\^_"))
@@ -722,7 +838,9 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
          (set-buffer-modified-p nil)
          (setq Info-current-subfile lastfilename)))
     (goto-char (point-min))
-    (search-forward "\n\^_")
+    (if (looking-at "\^_")
+       (forward-char 1)
+      (search-forward "\n\^_"))
     (if (numberp nodepos)
        (+ (- nodepos lastfilepos) (point)))))
 
@@ -732,8 +850,11 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
   (let ((case-fold-search t))
     (save-excursion
      ;; Find beginning of node.
-     (search-backward "\n\^_")
-     (forward-line 2)
+     (if (search-backward "\n\^_" nil 'move)
+        (forward-line 2)
+       (if (looking-at "\^_")
+          (forward-line 1)
+        (signal 'search-failed (list "\n\^_"))))
      ;; Get nodename spelled as it is in the node.
      (re-search-forward "Node:[ \t]*")
      (setq Info-current-node
@@ -764,7 +885,9 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
        (concat
         "  Info:  ("
         (if Info-current-file
-            (file-name-nondirectory Info-current-file)
+            (file-name-nondirectory (if (stringp Info-current-file)
+                                        Info-current-file
+                                      (or buffer-file-name "")))
           "")
         ")"
         (or Info-current-node ""))))
@@ -772,9 +895,15 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 ;; Go to an info node specified with a filename-and-nodename string
 ;; of the sort that is found in pointers in nodes.
 
-(defun Info-goto-node (nodename)
-  "Go to info node named NAME.  Give just NODENAME or (FILENAME)NODENAME."
-  (interactive (list (Info-read-node-name "Goto node: ")))
+(defun Info-goto-node (nodename &optional fork)
+  "Go to info node named NAME.  Give just NODENAME or (FILENAME)NODENAME.
+If FORK is non-nil, show the node in a new info buffer.
+If FORK is a string, it is the name to use for the new buffer."
+  (interactive (list (Info-read-node-name "Goto node: ") current-prefix-arg))
+  (info-initialize)
+  (if fork
+    (set-buffer
+     (clone-buffer (concat "*info-" (if (stringp fork) fork nodename) "*") t)))
   (let (filename)
     (string-match "\\s *\\((\\s *\\([^\t)]*\\)\\s *)\\s *\\|\\)\\(.*\\)"
                  nodename)
@@ -823,7 +952,8 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
   (or Info-current-file-completions
       (let ((compl nil)
            ;; Bind this in case the user sets it to nil.
-           (case-fold-search t))
+           (case-fold-search t)
+           (node-regexp "Node: *\\([^,\n]*\\) *[,\n\t]"))
        (save-excursion
          (save-restriction
            (if (marker-buffer Info-tag-table-marker)
@@ -838,16 +968,21 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
                                compl))))
              (widen)
              (goto-char (point-min))
+             ;; If the buffer begins with a node header, process that first.
+             (if (Info-node-at-bob-matching node-regexp)
+                 (setq compl (list (buffer-substring (match-beginning 1)
+                                                     (match-end 1)))))
+             ;; Now for the rest of the nodes.
              (while (search-forward "\n\^_" nil t)
                (forward-line 1)
                (let ((beg (point)))
                  (forward-line 1)
-                 (if (re-search-backward "Node: *\\([^,\n]*\\) *[,\n\t]"
-                                         beg t)
+                 (if (re-search-backward node-regexp beg t)
                      (setq compl 
                            (cons (list (buffer-substring (match-beginning 1)
                                                          (match-end 1)))
                                  compl))))))))
+       (setq compl (cons '("*") compl))
        (setq Info-current-file-completions compl))))
 \f
 (defun Info-restore-point (hl)
@@ -871,60 +1006,61 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
   (if (equal regexp "")
       (setq regexp Info-last-search)
     (setq Info-last-search regexp))
-  (let ((found ()) current
-       (onode Info-current-node)
-       (ofile Info-current-file)
-       (opoint (point))
-       (ostart (window-start))
-       (osubfile Info-current-subfile))
-    (save-excursion
-      (save-restriction
-       (widen)
-       (if (null Info-current-subfile)
-           (progn (re-search-forward regexp) (setq found (point)))
-         (condition-case err
+  (when regexp
+    (let ((found ()) current
+         (onode Info-current-node)
+         (ofile Info-current-file)
+         (opoint (point))
+         (ostart (window-start))
+         (osubfile Info-current-subfile))
+      (save-excursion
+       (save-restriction
+         (widen)
+         (if (null Info-current-subfile)
              (progn (re-search-forward regexp) (setq found (point)))
-           (search-failed nil)))))
-    (if (not found) ;can only happen in subfile case -- else would have erred
-       (unwind-protect
-           (let ((list ()))
-             (save-excursion
-               (set-buffer (marker-buffer Info-tag-table-marker))
-               (goto-char (point-min))
-               (search-forward "\n\^_\nIndirect:")
-               (save-restriction
-                 (narrow-to-region (point)
-                                   (progn (search-forward "\n\^_")
-                                          (1- (point))))
+           (condition-case err
+               (progn (re-search-forward regexp) (setq found (point)))
+             (search-failed nil)))))
+      (if (not found) ;can only happen in subfile case -- else would have erred
+         (unwind-protect
+             (let ((list ()))
+               (save-excursion
+                 (set-buffer (marker-buffer Info-tag-table-marker))
                  (goto-char (point-min))
-                 (search-forward (concat "\n" osubfile ": "))
-                 (beginning-of-line)
-                 (while (not (eobp))
-                   (re-search-forward "\\(^.*\\): [0-9]+$")
-                   (goto-char (+ (match-end 1) 2))
-                   (setq list (cons (cons (read (current-buffer))
-                                          (buffer-substring
-                                           (match-beginning 1) (match-end 1)))
-                                    list))
-                   (goto-char (1+ (match-end 0))))
-                 (setq list (nreverse list)
-                       current (car (car list))
-                       list (cdr list))))
-             (while list
-               (message "Searching subfile %s..." (cdr (car list)))
-               (Info-read-subfile (car (car list)))
-               (setq list (cdr list))
-;;             (goto-char (point-min))
-               (if (re-search-forward regexp nil t)
-                   (setq found (point) list ())))
-             (if found
-                 (message "")
-               (signal 'search-failed (list regexp))))
-         (if (not found)
-             (progn (Info-read-subfile osubfile)
-                    (goto-char opoint)
-                    (Info-select-node)
-                    (set-window-start (selected-window) ostart)))))
+                 (search-forward "\n\^_\nIndirect:")
+                 (save-restriction
+                   (narrow-to-region (point)
+                                     (progn (search-forward "\n\^_")
+                                            (1- (point))))
+                   (goto-char (point-min))
+                   (search-forward (concat "\n" osubfile ": "))
+                   (beginning-of-line)
+                   (while (not (eobp))
+                     (re-search-forward "\\(^.*\\): [0-9]+$")
+                     (goto-char (+ (match-end 1) 2))
+                     (setq list (cons (cons (read (current-buffer))
+                                            (buffer-substring
+                                             (match-beginning 1) (match-end 1)))
+                                      list))
+                     (goto-char (1+ (match-end 0))))
+                   (setq list (nreverse list)
+                         current (car (car list))
+                         list (cdr list))))
+               (while list
+                 (message "Searching subfile %s..." (cdr (car list)))
+                 (Info-read-subfile (car (car list)))
+                 (setq list (cdr list))
+;;;            (goto-char (point-min))
+                 (if (re-search-forward regexp nil t)
+                     (setq found (point) list ())))
+               (if found
+                   (message "")
+                 (signal 'search-failed (list regexp))))
+           (if (not found)
+               (progn (Info-read-subfile osubfile)
+                      (goto-char opoint)
+                      (Info-select-node)
+                      (set-window-start (selected-window) ostart)))))
     (widen)
     (goto-char found)
     (Info-select-node)
@@ -932,7 +1068,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
     (or (and (string-equal onode Info-current-node)
             (equal ofile Info-current-file))
        (setq Info-history (cons (list ofile onode opoint)
-                                Info-history)))))
+                                Info-history))))))
 \f
 ;; Extract the value of the node-pointer named NAME.
 ;; If there is none, use ERRORNAME in the error message; 
@@ -981,7 +1117,7 @@ In standalone mode, \\<Info-mode-map>\\[Info-exit] exits Emacs itself."
 If SAME-FILE is non-nil, do not move to a different Info file."
   (interactive)
   (let ((node (Info-extract-pointer "up")))
-    (and same-file
+    (and (or same-file (not (stringp Info-current-file)))
         (string-match "^(" node)
         (error "Up node is in another Info file"))
     (Info-goto-node node))
@@ -1165,7 +1301,7 @@ NAME may be an abbreviation of the reference name."
                                nil t))))))
 
 
-(defun Info-menu (menu-item)
+(defun Info-menu (menu-item &optional fork)
   "Go to node for menu item named (or abbreviated) NAME.
 Completion is allowed, and the menu item point is on is the default."
   (interactive
@@ -1184,10 +1320,10 @@ Completion is allowed, and the menu item point is on is the default."
            (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)))))))
+             (if (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)
@@ -1205,10 +1341,10 @@ Completion is allowed, and the menu item point is on is the default."
                 (setq item default)
                 ;; ask again
                 (setq item nil))))
-       (list item))))
+       (list item current-prefix-arg))))
   ;; there is a problem here in that if several menu items have the same
   ;; name you can only go to the node of the first with this command.
-  (Info-goto-node (Info-extract-menu-item menu-item)))
+  (Info-goto-node (Info-extract-menu-item menu-item) (if fork menu-item)))
   
 (defun Info-extract-menu-item (menu-item)
   (setq menu-item (regexp-quote menu-item))
@@ -1329,12 +1465,14 @@ N is the digit argument used to invoke this command."
 
 (defun Info-next-menu-item ()
   (interactive)
-  (save-excursion
-    (forward-line -1)
-    (search-forward "\n* menu:" nil t)
-    (or (search-forward "\n* " nil t)
-       (error "No more items in menu"))
-    (Info-goto-node (Info-extract-menu-node-name))))
+  (let ((node
+        (save-excursion
+          (forward-line -1)
+          (search-forward "\n* menu:" nil t)
+          (and (search-forward "\n* " nil t)
+               (Info-extract-menu-node-name)))))
+    (if node (Info-goto-node node)
+      (error "No more items in menu"))))
 
 (defun Info-last-menu-item ()
   (interactive)
@@ -1749,6 +1887,7 @@ If no reference to follow, moves to the next node, or up if none."
   (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 "\M-n" 'clone-buffer)
   (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)
@@ -1924,9 +2063,20 @@ Advanced commands:
   ;; This is for the sake of the invisible text we use handling titles.
   (make-local-variable 'line-move-ignore-invisible)
   (setq line-move-ignore-invisible t)
+  (add-hook (make-local-hook 'clone-buffer-hook) 'Info-clone-buffer-hook nil t)
   (Info-set-mode-line)
   (run-hooks 'Info-mode-hook))
 
+(defun Info-clone-buffer-hook ()
+  (when (bufferp Info-tag-table-buffer)
+    (setq Info-tag-table-buffer
+         (with-current-buffer Info-tag-table-buffer (clone-buffer)))
+    (let ((m Info-tag-table-marker))
+      (when (and (markerp m) (marker-position m))
+       (setq Info-tag-table-marker
+             (with-current-buffer Info-tag-table-buffer
+               (copy-marker (marker-position m))))))))
+
 (defvar Info-edit-map nil
   "Local keymap used within `e' command of Info.")
 (if Info-edit-map
@@ -1979,7 +2129,8 @@ Allowed only if variable `Info-enable-edit' is non-nil."
        (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")
+  '("ediff" "forms" "gnus" "info" ("mh" . "mh-e") "sc" "message"
+    ("dired" . "dired-x") ("c" . "ccmode") "viper")
   "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.