(shell-mode-map): Inherit comint-mode-map, but copy the completion menu.
[bpt/emacs.git] / lisp / man.el
index b18612e..7143563 100644 (file)
@@ -1,6 +1,6 @@
 ;;; man.el --- browse UNIX manual pages
 
-;; Copyright (C) 1993 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
 
 ;; Author:             Barry A. Warsaw <bwarsaw@cen.com>
 ;; Last-Modified:      31-Jul-1991
@@ -76,6 +76,9 @@
 ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
 ;; user variables
 
+(defvar manual-program "man"
+  "The name of the program that produces man pages.")
+
 (defvar Man-notify 'friendly
   "*Selects the behavior when manpage is ready.
 This variable may have one of the following values:
@@ -95,10 +98,10 @@ Any other value of `Man-notify' is equivalent to `meek'.")
 
 (defvar Man-reuse-okay-p t
   "*Reuse a manpage buffer if possible.
-When t, and a manpage buffer already exists with the same invocation,
-man just indicates the manpage is ready according to the value of
-`Man-notify'.  When nil, it always fires off a background process, putting
-the results in a uniquely named buffer.")
+If non-nil, and a manpage buffer already exists with the same
+invocation, man just indicates the manpage is ready according to the
+value of `Man-notify'.  When nil, it always fires off a background
+process, putting the results in a uniquely named buffer.")
 
 (defvar Man-downcase-section-letters-p t
   "*Letters in sections are converted to lower case.
@@ -145,11 +148,12 @@ the associated section number.")
       "-e '/Reformatting page.  Wait/d'"
       "-e '/Reformatting entry.  Wait/d'"
       "-e '/^ *\\([A-Za-z][A-Za-z.]*([0-9A-Za-z][-0-9A-Za-z+]*)\\).*\\1$/d'"
-      "-e '/^[ \\t]*Hewlett-Packard Company[ \\t]*- [0-9]* -.*$/d'"
-      "-e '/^[ \\t]*Hewlett-Packard[ \\t]*- [0-9]* -.*$/d'"
-      "-e '/^ *Page [0-9]*.*(printed [0-9\\/]*)$/d'"
+      "-e '/^[ \t]*Hewlett-Packard Company[ \t]*- [0-9]* -.*$/d'"
+      "-e '/^[ \t]*Hewlett-Packard[ \t]*- [0-9]* -.*$/d'"
+      "-e '/^  *- [0-9]* - *Formatted:.*[0-9]$/d'"
+      "-e '/^[ \t]*Page [0-9]*.*(printed [0-9\\/]*)$/d'"
       "-e '/^Printed [0-9].*[0-9]$/d'"
-      "-e '/^[ \\t]*X Version 1[01].*Release [0-9]/d'"
+      "-e '/^[ \t]*X Version 1[01].*Release [0-9]/d'"
       "-e '/^[A-za-z].*Last change:/d'"
       "-e '/^Sun Release [0-9].*[0-9]$/d'"
       "-e '/^\\n$/D'"
@@ -179,7 +183,7 @@ the manpage buffer.")
        mode-line-buffer-identification "   "
        global-mode-string
        " " Man-page-mode-string
-       "    %[(" mode-name minor-mode-alist mode-line-process ")%]----"
+       "    %[(" mode-name mode-line-process minor-mode-alist ")%]----"
        (-3 . "%p") "-%-")
   "*Mode line format for manual mode buffer.")
 
@@ -195,14 +199,16 @@ the manpage buffer.")
 (defvar Man-section-regexp "[0-9][a-zA-Z+]*\\|[LNln]"
   "*Regular expression describing a manpage section within parentheses.")
 
-(defvar Man-heading-regexp "^ ?[A-Z]"
+;; Unless some system actually adds leading whitespace other than one space,
+;; it is more reliable not to accept any other leading whitespace.
+(defvar Man-heading-regexp "^[ \t]*\\([A-Z][A-Z \t]+\\)$"
   "*Regular expression describing a manpage heading entry.")
 
 (defvar Man-see-also-regexp "SEE ALSO"
   "*Regular expression for SEE ALSO heading (or your equivalent).
 This regexp should not start with a `^' character.")
 
-(defvar Man-first-heading-regexp "^ ?NAME$\\|^ ?No manual entry for .*$"
+(defvar Man-first-heading-regexp "^[ \t]*NAME$\\|^[ \t]*No manual entry fo.*$"
   "*Regular expression describing first heading on a manpage.
 This regular expression should start with a `^' character.")
 
@@ -231,6 +237,7 @@ This regular expression should start with a `^' character.")
 (make-variable-buffer-local 'Man-page-list)
 (make-variable-buffer-local 'Man-current-page)
 (make-variable-buffer-local 'Man-page-mode-string)
+(make-variable-buffer-local 'Man-original-frame)
 
 (setq-default Man-sections-alist nil)
 (setq-default Man-refpages-alist nil)
@@ -274,7 +281,7 @@ This regular expression should start with a `^' character.")
 
 (defun Man-build-man-command ()
   "Builds the entire background manpage and cleaning command."
-  (let ((command (concat "man " Man-switches " %s 2>/dev/null"))
+  (let ((command (concat manual-program " " Man-switches " %s 2>/dev/null"))
        (flist Man-filter-list))
     (while flist
       (let ((pcom (car (car flist)))
@@ -282,7 +289,7 @@ This regular expression should start with a `^' character.")
        (setq flist (cdr flist))
        (if (or (not (stringp pcom))
                (not (listp pargs)))
-           (error "malformed Man-filter-list."))
+           (error "Malformed Man-filter-list"))
        (setq command (concat command " | " pcom
                              (mapconcat '(lambda (phrase) phrase)
                                         pargs " ")))))
@@ -420,17 +427,19 @@ This command is the top-level command in the man package.  It runs a Un*x
 command to retrieve and clean a manpage in the background and places the
 results in a Man mode (manpage browsing) buffer.  See variable
 `Man-notify' for what happens when the buffer is ready.
-Universal argument ARG, is passed to `Man-getpage-in-background'."
+Normally, if a buffer already exists for this man page, it will display
+immediately; either a prefix argument or a nil value to `Man-reuse-okay-p'
+overrides this and forces the man page to be regenerated."
   (interactive "P")
   (let* ((default-entry (Man-default-man-entry))
         (man-args
          (read-string (format "Manual-entry: %s"
-                       (if (string= default-entry "") ""
-                         (format "(default: %s) "
-                                 default-entry))))))
+                              (if (string= default-entry "") ""
+                                (format "(default: %s) "
+                                        default-entry))))))
     (and (string= man-args "")
         (if (string= default-entry "")
-            (error "No man args given.")
+            (error "No man args given")
           (setq man-args default-entry)))
 
     ;; Recognize the subject(section) syntax.
@@ -455,40 +464,60 @@ start a background process even if a buffer already exists and
             buffer)
        (Man-notify-when-ready buffer)
       (require 'env)
-      (message "Invoking man %s in background..." man-args)
+      (message "Invoking %s %s in background" manual-program man-args)
       (setq buffer (generate-new-buffer bufname))
+      (save-excursion
+       (set-buffer buffer)
+       (setq Man-original-frame (selected-frame)))
       (let ((process-environment (copy-sequence process-environment)))
        ;; Prevent any attempt to use display terminal fanciness.
        (setenv "TERM" "dumb")
        (set-process-sentinel
-        (start-process "man" buffer "sh" "-c"
+        (start-process manual-program buffer "sh" "-c"
                        (format (Man-build-man-command) man-args))
         'Man-bgproc-sentinel))
-    )))
+      )))
 
 (defun Man-notify-when-ready (man-buffer)
   "Notify the user when MAN-BUFFER is ready.
 See the variable `Man-notify' for the different notification behaviors."
-  (cond
-   ((eq Man-notify 'newframe)
-    (set-buffer man-buffer)
-    (new-frame Man-frame-parameters))
-   ((eq Man-notify 'bully)
-    (pop-to-buffer man-buffer)
-    (delete-other-windows))
-   ((eq Man-notify 'aggressive)
-    (pop-to-buffer man-buffer))
-   ((eq Man-notify 'friendly)
-    (display-buffer man-buffer 'not-this-window))
-   ((eq Man-notify 'polite)
-    (beep)
-    (message "Manual buffer %s is ready." (buffer-name man-buffer)))
-   ((eq Man-notify 'quiet)
-    (message "Manual buffer %s is ready." (buffer-name man-buffer)))
-   ((or (eq Man-notify 'meek)
-       t)
-    (message ""))
-   ))
+  (let ((saved-frame (save-excursion
+                      (set-buffer man-buffer)
+                      Man-original-frame)))
+    (cond
+     ((eq Man-notify 'newframe)
+      ;; Since we run asynchronously, perhaps while Emacs is waiting for input,
+      ;; we must not leave a different buffer current.
+      ;; We can't rely on the editor command loop to reselect
+      ;; the selected window's buffer.
+      (save-excursion
+       (set-buffer man-buffer)
+       (make-frame Man-frame-parameters)))
+     ((eq Man-notify 'bully)
+      (and window-system
+          (frame-live-p saved-frame)
+          (select-frame saved-frame))
+      (pop-to-buffer man-buffer)
+      (delete-other-windows))
+     ((eq Man-notify 'aggressive)
+      (and window-system
+          (frame-live-p saved-frame)
+          (select-frame saved-frame))
+      (pop-to-buffer man-buffer))
+     ((eq Man-notify 'friendly)
+      (and window-system
+          (frame-live-p saved-frame)
+          (select-frame saved-frame))
+      (display-buffer man-buffer 'not-this-window))
+     ((eq Man-notify 'polite)
+      (beep)
+      (message "Manual buffer %s is ready." (buffer-name man-buffer)))
+     ((eq Man-notify 'quiet)
+      (message "Manual buffer %s is ready." (buffer-name man-buffer)))
+     ((or (eq Man-notify 'meek)
+         t)
+      (message ""))
+     )))
 
 (defun Man-set-fonts ()
   (goto-char (point-min))
@@ -508,39 +537,40 @@ See the variable `Man-notify' for the different notification behaviors."
        (err-mess nil))
     (if (null (buffer-name Man-buffer)) ;; deleted buffer
        (set-process-buffer process nil)
-      (save-excursion
-       (set-buffer Man-buffer)
-       (goto-char (point-min))
-       (cond ((or (looking-at "No \\(manual \\)*entry for")
-                  (looking-at "[^\n]*: nothing appropriate$"))
-              (setq err-mess (buffer-substring (point) (Man-linepos 'eol))
-                    delete-buff t)
-              )
-             ((not (and (eq (process-status process) 'exit)
-                        (= (process-exit-status process) 0)))
-              (setq err-mess
-                    (concat (buffer-name Man-buffer)
-                            ": process "
-                            (let ((eos (1- (length msg))))
-                              (if (= (aref msg eos) ?\n)
-                                  (substring msg 0 eos) msg))))
-              (goto-char (point-max))
-              (insert (format "\nprocess %s" msg))
-              )))
-      (if delete-buff
-         (kill-buffer Man-buffer)
-       (save-window-excursion
-         (save-excursion
-           (set-buffer Man-buffer)
-           (Man-set-fonts)
-           (run-hooks 'Man-cooked-hook)
-           (Man-mode)
-           (set-buffer-modified-p nil)))
-       (Man-notify-when-ready Man-buffer))
-
-      (if err-mess
-         (error err-mess))
-      )))
+      (save-match-data
+       (save-excursion
+         (set-buffer Man-buffer)
+         (goto-char (point-min))
+         (let ((case-fold-search nil))
+           (cond ((or (looking-at "No \\(manual \\)*entry for")
+                      (looking-at "[^\n]*: nothing appropriate$"))
+                  (setq err-mess (buffer-substring (point) (Man-linepos 'eol))
+                        delete-buff t))
+                 ((not (and (eq (process-status process) 'exit)
+                            (= (process-exit-status process) 0)))
+                  (setq err-mess
+                        (concat (buffer-name Man-buffer)
+                                ": process "
+                                (let ((eos (1- (length msg))))
+                                  (if (= (aref msg eos) ?\n)
+                                      (substring msg 0 eos) msg))))
+                  (goto-char (point-max))
+                  (insert (format "\nprocess %s" msg))
+                  ))))
+       (if delete-buff
+           (kill-buffer Man-buffer)
+         (save-window-excursion
+           (save-excursion
+             (set-buffer Man-buffer)
+             (let ((case-fold-search nil))
+               (Man-set-fonts)
+               (run-hooks 'Man-cooked-hook)
+               (Man-mode))
+             (set-buffer-modified-p nil)))
+         (Man-notify-when-ready Man-buffer))
+
+       (if err-mess
+           (error err-mess))))))
 
 \f
 ;; ======================================================================
@@ -604,11 +634,11 @@ The following key bindings are currently in effect in the buffer:
   "Build the association list of manpage sections."
   (setq Man-sections-alist nil)
   (goto-char (point-min))
-  (while (re-search-forward Man-heading-regexp (point-max) t)
-    (aput 'Man-sections-alist
-         (buffer-substring (Man-linepos 'bol) (Man-linepos)))
-    (forward-line 1)
-    ))
+  (let ((case-fold-search nil))
+    (while (re-search-forward Man-heading-regexp (point-max) t)
+      (aput 'Man-sections-alist
+           (buffer-substring (match-beginning 1) (match-end 1)))
+      (forward-line 1))))
 
 (defun Man-build-references-alist ()
   "Build the association list of references (in the SEE ALSO section)."
@@ -621,23 +651,24 @@ The following key bindings are currently in effect in the buffer:
                     (point)))
              hyphenated
              (runningpoint -1))
-         (narrow-to-region start end)
-         (goto-char (point-min))
-         (back-to-indentation)
-         (while (and (not (eobp)) (/= (point) runningpoint))
-           (setq runningpoint (point))
-           (let* ((eow (re-search-forward Man-reference-regexp end t))
-                  (word (buffer-substring (match-beginning 0) (match-end 0)))
-                  (len (1- (length word))))
-             (if (not eow) nil
-               (if hyphenated
-                   (setq word (concat hyphenated word)
-                         hyphenated nil))
-               (if (= (aref word len) ?-)
-                   (setq hyphenated (substring word 0 len))
-                 (aput 'Man-refpages-alist word))))
-           (skip-chars-forward " \t\n,"))
-         ))))
+         (save-restriction
+           (narrow-to-region start end)
+           (goto-char (point-min))
+           (back-to-indentation)
+           (while (and (not (eobp)) (/= (point) runningpoint))
+             (setq runningpoint (point))
+             (let* ((eow (re-search-forward Man-reference-regexp end t))
+                    (word (buffer-substring
+                           (match-beginning 0) (match-end 0)))
+                    (len (1- (length word))))
+               (if (not eow) nil
+                 (if hyphenated
+                     (setq word (concat hyphenated word)
+                           hyphenated nil))
+                 (if (= (aref word len) ?-)
+                     (setq hyphenated (substring word 0 len))
+                   (aput 'Man-refpages-alist word))))
+             (skip-chars-forward " \t\n,")))))))
 
 (defun Man-build-page-list ()
   "Build the list of separate manpages in the buffer."
@@ -656,9 +687,9 @@ The following key bindings are currently in effect in the buffer:
          (goto-char (point-max))
          (setq page-end (point)))
        (setq Man-page-list (append Man-page-list
-                                  (list (cons page-start page-end)))
+                                   (list (cons page-start page-end)))
              page-start page-end)
-       ))))  
+       ))))
 
 \f
 ;; ======================================================================
