]> code.delx.au - gnu-emacs/blobdiff - lisp/man.el
Sync with Tramp 2.1.19.
[gnu-emacs] / lisp / man.el
index 03003a46d1bf40d4fe0741af9d7829619a28c525..658c1ebbcef6955c8a906e69ee9397154bec0f45 100644 (file)
@@ -1,7 +1,7 @@
 ;;; man.el --- browse UNIX manual pages -*- coding: iso-8859-1 -*-
 
-;; Copyright (C) 1993, 1994, 1996, 1997, 2001, 2002, 2003,
-;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994, 1996, 1997, 2001, 2002, 2003, 2004, 2005,
+;;   2006, 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
 
 ;; Author: Barry A. Warsaw <bwarsaw@cen.com>
 ;; Maintainer: FSF
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; 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, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -37,7 +35,7 @@
 
 ;; ========== Credits and History ==========
 ;; In mid 1991, several people posted some interesting improvements to
-;; man.el from the standard emacs 18.57 distribution.  I liked many of
+;; man.el from the standard Emacs 18.57 distribution.  I liked many of
 ;; these, but wanted everything in one single package, so I decided
 ;; to incorporate them into a single manual browsing mode.  While
 ;; much of the code here has been rewritten, and some features added,
@@ -76,7 +74,7 @@
 
 ;; ============= TODO ===========
 ;; - Add a command for printing.
-;; - The awk script deletes multiple blank lines.  This behaviour does
+;; - The awk script deletes multiple blank lines.  This behavior does
 ;;   not allow to understand if there was indeed a blank line at the
 ;;   end or beginning of a page (after the header, or before the
 ;;   footer).  A different algorithm should be used.  It is easy to
 ;;   only.  Is it worth doing?
 ;; - Allow a user option to mean that all the manpages should go in
 ;;   the same buffer, where they can be browsed with M-n and M-p.
-;; - Allow completion on the manpage name when calling man.  This
-;;   requires a reliable list of places where manpages can be found.  The
-;;   drawback would be that if the list is not complete, the user might
-;;   be led to believe that the manpages in the missing directories do
-;;   not exist.
 
 \f
 ;;; Code:
 (defgroup man nil
   "Browse UNIX manual pages."
   :prefix "Man-"
+  :group 'external
   :group 'help)
 
-
 (defvar Man-notify)
-(defvar Man-current-page)
-(defvar Man-page-list)
 (defcustom Man-filter-list nil
-  "*Manpage cleaning filter command phrases.
+  "Manpage cleaning filter command phrases.
 This variable contains a list of the following form:
 
 '((command-string phrase-string*)*)
@@ -127,13 +118,8 @@ the manpage buffer."
                               (string :tag "Phrase String"))))
   :group 'man)
 
-(defvar Man-original-frame)
-(defvar Man-arguments)
-(defvar Man-sections-alist)
-(defvar Man-refpages-alist)
 (defvar Man-uses-untabify-flag t
   "Non-nil means use `untabify' instead of `Man-untabify-command'.")
-(defvar Man-page-mode-string)
 (defvar Man-sed-script nil
   "Script for sed to nuke backspaces and ANSI codes from manpages.")
 
