Commit | Line | Data |
---|---|---|
effdc6a2 | 1 | ;;; man.el --- browse UNIX manual pages |
6594deb0 | 2 | |
effdc6a2 | 3 | ;; Copyright (C) 1993 Free Software Foundation, Inc. |
9750e079 | 4 | |
effdc6a2 RS |
5 | ;; Author: Barry A. Warsaw <bwarsaw@cen.com> |
6 | ;; Last-Modified: 31-Jul-1991 | |
7 | ;; Version: 1.1 | |
8 | ;; Keywords: help | |
9 | ;; Adapted-By: ESR | |
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 | ||
effdc6a2 RS |
29 | ;; This code provides a function, manual-entry, with which you can |
30 | ;; browse UNIX manual pages. Formatting is done in background so that | |
31 | ;; you can continue to use your Emacs while processing is going on. | |
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 | |
41 | ;; to encorporate them into a single manual browsing mode. While | |
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 | ||
59 | ;; ========== Features ========== | |
60 | ;; + Runs "man" in the background and pipes the results through a | |
61 | ;; series of sed and awk scripts so that all retrieving and cleaning | |
62 | ;; is done in the background. The cleaning commands are configurable. | |
63 | ;; + Syntax is the same as Un*x man | |
64 | ;; + Functionality is the same as Un*x man, including "man -k" and | |
c3343fcf | 65 | ;; "man <section>", etc. |
effdc6a2 RS |
66 | ;; + Provides a manual browsing mode with keybindings for traversing |
67 | ;; the sections of a manpage, following references in the SEE ALSO | |
68 | ;; section, and more. | |
69 | ;; + Multiple manpages created with the same man command are put into | |
70 | ;; a narrowed buffer circular list. | |
edbd2f74 | 71 | |
e5167999 ER |
72 | ;;; Code: |
73 | ||
effdc6a2 | 74 | (require 'assoc) |
effdc6a2 RS |
75 | |
76 | ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv | |
77 | ;; user variables | |
78 | ||
79 | (defvar Man-notify 'friendly | |
80 | "*Selects the behavior when manpage is ready. | |
81 | This variable may have one of the following values: | |
82 | ||
b51aeeff | 83 | newframe -- put the manpage in its own frame (see `Man-frame-parameters') |
effdc6a2 RS |
84 | bully -- make the manpage the current buffer and only window |
85 | aggressive -- make the manpage the current buffer in the other window | |
86 | friendly -- display manpage in other window but don't make current | |
87 | polite -- don't display manpage, but prints message when ready (beeps) | |
88 | quiet -- like `polite', but don't beep | |
89 | meek -- make no indication that manpage is ready | |
90 | ||
91 | Any other value of `Man-notify' is equivalent to `meek'.") | |
92 | ||
b51aeeff RS |
93 | (defvar Man-frame-parameters nil |
94 | "*Frame parameter list for creating a new frame for a manual page.") | |
95 | ||
effdc6a2 RS |
96 | (defvar Man-reuse-okay-p t |
97 | "*Reuse a manpage buffer if possible. | |
98 | When t, and a manpage buffer already exists with the same invocation, | |
99 | man just indicates the manpage is ready according to the value of | |
c3343fcf | 100 | `Man-notify'. When nil, it always fires off a background process, putting |
effdc6a2 RS |
101 | the results in a uniquely named buffer.") |
102 | ||
103 | (defvar Man-downcase-section-letters-p t | |
104 | "*Letters in sections are converted to lower case. | |
105 | Some Un*x man commands can't handle uppercase letters in sections, for | |
106 | example \"man 2V chmod\", but they are often displayed in the manpage | |
c3343fcf | 107 | with the upper case letter. When this variable is t, the section |
effdc6a2 RS |
108 | letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before |
109 | being sent to the man background process.") | |
110 | ||
111 | (defvar Man-circular-pages-p t | |
112 | "*If t, the manpage list is treated as circular for traversal.") | |
113 | ||
c3343fcf RS |
114 | ;; I changed this to nil because it is a bad idea |
115 | ;; to fail to recognize things in other sections. | |
effdc6a2 | 116 | (defvar Man-auto-section-alist |
c3343fcf RS |
117 | nil |
118 | ;; '((c-mode . ("2" "3")) | |
119 | ;; (c++-mode . ("2" "3")) | |
120 | ;; (c++-c-mode . ("2" "3")) | |
121 | ;; (shell-mode . ("1" "8")) | |
122 | ;; (cmushell-mode . ("1" "8")) | |
123 | ;; (text-mode . "1") | |
124 | ;; ) | |
effdc6a2 RS |
125 | "*Association list of major modes and their default section numbers. |
126 | List is of the form: (MAJOR-MODE . [SECTION | (SECTION*)]). If current | |
127 | major mode is not in list, then the default is to check for manpages | |
128 | in all sections.") | |
129 | ||
130 | (defvar Man-section-translations-alist | |
131 | '(("3C++" . "3") | |
ea1ebaed RS |
132 | ("3X" . "3") ; Xlib man pages |
133 | ("3X11" . "3") | |
effdc6a2 RS |
134 | ("1-UCB" . "")) |
135 | "*Association list of bogus sections to real section numbers. | |
136 | Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in | |
c3343fcf | 137 | their references which Un*x `man' does not recognize. This |
eb8c3be9 | 138 | association list is used to translate those sections, when found, to |
effdc6a2 RS |
139 | the associated section number.") |
140 | ||
141 | (defvar Man-filter-list | |
142 | '(("sed " | |
c3343fcf | 143 | (;;"-e 's/.\010//g'" |
effdc6a2 | 144 | "-e '/[Nn]o such file or directory/d'" |
fcb8a0c5 RS |
145 | "-e '/Reformatting page. Wait/d'" |
146 | "-e '/Reformatting entry. Wait/d'" | |
c2d606f4 | 147 | "-e '/^ *\\([A-Za-z][A-Za-z.]*([0-9A-Za-z][-0-9A-Za-z+]*)\\).*\\1$/d'" |
effdc6a2 RS |
148 | "-e '/^[ \\t]*Hewlett-Packard Company[ \\t]*- [0-9]* -.*$/d'" |
149 | "-e '/^[ \\t]*Hewlett-Packard[ \\t]*- [0-9]* -.*$/d'" | |
58732bd4 | 150 | "-e '/^ *- [0-9]* - *Formatted:.*[0-9]$/d'" |
effdc6a2 RS |
151 | "-e '/^ *Page [0-9]*.*(printed [0-9\\/]*)$/d'" |
152 | "-e '/^Printed [0-9].*[0-9]$/d'" | |
4cd88e9c | 153 | "-e '/^[ \\t]*X Version 1[01].*Release [0-9]/d'" |
1b88a44a | 154 | "-e '/^[A-za-z].*Last change:/d'" |
effdc6a2 RS |
155 | "-e '/^Sun Release [0-9].*[0-9]$/d'" |
156 | "-e '/^\\n$/D'" | |
157 | )) | |
6b33eb40 RS |
158 | ("awk '\n" |
159 | ("BEGIN { blankline=0; anonblank=0; }\n" | |
160 | "/^$/ { if (anonblank==0) next; }\n" | |
161 | "{ anonblank=1; }\n" | |
162 | "/^$/ { blankline++; next; }\n" | |
163 | "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n" | |
effdc6a2 RS |
164 | "'" |
165 | )) | |
166 | ) | |
167 | "*Manpage cleaning filter command phrases. | |
168 | This variable contains an association list of the following form: | |
169 | ||
170 | '((command-string (phrase-string*))*) | |
171 | ||
172 | Each phrase-string is concatenated onto the command-string to form a | |
c3343fcf | 173 | command filter. The (standard) output (and standard error) of the Un*x |
effdc6a2 | 174 | man command is piped through each command filter in the order the |
c3343fcf | 175 | commands appear in the association list. The final output is placed in |
effdc6a2 RS |
176 | the manpage buffer.") |
177 | ||
178 | (defvar Man-mode-line-format | |
179 | '("" mode-line-modified | |
180 | mode-line-buffer-identification " " | |
181 | global-mode-string | |
056a5ef3 | 182 | " " Man-page-mode-string |
effdc6a2 RS |
183 | " %[(" mode-name minor-mode-alist mode-line-process ")%]----" |
184 | (-3 . "%p") "-%-") | |
185 | "*Mode line format for manual mode buffer.") | |
186 | ||
187 | (defvar Man-mode-map nil | |
c3343fcf | 188 | "*Keymap for Man mode.") |
effdc6a2 | 189 | |
e660d0db RS |
190 | (defvar Man-mode-hook nil |
191 | "*Normal hook run when Man mode is enabled.") | |
effdc6a2 | 192 | |
c2d606f4 | 193 | (defvar Man-cooked-hook nil |
b09d7aa2 | 194 | "*Normal hook run after removing backspaces but before Man-mode processing.") |
c2d606f4 | 195 | |
f5f76002 | 196 | (defvar Man-section-regexp "[0-9][a-zA-Z+]*\\|[LNln]" |
effdc6a2 RS |
197 | "*Regular expression describing a manpage section within parentheses.") |
198 | ||
3eedeb85 | 199 | (defvar Man-heading-regexp "^ ?[A-Z]" |
effdc6a2 RS |
200 | "*Regular expression describing a manpage heading entry.") |
201 | ||
202 | (defvar Man-see-also-regexp "SEE ALSO" | |
203 | "*Regular expression for SEE ALSO heading (or your equivalent). | |
204 | This regexp should not start with a `^' character.") | |
205 | ||
3eedeb85 | 206 | (defvar Man-first-heading-regexp "^ ?NAME$\\|^ ?No manual entry for .*$" |
effdc6a2 RS |
207 | "*Regular expression describing first heading on a manpage. |
208 | This regular expression should start with a `^' character.") | |
209 | ||
3eedeb85 RS |
210 | (defvar Man-reference-regexp |
211 | "[-a-zA-Z0-9_][-a-zA-Z0-9_.]*\\(([0-9][a-zA-Z+]*)\\)?" | |
effdc6a2 RS |
212 | "*Regular expression describing a reference in the SEE ALSO section.") |
213 | ||
733155db JB |
214 | (defvar Man-switches "" |
215 | "*Switches passed to the man command, as a single string.") | |
effdc6a2 | 216 | |
9de0760c RS |
217 | ;; Would someone like to provide a good test for being on Solaris? |
218 | ;; We could give it its own value of system-type, but that has drawbacks; | |
219 | ;; it would require changes in lots of places that test system-type. | |
e660d0db RS |
220 | (defvar Man-specified-section-option |
221 | (if (string-match "-solaris[0-9.]*$" system-configuration) | |
222 | "-s" | |
223 | "") | |
224 | "*Option that indicates a specified a manual section name.") | |
9de0760c | 225 | |
effdc6a2 RS |
226 | ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
227 | ;; end user variables | |
effdc6a2 RS |
228 | \f |
229 | ;; other variables and keymap initializations | |
230 | (make-variable-buffer-local 'Man-sections-alist) | |
231 | (make-variable-buffer-local 'Man-refpages-alist) | |
232 | (make-variable-buffer-local 'Man-page-list) | |
233 | (make-variable-buffer-local 'Man-current-page) | |
234 | (make-variable-buffer-local 'Man-page-mode-string) | |
75db9a64 | 235 | (make-variable-buffer-local 'Man-original-frame) |
effdc6a2 RS |
236 | |
237 | (setq-default Man-sections-alist nil) | |
238 | (setq-default Man-refpages-alist nil) | |
239 | (setq-default Man-page-list nil) | |
240 | (setq-default Man-current-page 0) | |
241 | (setq-default Man-page-mode-string "1 (of 1)") | |
242 | ||
243 | (if Man-mode-map | |
244 | nil | |
245 | (setq Man-mode-map (make-keymap)) | |
246 | (suppress-keymap Man-mode-map) | |
247 | (define-key Man-mode-map " " 'scroll-up) | |
248 | (define-key Man-mode-map "\177" 'scroll-down) | |
249 | (define-key Man-mode-map "n" 'Man-next-section) | |
250 | (define-key Man-mode-map "p" 'Man-previous-section) | |
251 | (define-key Man-mode-map "\en" 'Man-next-manpage) | |
252 | (define-key Man-mode-map "\ep" 'Man-previous-manpage) | |
253 | (define-key Man-mode-map "," 'beginning-of-buffer) | |
254 | (define-key Man-mode-map "." 'end-of-buffer) | |
255 | (define-key Man-mode-map "r" 'Man-follow-manual-reference) | |
256 | (define-key Man-mode-map "t" 'toggle-truncate-lines) | |
257 | (define-key Man-mode-map "g" 'Man-goto-section) | |
258 | (define-key Man-mode-map "s" 'Man-goto-see-also-section) | |
259 | (define-key Man-mode-map "q" 'Man-quit) | |
260 | (define-key Man-mode-map "m" 'manual-entry) | |
effdc6a2 RS |
261 | (define-key Man-mode-map "?" 'describe-mode) |
262 | ) | |
263 | ||
264 | \f | |
265 | ;; ====================================================================== | |
266 | ;; utilities | |
267 | ||
268 | (defun Man-page-mode-string () | |
e660d0db | 269 | "Formats part of the mode line for Man mode." |
effdc6a2 RS |
270 | (format "%d (of %d)" Man-current-page (length Man-page-list))) |
271 | ||
272 | (defun Man-delete-trailing-newline (str) | |
273 | (if (string= (substring str (1- (length str))) "\n") | |
274 | (substring str 0 (1- (length str))) | |
275 | str)) | |
276 | ||
277 | (defun Man-build-man-command () | |
278 | "Builds the entire background manpage and cleaning command." | |
eaf1946c | 279 | (let ((command (concat "man " Man-switches " %s 2>/dev/null")) |
effdc6a2 RS |
280 | (flist Man-filter-list)) |
281 | (while flist | |
282 | (let ((pcom (car (car flist))) | |
283 | (pargs (car (cdr (car flist))))) | |
284 | (setq flist (cdr flist)) | |
285 | (if (or (not (stringp pcom)) | |
286 | (not (listp pargs))) | |
287 | (error "malformed Man-filter-list.")) | |
d902a611 | 288 | (setq command (concat command " | " pcom |
effdc6a2 | 289 | (mapconcat '(lambda (phrase) phrase) |
d902a611 | 290 | pargs " "))))) |
effdc6a2 RS |
291 | command)) |
292 | ||
293 | (defun Man-downcase (man-args) | |
294 | "Downcases section letters in MAN-ARGS." | |
295 | (let ((newargs "") | |
296 | (s 0) | |
297 | mstart mend | |
298 | (len (length man-args))) | |
299 | (while (and (< s len) | |
300 | (setq mstart (string-match Man-section-regexp man-args s))) | |
301 | (setq mend (match-end 0) | |
302 | newargs (concat newargs (substring man-args s mstart))) | |
303 | (setq newargs (concat newargs (downcase | |
304 | (substring man-args mstart mend))) | |
305 | s mend)) | |
306 | (concat newargs (substring man-args s len)))) | |
307 | ||
308 | (defun Man-translate-references (ref) | |
309 | "Translates REF from \"chmod(2V)\" to \"2v chmod\" style." | |
310 | (if (string-match (concat "(" Man-section-regexp ")$") ref) | |
311 | (let* ((word (progn (string-match "(" ref) | |
312 | (substring ref 0 (1- (match-end 0))))) | |
313 | (section-re (concat "(\\(" Man-section-regexp "\\))")) | |
314 | (section (if (string-match section-re ref) | |
315 | (substring ref (match-beginning 1) (match-end 1)) | |
316 | "")) | |
317 | (slist Man-section-translations-alist) | |
318 | ) | |
319 | (if Man-downcase-section-letters-p | |
320 | (setq section (Man-downcase section))) | |
321 | (while slist | |
322 | (let ((s1 (car (car slist))) | |
323 | (s2 (cdr (car slist)))) | |
324 | (setq slist (cdr slist)) | |
325 | (if Man-downcase-section-letters-p | |
326 | (setq s1 (Man-downcase s1))) | |
327 | (if (not (string= s1 section)) nil | |
328 | (setq section (if Man-downcase-section-letters-p | |
329 | (Man-downcase s2) | |
330 | s2) | |
331 | slist nil)))) | |
9de0760c | 332 | (concat Man-specified-section-option section " " word)) |
effdc6a2 RS |
333 | ref)) |
334 | ||
335 | (defun Man-linepos (&optional position col-p) | |
336 | "Return the character position at various line/buffer positions. | |
c3343fcf | 337 | Preserves the state of point, mark, etc. Optional arg POSITION can be one |
effdc6a2 RS |
338 | of the following symbols: |
339 | bol == beginning of line | |
340 | boi == beginning of indentation | |
341 | eol == end of line [default] | |
342 | bob == beginning of buffer | |
343 | eob == end of buffer | |
344 | ||
c3343fcf RS |
345 | Optional arg COL-P, if non-nil, means to return |
346 | the current column instead of character position." | |
effdc6a2 RS |
347 | (let ((tpnt (point)) |
348 | rval) | |
349 | (cond | |
350 | ((eq position 'bol) (beginning-of-line)) | |
351 | ((eq position 'boi) (back-to-indentation)) | |
352 | ((eq position 'bob) (goto-char (point-min))) | |
353 | ((eq position 'eob) (goto-char (point-max))) | |
354 | (t (end-of-line))) | |
355 | (setq rval (if col-p (current-column) (point))) | |
356 | (goto-char tpnt) | |
357 | rval)) | |
358 | ||
359 | \f | |
360 | ;; ====================================================================== | |
361 | ;; default man entry and get word under point | |
362 | ||
363 | (defun Man-default-man-args (manword) | |
c3343fcf | 364 | "Build the default man args from MANWORD and buffer's major mode." |
d902a611 JB |
365 | (let ((sections (cdr (assq major-mode Man-auto-section-alist)))) |
366 | (cond | |
367 | ((null sections) manword) | |
368 | ((consp sections) | |
75067849 RS |
369 | (mapconcat (lambda (n) (concat Man-specified-section-option |
370 | n " " manword)) | |
371 | sections " ")) | |
d902a611 JB |
372 | (t |
373 | (concat sections " " manword))))) | |
effdc6a2 RS |
374 | |
375 | (defun Man-default-man-entry () | |
376 | "Make a guess at a default manual entry. | |
377 | This guess is based on the text surrounding the cursor, and the | |
c3343fcf | 378 | default section number is selected from `Man-auto-section-alist'." |
d902a611 | 379 | (let (default-title) |
1a20f48d | 380 | (save-excursion |
effdc6a2 RS |
381 | |
382 | ;; Default man entry title is any word the cursor is on, | |
383 | ;; or if cursor not on a word, then nearest preceding | |
384 | ;; word. | |
385 | (and (not (looking-at "[a-zA-Z_]")) | |
386 | (skip-chars-backward "^a-zA-Z_")) | |
387 | (skip-chars-backward "(a-zA-Z_0-9") | |
388 | (and (looking-at "(") (forward-char 1)) | |
389 | (setq default-title | |
390 | (buffer-substring | |
391 | (point) | |
392 | (progn (skip-chars-forward "a-zA-Z0-9_") (point)))) | |
393 | ||
394 | ;; If looking at something like ioctl(2) or brc(1M), include | |
395 | ;; section number in default-entry | |
396 | (if (looking-at "[ \t]*([ \t]*[0-9][a-zA-Z]?[ \t]*)") | |
397 | (progn (skip-chars-forward "^0-9") | |
d902a611 | 398 | (setq default-title |
75067849 RS |
399 | (concat Man-specified-section-option |
400 | (buffer-substring | |
d902a611 JB |
401 | (point) |
402 | (progn | |
403 | (skip-chars-forward "0-9a-zA-Z") | |
404 | (point))) | |
405 | " " | |
406 | default-title))) | |
407 | (setq default-title (Man-default-man-args default-title))) | |
408 | default-title))) | |
effdc6a2 RS |
409 | |
410 | \f | |
411 | ;; ====================================================================== | |
412 | ;; top level command and background process sentinel | |
1a20f48d | 413 | |
cac0b95d RS |
414 | ;;; This alias makes completion more predictable if ignoring case. |
415 | ;;;###autoload | |
416 | (defalias 'man 'manual-entry) | |
417 | ||
effdc6a2 RS |
418 | ;;;###autoload |
419 | (defun manual-entry (arg) | |
420 | "Get a Un*x manual page and put it in a buffer. | |
c3343fcf | 421 | This command is the top-level command in the man package. It runs a Un*x |
effdc6a2 | 422 | command to retrieve and clean a manpage in the background and places the |
c3343fcf RS |
423 | results in a Man mode (manpage browsing) buffer. See variable |
424 | `Man-notify' for what happens when the buffer is ready. | |
fa8a9f30 KH |
425 | Normally, if a buffer already exists for this man page, it will display |
426 | immediately; either a prefix argument or a nil value to `Man-reuse-okay-p' | |
427 | overrides this and forces the man page to be regenerated." | |
effdc6a2 RS |
428 | (interactive "P") |
429 | (let* ((default-entry (Man-default-man-entry)) | |
430 | (man-args | |
431 | (read-string (format "Manual-entry: %s" | |
432 | (if (string= default-entry "") "" | |
433 | (format "(default: %s) " | |
434 | default-entry)))))) | |
435 | (and (string= man-args "") | |
436 | (if (string= default-entry "") | |
437 | (error "No man args given.") | |
438 | (setq man-args default-entry))) | |
d902a611 | 439 | |
d801ac09 | 440 | ;; Recognize the subject(section) syntax. |
d902a611 JB |
441 | (setq man-args (Man-translate-references man-args)) |
442 | ||
1b88a44a RS |
443 | ;; This is apparently already done correctly via Man-translate-references. |
444 | ;; (if Man-downcase-section-letters-p | |
445 | ;; (setq man-args (Man-downcase man-args))) | |
effdc6a2 RS |
446 | (Man-getpage-in-background man-args (consp arg)) |
447 | )) | |
aa228418 | 448 | |
e660d0db | 449 | (defun Man-getpage-in-background (topic &optional override-reuse-p) |
c3343fcf RS |
450 | "Uses TOPIC to build and fire off the manpage and cleaning command. |
451 | Optional OVERRIDE-REUSE-P, when non-nil, means to | |
effdc6a2 | 452 | start a background process even if a buffer already exists and |
c3343fcf | 453 | `Man-reuse-okay-p' is non-nil." |
e660d0db | 454 | (let* ((man-args topic) |
c3343fcf | 455 | (bufname (concat "*man " man-args "*")) |
effdc6a2 RS |
456 | (buffer (get-buffer bufname))) |
457 | (if (and Man-reuse-okay-p | |
458 | (not override-reuse-p) | |
459 | buffer) | |
460 | (Man-notify-when-ready buffer) | |
eaf1946c | 461 | (require 'env) |
effdc6a2 RS |
462 | (message "Invoking man %s in background..." man-args) |
463 | (setq buffer (generate-new-buffer bufname)) | |
75db9a64 KH |
464 | (save-excursion |
465 | (set-buffer buffer) | |
466 | (setq Man-original-frame (selected-frame))) | |
0322056b | 467 | (let ((process-environment (copy-sequence process-environment))) |
82c9fe8e RS |
468 | ;; Prevent any attempt to use display terminal fanciness. |
469 | (setenv "TERM" "dumb") | |
470 | (set-process-sentinel | |
471 | (start-process "man" buffer "sh" "-c" | |
13628415 RS |
472 | (format (Man-build-man-command) man-args)) |
473 | 'Man-bgproc-sentinel)) | |
474 | ))) | |
effdc6a2 RS |
475 | |
476 | (defun Man-notify-when-ready (man-buffer) | |
477 | "Notify the user when MAN-BUFFER is ready. | |
e0fcbb59 | 478 | See the variable `Man-notify' for the different notification behaviors." |
effdc6a2 | 479 | (cond |
b51aeeff RS |
480 | ((eq Man-notify 'newframe) |
481 | (set-buffer man-buffer) | |
482 | (new-frame Man-frame-parameters)) | |
e0fcbb59 | 483 | ((eq Man-notify 'bully) |
75db9a64 KH |
484 | (and (frame-live-p Man-original-frame) |
485 | (select-frame Man-original-frame)) | |
effdc6a2 RS |
486 | (pop-to-buffer man-buffer) |
487 | (delete-other-windows)) | |
e0fcbb59 | 488 | ((eq Man-notify 'aggressive) |
75db9a64 KH |
489 | (and (frame-live-p Man-original-frame) |
490 | (select-frame Man-original-frame)) | |
effdc6a2 | 491 | (pop-to-buffer man-buffer)) |
e0fcbb59 | 492 | ((eq Man-notify 'friendly) |
75db9a64 KH |
493 | (and (frame-live-p Man-original-frame) |
494 | (select-frame Man-original-frame)) | |
effdc6a2 | 495 | (display-buffer man-buffer 'not-this-window)) |
e0fcbb59 | 496 | ((eq Man-notify 'polite) |
effdc6a2 RS |
497 | (beep) |
498 | (message "Manual buffer %s is ready." (buffer-name man-buffer))) | |
e0fcbb59 | 499 | ((eq Man-notify 'quiet) |
effdc6a2 | 500 | (message "Manual buffer %s is ready." (buffer-name man-buffer))) |
e0fcbb59 | 501 | ((or (eq Man-notify 'meek) |
effdc6a2 RS |
502 | t) |
503 | (message "")) | |
504 | )) | |
505 | ||
c3343fcf RS |
506 | (defun Man-set-fonts () |
507 | (goto-char (point-min)) | |
d4029c89 | 508 | (while (re-search-forward "\\(.\b\\)+" nil t) |
c3343fcf RS |
509 | (let ((st (match-beginning 0)) (en (match-end 0))) |
510 | (goto-char st) | |
511 | (if window-system | |
d4029c89 | 512 | (put-text-property st (if (= en (point-max)) en (1+ en)) 'face |
c3343fcf RS |
513 | (if (looking-at "_") 'underline 'bold))) |
514 | (while (and (< (point) en) (looking-at ".\b")) | |
d4029c89 | 515 | (replace-match ""))))) |
c3343fcf | 516 | |
effdc6a2 RS |
517 | (defun Man-bgproc-sentinel (process msg) |
518 | "Manpage background process sentinel." | |
519 | (let ((Man-buffer (process-buffer process)) | |
520 | (delete-buff nil) | |
521 | (err-mess nil)) | |
522 | (if (null (buffer-name Man-buffer)) ;; deleted buffer | |
523 | (set-process-buffer process nil) | |
524 | (save-excursion | |
525 | (set-buffer Man-buffer) | |
526 | (goto-char (point-min)) | |
527 | (cond ((or (looking-at "No \\(manual \\)*entry for") | |
528 | (looking-at "[^\n]*: nothing appropriate$")) | |
529 | (setq err-mess (buffer-substring (point) (Man-linepos 'eol)) | |
530 | delete-buff t) | |
531 | ) | |
532 | ((not (and (eq (process-status process) 'exit) | |
533 | (= (process-exit-status process) 0))) | |
534 | (setq err-mess | |
535 | (concat (buffer-name Man-buffer) | |
536 | ": process " | |
537 | (let ((eos (1- (length msg)))) | |
538 | (if (= (aref msg eos) ?\n) | |
539 | (substring msg 0 eos) msg)))) | |
540 | (goto-char (point-max)) | |
541 | (insert (format "\nprocess %s" msg)) | |
542 | ))) | |
543 | (if delete-buff | |
544 | (kill-buffer Man-buffer) | |
545 | (save-window-excursion | |
546 | (save-excursion | |
547 | (set-buffer Man-buffer) | |
c3343fcf | 548 | (Man-set-fonts) |
c2d606f4 | 549 | (run-hooks 'Man-cooked-hook) |
effdc6a2 RS |
550 | (Man-mode) |
551 | (set-buffer-modified-p nil))) | |
552 | (Man-notify-when-ready Man-buffer)) | |
553 | ||
554 | (if err-mess | |
555 | (error err-mess)) | |
556 | ))) | |
557 | ||
558 | \f | |
559 | ;; ====================================================================== | |
560 | ;; set up manual mode in buffer and build alists | |
561 | ||
562 | (defun Man-mode () | |
c3343fcf | 563 | "A mode for browsing Un*x manual pages. |
effdc6a2 RS |
564 | |
565 | The following man commands are available in the buffer. Try | |
566 | \"\\[describe-key] <key> RET\" for more information: | |
aa228418 | 567 | |
effdc6a2 RS |
568 | \\[manual-entry] Prompt to retrieve a new manpage. |
569 | \\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section. | |
570 | \\[Man-next-manpage] Jump to next manpage in circular list. | |
571 | \\[Man-previous-manpage] Jump to previous manpage in circular list. | |
572 | \\[Man-next-section] Jump to next manpage section. | |
573 | \\[Man-previous-section] Jump to previous manpage section. | |
574 | \\[Man-goto-section] Go to a manpage section. | |
575 | \\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section. | |
576 | \\[Man-quit] Deletes the manpage, its buffer, and window. | |
effdc6a2 RS |
577 | \\[describe-mode] Prints this help text. |
578 | ||
579 | The following variables may be of some use. Try | |
580 | \"\\[describe-variable] <variable-name> RET\" for more information: | |
581 | ||
582 | Man-notify What happens when manpage formatting is done. | |
583 | Man-reuse-okay-p Okay to reuse already formatted buffer? | |
584 | Man-downcase-section-letters-p Force section letters to lower case? | |
585 | Man-circular-pages-p Multiple manpage list treated as circular? | |
586 | Man-auto-section-alist List of major modes and their section numbers. | |
587 | Man-section-translations-alist List of section numbers and their Un*x equiv. | |
588 | Man-filter-list Background manpage filter command. | |
e660d0db RS |
589 | Man-mode-line-format Mode line format for Man mode buffers. |
590 | Man-mode-map Keymap bindings for Man mode buffers. | |
591 | Man-mode-hook Normal hook run on entry to Man mode. | |
effdc6a2 RS |
592 | Man-section-regexp Regexp describing manpage section letters. |
593 | Man-heading-regexp Regexp describing section headers. | |
594 | Man-see-also-regexp Regexp for SEE ALSO section (or your equiv). | |
595 | Man-first-heading-regexp Regexp for first heading on a manpage. | |
596 | Man-reference-regexp Regexp matching a references in SEE ALSO. | |
733155db | 597 | Man-switches Background `man' command switches. |
effdc6a2 RS |
598 | |
599 | The following key bindings are currently in effect in the buffer: | |
600 | \\{Man-mode-map}" | |
601 | (interactive) | |
602 | (setq major-mode 'Man-mode | |
c3343fcf | 603 | mode-name "Man" |
effdc6a2 RS |
604 | buffer-auto-save-file-name nil |
605 | mode-line-format Man-mode-line-format | |
606 | truncate-lines t | |
607 | buffer-read-only t) | |
608 | (buffer-disable-undo (current-buffer)) | |
609 | (auto-fill-mode -1) | |
610 | (use-local-map Man-mode-map) | |
1a20f48d | 611 | (goto-char (point-min)) |
effdc6a2 | 612 | (Man-build-page-list) |
526504b8 RS |
613 | (Man-goto-page 1) |
614 | (run-hooks 'Man-mode-hook)) | |
1a20f48d | 615 | |
effdc6a2 RS |
616 | (defun Man-build-section-alist () |
617 | "Build the association list of manpage sections." | |
618 | (setq Man-sections-alist nil) | |
1a20f48d | 619 | (goto-char (point-min)) |
effdc6a2 RS |
620 | (while (re-search-forward Man-heading-regexp (point-max) t) |
621 | (aput 'Man-sections-alist | |
622 | (buffer-substring (Man-linepos 'bol) (Man-linepos))) | |
623 | (forward-line 1) | |
624 | )) | |
625 | ||
626 | (defun Man-build-references-alist () | |
627 | "Build the association list of references (in the SEE ALSO section)." | |
628 | (setq Man-refpages-alist nil) | |
629 | (save-excursion | |
630 | (if (Man-find-section Man-see-also-regexp) | |
631 | (let ((start (progn (forward-line 1) (point))) | |
632 | (end (progn | |
633 | (Man-next-section 1) | |
634 | (point))) | |
635 | hyphenated | |
636 | (runningpoint -1)) | |
637 | (narrow-to-region start end) | |
638 | (goto-char (point-min)) | |
639 | (back-to-indentation) | |
640 | (while (and (not (eobp)) (/= (point) runningpoint)) | |
641 | (setq runningpoint (point)) | |
3eedeb85 RS |
642 | (let* ((eow (re-search-forward Man-reference-regexp end t)) |
643 | (word (buffer-substring (match-beginning 0) (match-end 0))) | |
effdc6a2 RS |
644 | (len (1- (length word)))) |
645 | (if (not eow) nil | |
646 | (if hyphenated | |
647 | (setq word (concat hyphenated word) | |
648 | hyphenated nil)) | |
649 | (if (= (aref word len) ?-) | |
650 | (setq hyphenated (substring word 0 len)) | |
651 | (aput 'Man-refpages-alist word)))) | |
652 | (skip-chars-forward " \t\n,")) | |
653 | )))) | |
654 | ||
655 | (defun Man-build-page-list () | |
656 | "Build the list of separate manpages in the buffer." | |
657 | (setq Man-page-list nil) | |
658 | (save-excursion | |
659 | (let ((page-start (Man-linepos 'bob)) | |
660 | (page-end (Man-linepos 'eob)) | |
661 | (regexp Man-first-heading-regexp)) | |
662 | (goto-char (point-min)) | |
663 | (re-search-forward regexp (point-max) t) | |
664 | (while (not (eobp)) | |
665 | (if (re-search-forward regexp (point-max) t) | |
666 | (progn | |
667 | (setq page-end (Man-linepos 'bol)) | |
668 | (end-of-line)) | |
669 | (goto-char (point-max)) | |
670 | (setq page-end (point))) | |
671 | (setq Man-page-list (append Man-page-list | |
672 | (list (cons page-start page-end))) | |
673 | page-start page-end) | |
674 | )))) | |
675 | ||
676 | \f | |
677 | ;; ====================================================================== | |
e660d0db | 678 | ;; Man mode commands |
effdc6a2 RS |
679 | |
680 | (defun Man-next-section (n) | |
681 | "Move point to Nth next section (default 1)." | |
682 | (interactive "p") | |
683 | (if (looking-at Man-heading-regexp) | |
684 | (forward-line 1)) | |
685 | (if (re-search-forward Man-heading-regexp (point-max) t n) | |
686 | (beginning-of-line) | |
687 | (goto-char (point-max)))) | |
688 | ||
689 | (defun Man-previous-section (n) | |
690 | "Move point to Nth previous section (default 1)." | |
691 | (interactive "p") | |
692 | (if (looking-at Man-heading-regexp) | |
693 | (forward-line -1)) | |
694 | (if (re-search-backward Man-heading-regexp (point-min) t n) | |
695 | (beginning-of-line) | |
696 | (goto-char (point-min)))) | |
697 | ||
698 | (defun Man-find-section (section) | |
699 | "Move point to SECTION if it exists, otherwise don't move point. | |
700 | Returns t if section is found, nil otherwise." | |
701 | (let ((curpos (point))) | |
702 | (goto-char (point-min)) | |
c4b74c20 | 703 | (if (re-search-forward (concat "^\\s-?" section) (point-max) t) |
effdc6a2 RS |
704 | (progn (beginning-of-line) t) |
705 | (goto-char curpos) | |
706 | nil) | |
707 | )) | |
708 | ||
709 | (defun Man-goto-section () | |
710 | "Query for section to move point to." | |
711 | (interactive) | |
712 | (aput 'Man-sections-alist | |
713 | (let* ((default (aheadsym Man-sections-alist)) | |
714 | (completion-ignore-case t) | |
715 | chosen | |
716 | (prompt (concat "Go to section: (default " default ") "))) | |
717 | (setq chosen (completing-read prompt Man-sections-alist)) | |
718 | (if (or (not chosen) | |
719 | (string= chosen "")) | |
720 | default | |
721 | chosen))) | |
722 | (Man-find-section (aheadsym Man-sections-alist))) | |
723 | ||
724 | (defun Man-goto-see-also-section () | |
725 | "Move point the the \"SEE ALSO\" section. | |
c3343fcf | 726 | Actually the section moved to is described by `Man-see-also-regexp'." |
effdc6a2 RS |
727 | (interactive) |
728 | (if (not (Man-find-section Man-see-also-regexp)) | |
729 | (error (concat "No " Man-see-also-regexp | |
730 | " section found in current manpage.")))) | |
731 | ||
e660d0db | 732 | (defun Man-follow-manual-reference (arg reference) |
effdc6a2 | 733 | "Get one of the manpages referred to in the \"SEE ALSO\" section. |
e660d0db RS |
734 | Specify which reference to use; default is based on word at point. |
735 | Prefix argument ARG is passed to `Man-getpage-in-background'." | |
736 | (interactive | |
737 | (if (not Man-refpages-alist) | |
738 | (error "No references in current man page") | |
739 | (list current-prefix-arg | |
740 | (let* ((default (or | |
741 | (car (all-completions | |
742 | (save-excursion | |
743 | (skip-syntax-backward "w()") | |
744 | (skip-chars-forward " \t") | |
745 | (let ((word (current-word))) | |
746 | ;; strip a trailing '-': | |
747 | (if (string-match "-$" word) | |
748 | (substring word 0 (match-beginning 0)) | |
749 | word))) | |
750 | Man-refpages-alist)) | |
751 | (aheadsym Man-refpages-alist))) | |
752 | chosen | |
753 | (prompt (concat "Refer to: (default " default ") "))) | |
754 | (setq chosen (completing-read prompt Man-refpages-alist nil t)) | |
755 | (if (or (not chosen) | |
756 | (string= chosen "")) | |
757 | default | |
758 | chosen))))) | |
effdc6a2 | 759 | (if (not Man-refpages-alist) |
e660d0db RS |
760 | (error "No references found in current manpage") |
761 | (aput 'Man-refpages-alist reference) | |
effdc6a2 RS |
762 | (Man-getpage-in-background |
763 | (Man-translate-references (aheadsym Man-refpages-alist)) | |
e660d0db | 764 | arg))) |
effdc6a2 RS |
765 | |
766 | (defun Man-quit () | |
767 | "Kill the buffer containing the manpage." | |
768 | (interactive) | |
769 | (let ((buff (current-buffer))) | |
770 | (delete-windows-on buff) | |
771 | (kill-buffer buff))) | |
772 | ||
773 | (defun Man-goto-page (page) | |
774 | "Go to the manual page on page PAGE." | |
775 | (interactive | |
776 | (if (not Man-page-list) | |
777 | (error "You're looking at the only manpage in the buffer.") | |
778 | (format "nGo to manpage [1-%d]: " (length Man-page-list)))) | |
779 | (if (or (< page 1) | |
780 | (> page (length Man-page-list))) | |
781 | (error "No manpage %d found" page)) | |
782 | (let* ((page-range (nth (1- page) Man-page-list)) | |
783 | (page-start (car page-range)) | |
784 | (page-end (cdr page-range))) | |
785 | (setq Man-current-page page | |
786 | Man-page-mode-string (Man-page-mode-string)) | |
787 | (widen) | |
788 | (goto-char page-start) | |
789 | (narrow-to-region page-start page-end) | |
790 | (Man-build-section-alist) | |
4cd88e9c RS |
791 | ;; Don't let bugs in Man-build-references-alist |
792 | ;; interfere with ordinary use of this package. | |
793 | (condition-case nil | |
794 | (Man-build-references-alist) | |
795 | (error)) | |
effdc6a2 RS |
796 | (widen) |
797 | (narrow-to-region page-start page-end) | |
798 | (goto-char (point-min)))) | |
799 | ||
800 | ||
801 | (defun Man-next-manpage () | |
802 | "Find the next manpage entry in the buffer." | |
803 | (interactive) | |
804 | (if (= (length Man-page-list) 1) | |
805 | (error "This is the only manpage in the buffer.")) | |
806 | (if (< Man-current-page (length Man-page-list)) | |
807 | (Man-goto-page (1+ Man-current-page)) | |
808 | (if Man-circular-pages-p | |
809 | (Man-goto-page 1) | |
810 | (error "You're looking at the last manpage in the buffer.")))) | |
811 | ||
812 | (defun Man-previous-manpage () | |
813 | "Find the previous manpage entry in the buffer." | |
814 | (interactive) | |
815 | (if (= (length Man-page-list) 1) | |
816 | (error "This is the only manpage in the buffer.")) | |
817 | (if (> Man-current-page 1) | |
818 | (Man-goto-page (1- Man-current-page)) | |
819 | (if Man-circular-pages-p | |
820 | (Man-goto-page (length Man-page-list)) | |
821 | (error "You're looking at the first manpage in the buffer.")))) | |
733155db JB |
822 | \f |
823 | (provide 'man) | |
824 | ||
6594deb0 | 825 | ;;; man.el ends here |