@@ -667,27 +698,30 @@ The following key bindings are currently in effect in the buffer:
 (defun Man-next-section (n)
   "Move point to Nth next section (default 1)."
   (interactive "p")
-  (if (looking-at Man-heading-regexp)
-      (forward-line 1))
-  (if (re-search-forward Man-heading-regexp (point-max) t n)
-      (beginning-of-line)
-    (goto-char (point-max))))
+  (let ((case-fold-search nil))
+    (if (looking-at Man-heading-regexp)
+       (forward-line 1))
+    (if (re-search-forward Man-heading-regexp (point-max) t n)
+       (beginning-of-line)
+      (goto-char (point-max)))))
 
 (defun Man-previous-section (n)
   "Move point to Nth previous section (default 1)."
   (interactive "p")
-  (if (looking-at Man-heading-regexp)
-      (forward-line -1))
-  (if (re-search-backward Man-heading-regexp (point-min) t n)
-      (beginning-of-line)
-    (goto-char (point-min))))
+  (let ((case-fold-search nil))
+    (if (looking-at Man-heading-regexp)
+       (forward-line -1))
+    (if (re-search-backward Man-heading-regexp (point-min) t n)
+       (beginning-of-line)
+      (goto-char (point-min)))))
 
 (defun Man-find-section (section)
   "Move point to SECTION if it exists, otherwise don't move point.
 Returns t if section is found, nil otherwise."
-  (let ((curpos (point)))
+  (let ((curpos (point))
+       (case-fold-search nil))
     (goto-char (point-min))
-    (if (re-search-forward (concat "^\\s-?" section) (point-max) t)
+    (if (re-search-forward (concat "^[ \t]*" section) (point-max) t)
        (progn (beginning-of-line) t)
       (goto-char curpos)
       nil)
@@ -714,7 +748,7 @@ Actually the section moved to is described by `Man-see-also-regexp'."
   (interactive)
   (if (not (Man-find-section Man-see-also-regexp))
       (error (concat "No " Man-see-also-regexp
-                    " section found in current manpage."))))
+                    " section found in current manpage"))))
 
 (defun Man-follow-manual-reference (arg reference)
   "Get one of the manpages referred to in the \"SEE ALSO\" section.
@@ -732,7 +766,8 @@ Prefix argument ARG is passed to `Man-getpage-in-background'."
                                     (let ((word (current-word)))
                                       ;; strip a trailing '-':
                                       (if (string-match "-$" word)
-                                          (substring word 0 (match-beginning 0))
+                                          (substring word 0
+                                                     (match-beginning 0))
                                         word)))
                                   Man-refpages-alist))
                             (aheadsym Man-refpages-alist)))