@@ -141,28 +127,28 @@ the manpage buffer."
 ;; user variables
 
 (defcustom Man-fontify-manpage-flag t
-  "*Non-nil means make up the manpage with fonts."
+  "Non-nil means make up the manpage with fonts."
   :type 'boolean
   :group 'man)
 
 (defcustom Man-overstrike-face 'bold
-  "*Face to use when fontifying overstrike."
+  "Face to use when fontifying overstrike."
   :type 'face
   :group 'man)
 
 (defcustom Man-underline-face 'underline
-  "*Face to use when fontifying underlining."
+  "Face to use when fontifying underlining."
   :type 'face
   :group 'man)
 
 (defcustom Man-reverse-face 'highlight
-  "*Face to use when fontifying reverse video."
+  "Face to use when fontifying reverse video."
   :type 'face
   :group 'man)
 
 ;; Use the value of the obsolete user option Man-notify, if set.
 (defcustom Man-notify-method (if (boundp 'Man-notify) Man-notify 'friendly)
-  "*Selects the behavior when manpage is ready.
+  "Selects the behavior when manpage is ready.
 This variable may have one of the following values, where (sf) means
 that the frames are switched, so the manpage is displayed in the frame
 where the man command was called from:
@@ -183,7 +169,7 @@ Any other value of `Man-notify-method' is equivalent to `meek'."
   :group 'man)
 
 (defcustom Man-width nil
-  "*Number of columns for which manual pages should be formatted.
+  "Number of columns for which manual pages should be formatted.
 If nil, the width of the window selected at the moment of man
 invocation is used.  If non-nil, the width of the frame selected
 at the moment of man invocation is used.  The value also can be a
@@ -194,12 +180,12 @@ positive integer."
   :group 'man)
 
 (defcustom Man-frame-parameters nil
-  "*Frame parameter list for creating a new frame for a manual page."
+  "Frame parameter list for creating a new frame for a manual page."
   :type 'sexp
   :group 'man)
 
 (defcustom Man-downcase-section-letters-flag t
-  "*Non-nil means letters in sections are converted to lower case.
+  "Non-nil means letters in sections are converted to lower case.
 Some Un*x man commands can't handle uppercase letters in sections, for
 example \"man 2V chmod\", but they are often displayed in the manpage
 with the upper case letter.  When this variable is t, the section
@@ -209,7 +195,7 @@ being sent to the man background process."
   :group 'man)
 
 (defcustom Man-circular-pages-flag t
-  "*Non-nil means the manpage list is treated as circular for traversal."
+  "Non-nil means the manpage list is treated as circular for traversal."
   :type 'boolean
   :group 'man)
 
@@ -220,7 +206,7 @@ being sent to the man background process."
    ;; '("3X" . "3")                        ; Xlib man pages
    '("3X11" . "3")
    '("1-UCB" . ""))
-  "*Association list of bogus sections to real section numbers.
+  "Association list of bogus sections to real section numbers.
 Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in
 their references which Un*x `man' does not recognize.  This
 association list is used to translate those sections, when found, to
@@ -250,9 +236,6 @@ the associated section number."
 (defvar Man-awk-command "awk"
   "Command used for processing awk scripts.")
 
-(defvar Man-mode-map nil
-  "Keymap for Man mode.")
-
 (defvar Man-mode-hook nil
   "Hook run when Man mode is enabled.")
 
@@ -274,14 +257,17 @@ the associated section number."
            "(\\(" Man-section-regexp "\\))\\).*\\1"))
   "Regular expression describing the heading of a page.")
 
-(defvar Man-heading-regexp "^\\([A-Z][A-Z /-]+\\)$"
+(defvar Man-heading-regexp "^\\([A-Z][A-Z0-9 /-]+\\)$"
   "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 "^[ \t]*NAME$\\|^[ \t]*No manual entry fo.*$"
+;; This used to have leading space [ \t]*, but was removed because it
+;; causes false page splits on an occasional NAME with leading space
+;; inside a manpage.  And `Man-heading-regexp' doesn't have [ \t]* anyway.
+(defvar Man-first-heading-regexp "^NAME$\\|^[ \t]*No manual entry fo.*$"
   "Regular expression describing first heading on a manpage.
 This regular expression should start with a `^' character.")
 
@@ -297,7 +283,8 @@ This regular expression should start with a `^' character.")
   "Regular expression for SYNOPSIS heading (or your equivalent).
 This regexp should not start with a `^' character.")
 
-(defvar Man-files-regexp "FILES"
+(defvar Man-files-regexp "FILES\\>"
+  ;; Add \> so as not to match mount(8)'s FILESYSTEM INDEPENDENT MOUNT OPTIONS.
   "Regular expression for FILES heading (or your equivalent).
 This regexp should not start with a `^' character.")
 
@@ -349,20 +336,22 @@ Otherwise, the value is whatever the function
 ;; end user variables
 \f
 ;; other variables and keymap initializations
+(defvar Man-original-frame)
+(make-variable-buffer-local 'Man-original-frame)
+(defvar Man-arguments)
+(make-variable-buffer-local 'Man-arguments)
+(put 'Man-arguments 'permanent-local t)
+
+(defvar Man-sections-alist nil)
 (make-variable-buffer-local 'Man-sections-alist)
+(defvar Man-refpages-alist nil)
 (make-variable-buffer-local 'Man-refpages-alist)
+(defvar Man-page-list nil)
 (make-variable-buffer-local 'Man-page-list)
+(defvar Man-current-page 0)
 (make-variable-buffer-local 'Man-current-page)
+(defvar Man-page-mode-string "1 of 1")
 (make-variable-buffer-local 'Man-page-mode-string)
-(make-variable-buffer-local 'Man-original-frame)
-(make-variable-buffer-local 'Man-arguments)
-(put 'Man-arguments 'permanent-local t)
-
-(setq-default Man-sections-alist nil)
-(setq-default Man-refpages-alist nil)
-(setq-default Man-page-list nil)
-(setq-default Man-current-page 0)
-(setq-default Man-page-mode-string "1 of 1")
 
 (defconst Man-sysv-sed-script "\
 /\b/ { s/_\b//g
@@ -398,30 +387,32 @@ Otherwise, the value is whatever the function
     table)
   "Syntax table used in Man mode buffers.")
 
-(unless Man-mode-map
-  (setq Man-mode-map (make-sparse-keymap))
-  (suppress-keymap Man-mode-map)
-  (set-keymap-parent Man-mode-map button-buffer-map)
-
-  (define-key Man-mode-map " "    'scroll-up)
-  (define-key Man-mode-map "\177" 'scroll-down)
-  (define-key Man-mode-map "n"    'Man-next-section)
-  (define-key Man-mode-map "p"    'Man-previous-section)
-  (define-key Man-mode-map "\en"  'Man-next-manpage)
-  (define-key Man-mode-map "\ep"  'Man-previous-manpage)
-  (define-key Man-mode-map ">"    'end-of-buffer)
-  (define-key Man-mode-map "<"    'beginning-of-buffer)
-  (define-key Man-mode-map "."    'beginning-of-buffer)
-  (define-key Man-mode-map "r"    'Man-follow-manual-reference)
-  (define-key Man-mode-map "g"    'Man-goto-section)
-  (define-key Man-mode-map "s"    'Man-goto-see-also-section)
-  (define-key Man-mode-map "k"    'Man-kill)
-  (define-key Man-mode-map "q"    'Man-quit)
-  (define-key Man-mode-map "m"    'man)
-  ;; Not all the man references get buttons currently. The text in the
-  ;; manual page can contain references to other man pages
-  (define-key Man-mode-map "\r"   'man-follow)
-  (define-key Man-mode-map "?"    'describe-mode))
+(defvar Man-mode-map
+  (let ((map (make-sparse-keymap)))
+    (suppress-keymap map)
+    (set-keymap-parent map button-buffer-map)
+
+    (define-key map " "    'scroll-up)
+    (define-key map "\177" 'scroll-down)
+    (define-key map "n"    'Man-next-section)
+    (define-key map "p"    'Man-previous-section)
+    (define-key map "\en"  'Man-next-manpage)
+    (define-key map "\ep"  'Man-previous-manpage)
+    (define-key map ">"    'end-of-buffer)
+    (define-key map "<"    'beginning-of-buffer)
+    (define-key map "."    'beginning-of-buffer)
+    (define-key map "r"    'Man-follow-manual-reference)
+    (define-key map "g"    'Man-goto-section)
+    (define-key map "s"    'Man-goto-see-also-section)
+    (define-key map "k"    'Man-kill)
+    (define-key map "q"    'Man-quit)
+    (define-key map "m"    'man)
+    ;; Not all the man references get buttons currently.  The text in the
+    ;; manual page can contain references to other man pages
+    (define-key map "\r"   'man-follow)
+    (define-key map "?"    'describe-mode)
+    map)
+  "Keymap for Man mode.")
 
 ;; buttons
 (define-button-type 'Man-abstract-xref-man-page
@@ -430,9 +421,9 @@ Otherwise, the value is whatever the function
   'func nil
   'action #'Man-xref-button-action)
 
-(defun Man-xref-button-action (button) 
+(defun Man-xref-button-action (button)
   (let ((target (button-get button 'Man-target-string)))
-    (funcall 
+    (funcall
      (button-get button 'func)
      (cond ((null target)
            (button-label button))
@@ -440,7 +431,7 @@ Otherwise, the value is whatever the function
            (funcall target (button-start button)))
           (t target)))))
 
-(define-button-type 'Man-xref-man-page 
+(define-button-type 'Man-xref-man-page
   :supertype 'Man-abstract-xref-man-page
   'func 'man-follow)
 
@@ -491,36 +482,51 @@ This is necessary if one wants to dump man.el with Emacs."
        (apply 'list
         (cons
          Man-sed-command
-         (list
-          (if Man-sed-script
-              (concat "-e '" Man-sed-script "'")
-            "")
-          "-e '/^[\001-\032][\001-\032]*$/d'"
-          "-e '/\e[789]/s///g'"
-          "-e '/Reformatting page.  Wait/d'"
-          "-e '/Reformatting entry.  Wait/d'"
-          "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'"
-          "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'"
-          "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'"
-          "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'"
-          "-e '/^Printed[ \t][0-9].*[0-9]$/d'"
-          "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'"
-          "-e '/^[A-Za-z].*Last[ \t]change:/d'"
-          "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'"
-          "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'"
-          "-e '/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d'"
-          ))
-        (cons
-         Man-awk-command
-         (list
-          "'\n"
-          "BEGIN { blankline=0; anonblank=0; }\n"
-          "/^$/ { if (anonblank==0) next; }\n"
-          "{ anonblank=1; }\n"
-          "/^$/ { blankline++; next; }\n"
-          "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n"
-          "'"
-          ))
+         (if (eq system-type 'windows-nt)
+             ;; Windows needs ".." quoting, not '..'.
+             (list
+              "-e \"/Reformatting page.  Wait/d\""
+              "-e \"/Reformatting entry.  Wait/d\""
+              "-e \"/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d\""
+              "-e \"/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d\""
+              "-e \"/^Printed[ \t][0-9].*[0-9]$/d\""
+              "-e \"/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d\""
+              "-e \"/^[A-Za-z].*Last[ \t]change:/d\""
+              "-e \"/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d\""
+              "-e \"/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d\"")
+           (list
+            (if Man-sed-script
+                (concat "-e '" Man-sed-script "'")
+              "")
+            "-e '/^[\001-\032][\001-\032]*$/d'"
+            "-e '/\e[789]/s///g'"
+            "-e '/Reformatting page.  Wait/d'"
+            "-e '/Reformatting entry.  Wait/d'"
+            "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'"
+            "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'"
+            "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'"
+            "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'"
+            "-e '/^Printed[ \t][0-9].*[0-9]$/d'"
+            "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'"
+            "-e '/^[A-Za-z].*Last[ \t]change:/d'"
+            "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'"
+            "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'"
+            "-e '/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d'"
+            )))
+        ;; Windows doesn't support multi-line commands, so don't
+        ;; invoke Awk there.
+        (unless (eq system-type 'windows-nt)
+          (cons
+           Man-awk-command
+           (list
+            "'\n"
+            "BEGIN { blankline=0; anonblank=0; }\n"
+            "/^$/ { if (anonblank==0) next; }\n"
+            "{ anonblank=1; }\n"
+            "/^$/ { blankline++; next; }\n"
+            "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n"
+            "'"
+            )))
         (if (not Man-uses-untabify-flag)
             ;; The outer list will be stripped off by apply.
             (list (cons
@@ -645,31 +651,91 @@ a new value."
 
 \f
 ;; ======================================================================
-;; default man entry: get word under point
+;; default man entry: get word near point
 
-(defsubst Man-default-man-entry (&optional pos)
-  "Make a guess at a default manual entry based on the text at POS.
-If POS is nil, the current point is used."
-  (let (word)
+(defun Man-default-man-entry (&optional pos)
+  "Guess default manual entry based on the text near position POS.
+POS defaults to `point'."
+  (let (word start column distance)
     (save-excursion
-      (if pos (goto-char pos))
-      ;; Default man entry title is any word the cursor is on, or if
-      ;; cursor not on a word, then nearest preceding word.
-      (skip-chars-backward "-a-zA-Z0-9._+:")
-      (let ((start (point)))
-       (skip-chars-forward "-a-zA-Z0-9._+:")
-       (setq word (buffer-substring-no-properties start (point))))
-      (if (string-match "[._]+$" word)
-         (setq word (substring word 0 (match-beginning 0))))
-      ;; If looking at something like *strcat(... , remove the '*'
-      (if (string-match "^*" word)
-         (setq word (substring word 1)))
-      ;; If looking at something like ioctl(2) or brc(1M), include the
-      ;; section number in the returned value.  Remove text properties.
-      (concat word
-             (if (looking-at
-                  (concat "[ \t]*([ \t]*\\(" Man-section-regexp "\\)[ \t]*)"))
-                 (format "(%s)" (match-string-no-properties 1)))))))
+      (when pos (goto-char pos))
+      (setq pos (point))
+      ;; The default title is the nearest entry-like object before or
+      ;; after POS.
+      (if (and (skip-chars-backward " \ta-zA-Z0-9+")
+              (not (zerop (skip-chars-backward "(")))
+              ;; Try to handle the special case where POS is on a
+              ;; section number.
+              (looking-at
+               (concat "([ \t]*\\(" Man-section-regexp "\\)[ \t]*)"))
+              ;; We skipped a valid section number backwards, look at
+              ;; preceding text.
+              (or (and (skip-chars-backward ",; \t")
+                       (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:"))))
+                  ;; Not a valid entry, move POS after closing paren.
+                  (not (setq pos (match-end 0)))))
+         ;; We have a candidate, make `start' record its starting
+         ;; position.
+         (setq start (point))
+       ;; Otherwise look at char before POS.
+       (goto-char pos)
+       (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:")))
+           ;; Our candidate is just before or around POS.
+           (setq start (point))
+         ;; Otherwise record the current column and look backwards.
+         (setq column (current-column))
+         (skip-chars-backward ",; \t")
+         ;; Record the distance travelled.
+         (setq distance (- column (current-column)))
+         (when (looking-back
+                (concat "([ \t]*\\(?:" Man-section-regexp "\\)[ \t]*)"))
+           ;; Skip section number backwards.
+           (goto-char (match-beginning 0))
+           (skip-chars-backward " \t"))
+         (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:")))
+             (progn
+               ;; We have a candidate before POS ...
+               (setq start (point))
+               (goto-char pos)
+               (if (and (skip-chars-forward ",; \t")
+                        (< (- (current-column) column) distance)
+                        (looking-at "[-a-zA-Z0-9._+:]"))
+                   ;; ... but the one after POS is better.
+                   (setq start (point))
+                 ;; ... and anything after POS is worse.
+                 (goto-char start)))
+           ;; No candidate before POS.
+           (goto-char pos)
+           (skip-chars-forward ",; \t")
+           (setq start (point)))))
+      ;; We have found a suitable starting point, try to skip at least
+      ;; one character.
+      (skip-chars-forward "-a-zA-Z0-9._+:")
+      (setq word (buffer-substring-no-properties start (point)))
+      ;; If there is a continuation at the end of line, check the
+      ;; following line too, eg:
+      ;;     see this-
+      ;;     command-here(1)
+      ;; Note: This code gets executed iff our entry is after POS.
+      (when (looking-at "[ \t\r\n]+\\([-a-zA-Z0-9._+:]+\\)([0-9])")
+       (setq word (concat word (match-string-no-properties 1)))
+       ;; Make sure the section number gets included by the code below.
+       (goto-char (match-end 1)))
+      (when (string-match "[._]+$" word)
+       (setq word (substring word 0 (match-beginning 0))))
+      ;; The following was commented out since the preceding code
+      ;; should not produce a leading "*" in the first place.
+;;;       ;; If looking at something like *strcat(... , remove the '*'
+;;;       (when (string-match "^*" word)
+;;;    (setq word (substring word 1)))
+       (concat
+        word
+        (and (not (string-equal word ""))
+             ;; If looking at something like ioctl(2) or brc(1M),
+             ;; include the section number in the returned value.
+             (looking-at
+              (concat "[ \t]*([ \t]*\\(" Man-section-regexp "\\)[ \t]*)"))
+             (format "(%s)" (match-string-no-properties 1)))))))
 
 \f
 ;; ======================================================================
@@ -679,28 +745,127 @@ If POS is nil, the current point is used."
 ;;;###autoload
 (defalias 'manual-entry 'man)
 
+(defvar Man-completion-cache nil
+  ;; On my machine, "man -k" is so fast that a cache makes no sense,
+  ;; but apparently that's not the case in all cases, so let's add a cache.
+  "Cache of completion table of the form (PREFIX . TABLE).")
+
+(defun Man-completion-table (string pred action)
+  (cond
+   ((eq action 'lambda)
+    (not (string-match "([^)]*\\'" string)))
+   ((equal string "-k")
+    ;; Let SPC (minibuffer-complete-word) insert the space.
+    (complete-with-action action '("-k ") string pred))
+   (t
+    (let ((table (cdr Man-completion-cache))
+          (section nil)
+          (prefix string))
+      (when (string-match "\\`\\([[:digit:]].*?\\) " string)
+        (setq section (match-string 1 string))
+        (setq prefix (substring string (match-end 0))))
+      (unless (and Man-completion-cache
+                   (string-prefix-p (car Man-completion-cache) prefix))
+        (with-temp-buffer
+          (setq default-directory "/") ;; in case inherited doesn't exist
+          ;; Actually for my `man' the arg is a regexp.
+          ;; POSIX says it must be ERE and "man-db" seems to agree,
+          ;; whereas under MacOSX it seems to be BRE-style and doesn't
+          ;; accept backslashes at all.  Let's not bother to
+          ;; quote anything.
+          (let ((process-environment (copy-sequence process-environment)))
+            (setenv "COLUMNS" "999") ;; don't truncate long names
+            ;; manual-program might not even exist.  And since it's
+            ;; run differently in Man-getpage-in-background, an error
+            ;; here may not necessarily mean that we'll also get an
+            ;; error later.
+            (ignore-errors
+              (call-process manual-program nil '(t nil) nil
+                            "-k" (concat "^" prefix))))
+          (goto-char (point-min))
+          (while (re-search-forward "^\\([^ \t\n]+\\)\\(?: ?\\((.+?)\\)\\(?:[ \t]+- \\(.*\\)\\)?\\)?" nil t)
+            (push (propertize (concat (match-string 1) (match-string 2))
+                              'help-echo (match-string 3))
+                  table)))
+        ;; Cache the table for later reuse.
+        (setq Man-completion-cache (cons prefix table)))
+      ;; The table may contain false positives since the match is made
+      ;; by "man -k" not just on the manpage's name.
+      (if section
+          (let ((re (concat "(" (regexp-quote section) ")\\'")))
+            (dolist (comp (prog1 table (setq table nil)))
+              (if (string-match re comp)
+                  (push (substring comp 0 (match-beginning 0)) table)))
+            (completion-table-with-context (concat section " ") table
+                                           prefix pred action))
+        ;; If the current text looks like a possible section name,
+        ;; then add a completion entry that just adds a space so SPC
+        ;; can be used to insert a space.
+        (if (string-match "\\`[[:digit:]]" string)
+            (push (concat string " ") table))
+        (let ((res (complete-with-action action table string pred)))
+          ;; In case we're completing to a single name that exists in
+          ;; several sections, the longest prefix will look like "foo(".
+          (if (and (stringp res)
+                   (string-match "([^(]*\\'" res)
+                   ;; In case the paren was already in `prefix', don't
+                   ;; remove it.
+                   (> (match-beginning 0) (length prefix)))
+              (substring res 0 (match-beginning 0))
+            res)))))))
 
 ;;;###autoload
 (defun man (man-args)
   "Get a Un*x manual page and put it in a buffer.
-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-method' for what happens when the buffer is ready.
-If a buffer already exists for this man page, it will display immediately.
-
-To specify a man page from a certain section, type SUBJECT(SECTION) or
-SECTION SUBJECT when prompted for a manual entry.  To see manpages from
-all sections related to a subject, put something appropriate into the
-`Man-switches' variable, which see."
+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' browsing
+buffer.  See variable `Man-notify-method' for what happens when
+the buffer is ready.  If a buffer already exists for this man
+page, it will display immediately.
+
+For a manpage from a particular section, use either of the
+following.  \"cat(1)\" is how cross-references appear and is
+passed to man as \"1 cat\".
+
+    cat(1)
+    1 cat
+
+To see manpages from all sections related to a subject, use an
+\"all pages\" option (which might be \"-a\" if it's not the
+default), then step through with `Man-next-manpage' (\\<Man-mode-map>\\[Man-next-manpage]) etc.
+Add to `Man-switches' to make this option permanent.
+
+    -a chmod
+
+An explicit filename can be given too.  Use -l if it might
+otherwise look like a page name.
+
+    /my/file/name.1.gz
+    -l somefile.1
+
+An \"apropos\" query with -k gives a buffer of matching page
+names or descriptions.  The pattern argument is usually an
+\"egrep\" style regexp.
+
+    -k pattern"
+
   (interactive
    (list (let* ((default-entry (Man-default-man-entry))
-               (input (read-string
+               ;; ignore case because that's friendly for bizarre
+               ;; caps things like the X11 function names and because
+               ;; "man" itself is case-sensitive on the command line
+               ;; so you're accustomed not to bother about the case
+               ;; ("man -k" is case-insensitive similarly, so the
+               ;; table has everything available to complete)
+               (completion-ignore-case t)
+               (input (completing-read
                        (format "Manual entry%s"
                                (if (string= default-entry "")
                                    ": "
                                  (format " (default %s): " default-entry)))
-                       nil 'Man-topic-history default-entry)))
+                        'Man-completion-table
+                       nil nil nil 'Man-topic-history default-entry)))
           (if (string= input "")
               (error "No man args given")
             input))))
@@ -730,8 +895,7 @@ all sections related to a subject, put something appropriate into the
       (require 'env)
       (message "Invoking %s %s in the background" manual-program man-args)
       (setq buffer (generate-new-buffer bufname))
-      (save-excursion
-       (set-buffer buffer)
+      (with-current-buffer buffer
        (setq buffer-undo-list t)
        (setq Man-original-frame (selected-frame))
        (setq Man-arguments man-args))
@@ -742,7 +906,7 @@ all sections related to a subject, put something appropriate into the
            ;; We must decode the output by a coding system that the
            ;; system's locale suggests in multibyte mode.
            (coding-system-for-read
-            (if default-enable-multibyte-characters
+            (if (default-value 'enable-multibyte-characters)
                 locale-coding-system 'raw-text-unix))
            ;; Avoid possible error by using a directory that always exists.
            (default-directory
@@ -764,18 +928,21 @@ all sections related to a subject, put something appropriate into the
        ;;               minal (using an ioctl(2) if available, the value of
        ;;               $COLUMNS,  or falling back to 80 characters if nei-
        ;;               ther is available).
-       (if window-system
-           (unless (or (getenv "MANWIDTH") (getenv "COLUMNS"))
-             ;; This isn't strictly correct, since we don't know how
-             ;; the page will actually be displayed, but it seems
-             ;; reasonable.
-             (setenv "COLUMNS" (number-to-string
-                                (cond
-                                 ((and (integerp Man-width) (> Man-width 0))
-                                  Man-width)
-                                 (Man-width (frame-width))
-                                 ((window-width)))))))
+       (unless (or (getenv "MANWIDTH") (getenv "COLUMNS"))
+         ;; This isn't strictly correct, since we don't know how
+         ;; the page will actually be displayed, but it seems
+         ;; reasonable.
+         (setenv "COLUMNS" (number-to-string
+                            (cond
+                             ((and (integerp Man-width) (> Man-width 0))
+                              Man-width)
+                             (Man-width (frame-width))
+                             ((window-width))))))
        (setenv "GROFF_NO_SGR" "1")
+       ;; Since man-db 2.4.3-1, man writes plain text with no escape
+       ;; sequences when stdout is not a tty.  In 2.5.0, the following
+       ;; env-var was added to allow control of this (see Debian Bug#340673).
+       (setenv "MAN_KEEP_FORMATTING" "1")
        (if (fboundp 'start-process)
            (set-process-sentinel
             (start-process manual-program buffer
@@ -802,8 +969,7 @@ all sections related to a subject, put something appropriate into the
 (defun Man-notify-when-ready (man-buffer)
   "Notify the user when MAN-BUFFER is ready.
 See the variable `Man-notify-method' for the different notification behaviors."
-  (let ((saved-frame (save-excursion
-                      (set-buffer man-buffer)
+  (let ((saved-frame (with-current-buffer man-buffer
                       Man-original-frame)))
     (cond
      ((eq Man-notify-method 'newframe)
@@ -925,6 +1091,11 @@ Same for the ANSI bold and normal escape sequences."
     (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t)
       (replace-match "+")
       (put-text-property (1- (point)) (point) 'face 'bold))
+    ;; When the header is longer than the manpage name, groff tries to
+    ;; condense it to a shorter line interspered with ^H.  Remove ^H with
+    ;; their preceding chars (but don't put Man-overstrike-face).  (Bug#5566)
+    (goto-char (point-min))
+    (while (re-search-forward ".\b" nil t) (backward-delete-char 2))
     (goto-char (point-min))
     ;; Try to recognize common forms of cross references.
     (Man-highlight-references)
@@ -955,7 +1126,7 @@ default type, `Man-xref-man-page' is used for the buttons."
        (Man-highlight-references0 nil Man-apropos-regexp 1
                                   'Man-default-man-entry
                                   (or xref-man-type 'Man-xref-man-page)))
-    (Man-highlight-references0 Man-see-also-regexp Man-reference-regexp 1 
+    (Man-highlight-references0 Man-see-also-regexp Man-reference-regexp 1
                               'Man-default-man-entry
                               (or xref-man-type 'Man-xref-man-page))
     (Man-highlight-references0 Man-synopsis-regexp Man-header-regexp 0 2
@@ -975,14 +1146,14 @@ default type, `Man-xref-man-page' is used for the buttons."
                       (Man-next-section 1)
                       (point)))
                 (goto-char (point-min))
-                (point-max))))
+                nil)))
       (while (re-search-forward regexp end t)
        (make-text-button
         (match-beginning button-pos)
         (match-end button-pos)
         'type type
         'Man-target-string (cond
-                            ((numberp target) 
+                            ((numberp target)
                              (match-string target))
                             ((functionp target)
                              target)
@@ -1012,6 +1183,11 @@ script would have done them."
        ))
   (goto-char (point-min))
   (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) (replace-match "+"))
+  ;; When the header is longer than the manpage name, groff tries to
+  ;; condense it to a shorter line interspered with ^H.  Remove ^H with
+  ;; their preceding chars (but don't put Man-overstrike-face).  (Bug#5566)
+  (goto-char (point-min))
+  (while (re-search-forward ".\b" nil t) (backward-delete-char 2))
   (Man-softhyphen-to-minus)
   (message "%s man page cleaned up" Man-arguments))
 
@@ -1031,8 +1207,7 @@ manpage command."
        (or (stringp process)
            (set-process-buffer process nil))
 
-      (save-excursion
-       (set-buffer Man-buffer)
+      (with-current-buffer Man-buffer
        (let ((case-fold-search nil))
          (goto-char (point-min))
          (cond ((or (looking-at "No \\(manual \\)*entry for")
@@ -1041,6 +1216,18 @@ manpage command."
                                                  (progn
                                                    (end-of-line) (point)))
                       delete-buff t))
+
+               ;; "-k foo", successful exit, but no output (from man-db)
+               ;; ENHANCE-ME: share the check for -k with
+               ;; `Man-highlight-references'.  The \\s- bits here are
+               ;; meant to allow for multiple options with -k among them.
+               ((and (string-match "\\(\\`\\|\\s-\\)-k\\s-" Man-arguments)
+                     (eq (process-status process) 'exit)
+                     (= (process-exit-status process) 0)
+                     (= (point-min) (point-max)))
+                (setq err-mess (format "%s: no matches" Man-arguments)
+                      delete-buff t))
+
                ((or (stringp process)
                     (not (and (eq (process-status process) 'exit)
                               (= (process-exit-status process) 0))))
@@ -1078,7 +1265,7 @@ manpage command."
            (Man-notify-when-ready Man-buffer))
 
        (if err-mess
-           (error err-mess))
+           (error "%s" err-mess))
        ))))
 
 \f
@@ -1223,13 +1410,10 @@ The following key bindings are currently in effect in the buffer:
 
 (defun Man-strip-page-headers ()
   "Strip all the page headers but the first from the manpage."
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        (case-fold-search nil)
-       (page-list Man-page-list)
-       (page ())
        (header ""))
-    (while page-list
-      (setq page (car page-list))
+    (dolist (page Man-page-list)
       (and (nth 2 page)
           (goto-char (car page))
           (re-search-forward Man-first-heading-regexp nil t)
@@ -1243,17 +1427,14 @@ The following key bindings are currently in effect in the buffer:
           ;; line.
           ;; (setq header (concat "\n" header)))
           (while (search-forward header (nth 1 page) t)
-            (replace-match "")))
-      (setq page-list (cdr page-list)))))
+            (replace-match ""))))))
 
 (defun Man-unindent ()
   "Delete the leading spaces that indent the manpage."
-  (let ((buffer-read-only nil)
-       (case-fold-search nil)
-       (page-list Man-page-list))
-    (while page-list
-      (let ((page (car page-list))
-           (indent "")
+  (let ((inhibit-read-only t)
+       (case-fold-search nil))
+    (dolist (page Man-page-list)
+      (let ((indent "")
            (nindent 0))
        (narrow-to-region (car page) (car (cdr page)))
        (if Man-uses-untabify-flag
@@ -1281,7 +1462,6 @@ The following key bindings are currently in effect in the buffer:
              (or (eolp)
                  (delete-char nindent))
              (forward-line 1)))
-       (setq page-list (cdr page-list))
        ))))
 
 \f
@@ -1339,14 +1519,16 @@ Returns t if section is found, nil otherwise."
                  (string= chosen ""))
              default
            chosen)))
-  (Man-find-section (aheadsym Man-sections-alist)))
+  (unless (Man-find-section (aheadsym Man-sections-alist))
+    (error "Section not found")))
+
 
 (defun Man-goto-see-also-section ()
   "Move point to the \"SEE ALSO\" section.
 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
+      (error "%s" (concat "No " Man-see-also-regexp
                     " section found in the current manpage"))))
 
 (defun Man-possibly-hyphenated-word ()
@@ -1375,25 +1557,32 @@ Specify which REFERENCE to use; default is based on word at point."
   (interactive
    (if (not Man-refpages-alist)
        (error "There are no references in the current man page")
-     (list (let* ((default (or
-                           (car (all-completions
-                                 (let ((word
-                                        (or (Man-possibly-hyphenated-word)
-                                            "")))
-                                   ;; strip a trailing '-':
-                                   (if (string-match "-$" word)
-                                       (substring word 0
-                                                  (match-beginning 0))
-                                     word))
-                                 Man-refpages-alist))
-                           (aheadsym Man-refpages-alist)))
-                  chosen
-                  (prompt (concat "Refer to (default " default "): ")))
-             (setq chosen (completing-read prompt Man-refpages-alist))
-             (if (or (not chosen)
-                     (string= chosen ""))
-                 default
-               chosen)))))
+     (list
+      (let* ((default (or
+                      (car (all-completions
+                            (let ((word
+                                   (or (Man-possibly-hyphenated-word)
+                                       "")))
+                              ;; strip a trailing '-':
+                              (if (string-match "-$" word)
+                                  (substring word 0
+                                             (match-beginning 0))
+                                word))
+                            Man-refpages-alist))
+                      (aheadsym Man-refpages-alist)))
+            (defaults
+              (mapcar 'substring-no-properties
+                      (delete-dups
+                       (delq nil (cons default
+                                       (mapcar 'car Man-refpages-alist))))))
+            chosen
+            (prompt (concat "Refer to (default " default "): ")))
+       (setq chosen (completing-read prompt Man-refpages-alist
+                                     nil nil nil nil defaults))
+       (if (or (not chosen)
+               (string= chosen ""))
+           default
+         chosen)))))
   (if (not Man-refpages-alist)
       (error "Can't find any references in the current manpage")
     (aput 'Man-refpages-alist reference)
@@ -1466,7 +1655,7 @@ Specify which REFERENCE to use; default is based on word at point."
   (let ((path Man-header-file-path)
         complete-path)
     (while path
-      (setq complete-path (concat (car path) "/" file)
+      (setq complete-path (expand-file-name file (car path))
             path (cdr path))
       (if (file-readable-p complete-path)
           (progn (view-file complete-path)