diff lisp/packages/man.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/packages/man.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,1224 @@
+;;; man.el --- browse UNIX manual pages
+;; Keywords: help
+
+;; Copyright (C) 1985, 1993, 1994 Free Software Foundation, Inc.
+;;
+;; This file is part of XEmacs.
+
+;; XEmacs 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 2, or (at your option)
+;; any later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Synched up with: Not synched with FSF.
+;;; ICK!  This file is almost completely different from FSF.
+;;; Someone clarify please.
+
+;; Mostly rewritten by Alan K. Stebbens <aks@hub.ucsb.edu> 11-apr-90.
+;;
+;;  o  Match multiple man pages using TOPIC as a simple pattern
+;;  o  Search unformatted pages, even when formatted matches are found
+;;  o  Query the user as to which pages are desired
+;;  o  Use of the prefix arg to toggle/bypass the above features
+;;  o  Buffers named by the first topic in the buffer
+;;  o  Automatic uncompress for compressed man pages (.Z, .z, and .gz)
+;;  o  View the resulting buffer using M-x view mode
+;;
+;; Modified 16-mar-91 by Jamie Zawinski <jwz@lucid.com> to default the 
+;; manual topic to the symbol at point, just like find-tag does.
+;;
+;; Modified 22-mar-93 by jwz to use multiple fonts and follow xrefs with mouse.
+;;
+;; Modified 16-apr-93 by Dave Gillespie <daveg@synaptics.com> to make
+;; apropos work nicely; work correctly when bold or italic is unavailable; 
+;; reuse old buffer if topic is re-selected (in Manual-topic-buffer mode).
+;;
+;; Modified 4-apr-94 by jwz: merged in Tibor Polgar's code for manpath.conf.
+;;
+;; Modified 19-apr-94 by Tibor Polgar <tlp00@spg.amdahl.com> to add support for
+;; $PAGER variable to be emacsclient and properly process man pages (assuming
+;; the man pages were built by man in /tmp.  also fixed bug with man list being
+;; backwards.
+;;
+;; Modified 23-aug-94 by Tibor Polgar <tlp00@spg.amdahl.com> to add support for
+;; displaying only one instance of a man page (Manual-unique-man-sections-only)
+;; Fixed some more man page ordering bugs, bug with Manual-query-multiple-pages.
+;;
+;; Modified 29-nov-94 by Ben Wing <wing@spg.amdahl.com>: small fixes
+;; that should hopefully make things work under HPUX and IRIX.; 
+;;
+;; Modified 15-jul-95 by Dale Atems <atems@physics.wayne.edu>:
+;; some extensive rewriting to make things work right (more or less)
+;; under IRIX.
+;;
+;; Modified 08-mar-96 by Hubert Palme <palme@wrcs3.urz.uni-wuppertal.de>:
+;; added /usr/share/catman to the manual directory list for IRIX (5.3)
+;; 
+;; This file defines "manual-entry", and the remaining definitions all
+;; begin with "Manual-".  This makes the autocompletion on "M-x man" work.
+;;
+;; Variables of interest:
+;;
+;;	Manual-program
+;;	Manual-topic-buffer
+;;	Manual-buffer-view-mode
+;;	Manual-directory-list
+;;	Manual-formatted-directory-list
+;;	Manual-match-topic-exactly
+;;	Manual-query-multiple-pages
+;;	Manual-page-history
+;;	Manual-subdirectory-list
+;;	Manual-man-page-section-ids
+;;	Manual-formatted-page-prefix
+;;	Manual-unformatted-page-prefix
+;;	Manual-use-full-section-ids
+
+(defvar Manual-program "man" "\
+*Name of the program to invoke in order to format the source man pages.")
+
+(defvar Manual-section-switch (if (eq system-type 'usg-unix-v) "-s" nil)
+  "SysV needs this to work right.")
+
+(defvar Manual-topic-buffer t "\
+*Non-nil means \\[Manual-entry] should output the manual entry for TOPIC into
+a buffer named *man TOPIC*, otherwise, it should name the buffer
+*Manual Entry*.")
+
+(defvar Manual-buffer-view-mode t "\
+*Whether manual buffers should be placed in view-mode.
+nil means leave the buffer in fundamental-mode in another window.
+t means use `view-buffer' to display the man page in the current window.
+Any other value means use `view-buffer-other-window'.")
+
+(defvar Manual-match-topic-exactly t "\
+*Non-nil means that \\[manual-entry] will match the given TOPIC exactly, rather
+apply it as a pattern.  When this is nil, and \"Manual-query-multiple-pages\"
+is non-nil, then \\[manual-entry] will query you for all matching TOPICs.
+This variable only has affect on the preformatted man pages (the \"cat\" files),
+since the \"man\" command always does exact topic matches.")
+
+(defvar Manual-query-multiple-pages nil "\
+*Non-nil means that \\[manual-entry] will query the user about multiple man
+pages which match the given topic.  The query is done using the function 
+\"y-or-n-p\".  If this variable is nil, all man pages with topics matching the
+topic given to \\[manual-entry] will be inserted into the temporary buffer.
+See the variable \"Manual-match-topic-exactly\" to control the matching.")
+
+(defvar Manual-unique-man-sections-only nil
+  "*Only present one man page per section.  This variable is useful if the same or
+up/down level man pages for the same entry are present in mulitple man paths.
+When set to t, only the first entry found in a section is displayed, the others
+are ignored without any messages or warnings.  Note that duplicates can occur if
+the system has both formatted and unformatted version of the same page.")
+
+(defvar Manual-mode-hook nil
+  "Function or functions run on entry to Manual-mode.")
+
+(defvar Manual-directory-list nil "\
+*A list of directories used with the \"man\" command, where each directory
+contains a set of \"man?\" and \"cat?\" subdirectories.  If this variable is nil,
+it is initialized by \\[Manual-directory-list-init].")
+
+(defvar Manual-formatted-directory-list nil "\
+A list of directories containing formatted man pages.  Initialized by
+\\[Manual-directory-list-init].")
+
+(defvar Manual-unformatted-directory-list nil "\
+A list of directories containing the unformatted (source) man pages.  
+Initialized by \\[Manual-directory-list-init].")
+
+(defvar Manual-page-history nil "\
+A list of names of previously visited man page buffers.")
+
+(defvar Manual-manpath-config-file "/usr/lib/manpath.config"
+  "*Location of the manpath.config file, if any.")
+
+(defvar Manual-apropos-switch "-k"
+  "*Man apropos switch")
+
+;; New variables.
+
+(defvar Manual-subdirectory-list nil "\
+A list of all the subdirectories in which man pages may be found.
+Iniialized by Manual-directory-list-init.")
+
+;; This is for SGI systems; don't know what it should be otherwise.
+(defvar Manual-man-page-section-ids "1nl6823457poD" "\
+String containing all suffix characters for \"cat\" and \"man\"
+that identify valid sections of the Un*x manual.") 
+
+(defvar Manual-formatted-page-prefix "cat" "\
+Prefix for directories where formatted man pages are to be found.
+Defaults to \"cat\".")
+
+(defvar Manual-unformatted-page-prefix "man" "\
+Prefix for directories where unformatted man pages are to be found.
+Defaults to \"man\".")
+
+(defvar Manual-leaf-signature "" "\
+Regexp for identifying \"leaf\" subdirectories in the search path.
+If empty, initialized by Manual-directory-list-init.")
+
+(defvar Manual-use-full-section-ids t "\
+If non-nil, pass full section ids to Manual-program, otherwise pass
+only the first character. Defaults to 't'.")
+
+(defvar Manual-use-subdirectory-list (eq system-type 'irix) "\
+This makes manual-entry work correctly on SGI machines but it
+imposes a large startup cost which is why it is not simply on by
+default on all systems.")
+
+(defvar Manual-use-rosetta-man (not (null (locate-file "rman" exec-path))) "\
+If non-nil, use RosettaMan (rman) to filter man pages.
+This makes man-page cleanup virtually instantaneous, instead of
+potentially taking a long time.
+
+Here is information on RosettaMan, from Neal.Becker@comsat.com (Neal Becker):
+
+RosettaMan is a filter for UNIX manual pages.  It takes as input man
+pages formatted for a variety of UNIX flavors (not [tn]roff source)
+and produces as output a variety of file formats.  Currently
+RosettaMan accepts man pages as formatted by the following flavors of
+UNIX: Hewlett-Packard HP-UX, AT&T System V, SunOS, Sun Solaris, OSF/1,
+DEC Ultrix, SGI IRIX, Linux, SCO; and produces output for the following
+formats: printable ASCII only (stripping page headers and footers),
+section and subsection headers only, TkMan, [tn]roff, Ensemble, RTF,
+SGML (soon--I finally found a DTD), HTML, MIME, LaTeX, LaTeX 2e, Perl 5's pod.
+
+RosettaMan improves on other man page filters in several ways: (1) its
+analysis recognizes the structural pieces of man pages, enabling high
+quality output, (2) its modular structure permits easy augmentation of
+output formats, (3) it accepts man pages formatted with the varient
+macros of many different flavors of UNIX, and (4) it doesn't require
+modification or cooperation with any other program.
+
+RosettaMan is a rewrite of TkMan's man page filter, called bs2tk.  (If
+you haven't heard about TkMan, a hypertext man page browser, you
+should grab it via anonymous ftp from ftp.cs.berkeley.edu:
+/ucb/people/phelps/tkman.tar.Z.)  Whereas bs2tk generated output only for
+TkMan, RosettaMan generalizes the process so that the analysis can be
+leveraged to new output formats.  A single analysis engine recognizes
+section heads, subsection heads, body text, lists, references to other
+man pages, boldface, italics, bold italics, special characters (like
+bullets), tables (to a degree) and strips out page headers and
+footers.  The engine sends signals to the selected output functions so
+that an enhancement in the engine improves the quality of output of
+all of them.  Output format functions are easy to add, and thus far
+average about about 75 lines of C code each.
+
+
+
+*** NOTES ON CURRENT VERSION ***
+
+Help!  I'm looking for people to help with the following projects.
+\(1) Better RTF output format.  The current one works, but could be
+made better.  (2) Roff macros that produce text that is easily
+parsable.  RosettaMan handles a great variety, but some things, like
+H-P's tables, are intractable.  If you write an output format or
+otherwise improve RosettaMan, please send in your code so that I may
+share the wealth in future releases.
+
+This version can try to identify tables (turn this on with the -T
+switch) by looking for lines with a large amount of interword spacing,
+reasoning that this is space between columns of a table.  This
+heuristic doesn't always work and sometimes misidentifies ordinary
+text as tables.  In general I think it is impossible to perfectly
+identify tables from nroff formatted text.  However, I do think the
+heuristics can be tuned, so if you have a collection of manual pages
+with unrecognized tables, send me the lot, in formatted form (i.e.,
+after formatting with nroff -man), and uuencode them to preserve the
+control characters.  Better, if you can think of heuristics that
+distinguish tables from ordinary text, I'd like to hear them.
+
+
+Notes for HTML consumers: This filter does real (heuristic)
+parsing--no <PRE>!  Man page references are turned into hypertext links.")
+
+(make-face 'man-italic)
+(or (face-differs-from-default-p 'man-italic)
+    (copy-face 'italic 'man-italic))
+;; XEmacs (from Darrell Kindred): underlining is annoying due to
+;; large blank spaces in this face.
+;; (or (face-differs-from-default-p 'man-italic)
+;;    (set-face-underline-p 'man-italic t))
+
+(make-face 'man-bold)
+(or (face-differs-from-default-p 'man-bold)
+    (copy-face 'bold 'man-bold))
+(or (face-differs-from-default-p 'man-bold)
+    (copy-face 'man-italic 'man-bold))
+
+(make-face 'man-heading)
+(or (face-differs-from-default-p 'man-heading)
+    (copy-face 'man-bold 'man-heading))
+
+(make-face 'man-xref)
+(or (face-differs-from-default-p 'man-xref)
+    (set-face-underline-p 'man-xref t))
+
+;; Manual-directory-list-init
+;; Initialize the directory lists.
+
+(defun Manual-directory-list-init (&optional arg) 
+  "Initialize the Manual-directory-list variable from $MANPATH
+if it is not already set, or if a prefix argument is provided."
+  (interactive "P")
+  (if arg (setq Manual-directory-list nil))
+  (if (null Manual-directory-list)
+      (let ((manpath (getenv "MANPATH"))
+	    (global (Manual-manpath-config-contents))
+	    (dirlist nil)
+	    dir)
+	(cond ((and manpath global)
+	       (setq manpath (concat manpath ":" global)))
+	      (global
+	       (setq manpath global))
+	      ((not manpath)
+	       ;; XEmacs - (bpw/stig) Unix-specifix hack for lusers w/ no manpath
+	       (setq manpath "/usr/local/man:/usr/share/man:/usr/share/catman:/usr/contrib/man:/usr/X11/man:/usr/man:/usr/catman")))
+	;; Make sure that any changes we've made internally are seen by man.
+	(setenv "MANPATH" manpath)
+	(while (string-match "\\`:*\\([^:]+\\)" manpath)
+	  (setq dir (substring manpath (match-beginning 1) (match-end 1)))
+	  (and (not (member dir dirlist))
+	       (setq dirlist (cons dir dirlist)))
+	  (setq manpath (substring manpath (match-end 0))))
+	(setq dirlist (nreverse dirlist))
+	(setq Manual-directory-list dirlist)
+	(setq Manual-subdirectory-list nil)
+	(setq Manual-formatted-directory-list nil)
+	(setq Manual-unformatted-directory-list nil)))
+  (if (string-equal Manual-leaf-signature "")
+      (setq Manual-leaf-signature
+	    (concat "/\\("
+		    Manual-formatted-page-prefix
+		    "\\|" Manual-unformatted-page-prefix
+		    "\\)"
+		    "[" Manual-man-page-section-ids
+		    "].?/.")))
+  (if Manual-use-subdirectory-list
+      (progn
+	(if (null Manual-subdirectory-list)
+	    (setq Manual-subdirectory-list
+		  (Manual-all-subdirectories Manual-directory-list
+					     Manual-leaf-signature nil)))
+	(if (null Manual-formatted-directory-list)
+	    (setq Manual-formatted-directory-list
+		  (Manual-filter-subdirectories Manual-subdirectory-list
+						Manual-formatted-page-prefix)))
+	(if (null Manual-unformatted-directory-list)
+	    (setq Manual-unformatted-directory-list
+		  (Manual-filter-subdirectories Manual-subdirectory-list
+						Manual-unformatted-page-prefix))))
+    (if (null Manual-formatted-directory-list)
+        (setq Manual-formatted-directory-list
+	      (Manual-select-subdirectories Manual-directory-list
+					    Manual-formatted-page-prefix)))
+    (if (null Manual-unformatted-directory-list)
+        (setq Manual-unformatted-directory-list
+	      (Manual-select-subdirectories Manual-directory-list
+					    Manual-unformatted-page-prefix)))))
+
+
+(defun Manual-manpath-config-contents ()
+  "Parse the `Manual-manpath-config-file' file, if any.
+Returns a string like in $MANPATH."
+  (if (and Manual-manpath-config-file
+	   (file-readable-p Manual-manpath-config-file))
+      (let ((buf (get-buffer-create " *Manual-config*"))
+	    path)
+	(set-buffer buf)
+	(buffer-disable-undo buf)
+	(erase-buffer)
+	(insert-file-contents Manual-manpath-config-file)
+	(while (re-search-forward "^\\(MANDATORY_MANPATH\\|MANPATH_MAP\\)"
+				  nil t)
+	  (and (re-search-forward "\\(/[^ \t\n]+\\)[ \t]*$")
+	       (setq path (concat path (buffer-substring (match-beginning 1)
+							 (match-end 1))
+				  ":"))))
+	(kill-buffer buf)
+	path)))
+;;
+;; manual-entry  -- The "main" user function
+;;
+
+;;;###autoload
+(defun manual-entry (topic &optional arg silent)
+  "Display the Unix manual entry (or entries) for TOPIC.
+If prefix arg is given, modify the search according to the value:
+  2 = complement default exact matching of the TOPIC name;
+      exact matching default is specified by `Manual-match-topic-exactly'
+  3 = force a search of the unformatted man directories
+  4 = both 2 and 3
+The manual entries are searched according to the variable
+Manual-directory-list, which should be a list of directories.  If
+Manual-directory-list is nil, \\[Manual-directory-list-init] is
+invoked to create this list from the MANPATH environment variable.
+See the variable Manual-topic-buffer which controls how the buffer
+is named.  See also the variables Manual-match-topic-exactly,
+Manual-query-multiple-pages, and Manual-buffer-view-mode."
+  (interactive
+   (list (let* ((fmh "-A-Za-z0-9_.")
+		(default (save-excursion
+			   (buffer-substring
+			    (progn
+			      (re-search-backward "\\sw" nil t)
+			      (skip-chars-backward fmh) (point))
+			    (progn (skip-chars-forward fmh) (point)))))
+		(thing (read-string
+			(if (equal default "") "Manual entry: "
+			  (concat "Manual entry: (default " default ") ")))))
+	   (if (equal thing "") default thing))
+	 (prefix-numeric-value current-prefix-arg)))
+  ;;(interactive "sManual entry (topic): \np")
+  (or arg (setq arg 1))
+  (Manual-directory-list-init nil)
+  (let ((exact (if (or (= arg 2) (= arg 4))
+		   (not Manual-match-topic-exactly)
+		 Manual-match-topic-exactly))
+	(force (if (>= arg 3)
+                   t
+                   nil))
+	section fmtlist manlist apropos-mode)
+    (let ((case-fold-search nil))
+      (if (and (null section)
+	       (string-match
+		"\\`[ \t]*\\([^( \t]+\\)[ \t]*(\\(.+\\))[ \t]*\\'" topic))
+	  (setq section (substring topic (match-beginning 2)
+				   (match-end 2))
+		topic (substring topic (match-beginning 1)
+				 (match-end 1)))
+	(if (string-match "\\`[ \t]*-k[ \t]+\\([^ \t]+\\)\\'" topic)
+	    (setq section "-k"
+		  topic (substring topic (match-beginning 1))))))
+    (if (equal section "-k")
+	(setq apropos-mode t)
+      (or silent
+	  (message "Looking for formatted entry for %s%s..."
+		   topic (if section (concat "(" section ")") "")))
+      (setq fmtlist (Manual-select-man-pages
+                      Manual-formatted-directory-list
+                      topic section exact '()))
+      (if (or force (not section) (null fmtlist))
+	  (progn
+	    (or silent
+		(message "%sooking for unformatted entry for %s%s..."
+			 (if fmtlist "L" "No formatted entry, l")
+			 topic (if section (concat "(" section ")") "")))
+	    (setq manlist (Manual-select-man-pages
+                            Manual-unformatted-directory-list
+                            topic section exact (if force '() fmtlist))))))
+
+    ;; Delete duplicate man pages (a file of the same name in multiple
+    ;; directories.)
+    (or nil ;force
+        (let ((rest (append fmtlist manlist)))
+          (while rest
+            (let ((rest2 (cdr rest)))
+              (while rest2
+                (if (equal (file-name-nondirectory (car rest))
+                           (file-name-nondirectory (car rest2)))
+                    (setq fmtlist (delq (car rest2) fmtlist)
+                          manlist (delq (car rest2) manlist)))
+                (setq rest2 (cdr rest2))))
+            (setq rest (cdr rest)))))
+
+    (if (not (or fmtlist manlist apropos-mode))
+        (progn
+          (message "No entries found for %s%s" topic
+                   (if section (concat "(" section ")") ""))
+          nil)
+      (let ((bufname (cond ((not Manual-topic-buffer)
+                            ;; What's the point of retaining this?
+                            (if apropos-mode
+                                "*Manual Apropos*"
+                                "*Manual Entry*"))
+                           (apropos-mode
+                            (concat "*man apropos " topic "*"))
+                           (t
+                            (concat "*man "
+                                    (cond (exact
+                                           (if section
+                                               (concat topic "." section)
+                                               topic))
+                                          ((or (cdr fmtlist) (cdr manlist)
+                                               (and fmtlist manlist))
+                                           ;; more than one entry found
+                                           (concat topic "..."))
+                                          (t
+                                           (file-name-nondirectory
+                                            (car (or fmtlist manlist)))))
+                                    "*"))))
+            (temp-buffer-show-function 
+             (cond ((eq 't Manual-buffer-view-mode) 'view-buffer)
+                   ((eq 'nil Manual-buffer-view-mode)
+                    temp-buffer-show-function)
+                   (t 'view-buffer-other-window))))
+
+        (if apropos-mode
+            (setq manlist (list (format "%s.%s" topic section))))
+
+        (cond
+          ((and Manual-topic-buffer (get-buffer bufname))
+           ;; reselect an old man page buffer if it exists already.
+           (save-excursion
+             (set-buffer (get-buffer bufname))
+             (Manual-mode))
+           (if temp-buffer-show-function
+               (funcall temp-buffer-show-function (get-buffer bufname))
+               (display-buffer bufname)))
+          (t
+           (with-output-to-temp-buffer bufname
+             (buffer-disable-undo standard-output)
+             (save-excursion
+               (set-buffer standard-output)
+               (setq buffer-read-only nil)
+               (erase-buffer)
+	       (Manual-insert-pages fmtlist manlist apropos-mode)
+               (set-buffer-modified-p nil)
+               (Manual-mode)
+               ))))
+        (setq Manual-page-history
+              (cons (buffer-name)
+                    (delete (buffer-name) Manual-page-history)))
+        (message nil)
+        t))))
+
+(defun Manpage-apropos (topic &optional arg silent)
+  "Apropos on Unix manual pages for TOPIC.
+It calls the function `manual-entry'. Look at this function for
+further description. Look also at the variable `Manual-apropos-switch',
+if this function doesn't work on your system."
+  (interactive
+   (list (let* ((fmh "-A-Za-z0-9_.")
+		(default (save-excursion
+			   (buffer-substring
+			    (progn
+			      (re-search-backward "\\sw" nil t)
+			      (skip-chars-backward fmh) (point))
+			    (progn (skip-chars-forward fmh) (point)))))
+		(thing (read-string
+			(if (equal default "") "Manual entry: "
+			  (concat "Manual entry: (default " default ") ")))))
+	   (if (equal thing "") default thing))
+	 (prefix-numeric-value current-prefix-arg)))
+  (manual-entry (concat Manual-apropos-switch " " topic) arg silent))
+
+(defun Manual-insert-pages (fmtlist manlist apropos-mode)
+  (let ((sep (make-string 65 ?-))
+	name start end topic section)
+    (while fmtlist			; insert any formatted files
+      (setq name (car fmtlist))
+      (goto-char (point-max))
+      (setq start (point))
+      ;; In case the file can't be read or uncompressed or
+      ;; something like that.
+      (condition-case ()
+	  (Manual-insert-man-file name)
+	(file-error nil))
+      (goto-char (point-max))
+      (setq end (point))
+      (save-excursion
+	(save-restriction
+	  (message "Cleaning manual entry for %s..."
+		   (file-name-nondirectory name))
+	  (narrow-to-region start end)
+	  (Manual-nuke-nroff-bs)
+	  (goto-char (point-min))
+	  (insert "File: " name "\n")
+	  (goto-char (point-max))
+	  ))
+      (if (or (cdr fmtlist) manlist)
+	  (insert "\n\n" sep "\n"))
+      (setq fmtlist (cdr fmtlist)))
+
+    (while manlist			; process any unformatted files
+      (setq name (car manlist))
+      (or (string-match "\\([^/]+\\)\\.\\([^./]+\\)\\(\\.gz\\'\\)" name)
+	  (string-match "\\([^/]+\\)\\.\\([^./]+\\)\\'" name))
+      (setq topic (substring name (match-beginning 1) (match-end 1)))
+      (setq section (substring name (match-beginning 2) (match-end 2)))
+      ;; This won't work under IRIX, because SGI man accepts only the
+      ;; "main" (one-character) section id, not full section ids
+      ;; like 1M, 3X, etc. Put (setq Manual-use-full-section-ids nil)
+      ;; in your .emacs to work around this problem.
+      (if (not (or Manual-use-full-section-ids (string-equal section "")))
+	  (setq section (substring section 0 1)))
+      (message "Invoking man %s%s %s..."
+	       (if Manual-section-switch
+		   (concat Manual-section-switch " ")
+		 "")
+	       section topic)
+      (setq start (point))
+      (Manual-run-formatter name topic section)
+      (setq end (point))
+      (save-excursion
+	(save-restriction
+	  (message "Cleaning manual entry for %s(%s)..." topic section)
+	  (narrow-to-region start end)
+	  (Manual-nuke-nroff-bs apropos-mode)
+	  (goto-char (point-min))
+	  (insert "File: " name "\n")
+	  (goto-char (point-max))
+	  ))
+      (if (cdr manlist)
+	  (insert "\n\n" sep "\n"))
+      (setq manlist (cdr manlist))))
+  (if (< (buffer-size) 200)
+      (progn
+	(goto-char (point-min))
+	(if (looking-at "^File: ")
+	    (forward-line 1))
+	(error (buffer-substring (point) (progn (end-of-line) (point))))))
+  nil)
+
+
+(defun Manual-run-formatter (name topic section)
+  (cond
+   ((string-match "roff\\'" Manual-program)
+    ;; kludge kludge
+    (call-process Manual-program nil t nil "-Tman" "-man" name))
+
+   (t
+    (call-process Manual-program nil t nil
+                  (concat Manual-section-switch section) topic))))
+
+   ;(Manual-use-rosetta-man
+   ; (call-process "/bin/sh" nil t nil "-c"
+   ;               (format "man %s %s | rman" section topic)))
+
+
+(defvar Manual-mode-map
+  (let ((m (make-sparse-keymap)))
+    (set-keymap-name m 'Manual-mode-map)
+    (define-key m "l" 'Manual-last-page)
+    (define-key m 'button2 'Manual-follow-xref)
+    (define-key m 'button3 'Manual-popup-menu)
+    m))
+
+(defun Manual-mode ()
+  (kill-all-local-variables)
+  (setq buffer-read-only t)
+  (use-local-map Manual-mode-map)
+  (setq major-mode 'Manual-mode
+	mode-name "Manual")
+  ;; man pages with long lines are buggy!
+  ;; This looks slightly better if they only
+  ;; overran by a couple of chars.
+  (setq truncate-lines t)
+  ;; turn off horizontal scrollbars in this buffer
+  (set-specifier scrollbar-height (cons (current-buffer) 0))
+  (run-hooks 'Manual-mode-hook))
+
+(defun Manual-last-page ()
+  (interactive)
+  (while (or (not (get-buffer (car (or Manual-page-history
+				       (error "No more history.")))))
+	     (eq (get-buffer (car Manual-page-history)) (current-buffer)))
+    (setq Manual-page-history (cdr Manual-page-history)))
+  (switch-to-buffer (car Manual-page-history)))
+
+
+;; Manual-select-subdirectories
+;; Given a DIRLIST and a SUBDIR name, return all subdirectories of the former which
+;; match the latter.
+
+(defun Manual-select-subdirectories (dirlist subdir)
+  (let ((dirs '())
+        (case-fold-search nil)
+        (match (concat "\\`" (regexp-quote subdir)))
+        d)
+    (while dirlist
+      (setq d (car dirlist) dirlist (cdr dirlist))
+      (if (file-directory-p d)
+          (let ((files (directory-files d t match nil 'dirs-only))
+		(dir-temp '()))
+            (while files
+              (if (file-executable-p (car files))
+                  (setq dir-temp (cons (file-name-as-directory (car files))
+                                   dir-temp)))
+              (setq files (cdr files)))
+	    (and dir-temp
+		 (setq dirs (append dirs (nreverse dir-temp)))))))
+    dirs))
+
+
+;; Manual-filter-subdirectories
+;; Given a DIRLIST and a SUBDIR name, return all members of the former
+;; which match the latter.
+
+(defun Manual-filter-subdirectories (dirlist subdir)
+  (let ((match (concat
+		"/"
+		(regexp-quote subdir)
+		"[" Manual-man-page-section-ids "]"))
+	slist dir)
+    (while dirlist
+      (setq dir (car dirlist) dirlist (cdr dirlist))
+      (if (and (file-executable-p dir) (string-match match dir))
+	    (setq slist (cons dir slist))))
+    (nreverse slist)))
+
+
+(defun Manual-all-subdirectories (dirlist leaf-signature dirs &optional silent) "\
+Given a DIRLIST, return a backward-sorted list of all subdirectories
+thereof, prepended to DIRS if non-nil. This function calls itself
+recursively until subdirectories matching LEAF-SIGNATURE are reached,
+or the hierarchy has been thoroughly searched. This code is a modified
+version of a function written by Tim Bradshaw (tfb@ed.ac.uk)."
+  (Manual-all-subdirectories-noloop dirlist leaf-signature dirs nil silent))
+
+(defun Manual-all-subdirectories-noloop (dirlist leaf-signature dirs been &optional silent) "\
+Does the job of manual-all-subdirectories and keeps track of where it
+has been to avoid loops."
+  (let (dir)
+    (while dirlist
+      (setq dir (car dirlist) dirlist (cdr dirlist))
+      (if (file-directory-p dir)
+	  (let ((dir-temp (cons (file-name-as-directory dir) dirs)))
+	    ;; Without feedback the user might wonder about the delay!
+	    (or silent (message
+			"Building list of search directories... %s"
+			(car dir-temp)))
+	    (if (member (file-truename dir) been)
+		()		 ; Ignore. We have been here before
+	      (setq been (cons (file-truename dir) been))
+	      (setq dirs
+		    (if (string-match leaf-signature dir)
+			dir-temp
+		      (Manual-all-subdirectories-noloop
+		       (directory-files dir t "[^.]$" nil 'dirs-only)
+		       leaf-signature dir-temp been silent))))))))
+  dirs)
+
+
+(defvar Manual-bogus-file-pattern "\\.\\(lpr\\|ps\\|PS\\)\\'"
+  "Some systems have files in the man/man*/ directories which aren't man pages.
+This pattern is used to prune those files.")
+
+;; Manual-select-man-pages
+;;
+;; Given a DIRLIST, discover all filenames which complete given the TOPIC
+;; and SECTION.
+
+;; ## Note: BSD man looks for .../man1/foo.1 and .../man1/$MACHINE/foo.1
+
+;; ## Fixed for SGI IRIX 5.x on Sat Jul 15 1995 by Dale Atems
+;; (atems@physics.wayne.edu).
+
+(defun Manual-select-man-pages (dirlist topic section exact shadow)
+  (let ((case-fold-search nil))
+    (and section
+      (let ((l '())
+	    ;;(match (concat (substring section 0 1) "/?\\'"))
+	    ;;                                          ^^^
+	    ;; We'll lose any pages inside subdirectories of the "standard"
+	    ;; ones if we insist on this! The following regexp should
+	    ;; match any directory ending with the full section id or
+	    ;; its first character, or any direct subdirectory thereof:
+	    (match (concat "\\("
+			   (regexp-quote section)
+			   "\\|"
+			   (substring section 0 1)
+			   "\\)/?"))
+	    d)
+	(while dirlist
+	  (setq d (car dirlist) dirlist (cdr dirlist))
+	  (if (string-match match d)
+	      (setq l (cons d l))))
+	(setq dirlist l)))
+    (if shadow
+        (setq shadow (concat "/\\("
+                             (mapconcat #'(lambda (n)
+                                            (regexp-quote
+                                             (file-name-nondirectory n)))
+                                        shadow
+                                        "\\|")
+                             "\\)\\'")))
+    (let ((manlist '())
+          (match (concat "\\`"
+                           (regexp-quote topic)
+			    ;; **Note: on IRIX the preformatted pages
+			    ;; are packed, so they end with ".z". This
+			    ;; way you miss them if you specify a
+			    ;; section. I don't see any point to it here
+			    ;; even on BSD systems since we're looking
+			    ;; one level down already, but I can't test
+			    ;; this. More thought needed (???)
+
+			   (cond ((and section
+				       (not Manual-use-subdirectory-list))
+				  (concat "\\." (regexp-quote section)))
+                                 (exact
+                                  ;; If Manual-match-topic-exactly is
+                                  ;; set, then we must make sure the
+                                  ;; completions are exact, except for
+                                  ;; trailing weird characters after
+                                  ;; the section.
+                                  "\\.")
+                                 (t
+                                  ""))))
+          dir)
+      (while dirlist
+        (setq dir (car dirlist) dirlist (cdr dirlist))
+        (if (not (file-directory-p dir))
+            (progn
+              (message "warning: %s is not a directory" dir)
+              ;;(sit-for 1)
+              )
+            (let ((files (directory-files dir t match nil t))
+                  f)
+              (while files
+                (setq f (car files) files (cdr files))
+                (cond ((string-match Manual-bogus-file-pattern f)
+		       ;(message "Bogus fule %s" f) (sit-for 2)
+                       )
+		      ((and shadow (string-match shadow f))
+                       ;(message "Shadowed %s" f) (sit-for 2)
+                       )
+                      ((not (file-readable-p f))
+                       ;(message "Losing with %s" f) (sit-for 2)
+                       )
+                      (t
+                       (setq manlist (cons f manlist))))))))
+      (setq manlist (nreverse manlist))
+      (and Manual-unique-man-sections-only
+	   (setq manlist (Manual-clean-to-unique-pages-only manlist)))
+      (if (and manlist Manual-query-multiple-pages)
+          (apply #'append
+                 (mapcar #'(lambda (page)
+                             (and page 
+                                  (y-or-n-p (format "Read %s? " page))
+				  (list page)))
+                         manlist))
+          manlist))))
+
+(defun Manual-clean-to-unique-pages-only (manlist)
+  "Prune the current list of pages down to a unique set."
+  (let (page-name unique-pages)
+    (apply 'append
+	   (mapcar '(lambda (page)
+		      (cond (page
+			     (and (string-match ".*/\\(.*\\)" page)
+				  (setq page-name (substring page (match-beginning 1)
+							     (match-end 1)))
+				  ;; try to clip off .Z, .gz suffixes
+				  (and (string-match "\\(.*\\)\\.\\(.+\\)\\.\\(.+\\)"
+						     page-name)
+				       (setq page-name
+					     (substring page-name (match-beginning 1)
+							(match-end 2)))))
+			     ;; add Manual-unique-pages if it isn't there
+			     ;;  and return file
+			     (if (and unique-pages
+				      page-name
+				      (string-match (concat "\\b" page-name "\\b")
+						    unique-pages))
+				 nil
+			       (setq unique-pages (concat unique-pages
+								 page-name
+								 " "))
+			       (list page)))))
+		   manlist))))
+			    
+
+
+(defun Manual-insert-man-file (name)
+  ;; Insert manual file (unpacked as necessary) into buffer
+  (cond ((equal (substring name -3) ".gz")
+	 (call-process "gunzip" nil t nil "--stdout" name))
+        ((or (equal (substring name -2) ".Z")
+	     ;; HPUX uses directory names that end in .Z and compressed
+	     ;; files that don't.  How gratuitously random.
+             (let ((case-fold-search nil))
+               (string-match "\\.Z/" name)))
+	 (call-process "zcat" name t nil)) ;; XEmacs change for HPUX
+	((equal (substring name -2) ".z")
+	 (call-process "pcat" nil t nil name))
+	(t
+	 (insert-file-contents name))))
+
+(defmacro Manual-delete-char (n)
+  ;; in v19, delete-char is compiled as a function call, but delete-region
+  ;; is byte-coded, so it's much faster.
+  ;; (We were spending 40% of our time in delete-char alone.)
+  (list 'delete-region '(point) (list '+ '(point) n)))
+
+;; Hint: BS stands for more things than "back space"
+(defun Manual-nuke-nroff-bs (&optional apropos-mode)
+  (interactive "*")
+  (if Manual-use-rosetta-man
+      (call-process-region (point-min) (point-max) "rman" t t nil)
+    ;;
+    ;; turn underlining into italics
+    ;;
+    (goto-char (point-min))
+    (while (search-forward "_\b" nil t)
+      ;; searching for underscore-backspace and then comparing the following
+      ;; chars until the sequence ends turns out to be much faster than searching
+      ;; for a regexp which matches the whole sequence.
+      (let ((s (match-beginning 0)))
+	(goto-char s)
+	(while (and (= (following-char) ?_)
+		    (= (char-after (1+ (point))) ?\b))
+	  (Manual-delete-char 2)
+	  (forward-char 1))
+	(set-extent-face (make-extent s (point)) 'man-italic)))
+    ;;
+    ;; turn overstriking into bold
+    ;;
+    (goto-char (point-min))
+    (while (re-search-forward "\\([^\n]\\)\\(\b\\1\\)" nil t)
+      ;; Surprisingly, searching for the above regexp is faster than searching
+      ;; for a backspace and then comparing the preceding and following chars,
+      ;; I presume because there are many false matches, meaning more funcalls
+      ;; to re-search-forward.
+      (let ((s (match-beginning 0)))
+	(goto-char s)
+	;; Some systems (SGI) overstrike multiple times, eg, "M\bM\bM\bM".
+	(while (looking-at "\\([^\n]\\)\\(\b\\1\\)+")
+	  (delete-region (+ (point) 1) (match-end 0))
+	  (forward-char 1))
+	(set-extent-face (make-extent s (point)) 'man-bold)))
+    ;;
+    ;; hack bullets: o^H+ --> +
+    (goto-char (point-min))
+    (while (search-forward "\b" nil t)
+      (Manual-delete-char -2))
+
+    (if (> (buffer-size) 100) ; minor kludge
+	(Manual-nuke-nroff-bs-footers))
+    ) ;; not Manual-use-rosetta-man
+  ;;
+  ;; turn subsection header lines into bold
+  ;;
+  (goto-char (point-min))
+  (if apropos-mode
+      (while (re-search-forward "[a-zA-Z0-9] ([0-9]" nil t)
+	(forward-char -2)
+	(delete-backward-char 1))
+
+    ;;    (while (re-search-forward "^[^ \t\n]" nil t)
+    ;;      (set-extent-face (make-extent (match-beginning 0)
+    ;;                                   (progn (end-of-line) (point)))
+    ;;                      'man-heading))
+
+    ;; boldface the first line
+    (if (looking-at "[^ \t\n].*$")
+	(set-extent-face (make-extent (match-beginning 0) (match-end 0))
+			 'man-bold))
+
+    ;; boldface subsequent title lines
+    ;; Regexp to match section headers changed to match a non-indented
+    ;; line preceded by a blank line and followed by an indented line. 
+    ;; This seems to work ok for manual pages but gives better results
+    ;; with other nroff'd files
+    (while (re-search-forward "\n\n\\([^ \t\n].*\\)\n[ \t]+[^ \t\n]" nil t)
+      (goto-char (match-end 1))
+      (set-extent-face (make-extent (match-beginning 1) (match-end 1))
+		       'man-heading)
+      (forward-line 1))
+    )
+
+  (if Manual-use-rosetta-man
+      nil
+    ;; Zap ESC7,  ESC8, and ESC9
+    ;; This is for Sun man pages like "man 1 csh"
+    (goto-char (point-min))
+    (while (re-search-forward "\e[789]" nil t)
+      (replace-match "")))
+  
+  ;; Nuke blanks lines at start.
+  ;;  (goto-char (point-min))
+  ;;  (skip-chars-forward "\n")
+  ;;  (delete-region (point-min) (point))
+
+  (Manual-mouseify-xrefs)
+  )
+
+(fset 'nuke-nroff-bs 'Manual-nuke-nroff-bs) ; use old name
+
+
+(defun Manual-nuke-nroff-bs-footers ()
+  ;; Nuke headers and footers.
+  ;;
+  ;; nroff assumes pages are 66 lines high.  We assume that, and that the
+  ;; first and last line on each page is expendible.  There is no way to
+  ;; tell the difference between a page break in the middle of a paragraph
+  ;; and a page break between paragraphs (the amount of extra whitespace
+  ;; that nroff inserts is the same in both cases) so this might strip out
+  ;; a blank line were one should remain.  I think that's better than
+  ;; leaving in a blank line where there shouldn't be one.  (Need I say
+  ;; it: FMH.)
+  ;;
+  ;; Note that if nroff spits out error messages, pages will be more than
+  ;; 66 lines high, and we'll lose badly.  That's ok because standard
+  ;; nroff doesn't do any diagnostics, and the "gnroff" wrapper for groff
+  ;; turns off error messages for compatibility.  (At least, it's supposed
+  ;; to.)
+  ;; 
+  (goto-char (point-min))
+  ;; first lose the status output
+  (let ((case-fold-search t))
+    (if (and (not (looking-at "[^\n]*warning"))
+	     (looking-at "Reformatting.*\n"))
+	(delete-region (match-beginning 0) (match-end 0))))
+
+  ;; kludge around a groff bug where it won't keep quiet about some
+  ;; warnings even with -Wall or -Ww.
+  (cond ((looking-at "grotty:")
+	 (while (looking-at "grotty:")
+	   (delete-region (point) (progn (forward-line 1) (point))))
+	 (if (looking-at " *done\n")
+	     (delete-region (point) (match-end 0)))))
+
+  (let ((pages '())
+	p)
+    ;; collect the page boundary markers before we start deleting, to make
+    ;; it easier to strip things out without changing the page sizes.
+    (while (not (eobp))
+      (forward-line 66)
+      (setq pages (cons (point-marker) pages)))
+    (setq pages (nreverse pages))
+    (while pages
+      (goto-char (car pages))
+      (set-marker (car pages) nil)
+      ;;
+      ;; The lines are: 3 blank; footer; 6 blank; header; 3 blank.
+      ;; We're in between the previous footer and the following header,
+      ;;
+      ;; First lose 3 blank lines, the header, and then 3 more.
+      ;;
+      (setq p (point))
+      (skip-chars-forward "\n")
+      (delete-region p (point))
+      (and (looking-at "[^\n]+\n\n?\n?\n?")
+	   (delete-region (match-beginning 0) (match-end 0)))
+      ;;
+      ;; Next lose the footer, and the 3 blank lines after, and before it.
+      ;; But don't lose the last footer of the manual entry; that contains
+      ;; the "last change" date, so it's not completely uninteresting.
+      ;; (Actually lose all blank lines before it; sh(1) needs this.)
+      ;;
+      (skip-chars-backward "\n")
+      (beginning-of-line)
+      (if (null (cdr pages))
+	  nil
+	(and (looking-at "[^\n]+\n\n?\n?\n?")
+	     (delete-region (match-beginning 0) (match-end 0))))
+      (setq p (point))
+      (skip-chars-backward "\n")
+      (if (> (- p (point)) 4)
+	  (delete-region (+ 2 (point)) p)
+	(delete-region (1+ (point)) p))
+;      (and (looking-at "\n\n?\n?")
+;	   (delete-region (match-beginning 0) (match-end 0)))
+
+      (setq pages (cdr pages)))
+    ;;
+    ;; Now nuke the extra blank lines at the beginning and end.
+    (goto-char (point-min))
+    (if (looking-at "\n+")
+	(delete-region (match-beginning 0) (match-end 0)))
+    (forward-line 1)
+    (if (looking-at "\n\n+")
+	(delete-region (1+ (match-beginning 0)) (match-end 0)))
+    (goto-char (point-max))
+    (skip-chars-backward "\n")
+    (delete-region (point) (point-max))
+    (beginning-of-line)
+    (forward-char -1)
+    (setq p (point))
+    (skip-chars-backward "\n")
+    (if (= ?\n (following-char)) (forward-char 1))
+    (if (> (point) (1+ p))
+	(delete-region (point) p))
+    ))
+
+;(defun Manual-nuke-nroff-bs-footers ()
+;  ;; Nuke headers: "MORE(1) UNIX Programmer's Manual MORE(1)"
+;  (goto-char (point-min))
+;  (while (re-search-forward "^ *\\([A-Za-z][-_A-Za-z0-9]*([0-9A-Za-z]+)\\).*\\1$" nil t)
+;    (replace-match ""))
+;  
+;  ;;
+;  ;; it would appear that we have a choice between sometimes introducing
+;  ;; an extra blank line when a paragraph was broken by a footer, and
+;  ;; sometimes not putting in a blank line between two paragraphs when
+;  ;; a footer appeared right between them.  FMH; I choose the latter.
+;  ;;
+;
+;  ;; Nuke footers: "Printed 12/3/85	27 April 1981	1"
+;  ;;    Sun appear to be on drugz:
+;  ;;     "Sun Release 3.0B  Last change: 1 February 1985     1"
+;  ;;    HP are even worse!
+;  ;;     "     Hewlett-Packard   -1- (printed 12/31/99)"  FMHWA12ID!!
+;  ;;    System V (well WICATs anyway):
+;  ;;     "Page 1			  (printed 7/24/85)"
+;  ;;    Who is administering PCP to these corporate bozos?
+;  (goto-char (point-min))
+;  (while (re-search-forward
+;	   (cond
+;	    ((eq system-type 'hpux)
+;	     "\n\n?[ \t]*Hewlett-Packard\\(\\| Company\\)[ \t]*- [0-9]* -.*\n")
+;	    ((eq system-type 'dgux-unix)
+;	     "\n\n?[ \t]*Licensed material--.*Page [0-9]*\n")
+;	    ((eq system-type 'usg-unix-v)
+;	     "\n\n? *Page [0-9]*.*(printed [0-9/]*)\n")
+;	    (t
+;	     "\n\n?\\(Printed\\|Sun Release\\) [0-9].*[0-9]\n"))
+;	   nil t)
+;    (replace-match ""))
+;
+;  ;;    Also, hack X footers:
+;  ;;     "X Version 11         Last change: Release 5         1"
+;  (goto-char (point-min))
+;  (while (re-search-forward "\n\n?X Version [^\n]+\n" nil t)
+;    (replace-match ""))
+;
+;  ;; Crunch blank lines
+;  (goto-char (point-min))
+;  (while (re-search-forward "\n\n\n\n*" nil t)
+;    (replace-match "\n\n"))
+;  )
+
+(defun Manual-mouseify-xrefs ()
+  (goto-char (point-min))
+  (forward-line 1)
+  (let ((case-fold-search nil)
+	s e name extent)
+    ;; possibly it would be faster to rewrite this expression to search for
+    ;; a less common sequence first (like "([0-9]") and then back up to see
+    ;; if it's really a match.  This function is 15% of the total time, 13%
+    ;; of which is this call to re-search-forward.
+    (while (re-search-forward "[a-zA-Z_][-a-zA-Z0-9_.]*([0-9][a-zA-Z0-9]*)"
+			      nil t)
+      (setq s (match-beginning 0)
+	    e (match-end 0)
+	    name (buffer-substring s e))
+      (goto-char s)
+      (skip-chars-backward " \t")
+      (if (and (bolp)
+	       (progn (backward-char 1) (= (preceding-char) ?-)))
+	  (progn
+	    (setq s (point))
+	    (skip-chars-backward "-a-zA-Z0-9_.")
+	    (setq name (concat (buffer-substring (point) (1- s)) name))
+	    (setq s (point))))
+      ;; if there are upper case letters in the section, downcase them.
+      (if (string-match "(.*[A-Z]+.*)$" name)
+	  (setq name (concat (substring name 0 (match-beginning 0))
+			     (downcase (substring name (match-beginning 0))))))
+      ;; (setq already-fontified (extent-at s))
+      (setq extent (make-extent s e))
+      (set-extent-property extent 'man (list 'Manual-follow-xref name))
+      (set-extent-property extent 'highlight t)
+      ;; (if (not already-fontified)...
+      (set-extent-face extent 'man-xref)
+      (goto-char e))))
+
+(defun Manual-follow-xref (&optional name-or-event)
+  "Invoke `manual-entry' on the cross-reference under the mouse.
+When invoked noninteractively, the arg may be an xref string to parse instead."
+  (interactive "e")
+  (if (eventp name-or-event)
+      (let* ((p (event-point name-or-event))
+	     (extent (and p (extent-at p
+			     (event-buffer name-or-event)
+			     'highlight)))
+	     (data (and extent (extent-property extent 'man))))
+	(if (eq (car-safe data) 'Manual-follow-xref)
+	    (eval data)
+	  (error "no manual cross-reference there.")))
+    (let ((Manual-match-topic-exactly t)
+	  (Manual-query-multiple-pages nil))
+      (or (manual-entry name-or-event)
+	  ;; If that didn't work, maybe it's in a different section than the
+	  ;; man page writer expected.  For example, man pages tend assume
+	  ;; that all user programs are in section 1, but X tends to generate
+	  ;; makefiles that put things in section "n" instead...
+	  (and (string-match "[ \t]*([^)]+)\\'" name-or-event)
+	       (progn
+		 (message "No entries found for %s; checking other sections..."
+			  name-or-event)
+		 (manual-entry
+		  (substring name-or-event 0 (match-beginning 0))
+		  nil t)))))))
+
+(defun Manual-popup-menu (&optional event)
+  "Pops up a menu of cross-references in this manual page.
+If there is a cross-reference under the mouse button which invoked this
+command, it will be the first item on the menu.  Otherwise, they are
+on the menu in the order in which they appear in the buffer."
+  (interactive "e")
+  (let ((buffer (current-buffer))
+	(sep "---")
+	(prefix "Show Manual Page for ")
+	xref items)
+    (cond (event
+	   (setq buffer (event-buffer event))
+	   (let* ((p (event-point event))
+		  (extent (and p (extent-at p buffer 'highlight)))
+		  (data (and extent (extent-property extent 'man))))
+	     (if (eq (car-safe data) 'Manual-follow-xref)
+		 (setq xref (nth 1 data))))))
+    (if xref (setq items (list sep xref)))
+    (map-extents #'(lambda (extent ignore)
+		     (let ((data (extent-property extent 'man)))
+		       (if (and (eq (car-safe data) 'Manual-follow-xref)
+				(not (member (nth 1 data) items)))
+			   (setq items (cons (nth 1 data) items)))
+		    nil))
+		 buffer)
+    (if (eq sep (car items)) (setq items (cdr items)))
+    (let ((popup-menu-titles nil))
+      (popup-menu
+       (cons "Manual Entry"
+	     (mapcar #'(lambda (item)
+			 (if (eq item sep)
+			     item
+                           (vector (concat prefix item)
+                                   (list 'Manual-follow-xref item) t)))
+		     (nreverse items)))))))
+
+(defun pager-cleanup-hook ()
+  "cleanup man page if called via $PAGER"
+  (let ((buf-name (or buffer-file-name (buffer-name))))
+	(if (and (or (string-match "^/tmp/man[0-9]+" buf-name)
+		     (string-match ".*/man/\\(man\\|cat\\)[1-9a-z]/" buf-name))
+		 (not (string-match Manual-bogus-file-pattern buf-name)))
+	    (let (buffer manpage)
+	      (require 'man)
+	      (goto-char (point-min))
+	      (setq buffer-read-only nil)
+	      (Manual-nuke-nroff-bs)
+	      (goto-char (point-min))
+	      (if (re-search-forward "[^ \t]")
+		  (goto-char (- (point) 1)))
+	      (if (looking-at "\\([a-zA-Z0-9]+\\)[ \t]*(")
+		  (setq manpage (buffer-substring (match-beginning 1) (match-end 1)))
+		(setq manpage "???"))
+	      (setq buffer
+		    (rename-buffer
+		     (generate-new-buffer-name (concat "*man " manpage "*"))))
+	      (setq buffer-file-name nil)
+	      (goto-char (point-min))
+	      (insert (format "%s\n" buf-name))
+	      (goto-char (point-min))
+	      (buffer-disable-undo buffer)
+	      (set-buffer-modified-p nil)
+	      (Manual-mode)
+	      ))))
+
+(add-hook 'server-visit-hook 'pager-cleanup-hook)
+(provide 'man)