(gnus-group-set-mode-line): Allow user defined specs in the group
[bpt/emacs.git] / lisp / man.el
CommitLineData
effdc6a2 1;;; man.el --- browse UNIX manual pages
6594deb0 2
d733c5ec 3;; Copyright (C) 1993, 1994 Free Software Foundation, Inc.
9750e079 4
effdc6a2 5;; Author: Barry A. Warsaw <bwarsaw@cen.com>
effdc6a2 6;; Keywords: help
b3435a2f 7;; Adapted-By: ESR, pot
e5167999 8
1a20f48d
JA
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
e5167999 13;; the Free Software Foundation; either version 2, or (at your option)
1a20f48d
JA
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to
23;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
edbd2f74
ER
25;;; Commentary:
26
b3435a2f
FP
27;; This code provides a function, `man', with which you can browse
28;; UNIX manual pages. Formatting is done in background so that you
29;; can continue to use your Emacs while processing is going on.
effdc6a2
RS
30;;
31;; The mode also supports hypertext-like following of manual page SEE
32;; ALSO references, and other features. See below or do `?' in a
33;; manual page buffer for details.
34
35;; ========== Credits and History ==========
36;; In mid 1991, several people posted some interesting improvements to
37;; man.el from the standard emacs 18.57 distribution. I liked many of
38;; these, but wanted everthing in one single package, so I decided
b3435a2f 39;; to incorporate them into a single manual browsing mode. While
effdc6a2
RS
40;; much of the code here has been rewritten, and some features added,
41;; these folks deserve lots of credit for providing the initial
42;; excellent packages on which this one is based.
43
44;; Nick Duffek <duffek@chaos.cs.brandeis.edu>, posted a very nice
45;; improvement which retrieved and cleaned the manpages in a
46;; background process, and which correctly deciphered such options as
47;; man -k.
48
49;; Eric Rose <erose@jessica.stanford.edu>, submitted manual.el which
50;; provided a very nice manual browsing mode.
51
eb8c3be9 52;; This package was available as `superman.el' from the LCD package
effdc6a2
RS
53;; for some time before it was accepted into Emacs 19. The entry
54;; point and some other names have been changed to make it a drop-in
55;; replacement for the old man.el package.
56
b3435a2f
FP
57;; Francesco Potorti` <pot@cnuce.cnr.it> cleaned it up thoroughly,
58;; making it faster, more robust and more tolerant of different
59;; systems' man idiosynchrasies.
60
effdc6a2
RS
61;; ========== Features ==========
62;; + Runs "man" in the background and pipes the results through a
63;; series of sed and awk scripts so that all retrieving and cleaning
64;; is done in the background. The cleaning commands are configurable.
65;; + Syntax is the same as Un*x man
66;; + Functionality is the same as Un*x man, including "man -k" and
c3343fcf 67;; "man <section>", etc.
effdc6a2
RS
68;; + Provides a manual browsing mode with keybindings for traversing
69;; the sections of a manpage, following references in the SEE ALSO
70;; section, and more.
71;; + Multiple manpages created with the same man command are put into
72;; a narrowed buffer circular list.
edbd2f74 73
b3435a2f
FP
74;; ============= TODO ===========
75;; - Add a command for printing.
76;; - The awk script deletes multiple blank lines. This behaviour does
77;; not allow to understand if there was indeed a blank line at the
78;; end or beginning of a page (after the header, or before the
79;; footer). A different algorithm should be used. It is easy to
80;; compute how many blank lines there are before and after the page
81;; headers, and after the page footer. But it is possible to compute
82;; the number of blank lines before the page footer by euristhics
83;; only. Is it worth doing?
98fd7017
FP
84;; - Allow a user option to mean that all the manpages should go in
85;; the same buffer, where they can be browsed with M-n and M-p.
b3435a2f
FP
86;; - Allow completion on the manpage name when calling man. This
87;; requires a reliable list of places where manpages can be found. The
88;; drawback would be that if the list is not complete, the user might
89;; be led to believe that the manpages in the missing directories do
90;; not exist.
91
d0aede3f 92\f
e5167999
ER
93;;; Code:
94
effdc6a2 95(require 'assoc)
effdc6a2 96
d0aede3f
FP
97;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
98;; empty defvars (keep the compiler quiet)
99
100(defvar Man-notify)
101(defvar Man-current-page)
102(defvar Man-page-list)
103(defvar Man-filter-list)
104(defvar Man-original-frame)
105(defvar Man-arguments)
d0aede3f
FP
106(defvar Man-sections-alist)
107(defvar Man-refpages-alist)
108(defvar Man-uses-untabify-flag)
109(defvar Man-page-mode-string)
110(defvar Man-sed-script)
111
effdc6a2
RS
112;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
113;; user variables
114
6e7e47f6
FP
115(defvar Man-fontify-manpage-flag t
116 "*Make up the manpage with fonts.")
117
118(defvar Man-overstrike-face 'bold
119 "*Face to use when fontifying overstrike.")
120
121(defvar Man-underline-face 'underline
b0d77652 122 "*Face to use when fontifying underlining.")
2788b7c9 123
b3435a2f 124;; Use the value of the obsolete user option Man-notify, if set.
799ac634 125(defvar Man-notify-method (if (boundp 'Man-notify) Man-notify 'friendly)
effdc6a2 126 "*Selects the behavior when manpage is ready.
b3435a2f
FP
127This variable may have one of the following values, where (sf) means
128that the frames are switched, so the manpage is displayed in the frame
129where the man command was called from:
effdc6a2 130
b51aeeff 131newframe -- put the manpage in its own frame (see `Man-frame-parameters')
b3435a2f
FP
132pushy -- make the manpage the current buffer in the current window
133bully -- make the manpage the current buffer and only window (sf)
134aggressive -- make the manpage the current buffer in the other window (sf)
135friendly -- display manpage in the other window but don't make current (sf)
136polite -- don't display manpage, but prints message and beep when ready
effdc6a2 137quiet -- like `polite', but don't beep
b3435a2f 138meek -- make no indication that the manpage is ready
effdc6a2 139
799ac634 140Any other value of `Man-notify-method' is equivalent to `meek'.")
effdc6a2 141
b51aeeff
RS
142(defvar Man-frame-parameters nil
143 "*Frame parameter list for creating a new frame for a manual page.")
144
b3435a2f 145(defvar Man-downcase-section-letters-flag t
effdc6a2
RS
146 "*Letters in sections are converted to lower case.
147Some Un*x man commands can't handle uppercase letters in sections, for
148example \"man 2V chmod\", but they are often displayed in the manpage
c3343fcf 149with the upper case letter. When this variable is t, the section
effdc6a2
RS
150letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before
151being sent to the man background process.")
152
b3435a2f 153(defvar Man-circular-pages-flag t
effdc6a2
RS
154 "*If t, the manpage list is treated as circular for traversal.")
155
effdc6a2 156(defvar Man-section-translations-alist
b3435a2f
FP
157 (list
158 '("3C++" . "3")
159 ;; Some systems have a real 3x man section, so let's comment this.
160 ;; '("3X" . "3") ; Xlib man pages
161 '("3X11" . "3")
162 '("1-UCB" . ""))
effdc6a2
RS
163 "*Association list of bogus sections to real section numbers.
164Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in
c3343fcf 165their references which Un*x `man' does not recognize. This
eb8c3be9 166association list is used to translate those sections, when found, to
effdc6a2
RS
167the associated section number.")
168
6e7e47f6
FP
169(defvar manual-program "man"
170 "The name of the program that produces man pages.")
171
b3435a2f 172(defvar Man-untabify-command "pr"
6e7e47f6 173 "Command used for untabifying.")
b3435a2f
FP
174
175(defvar Man-untabify-command-args (list "-t" "-e")
6e7e47f6 176 "List of arguments to be passed to Man-untabify-command (which see).")
b3435a2f
FP
177
178(defvar Man-sed-command "sed"
6e7e47f6 179 "Command used for processing sed scripts.")
b3435a2f
FP
180
181(defvar Man-awk-command "awk"
6e7e47f6 182 "Command used for processing awk scripts.")
effdc6a2
RS
183
184(defvar Man-mode-line-format
185 '("" mode-line-modified
b3435a2f 186 mode-line-buffer-identification " "
effdc6a2 187 global-mode-string
056a5ef3 188 " " Man-page-mode-string
b3435a2f 189 " %[(" mode-name mode-line-process minor-mode-alist ")%]----"
effdc6a2 190 (-3 . "%p") "-%-")
6e7e47f6 191 "Mode line format for manual mode buffer.")
effdc6a2
RS
192
193(defvar Man-mode-map nil
6e7e47f6 194 "Keymap for Man mode.")
effdc6a2 195
e660d0db 196(defvar Man-mode-hook nil
6e7e47f6 197 "Hook run when Man mode is enabled.")
effdc6a2 198
c2d606f4 199(defvar Man-cooked-hook nil
6e7e47f6 200 "Hook run after removing backspaces but before Man-mode processing.")
b3435a2f
FP
201
202(defvar Man-name-regexp "[-a-zA-Z0-9_][-a-zA-Z0-9_.]*"
6e7e47f6 203 "Regular expression describing the name of a manpage (without section).")
c2d606f4 204
f5f76002 205(defvar Man-section-regexp "[0-9][a-zA-Z+]*\\|[LNln]"
6e7e47f6 206 "Regular expression describing a manpage section within parentheses.")
effdc6a2 207
b3435a2f
FP
208(defvar Man-page-header-regexp
209 (concat "^[ \t]*\\(" Man-name-regexp
210 "(\\(" Man-section-regexp "\\))\\).*\\1")
6e7e47f6 211 "Regular expression describing the heading of a page.")
b3435a2f
FP
212
213(defvar Man-heading-regexp "^\\([A-Z][A-Z ]+\\)$"
6e7e47f6 214 "Regular expression describing a manpage heading entry.")
effdc6a2
RS
215
216(defvar Man-see-also-regexp "SEE ALSO"
6e7e47f6 217 "Regular expression for SEE ALSO heading (or your equivalent).
effdc6a2
RS
218This regexp should not start with a `^' character.")
219
2cd4790e 220(defvar Man-first-heading-regexp "^[ \t]*NAME$\\|^[ \t]*No manual entry fo.*$"
6e7e47f6 221 "Regular expression describing first heading on a manpage.
effdc6a2
RS
222This regular expression should start with a `^' character.")
223
3eedeb85 224(defvar Man-reference-regexp
b3435a2f 225 (concat "\\(" Man-name-regexp "\\)(\\(" Man-section-regexp "\\))")
6e7e47f6 226 "Regular expression describing a reference in the SEE ALSO section.")
effdc6a2 227
733155db 228(defvar Man-switches ""
6e7e47f6 229 "Switches passed to the man command, as a single string.")
effdc6a2 230
e660d0db
RS
231(defvar Man-specified-section-option
232 (if (string-match "-solaris[0-9.]*$" system-configuration)
233 "-s"
234 "")
6e7e47f6 235 "Option that indicates a specified a manual section name.")
9de0760c 236
effdc6a2
RS
237;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
238;; end user variables
effdc6a2
RS
239\f
240;; other variables and keymap initializations
241(make-variable-buffer-local 'Man-sections-alist)
242(make-variable-buffer-local 'Man-refpages-alist)
243(make-variable-buffer-local 'Man-page-list)
244(make-variable-buffer-local 'Man-current-page)
245(make-variable-buffer-local 'Man-page-mode-string)
75db9a64 246(make-variable-buffer-local 'Man-original-frame)
b3435a2f 247(make-variable-buffer-local 'Man-arguments)
effdc6a2
RS
248
249(setq-default Man-sections-alist nil)
250(setq-default Man-refpages-alist nil)
251(setq-default Man-page-list nil)
252(setq-default Man-current-page 0)
b3435a2f 253(setq-default Man-page-mode-string "1 of 1")
effdc6a2 254
6e7e47f6
FP
255(defconst Man-sysv-sed-script "\
256/\b/ { s/_\b//g
257 s/\b_//g
258 s/o\b+/o/g
f90531cc 259 s/+\bo/o/g
6e7e47f6
FP
260 :ovstrk
261 s/\\(.\\)\b\\1/\\1/g
262 t ovstrk
263 }
264/\e\\[[0-9][0-9]*m/ s///g"
265 "Script for sysV-like sed to nuke backspaces and ANSI codes from manpages.")
266
267(defconst Man-berkeley-sed-script "\
268/\b/ { s/_\b//g\\
269 s/\b_//g\\
270 s/o\b+/o/g\\
f90531cc 271 s/+\bo/o/g\\
6e7e47f6
FP
272 :ovstrk\\
273 s/\\(.\\)\b\\1/\\1/g\\
274 t ovstrk\\
275 }\\
276/\e\\[[0-9][0-9]*m/ s///g"
277 "Script for berkeley-like sed to nuke backspaces and ANSI codes from manpages.")
278
effdc6a2
RS
279(if Man-mode-map
280 nil
281 (setq Man-mode-map (make-keymap))
282 (suppress-keymap Man-mode-map)
283 (define-key Man-mode-map " " 'scroll-up)
284 (define-key Man-mode-map "\177" 'scroll-down)
285 (define-key Man-mode-map "n" 'Man-next-section)
286 (define-key Man-mode-map "p" 'Man-previous-section)
287 (define-key Man-mode-map "\en" 'Man-next-manpage)
288 (define-key Man-mode-map "\ep" 'Man-previous-manpage)
b3435a2f
FP
289 (define-key Man-mode-map ">" 'end-of-buffer)
290 (define-key Man-mode-map "<" 'beginning-of-buffer)
291 (define-key Man-mode-map "." 'beginning-of-buffer)
effdc6a2
RS
292 (define-key Man-mode-map "r" 'Man-follow-manual-reference)
293 (define-key Man-mode-map "t" 'toggle-truncate-lines)
294 (define-key Man-mode-map "g" 'Man-goto-section)
295 (define-key Man-mode-map "s" 'Man-goto-see-also-section)
b3435a2f 296 (define-key Man-mode-map "k" 'Man-kill)
effdc6a2 297 (define-key Man-mode-map "q" 'Man-quit)
b3435a2f 298 (define-key Man-mode-map "m" 'man)
effdc6a2
RS
299 (define-key Man-mode-map "?" 'describe-mode)
300 )
301
302\f
303;; ======================================================================
304;; utilities
305
b3435a2f
FP
306(defsubst Man-init-defvars ()
307 "Used for initialising variables based on the value of window-system.
308This is necessary if one wants to dump man.el with emacs."
309
b3435a2f
FP
310 ;; The following is necessary until fonts are implemented on
311 ;; terminals.
312 (setq Man-fontify-manpage-flag (and Man-fontify-manpage-flag
313 window-system))
314
315 (defconst Man-uses-untabify-flag t
316 ;; don't use pr: it is buggy
317 ;; (or (not (file-readable-p "/etc/passwd"))
318 ;; (/= 0 (apply 'call-process
319 ;; Man-untabify-command nil nil nil
320 ;; (append Man-untabify-command-args
321 ;; (list "/etc/passwd")))))
d0aede3f 322 "When non-nil use `untabify' instead of Man-untabify-command.")
b3435a2f
FP
323
324 (defconst Man-sed-script
325 (cond
326 (Man-fontify-manpage-flag
327 nil)
328 ((= 0 (call-process Man-sed-command nil nil nil Man-sysv-sed-script))
329 Man-sysv-sed-script)
330 ((= 0 (call-process Man-sed-command nil nil nil Man-berkeley-sed-script))
331 Man-berkeley-sed-script)
332 (t
333 nil))
334 "Script for sed to nuke backspaces and ANSI codes from manpages.")
335
336 (defvar Man-filter-list
337 (list
338 (cons
339 Man-sed-command
340 (list
341 (if Man-sed-script
342 (concat "-e '" Man-sed-script "'")
343 "")
2b5de615 344 "-e '/^[\001-\032][\001-\032]*$/d'"
b3435a2f 345 "-e '/\e[789]/s///g'"
b3435a2f
FP
346 "-e '/Reformatting page. Wait/d'"
347 "-e '/Reformatting entry. Wait/d'"
348 "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'"
349 "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'"
350 "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'"
351 "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'"
352 "-e '/^Printed[ \t][0-9].*[0-9]$/d'"
353 "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'"
354 "-e '/^[A-za-z].*Last[ \t]change:/d'"
355 "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'"
f7046d47 356 "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'"
b3435a2f
FP
357 ))
358 (cons
359 Man-awk-command
360 (list
361 "'\n"
362 "BEGIN { blankline=0; anonblank=0; }\n"
363 "/^$/ { if (anonblank==0) next; }\n"
364 "{ anonblank=1; }\n"
365 "/^$/ { blankline++; next; }\n"
366 "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n"
367 "'"
368 ))
369 (if (not Man-uses-untabify-flag)
370 (cons
371 Man-untabify-command
372 Man-untabify-command-args)
373 ))
374 "*Manpage cleaning filter command phrases.
375This variable contains a list of the following form:
376
377'((command-string phrase-string*)*)
effdc6a2 378
b3435a2f
FP
379Each phrase-string is concatenated onto the command-string to form a
380command filter. The (standard) output (and standard error) of the Un*x
381man command is piped through each command filter in the order the
382commands appear in the association list. The final output is placed in
383the manpage buffer.")
384)
effdc6a2 385
d0aede3f
FP
386(defsubst Man-match-substring (&optional n string)
387 "Return the substring matched by the last search.
388Optional arg N means return the substring matched by the Nth paren
389grouping. Optional second arg STRING means return a substring from
390that string instead of from the current buffer."
391 (if (null n) (setq n 0))
392 (if string
393 (substring string (match-beginning n) (match-end n))
394 (buffer-substring (match-beginning n) (match-end n))))
395
b3435a2f
FP
396(defsubst Man-make-page-mode-string ()
397 "Formats part of the mode line for Man mode."
398 (format "%s page %d of %d"
399 (or (nth 2 (nth (1- Man-current-page) Man-page-list))
400 "")
401 Man-current-page
402 (length Man-page-list)))
403
404(defsubst Man-build-man-command ()
effdc6a2 405 "Builds the entire background manpage and cleaning command."
17077ab3 406 (let ((command (concat manual-program " " Man-switches " %s 2>/dev/null"))
effdc6a2 407 (flist Man-filter-list))
b3435a2f 408 (while (and flist (car flist))
effdc6a2 409 (let ((pcom (car (car flist)))
b3435a2f
FP
410 (pargs (cdr (car flist))))
411 (setq command
412 (concat command " | " pcom " "
413 (mapconcat '(lambda (phrase)
414 (if (not (stringp phrase))
415 (error "Malformed Man-filter-list"))
416 phrase)
417 pargs " ")))
418 (setq flist (cdr flist))))
effdc6a2
RS
419 command))
420
effdc6a2 421(defun Man-translate-references (ref)
b3435a2f
FP
422 "Translates REF from \"chmod(2V)\" to \"2v chmod\" style.
423Leave it as is if already in that style. Possibly downcase and
424translate the section (see the Man-downcase-section-letters-flag
2f2228c0 425and the Man-section-translations-alist variables)."
b3435a2f
FP
426 (let ((name "")
427 (section "")
428 (slist Man-section-translations-alist))
effdc6a2 429 (cond
b3435a2f 430 ;; "chmod(2V)" case ?
2f2228c0 431 ((string-match (concat "^" Man-reference-regexp "$") ref)
b3435a2f
FP
432 (setq name (Man-match-substring 1 ref)
433 section (Man-match-substring 2 ref)))
434 ;; "2v chmod" case ?
2f2228c0 435 ((string-match (concat "^\\(" Man-section-regexp
b3435a2f
FP
436 "\\) +\\(" Man-name-regexp "\\)$") ref)
437 (setq name (Man-match-substring 2 ref)
438 section (Man-match-substring 1 ref))))
439 (if (string= name "")
440 ref ; Return the reference as is
441 (if Man-downcase-section-letters-flag
442 (setq section (downcase section)))
443 (while slist
444 (let ((s1 (car (car slist)))
445 (s2 (cdr (car slist))))
446 (setq slist (cdr slist))
447 (if Man-downcase-section-letters-flag
448 (setq s1 (downcase s1)))
449 (if (not (string= s1 section)) nil
450 (setq section (if Man-downcase-section-letters-flag
451 (downcase s2)
452 s2)
453 slist nil))))
454 (concat Man-specified-section-option section " " name))))
455
effdc6a2
RS
456\f
457;; ======================================================================
b3435a2f 458;; default man entry: get word under point
effdc6a2 459
b3435a2f 460(defsubst Man-default-man-entry ()
effdc6a2
RS
461 "Make a guess at a default manual entry.
462This guess is based on the text surrounding the cursor, and the
c3343fcf 463default section number is selected from `Man-auto-section-alist'."
d902a611 464 (let (default-title)
1a20f48d 465 (save-excursion
effdc6a2 466
b3435a2f
FP
467 ;; Default man entry title is any word the cursor is on, or if
468 ;; cursor not on a word, then nearest preceding word. Cannot
469 ;; use the current-word function because it skips the dots.
470 (if (not (looking-at "[-a-zA-Z_.]"))
471 (skip-chars-backward "^a-zA-Z"))
472 (skip-chars-backward "-(a-zA-Z_0-9_.")
473 (if (looking-at "(") (forward-char 1))
effdc6a2
RS
474 (setq default-title
475 (buffer-substring
476 (point)
b3435a2f 477 (progn (skip-chars-forward "-a-zA-Z0-9_.") (point))))
effdc6a2 478
b3435a2f 479 ;; If looking at something like ioctl(2) or brc(1M), include the
c448c134
FP
480 ;; section number in the returned value. Remove text properties.
481 (let ((result (concat
482 default-title
483 (if (looking-at
484 (concat "[ \t]*([ \t]*\\("
485 Man-section-regexp "\\)[ \t]*)"))
486 (format "(%s)" (Man-match-substring 1))))))
487 (set-text-properties 0 (length result) nil result)
0006272a 488 result))))
b3435a2f 489
effdc6a2
RS
490\f
491;; ======================================================================
b3435a2f 492;; Top level command and background process sentinel
1a20f48d 493
b3435a2f 494;; For compatibility with older versions.
cac0b95d 495;;;###autoload
b3435a2f 496(defalias 'manual-entry 'man)
cac0b95d 497
effdc6a2 498;;;###autoload
98fd7017 499(defun man (man-args)
effdc6a2 500 "Get a Un*x manual page and put it in a buffer.
c3343fcf 501This command is the top-level command in the man package. It runs a Un*x
effdc6a2 502command to retrieve and clean a manpage in the background and places the
c3343fcf 503results in a Man mode (manpage browsing) buffer. See variable
799ac634 504`Man-notify-method' for what happens when the buffer is ready.
98fd7017 505If a buffer already exists for this man page, it will display immediately."
b3435a2f 506 (interactive
98fd7017
FP
507 (list (let* ((default-entry (Man-default-man-entry))
508 (input (read-string
509 (format "Manual entry%s: "
510 (if (string= default-entry "")
511 ""
512 (format " (default %s)" default-entry))))))
513 (if (string= input "")
514 (if (string= default-entry "")
515 (error "No man args given")
516 default-entry)
517 input))))
b3435a2f
FP
518
519 ;; Init the man package variables, if not already done.
520 (Man-init-defvars)
521
522 ;; Possibly translate the "subject(section)" syntax into the
523 ;; "section subject" syntax and possibly downcase the section.
524 (setq man-args (Man-translate-references man-args))
525
98fd7017 526 (Man-getpage-in-background man-args))
b3435a2f 527
aa228418 528
98fd7017
FP
529(defun Man-getpage-in-background (topic)
530 "Uses TOPIC to build and fire off the manpage and cleaning command."
e660d0db 531 (let* ((man-args topic)
d0aede3f 532 (bufname (concat "*Man " man-args "*"))
effdc6a2 533 (buffer (get-buffer bufname)))
98fd7017 534 (if buffer
effdc6a2 535 (Man-notify-when-ready buffer)
eaf1946c 536 (require 'env)
b3435a2f 537 (message "Invoking %s %s in the background" manual-program man-args)
effdc6a2 538 (setq buffer (generate-new-buffer bufname))
75db9a64
KH
539 (save-excursion
540 (set-buffer buffer)
b3435a2f
FP
541 (setq Man-original-frame (selected-frame))
542 (setq Man-arguments man-args))
0322056b 543 (let ((process-environment (copy-sequence process-environment)))
82c9fe8e
RS
544 ;; Prevent any attempt to use display terminal fanciness.
545 (setenv "TERM" "dumb")
546 (set-process-sentinel
2788b7c9 547 (start-process manual-program buffer "sh" "-c"
13628415 548 (format (Man-build-man-command) man-args))
98fd7017 549 'Man-bgproc-sentinel)))))
effdc6a2
RS
550
551(defun Man-notify-when-ready (man-buffer)
552 "Notify the user when MAN-BUFFER is ready.
799ac634 553See the variable `Man-notify-method' for the different notification behaviors."
c9bf42f9
RS
554 (let ((saved-frame (save-excursion
555 (set-buffer man-buffer)
556 Man-original-frame)))
557 (cond
799ac634 558 ((eq Man-notify-method 'newframe)
b3435a2f
FP
559 ;; Since we run asynchronously, perhaps while Emacs is waiting
560 ;; for input, we must not leave a different buffer current. We
561 ;; can't rely on the editor command loop to reselect the
562 ;; selected window's buffer.
c9bf42f9
RS
563 (save-excursion
564 (set-buffer man-buffer)
565 (make-frame Man-frame-parameters)))
799ac634 566 ((eq Man-notify-method 'pushy)
b3435a2f 567 (switch-to-buffer man-buffer))
799ac634 568 ((eq Man-notify-method 'bully)
c9bf42f9
RS
569 (and window-system
570 (frame-live-p saved-frame)
571 (select-frame saved-frame))
572 (pop-to-buffer man-buffer)
573 (delete-other-windows))
799ac634 574 ((eq Man-notify-method 'aggressive)
c9bf42f9
RS
575 (and window-system
576 (frame-live-p saved-frame)
577 (select-frame saved-frame))
578 (pop-to-buffer man-buffer))
799ac634 579 ((eq Man-notify-method 'friendly)
c9bf42f9
RS
580 (and window-system
581 (frame-live-p saved-frame)
582 (select-frame saved-frame))
583 (display-buffer man-buffer 'not-this-window))
799ac634 584 ((eq Man-notify-method 'polite)
c9bf42f9 585 (beep)
b3435a2f 586 (message "Manual buffer %s is ready" (buffer-name man-buffer)))
799ac634 587 ((eq Man-notify-method 'quiet)
b3435a2f 588 (message "Manual buffer %s is ready" (buffer-name man-buffer)))
799ac634 589 ((or (eq Man-notify-method 'meek)
c9bf42f9
RS
590 t)
591 (message ""))
592 )))
effdc6a2 593
b3435a2f
FP
594(defun Man-fontify-manpage ()
595 "Convert overstriking and underlining to the correct fonts.
596Same for the ANSI bold and normal escape sequences."
597 (interactive)
598 (message "Please wait: making up the %s man page..." Man-arguments)
c3343fcf 599 (goto-char (point-min))
b3435a2f
FP
600 (while (search-forward "\e[1m" nil t)
601 (delete-backward-char 4)
602 (put-text-property (point)
603 (progn (if (search-forward "\e[0m" nil 'move)
604 (delete-backward-char 4))
605 (point))
6e7e47f6 606 'face Man-overstrike-face))
b3435a2f
FP
607 (goto-char (point-min))
608 (while (search-forward "_\b" nil t)
609 (backward-delete-char 2)
6e7e47f6 610 (put-text-property (point) (1+ (point)) 'face Man-underline-face))
b3435a2f
FP
611 (goto-char (point-min))
612 (while (search-forward "\b_" nil t)
613 (backward-delete-char 2)
6e7e47f6 614 (put-text-property (1- (point)) (point) 'face Man-underline-face))
b3435a2f 615 (goto-char (point-min))
6024daef
FP
616 (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t)
617 (replace-match "\\1")
6e7e47f6 618 (put-text-property (1- (point)) (point) 'face Man-overstrike-face))
6024daef 619 (goto-char (point-min))
f90531cc
FP
620 (while (re-search-forward "o\b\\+\\|\\+\bo" nil t)
621 (replace-match "o")
b3435a2f
FP
622 (put-text-property (1- (point)) (point) 'face 'bold))
623 (goto-char (point-min))
f7046d47 624 (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t)
6024daef 625 (replace-match "+")
b3435a2f 626 (put-text-property (1- (point)) (point) 'face 'bold))
86884919
RS
627 ;; \255 is some kind of dash in Latin-1.
628 (goto-char (point-min))
629 (while (search-forward "\255" nil t) (replace-match "-"))
b3435a2f
FP
630 (message "%s man page made up" Man-arguments))
631
632(defun Man-cleanup-manpage ()
633 "Remove overstriking and underlining from the current buffer."
634 (interactive)
f7046d47
FP
635 (message "Please wait: cleaning up the %s man page..."
636 Man-arguments)
9931e1ba 637 (if (or (interactive-p) (not Man-sed-script))
f7046d47
FP
638 (progn
639 (goto-char (point-min))
640 (while (search-forward "_\b" nil t) (backward-delete-char 2))
641 (goto-char (point-min))
642 (while (search-forward "\b_" nil t) (backward-delete-char 2))
643 (goto-char (point-min))
644 (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t)
645 (replace-match "\\1"))
646 (goto-char (point-min))
647 (while (re-search-forward "\e\\[[0-9]+m" nil t) (replace-match ""))
648 (goto-char (point-min))
f90531cc 649 (while (re-search-forward "o\b\\+\\|\\+\bo" nil t) (replace-match "o"))
f7046d47 650 ))
6024daef 651 (goto-char (point-min))
f7046d47 652 (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) (replace-match "+"))
86884919
RS
653 ;; \255 is some kind of dash in Latin-1.
654 (goto-char (point-min))
655 (while (search-forward "\255" nil t) (replace-match "-"))
b3435a2f 656 (message "%s man page cleaned up" Man-arguments))
c3343fcf 657
effdc6a2
RS
658(defun Man-bgproc-sentinel (process msg)
659 "Manpage background process sentinel."
660 (let ((Man-buffer (process-buffer process))
661 (delete-buff nil)
8c79b392 662 (err-mess nil))
b3435a2f 663
effdc6a2
RS
664 (if (null (buffer-name Man-buffer)) ;; deleted buffer
665 (set-process-buffer process nil)
b3435a2f
FP
666
667 (save-excursion
668 (set-buffer Man-buffer)
1d56cc39
RS
669 (let ((case-fold-search nil))
670 (goto-char (point-min))
671 (cond ((or (looking-at "No \\(manual \\)*entry for")
672 (looking-at "[^\n]*: nothing appropriate$"))
673 (setq err-mess (buffer-substring (point)
674 (progn
675 (end-of-line) (point)))
676 delete-buff t))
677 ((not (and (eq (process-status process) 'exit)
678 (= (process-exit-status process) 0)))
679 (setq err-mess
680 (concat (buffer-name Man-buffer)
681 ": process "
682 (let ((eos (1- (length msg))))
683 (if (= (aref msg eos) ?\n)
684 (substring msg 0 eos) msg))))
685 (goto-char (point-max))
686 (insert (format "\nprocess %s" msg))
687 ))
688 (if delete-buff
689 (kill-buffer Man-buffer)
690 (if Man-fontify-manpage-flag
691 (Man-fontify-manpage)
692 (Man-cleanup-manpage))
693 (run-hooks 'Man-cooked-hook)
694 (Man-mode)
695 (set-buffer-modified-p nil)
696 ))
697 ;; Restore case-fold-search before calling
698 ;; Man-notify-when-ready because it may switch buffers.
699
700 (if (not delete-buff)
701 (Man-notify-when-ready Man-buffer))
702
703 (if err-mess
704 (error err-mess))
705 ))))
effdc6a2
RS
706
707\f
708;; ======================================================================
709;; set up manual mode in buffer and build alists
710
711(defun Man-mode ()
c3343fcf 712 "A mode for browsing Un*x manual pages.
effdc6a2
RS
713
714The following man commands are available in the buffer. Try
715\"\\[describe-key] <key> RET\" for more information:
aa228418 716
b3435a2f 717\\[man] Prompt to retrieve a new manpage.
effdc6a2
RS
718\\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section.
719\\[Man-next-manpage] Jump to next manpage in circular list.
720\\[Man-previous-manpage] Jump to previous manpage in circular list.
721\\[Man-next-section] Jump to next manpage section.
722\\[Man-previous-section] Jump to previous manpage section.
723\\[Man-goto-section] Go to a manpage section.
724\\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section.
b3435a2f
FP
725\\[Man-quit] Deletes the manpage window, bury its buffer.
726\\[Man-kill] Deletes the manpage window, kill its buffer.
effdc6a2
RS
727\\[describe-mode] Prints this help text.
728
729The following variables may be of some use. Try
730\"\\[describe-variable] <variable-name> RET\" for more information:
731
799ac634 732Man-notify-method What happens when manpage formatting is done.
b3435a2f
FP
733Man-downcase-section-letters-flag Force section letters to lower case.
734Man-circular-pages-flag Treat multiple manpage list as circular.
effdc6a2
RS
735Man-auto-section-alist List of major modes and their section numbers.
736Man-section-translations-alist List of section numbers and their Un*x equiv.
737Man-filter-list Background manpage filter command.
e660d0db
RS
738Man-mode-line-format Mode line format for Man mode buffers.
739Man-mode-map Keymap bindings for Man mode buffers.
740Man-mode-hook Normal hook run on entry to Man mode.
effdc6a2
RS
741Man-section-regexp Regexp describing manpage section letters.
742Man-heading-regexp Regexp describing section headers.
743Man-see-also-regexp Regexp for SEE ALSO section (or your equiv).
744Man-first-heading-regexp Regexp for first heading on a manpage.
745Man-reference-regexp Regexp matching a references in SEE ALSO.
733155db 746Man-switches Background `man' command switches.
effdc6a2
RS
747
748The following key bindings are currently in effect in the buffer:
749\\{Man-mode-map}"
750 (interactive)
751 (setq major-mode 'Man-mode
c3343fcf 752 mode-name "Man"
effdc6a2
RS
753 buffer-auto-save-file-name nil
754 mode-line-format Man-mode-line-format
755 truncate-lines t
756 buffer-read-only t)
757 (buffer-disable-undo (current-buffer))
758 (auto-fill-mode -1)
759 (use-local-map Man-mode-map)
effdc6a2 760 (Man-build-page-list)
b3435a2f
FP
761 (Man-strip-page-headers)
762 (Man-unindent)
526504b8
RS
763 (Man-goto-page 1)
764 (run-hooks 'Man-mode-hook))
1a20f48d 765
b3435a2f 766(defsubst Man-build-section-alist ()
effdc6a2
RS
767 "Build the association list of manpage sections."
768 (setq Man-sections-alist nil)
1a20f48d 769 (goto-char (point-min))
2cd4790e
KH
770 (let ((case-fold-search nil))
771 (while (re-search-forward Man-heading-regexp (point-max) t)
b3435a2f 772 (aput 'Man-sections-alist (Man-match-substring 1))
2cd4790e 773 (forward-line 1))))
effdc6a2 774
b3435a2f 775(defsubst Man-build-references-alist ()
effdc6a2
RS
776 "Build the association list of references (in the SEE ALSO section)."
777 (setq Man-refpages-alist nil)
778 (save-excursion
779 (if (Man-find-section Man-see-also-regexp)
780 (let ((start (progn (forward-line 1) (point)))
781 (end (progn
782 (Man-next-section 1)
783 (point)))
784 hyphenated
785 (runningpoint -1))
2cd4790e
KH
786 (save-restriction
787 (narrow-to-region start end)
788 (goto-char (point-min))
789 (back-to-indentation)
790 (while (and (not (eobp)) (/= (point) runningpoint))
791 (setq runningpoint (point))
b3435a2f
FP
792 (if (re-search-forward Man-reference-regexp end t)
793 (let* ((word (Man-match-substring 0))
794 (len (1- (length word))))
795 (if hyphenated
796 (setq word (concat hyphenated word)
797 hyphenated nil))
798 (if (= (aref word len) ?-)
799 (setq hyphenated (substring word 0 len))
800 (aput 'Man-refpages-alist word))))
2cd4790e 801 (skip-chars-forward " \t\n,")))))))
effdc6a2 802
d0aede3f 803(defun Man-build-page-list ()
effdc6a2
RS
804 "Build the list of separate manpages in the buffer."
805 (setq Man-page-list nil)
b3435a2f
FP
806 (let ((page-start (point-min))
807 (page-end (point-max))
808 (header ""))
809 (goto-char page-start)
810 ;; (switch-to-buffer (current-buffer))(debug)
811 (while (not (eobp))
812 (setq header
813 (if (looking-at Man-page-header-regexp)
814 (Man-match-substring 1)
815 nil))
816 ;; Go past both the current and the next Man-first-heading-regexp
817 (if (re-search-forward Man-first-heading-regexp nil 'move 2)
818 (let ((p (progn (beginning-of-line) (point))))
819 ;; We assume that the page header is delimited by blank
820 ;; lines and that it contains at most one blank line. So
821 ;; if we back by three blank lines we will be sure to be
822 ;; before the page header but not before the possible
823 ;; previous page header.
824 (search-backward "\n\n" nil t 3)
825 (if (re-search-forward Man-page-header-regexp p 'move)
826 (beginning-of-line))))
827 (setq page-end (point))
828 (setq Man-page-list (append Man-page-list
829 (list (list (copy-marker page-start)
830 (copy-marker page-end)
831 header))))
832 (setq page-start page-end)
833 )))
834
d0aede3f 835(defun Man-strip-page-headers ()
b3435a2f
FP
836 "Strip all the page headers but the first from the manpage."
837 (let ((buffer-read-only nil)
838 (case-fold-search nil)
839 (page-list Man-page-list)
840 (page ())
841 (header ""))
842 (while page-list
843 (setq page (car page-list))
844 (and (nth 2 page)
845 (goto-char (car page))
846 (re-search-forward Man-first-heading-regexp nil t)
847 (setq header (buffer-substring (car page) (match-beginning 0)))
848 ;; Since the awk script collapses all successive blank
849 ;; lines into one, and since we don't want to get rid of
850 ;; the fast awk script, one must choose between adding
851 ;; spare blank lines between pages when there were none and
852 ;; deleting blank lines at page boundaries when there were
853 ;; some. We choose the first, so we comment the following
854 ;; line.
855 ;; (setq header (concat "\n" header)))
856 (while (search-forward header (nth 1 page) t)
857 (replace-match "")))
858 (setq page-list (cdr page-list)))))
859
d0aede3f 860(defun Man-unindent ()
b3435a2f
FP
861 "Delete the leading spaces that indent the manpage."
862 (let ((buffer-read-only nil)
863 (case-fold-search nil)
864 (page-list Man-page-list))
865 (while page-list
866 (let ((page (car page-list))
867 (indent "")
868 (nindent 0))
869 (narrow-to-region (car page) (car (cdr page)))
870 (if Man-uses-untabify-flag
871 (untabify (point-min) (point-max)))
872 (if (catch 'unindent
873 (goto-char (point-min))
874 (if (not (re-search-forward Man-first-heading-regexp nil t))
875 (throw 'unindent nil))
876 (beginning-of-line)
877 (setq indent (buffer-substring (point)
878 (progn
879 (skip-chars-forward " ")
880 (point))))
881 (setq nindent (length indent))
882 (if (zerop nindent)
883 (throw 'unindent nil))
884 (setq indent (concat indent "\\|$"))
885 (goto-char (point-min))
886 (while (not (eobp))
887 (if (looking-at indent)
888 (forward-line 1)
889 (throw 'unindent nil)))
890 (goto-char (point-min)))
891 (while (not (eobp))
892 (or (eolp)
893 (delete-char nindent))
894 (forward-line 1)))
895 (setq page-list (cdr page-list))
2cd4790e 896 ))))
effdc6a2
RS
897
898\f
899;; ======================================================================
e660d0db 900;; Man mode commands
effdc6a2
RS
901
902(defun Man-next-section (n)
903 "Move point to Nth next section (default 1)."
904 (interactive "p")
2cd4790e
KH
905 (let ((case-fold-search nil))
906 (if (looking-at Man-heading-regexp)
907 (forward-line 1))
908 (if (re-search-forward Man-heading-regexp (point-max) t n)
909 (beginning-of-line)
910 (goto-char (point-max)))))
effdc6a2
RS
911
912(defun Man-previous-section (n)
913 "Move point to Nth previous section (default 1)."
914 (interactive "p")
2cd4790e
KH
915 (let ((case-fold-search nil))
916 (if (looking-at Man-heading-regexp)
917 (forward-line -1))
918 (if (re-search-backward Man-heading-regexp (point-min) t n)
919 (beginning-of-line)
920 (goto-char (point-min)))))
effdc6a2
RS
921
922(defun Man-find-section (section)
923 "Move point to SECTION if it exists, otherwise don't move point.
924Returns t if section is found, nil otherwise."
2cd4790e
KH
925 (let ((curpos (point))
926 (case-fold-search nil))
effdc6a2 927 (goto-char (point-min))
b3435a2f 928 (if (re-search-forward (concat "^" section) (point-max) t)
effdc6a2
RS
929 (progn (beginning-of-line) t)
930 (goto-char curpos)
931 nil)
932 ))
933
934(defun Man-goto-section ()
935 "Query for section to move point to."
936 (interactive)
937 (aput 'Man-sections-alist
938 (let* ((default (aheadsym Man-sections-alist))
939 (completion-ignore-case t)
940 chosen
941 (prompt (concat "Go to section: (default " default ") ")))
942 (setq chosen (completing-read prompt Man-sections-alist))
943 (if (or (not chosen)
944 (string= chosen ""))
945 default
946 chosen)))
947 (Man-find-section (aheadsym Man-sections-alist)))
948
949(defun Man-goto-see-also-section ()
950 "Move point the the \"SEE ALSO\" section.
c3343fcf 951Actually the section moved to is described by `Man-see-also-regexp'."
effdc6a2
RS
952 (interactive)
953 (if (not (Man-find-section Man-see-also-regexp))
954 (error (concat "No " Man-see-also-regexp
b3435a2f 955 " section found in the current manpage"))))
effdc6a2 956
2151a1cf 957(defun Man-follow-manual-reference (reference)
effdc6a2 958 "Get one of the manpages referred to in the \"SEE ALSO\" section.
2151a1cf 959Specify which reference to use; default is based on word at point."
e660d0db
RS
960 (interactive
961 (if (not Man-refpages-alist)
b3435a2f 962 (error "There are no references in the current man page")
2151a1cf 963 (list (let* ((default (or
e660d0db
RS
964 (car (all-completions
965 (save-excursion
966 (skip-syntax-backward "w()")
967 (skip-chars-forward " \t")
968 (let ((word (current-word)))
969 ;; strip a trailing '-':
970 (if (string-match "-$" word)
2cd4790e
KH
971 (substring word 0
972 (match-beginning 0))
e660d0db
RS
973 word)))
974 Man-refpages-alist))
975 (aheadsym Man-refpages-alist)))
976 chosen
977 (prompt (concat "Refer to: (default " default ") ")))
978 (setq chosen (completing-read prompt Man-refpages-alist nil t))
979 (if (or (not chosen)
980 (string= chosen ""))
981 default
982 chosen)))))
effdc6a2 983 (if (not Man-refpages-alist)
b3435a2f 984 (error "Can't find any references in the current manpage")
e660d0db 985 (aput 'Man-refpages-alist reference)
effdc6a2 986 (Man-getpage-in-background
2151a1cf 987 (Man-translate-references (aheadsym Man-refpages-alist)))))
effdc6a2 988
b3435a2f 989(defun Man-kill ()
effdc6a2
RS
990 "Kill the buffer containing the manpage."
991 (interactive)
992 (let ((buff (current-buffer)))
993 (delete-windows-on buff)
b3435a2f
FP
994 (kill-buffer buff))
995 (if (and window-system
799ac634 996 (or (eq Man-notify-method 'newframe)
b3435a2f 997 (and pop-up-frames
799ac634 998 (eq Man-notify-method 'bully))))
b3435a2f
FP
999 (delete-frame)))
1000
1001(defun Man-quit ()
1002 "Bury the buffer containing the manpage."
1003 (interactive)
1004 (let ((buff (current-buffer)))
1005 (delete-windows-on buff)
1006 (bury-buffer buff))
1007 (if (and window-system
799ac634 1008 (or (eq Man-notify-method 'newframe)
b3435a2f 1009 (and pop-up-frames
799ac634 1010 (eq Man-notify-method 'bully))))
b3435a2f 1011 (delete-frame)))
effdc6a2
RS
1012
1013(defun Man-goto-page (page)
1014 "Go to the manual page on page PAGE."
1015 (interactive
b3435a2f
FP
1016 (if (not Man-page-list)
1017 (let ((args Man-arguments))
1018 (kill-buffer (current-buffer))
1019 (error "Can't find the %s manpage" args))
1020 (if (= (length Man-page-list) 1)
1021 (error "You're looking at the only manpage in the buffer")
1022 (list (read-minibuffer (format "Go to manpage [1-%d]: "
1023 (length Man-page-list)))))))
1024 (if (not Man-page-list)
1025 (let ((args Man-arguments))
1026 (kill-buffer (current-buffer))
1027 (error "Can't find the %s manpage" args)))
effdc6a2
RS
1028 (if (or (< page 1)
1029 (> page (length Man-page-list)))
1030 (error "No manpage %d found" page))
1031 (let* ((page-range (nth (1- page) Man-page-list))
1032 (page-start (car page-range))
b3435a2f 1033 (page-end (car (cdr page-range))))
effdc6a2 1034 (setq Man-current-page page
b3435a2f 1035 Man-page-mode-string (Man-make-page-mode-string))
effdc6a2
RS
1036 (widen)
1037 (goto-char page-start)
1038 (narrow-to-region page-start page-end)
1039 (Man-build-section-alist)
b3435a2f 1040 (Man-build-references-alist)
effdc6a2
RS
1041 (goto-char (point-min))))
1042
1043
1044(defun Man-next-manpage ()
1045 "Find the next manpage entry in the buffer."
1046 (interactive)
1047 (if (= (length Man-page-list) 1)
9f2863b2 1048 (error "This is the only manpage in the buffer"))
effdc6a2
RS
1049 (if (< Man-current-page (length Man-page-list))
1050 (Man-goto-page (1+ Man-current-page))
b3435a2f 1051 (if Man-circular-pages-flag
effdc6a2 1052 (Man-goto-page 1)
9f2863b2 1053 (error "You're looking at the last manpage in the buffer"))))
effdc6a2
RS
1054
1055(defun Man-previous-manpage ()
1056 "Find the previous manpage entry in the buffer."
1057 (interactive)
1058 (if (= (length Man-page-list) 1)
9f2863b2 1059 (error "This is the only manpage in the buffer"))
effdc6a2
RS
1060 (if (> Man-current-page 1)
1061 (Man-goto-page (1- Man-current-page))
b3435a2f 1062 (if Man-circular-pages-flag
effdc6a2 1063 (Man-goto-page (length Man-page-list))
9f2863b2 1064 (error "You're looking at the first manpage in the buffer"))))
733155db
JB
1065\f
1066(provide 'man)
1067
6594deb0 1068;;; man.el ends here