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