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