@@ -760,9 +795,10 @@ Prefix argument ARG is passed to `Man-getpage-in-background'."
 (defun Man-goto-page (page)
   "Go to the manual page on page PAGE."
   (interactive
-   (if (not Man-page-list)
+   (if (= (length Man-page-list) 1)
        (error "You're looking at the only manpage in the buffer.")
-     (format "nGo to manpage [1-%d]: " (length Man-page-list))))
+     (list (read-minibuffer (format "Go to manpage [1-%d]: "
+                                   (length Man-page-list))))))
   (if (or (< page 1)
          (> page (length Man-page-list)))
       (error "No manpage %d found" page))
@@ -789,23 +825,23 @@ Prefix argument ARG is passed to `Man-getpage-in-background'."
   "Find the next manpage entry in the buffer."
   (interactive)
   (if (= (length Man-page-list) 1)
-      (error "This is the only manpage in the buffer."))
+      (error "This is the only manpage in the buffer"))
   (if (< Man-current-page (length Man-page-list))
       (Man-goto-page (1+ Man-current-page))
     (if Man-circular-pages-p
        (Man-goto-page 1)
-      (error "You're looking at the last manpage in the buffer."))))
+      (error "You're looking at the last manpage in the buffer"))))
 
 (defun Man-previous-manpage ()
   "Find the previous manpage entry in the buffer."
   (interactive)
   (if (= (length Man-page-list) 1)
-      (error "This is the only manpage in the buffer."))
+      (error "This is the only manpage in the buffer"))
   (if (> Man-current-page 1)
       (Man-goto-page (1- Man-current-page))
     (if Man-circular-pages-p
        (Man-goto-page (length Man-page-list))
-      (error "You're looking at the first manpage in the buffer."))))
+      (error "You're looking at the first manpage in the buffer"))))
 \f
 (provide 'man)