Commit | Line | Data |
---|---|---|
327a6cca | 1 | ;;; man.el --- browse UNIX manual pages -*- coding: utf-8 -*- |
55535639 | 2 | |
ab422c4d PE |
3 | ;; Copyright (C) 1993-1994, 1996-1997, 2001-2013 Free Software |
4 | ;; Foundation, Inc. | |
55535639 PJ |
5 | |
6 | ;; Author: Barry A. Warsaw <bwarsaw@cen.com> | |
7 | ;; Maintainer: FSF | |
8 | ;; Keywords: help | |
9 | ;; Adapted-By: ESR, pot | |
10 | ||
11 | ;; This file is part of GNU Emacs. | |
12 | ||
eb3fa2cf | 13 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
55535639 | 14 | ;; it under the terms of the GNU General Public License as published by |
eb3fa2cf GM |
15 | ;; the Free Software Foundation, either version 3 of the License, or |
16 | ;; (at your option) any later version. | |
55535639 PJ |
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 | |
eb3fa2cf | 24 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
55535639 PJ |
25 | |
26 | ;;; Commentary: | |
27 | ||
28 | ;; This code provides a function, `man', with which you can browse | |
29 | ;; UNIX manual pages. Formatting is done in background so that you | |
30 | ;; can continue to use your Emacs while processing is going on. | |
31 | ;; | |
32 | ;; The mode also supports hypertext-like following of manual page SEE | |
33 | ;; ALSO references, and other features. See below or do `?' in a | |
34 | ;; manual page buffer for details. | |
35 | ||
36 | ;; ========== Credits and History ========== | |
37 | ;; In mid 1991, several people posted some interesting improvements to | |
a88459cd | 38 | ;; man.el from the standard Emacs 18.57 distribution. I liked many of |
55535639 PJ |
39 | ;; these, but wanted everything in one single package, so I decided |
40 | ;; to incorporate them into a single manual browsing mode. While | |
41 | ;; much of the code here has been rewritten, and some features added, | |
42 | ;; these folks deserve lots of credit for providing the initial | |
43 | ;; excellent packages on which this one is based. | |
44 | ||
45 | ;; Nick Duffek <duffek@chaos.cs.brandeis.edu>, posted a very nice | |
46 | ;; improvement which retrieved and cleaned the manpages in a | |
47 | ;; background process, and which correctly deciphered such options as | |
48 | ;; man -k. | |
49 | ||
50 | ;; Eric Rose <erose@jessica.stanford.edu>, submitted manual.el which | |
51 | ;; provided a very nice manual browsing mode. | |
52 | ||
53 | ;; This package was available as `superman.el' from the LCD package | |
54 | ;; for some time before it was accepted into Emacs 19. The entry | |
55 | ;; point and some other names have been changed to make it a drop-in | |
56 | ;; replacement for the old man.el package. | |
57 | ||
58 | ;; Francesco Potorti` <pot@cnuce.cnr.it> cleaned it up thoroughly, | |
59 | ;; making it faster, more robust and more tolerant of different | |
60 | ;; systems' man idiosyncrasies. | |
61 | ||
62 | ;; ========== Features ========== | |
63 | ;; + Runs "man" in the background and pipes the results through a | |
64 | ;; series of sed and awk scripts so that all retrieving and cleaning | |
4cb071a4 | 65 | ;; is done in the background. The cleaning commands are configurable. |
55535639 PJ |
66 | ;; + Syntax is the same as Un*x man |
67 | ;; + Functionality is the same as Un*x man, including "man -k" and | |
68 | ;; "man <section>", etc. | |
69 | ;; + Provides a manual browsing mode with keybindings for traversing | |
70 | ;; the sections of a manpage, following references in the SEE ALSO | |
71 | ;; section, and more. | |
72 | ;; + Multiple manpages created with the same man command are put into | |
73 | ;; a narrowed buffer circular list. | |
74 | ||
75 | ;; ============= TODO =========== | |
76 | ;; - Add a command for printing. | |
fffa137c | 77 | ;; - The awk script deletes multiple blank lines. This behavior does |
55535639 PJ |
78 | ;; not allow to understand if there was indeed a blank line at the |
79 | ;; end or beginning of a page (after the header, or before the | |
80 | ;; footer). A different algorithm should be used. It is easy to | |
81 | ;; compute how many blank lines there are before and after the page | |
82 | ;; headers, and after the page footer. But it is possible to compute | |
72397ff1 | 83 | ;; the number of blank lines before the page footer by heuristics |
55535639 PJ |
84 | ;; only. Is it worth doing? |
85 | ;; - Allow a user option to mean that all the manpages should go in | |
86 | ;; the same buffer, where they can be browsed with M-n and M-p. | |
55535639 PJ |
87 | |
88 | \f | |
89 | ;;; Code: | |
90 | ||
456e62c2 | 91 | (require 'ansi-color) |
4edd9faf | 92 | (require 'button) |
55535639 | 93 | |
55535639 PJ |
94 | (defgroup man nil |
95 | "Browse UNIX manual pages." | |
96 | :prefix "Man-" | |
ff90f4b0 | 97 | :group 'external |
55535639 PJ |
98 | :group 'help) |
99 | ||
55535639 | 100 | (defvar Man-notify) |
dee4ef93 | 101 | |
55535639 | 102 | (defcustom Man-filter-list nil |
9201cc28 | 103 | "Manpage cleaning filter command phrases. |
55535639 PJ |
104 | This variable contains a list of the following form: |
105 | ||
106 | '((command-string phrase-string*)*) | |
107 | ||
108 | Each phrase-string is concatenated onto the command-string to form a | |
109 | command filter. The (standard) output (and standard error) of the Un*x | |
110 | man command is piped through each command filter in the order the | |
111 | commands appear in the association list. The final output is placed in | |
112 | the manpage buffer." | |
113 | :type '(repeat (list (string :tag "Command String") | |
114 | (repeat :inline t | |
115 | (string :tag "Phrase String")))) | |
116 | :group 'man) | |
117 | ||
55535639 PJ |
118 | (defvar Man-uses-untabify-flag t |
119 | "Non-nil means use `untabify' instead of `Man-untabify-command'.") | |
55535639 PJ |
120 | (defvar Man-sed-script nil |
121 | "Script for sed to nuke backspaces and ANSI codes from manpages.") | |
122 | ||
55535639 | 123 | (defcustom Man-fontify-manpage-flag t |
a88459cd | 124 | "Non-nil means make up the manpage with fonts." |
55535639 PJ |
125 | :type 'boolean |
126 | :group 'man) | |
127 | ||
456e62c2 WJ |
128 | (defface Man-overstrike |
129 | '((t (:inherit bold))) | |
a88459cd | 130 | "Face to use when fontifying overstrike." |
456e62c2 | 131 | :group 'man |
2a1e2476 | 132 | :version "24.3") |
55535639 | 133 | |
456e62c2 WJ |
134 | (defface Man-underline |
135 | '((t (:inherit underline))) | |
a88459cd | 136 | "Face to use when fontifying underlining." |
456e62c2 | 137 | :group 'man |
2a1e2476 | 138 | :version "24.3") |
55535639 | 139 | |
456e62c2 WJ |
140 | (defface Man-reverse |
141 | '((t (:inherit highlight))) | |
a88459cd | 142 | "Face to use when fontifying reverse video." |
456e62c2 | 143 | :group 'man |
2a1e2476 | 144 | :version "24.3") |
456e62c2 WJ |
145 | |
146 | (defvar Man-ansi-color-map (let ((ansi-color-faces-vector | |
147 | [ default Man-overstrike default Man-underline | |
148 | Man-underline default default Man-reverse ])) | |
149 | (ansi-color-make-color-map)) | |
150 | "The value used here for `ansi-color-map'.") | |
079c2d00 | 151 | |
55535639 PJ |
152 | ;; Use the value of the obsolete user option Man-notify, if set. |
153 | (defcustom Man-notify-method (if (boundp 'Man-notify) Man-notify 'friendly) | |
a88459cd | 154 | "Selects the behavior when manpage is ready. |
55535639 PJ |
155 | This variable may have one of the following values, where (sf) means |
156 | that the frames are switched, so the manpage is displayed in the frame | |
157 | where the man command was called from: | |
158 | ||
159 | newframe -- put the manpage in its own frame (see `Man-frame-parameters') | |
160 | pushy -- make the manpage the current buffer in the current window | |
161 | bully -- make the manpage the current buffer and only window (sf) | |
162 | aggressive -- make the manpage the current buffer in the other window (sf) | |
163 | friendly -- display manpage in the other window but don't make current (sf) | |
164 | polite -- don't display manpage, but prints message and beep when ready | |
165 | quiet -- like `polite', but don't beep | |
166 | meek -- make no indication that the manpage is ready | |
167 | ||
168 | Any other value of `Man-notify-method' is equivalent to `meek'." | |
169 | :type '(radio (const newframe) (const pushy) (const bully) | |
170 | (const aggressive) (const friendly) | |
171 | (const polite) (const quiet) (const meek)) | |
172 | :group 'man) | |
173 | ||
aec2bd36 | 174 | (defcustom Man-width nil |
a88459cd | 175 | "Number of columns for which manual pages should be formatted. |
aec2bd36 JL |
176 | If nil, the width of the window selected at the moment of man |
177 | invocation is used. If non-nil, the width of the frame selected | |
178 | at the moment of man invocation is used. The value also can be a | |
179 | positive integer." | |
180 | :type '(choice (const :tag "Window width" nil) | |
181 | (const :tag "Frame width" t) | |
182 | (integer :tag "Specific width" :value 65)) | |
183 | :group 'man) | |
184 | ||
55535639 | 185 | (defcustom Man-frame-parameters nil |
a88459cd | 186 | "Frame parameter list for creating a new frame for a manual page." |
55535639 PJ |
187 | :type 'sexp |
188 | :group 'man) | |
189 | ||
190 | (defcustom Man-downcase-section-letters-flag t | |
a88459cd | 191 | "Non-nil means letters in sections are converted to lower case. |
55535639 PJ |
192 | Some Un*x man commands can't handle uppercase letters in sections, for |
193 | example \"man 2V chmod\", but they are often displayed in the manpage | |
194 | with the upper case letter. When this variable is t, the section | |
195 | letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before | |
196 | being sent to the man background process." | |
197 | :type 'boolean | |
198 | :group 'man) | |
199 | ||
200 | (defcustom Man-circular-pages-flag t | |
a88459cd | 201 | "Non-nil means the manpage list is treated as circular for traversal." |
55535639 PJ |
202 | :type 'boolean |
203 | :group 'man) | |
204 | ||
205 | (defcustom Man-section-translations-alist | |
206 | (list | |
207 | '("3C++" . "3") | |
208 | ;; Some systems have a real 3x man section, so let's comment this. | |
209 | ;; '("3X" . "3") ; Xlib man pages | |
210 | '("3X11" . "3") | |
211 | '("1-UCB" . "")) | |
a88459cd | 212 | "Association list of bogus sections to real section numbers. |
55535639 PJ |
213 | Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in |
214 | their references which Un*x `man' does not recognize. This | |
215 | association list is used to translate those sections, when found, to | |
216 | the associated section number." | |
217 | :type '(repeat (cons (string :tag "Bogus Section") | |
218 | (string :tag "Real Section"))) | |
219 | :group 'man) | |
220 | ||
ac2eceee | 221 | ;; FIXME see comments at ffap-c-path. |
4edd9faf | 222 | (defcustom Man-header-file-path |
ac2eceee GM |
223 | (let ((arch (with-temp-buffer |
224 | (when (eq 0 (ignore-errors | |
225 | (call-process "gcc" nil '(t nil) nil | |
226 | "-print-multiarch"))) | |
227 | (goto-char (point-min)) | |
228 | (buffer-substring (point) (line-end-position))))) | |
229 | (base '("/usr/include" "/usr/local/include"))) | |
230 | (if (zerop (length arch)) | |
231 | base | |
232 | (append base (list (expand-file-name arch "/usr/include"))))) | |
4edd9faf | 233 | "C Header file search path used in Man." |
ac2eceee | 234 | :version "24.1" ; add multiarch |
4edd9faf JB |
235 | :type '(repeat string) |
236 | :group 'man) | |
237 | ||
398a825b SM |
238 | (defcustom Man-name-local-regexp (concat "^" (regexp-opt '("NOM" "NAME")) "$") |
239 | "Regexp that matches the text that precedes the command's name. | |
45be326a | 240 | Used in `bookmark-set' to get the default bookmark name." |
2bed3f04 | 241 | :version "24.1" |
45be326a TV |
242 | :type 'string :group 'bookmark) |
243 | ||
dee4ef93 CY |
244 | (defcustom manual-program "man" |
245 | "Program used by `man' to produce man pages." | |
246 | :type 'string | |
247 | :group 'man) | |
55535639 | 248 | |
dee4ef93 CY |
249 | (defcustom Man-untabify-command "pr" |
250 | "Program used by `man' for untabifying." | |
251 | :type 'string | |
252 | :group 'man) | |
55535639 | 253 | |
dee4ef93 CY |
254 | (defcustom Man-untabify-command-args (list "-t" "-e") |
255 | "List of arguments to be passed to `Man-untabify-command' (which see)." | |
256 | :type '(repeat string) | |
257 | :group 'man) | |
55535639 | 258 | |
dee4ef93 CY |
259 | (defcustom Man-sed-command "sed" |
260 | "Program used by `man' to process sed scripts." | |
261 | :type 'string | |
262 | :group 'man) | |
55535639 | 263 | |
dee4ef93 CY |
264 | (defcustom Man-awk-command "awk" |
265 | "Program used by `man' to process awk scripts." | |
266 | :type 'string | |
267 | :group 'man) | |
55535639 | 268 | |
dee4ef93 CY |
269 | (defcustom Man-mode-hook nil |
270 | "Hook run when Man mode is enabled." | |
271 | :type 'hook | |
272 | :group 'man) | |
55535639 | 273 | |
dee4ef93 CY |
274 | (defcustom Man-cooked-hook nil |
275 | "Hook run after removing backspaces but before `Man-mode' processing." | |
276 | :type 'hook | |
277 | :group 'man) | |
55535639 | 278 | |
327a6cca | 279 | (defvar Man-name-regexp "[-a-zA-Z0-9_+][-a-zA-Z0-9_.:+]*" |
55535639 PJ |
280 | "Regular expression describing the name of a manpage (without section).") |
281 | ||
19437ce5 | 282 | (defvar Man-section-regexp "[0-9][a-zA-Z0-9+]*\\|[LNln]" |
55535639 PJ |
283 | "Regular expression describing a manpage section within parentheses.") |
284 | ||
285 | (defvar Man-page-header-regexp | |
a1de6c6a | 286 | (if (string-match "-solaris2\\." system-configuration) |
55535639 PJ |
287 | (concat "^[-A-Za-z0-9_].*[ \t]\\(" Man-name-regexp |
288 | "(\\(" Man-section-regexp "\\))\\)$") | |
289 | (concat "^[ \t]*\\(" Man-name-regexp | |
290 | "(\\(" Man-section-regexp "\\))\\).*\\1")) | |
291 | "Regular expression describing the heading of a page.") | |
292 | ||
30971bf9 | 293 | (defvar Man-heading-regexp "^\\([A-Z][A-Z0-9 /-]+\\)$" |
55535639 PJ |
294 | "Regular expression describing a manpage heading entry.") |
295 | ||
296 | (defvar Man-see-also-regexp "SEE ALSO" | |
297 | "Regular expression for SEE ALSO heading (or your equivalent). | |
298 | This regexp should not start with a `^' character.") | |
299 | ||
30971bf9 JL |
300 | ;; This used to have leading space [ \t]*, but was removed because it |
301 | ;; causes false page splits on an occasional NAME with leading space | |
302 | ;; inside a manpage. And `Man-heading-regexp' doesn't have [ \t]* anyway. | |
303 | (defvar Man-first-heading-regexp "^NAME$\\|^[ \t]*No manual entry fo.*$" | |
55535639 PJ |
304 | "Regular expression describing first heading on a manpage. |
305 | This regular expression should start with a `^' character.") | |
306 | ||
307 | (defvar Man-reference-regexp | |
0dd8b6da LMI |
308 | (concat "\\(" Man-name-regexp |
309 | "\\(\n[ \t]+" Man-name-regexp "\\)*\\)[ \t]*(\\(" | |
310 | Man-section-regexp "\\))") | |
55535639 PJ |
311 | "Regular expression describing a reference to another manpage.") |
312 | ||
30abc4f4 MY |
313 | (defvar Man-apropos-regexp |
314 | (concat "\\\[\\(" Man-name-regexp "\\)\\\][ \t]*(\\(" Man-section-regexp "\\))") | |
315 | "Regular expression describing a reference to manpages in \"man -k output\".") | |
316 | ||
4edd9faf JB |
317 | (defvar Man-synopsis-regexp "SYNOPSIS" |
318 | "Regular expression for SYNOPSIS heading (or your equivalent). | |
319 | This regexp should not start with a `^' character.") | |
320 | ||
e8defde3 SM |
321 | (defvar Man-files-regexp "FILES\\>" |
322 | ;; Add \> so as not to match mount(8)'s FILESYSTEM INDEPENDENT MOUNT OPTIONS. | |
4edd9faf JB |
323 | "Regular expression for FILES heading (or your equivalent). |
324 | This regexp should not start with a `^' character.") | |
325 | ||
326 | (defvar Man-include-regexp "#[ \t]*include[ \t]*" | |
327 | "Regular expression describing the #include (directive of cpp).") | |
328 | ||
13780e21 | 329 | (defvar Man-file-name-regexp "[^<>\", \t\n]+" |
4edd9faf JB |
330 | "Regular expression describing <> in #include line (directive of cpp).") |
331 | ||
332 | (defvar Man-normal-file-prefix-regexp "[/~$]" | |
333 | "Regular expression describing a file path appeared in FILES section.") | |
334 | ||
335 | (defvar Man-header-regexp | |
336 | (concat "\\(" Man-include-regexp "\\)" | |
337 | "[<\"]" | |
338 | "\\(" Man-file-name-regexp "\\)" | |
339 | "[>\"]") | |
340 | "Regular expression describing references to header files.") | |
341 | ||
342 | (defvar Man-normal-file-regexp | |
343 | (concat Man-normal-file-prefix-regexp Man-file-name-regexp) | |
344 | "Regular expression describing references to normal files.") | |
345 | ||
55535639 | 346 | ;; This includes the section as an optional part to catch hyphenated |
40b1a3a9 | 347 | ;; references to manpages. |
55535639 PJ |
348 | (defvar Man-hyphenated-reference-regexp |
349 | (concat "\\(" Man-name-regexp "\\)\\((\\(" Man-section-regexp "\\))\\)?") | |
350 | "Regular expression describing a reference in the SEE ALSO section.") | |
351 | ||
dee4ef93 | 352 | (defcustom Man-switches "" |
7e03f4c8 | 353 | "Switches passed to the man command, as a single string. |
dee4ef93 CY |
354 | For example, the -a switch lets you see all the manpages for a |
355 | specified subject, if your `man' program supports it." | |
356 | :type 'string | |
357 | :group 'man) | |
55535639 PJ |
358 | |
359 | (defvar Man-specified-section-option | |
360 | (if (string-match "-solaris[0-9.]*$" system-configuration) | |
361 | "-s" | |
362 | "") | |
363 | "Option that indicates a specified a manual section name.") | |
364 | ||
aec2bd36 JL |
365 | (defvar Man-support-local-filenames 'auto-detect |
366 | "Internal cache for the value of the function `Man-support-local-filenames'. | |
367 | `auto-detect' means the value is not yet determined. | |
368 | Otherwise, the value is whatever the function | |
369 | `Man-support-local-filenames' should return.") | |
370 | ||
55535639 PJ |
371 | \f |
372 | ;; other variables and keymap initializations | |
a88459cd SM |
373 | (defvar Man-original-frame) |
374 | (make-variable-buffer-local 'Man-original-frame) | |
375 | (defvar Man-arguments) | |
376 | (make-variable-buffer-local 'Man-arguments) | |
377 | (put 'Man-arguments 'permanent-local t) | |
378 | ||
8b6c19f4 SM |
379 | (defvar Man--sections nil) |
380 | (make-variable-buffer-local 'Man--sections) | |
381 | (defvar Man--refpages nil) | |
382 | (make-variable-buffer-local 'Man--refpages) | |
a88459cd | 383 | (defvar Man-page-list nil) |
55535639 | 384 | (make-variable-buffer-local 'Man-page-list) |
a88459cd | 385 | (defvar Man-current-page 0) |
55535639 | 386 | (make-variable-buffer-local 'Man-current-page) |
a88459cd | 387 | (defvar Man-page-mode-string "1 of 1") |
55535639 | 388 | (make-variable-buffer-local 'Man-page-mode-string) |
55535639 PJ |
389 | |
390 | (defconst Man-sysv-sed-script "\ | |
391 | /\b/ { s/_\b//g | |
392 | s/\b_//g | |
393 | s/o\b+/o/g | |
394 | s/+\bo/o/g | |
395 | :ovstrk | |
396 | s/\\(.\\)\b\\1/\\1/g | |
397 | t ovstrk | |
398 | } | |
399 | /\e\\[[0-9][0-9]*m/ s///g" | |
400 | "Script for sysV-like sed to nuke backspaces and ANSI codes from manpages.") | |
401 | ||
402 | (defconst Man-berkeley-sed-script "\ | |
403 | /\b/ { s/_\b//g\\ | |
404 | s/\b_//g\\ | |
405 | s/o\b+/o/g\\ | |
406 | s/+\bo/o/g\\ | |
407 | :ovstrk\\ | |
408 | s/\\(.\\)\b\\1/\\1/g\\ | |
409 | t ovstrk\\ | |
410 | }\\ | |
411 | /\e\\[[0-9][0-9]*m/ s///g" | |
412 | "Script for berkeley-like sed to nuke backspaces and ANSI codes from manpages.") | |
413 | ||
ea1f948d JL |
414 | (defvar Man-topic-history nil "Topic read history.") |
415 | ||
55535639 PJ |
416 | (defvar man-mode-syntax-table |
417 | (let ((table (copy-syntax-table (standard-syntax-table)))) | |
418 | (modify-syntax-entry ?. "w" table) | |
419 | (modify-syntax-entry ?_ "w" table) | |
a46f2d6d | 420 | (modify-syntax-entry ?: "w" table) ; for PDL::Primitive in Perl man pages |
55535639 PJ |
421 | table) |
422 | "Syntax table used in Man mode buffers.") | |
423 | ||
a88459cd SM |
424 | (defvar Man-mode-map |
425 | (let ((map (make-sparse-keymap))) | |
426 | (suppress-keymap map) | |
427 | (set-keymap-parent map button-buffer-map) | |
428 | ||
ce3cefcc CY |
429 | (define-key map " " 'scroll-up-command) |
430 | (define-key map "\177" 'scroll-down-command) | |
a88459cd SM |
431 | (define-key map "n" 'Man-next-section) |
432 | (define-key map "p" 'Man-previous-section) | |
433 | (define-key map "\en" 'Man-next-manpage) | |
434 | (define-key map "\ep" 'Man-previous-manpage) | |
435 | (define-key map ">" 'end-of-buffer) | |
436 | (define-key map "<" 'beginning-of-buffer) | |
437 | (define-key map "." 'beginning-of-buffer) | |
438 | (define-key map "r" 'Man-follow-manual-reference) | |
439 | (define-key map "g" 'Man-goto-section) | |
440 | (define-key map "s" 'Man-goto-see-also-section) | |
441 | (define-key map "k" 'Man-kill) | |
442 | (define-key map "q" 'Man-quit) | |
443 | (define-key map "m" 'man) | |
444 | ;; Not all the man references get buttons currently. The text in the | |
445 | ;; manual page can contain references to other man pages | |
446 | (define-key map "\r" 'man-follow) | |
447 | (define-key map "?" 'describe-mode) | |
448 | map) | |
449 | "Keymap for Man mode.") | |
55535639 | 450 | |
4edd9faf | 451 | ;; buttons |
50071f01 | 452 | (define-button-type 'Man-abstract-xref-man-page |
2bac7f17 | 453 | 'follow-link t |
50071f01 MY |
454 | 'help-echo "mouse-2, RET: display this man page" |
455 | 'func nil | |
38363db7 CY |
456 | 'action #'Man-xref-button-action) |
457 | ||
9201cc28 | 458 | (defun Man-xref-button-action (button) |
38363db7 | 459 | (let ((target (button-get button 'Man-target-string))) |
9201cc28 | 460 | (funcall |
38363db7 CY |
461 | (button-get button 'func) |
462 | (cond ((null target) | |
463 | (button-label button)) | |
464 | ((functionp target) | |
465 | (funcall target (button-start button))) | |
466 | (t target))))) | |
50071f01 | 467 | |
9201cc28 | 468 | (define-button-type 'Man-xref-man-page |
50071f01 MY |
469 | :supertype 'Man-abstract-xref-man-page |
470 | 'func 'man-follow) | |
471 | ||
4edd9faf JB |
472 | |
473 | (define-button-type 'Man-xref-header-file | |
500e05aa JB |
474 | 'action (lambda (button) |
475 | (let ((w (button-get button 'Man-target-string))) | |
476 | (unless (Man-view-header-file w) | |
477 | (error "Cannot find header file: %s" w)))) | |
2bac7f17 | 478 | 'follow-link t |
500e05aa | 479 | 'help-echo "mouse-2: display this header file") |
4edd9faf JB |
480 | |
481 | (define-button-type 'Man-xref-normal-file | |
482 | 'action (lambda (button) | |
483 | (let ((f (substitute-in-file-name | |
484 | (button-get button 'Man-target-string)))) | |
485 | (if (file-exists-p f) | |
486 | (if (file-readable-p f) | |
487 | (view-file f) | |
488 | (error "Cannot read a file: %s" f)) | |
489 | (error "Cannot find a file: %s" f)))) | |
2bac7f17 | 490 | 'follow-link t |
5bad6053 | 491 | 'help-echo "mouse-2: display this file") |
4edd9faf | 492 | |
55535639 PJ |
493 | \f |
494 | ;; ====================================================================== | |
495 | ;; utilities | |
496 | ||
497 | (defun Man-init-defvars () | |
927c60bd | 498 | "Used for initializing variables based on display's color support. |
55535639 PJ |
499 | This is necessary if one wants to dump man.el with Emacs." |
500 | ||
501 | ;; Avoid possible error in call-process by using a directory that must exist. | |
502 | (let ((default-directory "/")) | |
503 | (setq Man-sed-script | |
504 | (cond | |
505 | (Man-fontify-manpage-flag | |
506 | nil) | |
15502042 | 507 | ((eq 0 (call-process Man-sed-command nil nil nil Man-sysv-sed-script)) |
55535639 | 508 | Man-sysv-sed-script) |
15502042 | 509 | ((eq 0 (call-process Man-sed-command nil nil nil Man-berkeley-sed-script)) |
55535639 PJ |
510 | Man-berkeley-sed-script) |
511 | (t | |
512 | nil)))) | |
513 | ||
514 | (setq Man-filter-list | |
515 | ;; Avoid trailing nil which confuses customize. | |
516 | (apply 'list | |
517 | (cons | |
518 | Man-sed-command | |
1bf0da02 EZ |
519 | (if (eq system-type 'windows-nt) |
520 | ;; Windows needs ".." quoting, not '..'. | |
521 | (list | |
522 | "-e \"/Reformatting page. Wait/d\"" | |
523 | "-e \"/Reformatting entry. Wait/d\"" | |
524 | "-e \"/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d\"" | |
525 | "-e \"/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d\"" | |
526 | "-e \"/^Printed[ \t][0-9].*[0-9]$/d\"" | |
527 | "-e \"/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d\"" | |
528 | "-e \"/^[A-Za-z].*Last[ \t]change:/d\"" | |
529 | "-e \"/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d\"" | |
530 | "-e \"/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d\"") | |
531 | (list | |
532 | (if Man-sed-script | |
533 | (concat "-e '" Man-sed-script "'") | |
534 | "") | |
535 | "-e '/^[\001-\032][\001-\032]*$/d'" | |
536 | "-e '/\e[789]/s///g'" | |
537 | "-e '/Reformatting page. Wait/d'" | |
538 | "-e '/Reformatting entry. Wait/d'" | |
539 | "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'" | |
540 | "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'" | |
541 | "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'" | |
542 | "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'" | |
543 | "-e '/^Printed[ \t][0-9].*[0-9]$/d'" | |
544 | "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'" | |
545 | "-e '/^[A-Za-z].*Last[ \t]change:/d'" | |
546 | "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'" | |
547 | "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'" | |
548 | "-e '/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d'" | |
549 | ))) | |
550 | ;; Windows doesn't support multi-line commands, so don't | |
551 | ;; invoke Awk there. | |
552 | (unless (eq system-type 'windows-nt) | |
553 | (cons | |
554 | Man-awk-command | |
555 | (list | |
556 | "'\n" | |
557 | "BEGIN { blankline=0; anonblank=0; }\n" | |
558 | "/^$/ { if (anonblank==0) next; }\n" | |
559 | "{ anonblank=1; }\n" | |
560 | "/^$/ { blankline++; next; }\n" | |
561 | "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n" | |
562 | "'" | |
563 | ))) | |
55535639 PJ |
564 | (if (not Man-uses-untabify-flag) |
565 | ;; The outer list will be stripped off by apply. | |
566 | (list (cons | |
567 | Man-untabify-command | |
568 | Man-untabify-command-args)) | |
569 | ))) | |
570 | ) | |
571 | ||
55535639 PJ |
572 | (defsubst Man-make-page-mode-string () |
573 | "Formats part of the mode line for Man mode." | |
574 | (format "%s page %d of %d" | |
575 | (or (nth 2 (nth (1- Man-current-page) Man-page-list)) | |
576 | "") | |
577 | Man-current-page | |
578 | (length Man-page-list))) | |
579 | ||
580 | (defsubst Man-build-man-command () | |
581 | "Builds the entire background manpage and cleaning command." | |
582 | (let ((command (concat manual-program " " Man-switches | |
aec2bd36 JL |
583 | (cond |
584 | ;; Already has %s | |
585 | ((string-match "%s" manual-program) "") | |
586 | ;; Stock MS-DOS shells cannot redirect stderr; | |
587 | ;; `call-process' below sends it to /dev/null, | |
588 | ;; so we don't need `2>' even with DOS shells | |
589 | ;; which do support stderr redirection. | |
590 | ((not (fboundp 'start-process)) " %s") | |
591 | ((concat " %s 2>" null-device))))) | |
55535639 PJ |
592 | (flist Man-filter-list)) |
593 | (while (and flist (car flist)) | |
594 | (let ((pcom (car (car flist))) | |
595 | (pargs (cdr (car flist)))) | |
596 | (setq command | |
597 | (concat command " | " pcom " " | |
598 | (mapconcat (lambda (phrase) | |
599 | (if (not (stringp phrase)) | |
600 | (error "Malformed Man-filter-list")) | |
601 | phrase) | |
602 | pargs " "))) | |
7e734986 | 603 | (setq flist (cdr flist)))) |
55535639 PJ |
604 | command)) |
605 | ||
7e734986 JB |
606 | |
607 | (defun Man-translate-cleanup (string) | |
608 | "Strip leading, trailing and middle spaces." | |
609 | (when (stringp string) | |
610 | ;; Strip leading and trailing | |
611 | (if (string-match "^[ \t\f\r\n]*\\(.+[^ \t\f\r\n]\\)" string) | |
612 | (setq string (match-string 1 string))) | |
613 | ;; middle spaces | |
614 | (setq string (replace-regexp-in-string "[\t\r\n]" " " string)) | |
615 | (setq string (replace-regexp-in-string " +" " " string)) | |
616 | string)) | |
617 | ||
55535639 PJ |
618 | (defun Man-translate-references (ref) |
619 | "Translates REF from \"chmod(2V)\" to \"2v chmod\" style. | |
620 | Leave it as is if already in that style. Possibly downcase and | |
927c60bd JB |
621 | translate the section (see the `Man-downcase-section-letters-flag' |
622 | and the `Man-section-translations-alist' variables)." | |
55535639 | 623 | (let ((name "") |
7e734986 JB |
624 | (section "") |
625 | (slist Man-section-translations-alist)) | |
626 | (setq ref (Man-translate-cleanup ref)) | |
55535639 PJ |
627 | (cond |
628 | ;; "chmod(2V)" case ? | |
629 | ((string-match (concat "^" Man-reference-regexp "$") ref) | |
0dd8b6da LMI |
630 | (setq name (replace-regexp-in-string "[\n\t ]" "" (match-string 1 ref)) |
631 | section (match-string 3 ref))) | |
55535639 PJ |
632 | ;; "2v chmod" case ? |
633 | ((string-match (concat "^\\(" Man-section-regexp | |
634 | "\\) +\\(" Man-name-regexp "\\)$") ref) | |
72397ff1 SM |
635 | (setq name (match-string 2 ref) |
636 | section (match-string 1 ref)))) | |
55535639 PJ |
637 | (if (string= name "") |
638 | ref ; Return the reference as is | |
639 | (if Man-downcase-section-letters-flag | |
640 | (setq section (downcase section))) | |
641 | (while slist | |
642 | (let ((s1 (car (car slist))) | |
643 | (s2 (cdr (car slist)))) | |
644 | (setq slist (cdr slist)) | |
645 | (if Man-downcase-section-letters-flag | |
646 | (setq s1 (downcase s1))) | |
647 | (if (not (string= s1 section)) nil | |
648 | (setq section (if Man-downcase-section-letters-flag | |
649 | (downcase s2) | |
650 | s2) | |
651 | slist nil)))) | |
652 | (concat Man-specified-section-option section " " name)))) | |
653 | ||
aec2bd36 | 654 | (defun Man-support-local-filenames () |
3ab7ebb9 GM |
655 | "Return non-nil if the man command supports local filenames. |
656 | Different man programs support this feature in different ways. | |
657 | The default Debian man program (\"man-db\") has a `--local-file' | |
658 | \(or `-l') option for this purpose. The default Red Hat man | |
659 | program has no such option, but interprets any name containing | |
660 | a \"/\" as a local filename. The function returns either `man-db' | |
661 | `man', or nil." | |
662 | (if (eq Man-support-local-filenames 'auto-detect) | |
663 | (setq Man-support-local-filenames | |
664 | (with-temp-buffer | |
665 | (let ((default-directory | |
666 | ;; Ensure that `default-directory' exists and is readable. | |
667 | (if (and (file-directory-p default-directory) | |
668 | (file-readable-p default-directory)) | |
669 | default-directory | |
670 | (expand-file-name "~/")))) | |
671 | (ignore-errors | |
672 | (call-process manual-program nil t nil "--help"))) | |
673 | (cond ((search-backward "--local-file" nil 'move) | |
674 | 'man-db) | |
675 | ;; This feature seems to be present in at least ver 1.4f, | |
676 | ;; which is about 20 years old. | |
677 | ;; I don't know if this version has an official name? | |
678 | ((looking-at "^man, versione? [1-9]") | |
679 | 'man)))) | |
680 | Man-support-local-filenames)) | |
aec2bd36 | 681 | |
55535639 PJ |
682 | \f |
683 | ;; ====================================================================== | |
d8b3b1a1 | 684 | ;; default man entry: get word near point |
55535639 | 685 | |
d8b3b1a1 MR |
686 | (defun Man-default-man-entry (&optional pos) |
687 | "Guess default manual entry based on the text near position POS. | |
688 | POS defaults to `point'." | |
10c877fe | 689 | (let (word start column distance) |
55535639 | 690 | (save-excursion |
d8b3b1a1 MR |
691 | (when pos (goto-char pos)) |
692 | (setq pos (point)) | |
693 | ;; The default title is the nearest entry-like object before or | |
694 | ;; after POS. | |
695 | (if (and (skip-chars-backward " \ta-zA-Z0-9+") | |
696 | (not (zerop (skip-chars-backward "("))) | |
697 | ;; Try to handle the special case where POS is on a | |
698 | ;; section number. | |
699 | (looking-at | |
700 | (concat "([ \t]*\\(" Man-section-regexp "\\)[ \t]*)")) | |
701 | ;; We skipped a valid section number backwards, look at | |
702 | ;; preceding text. | |
703 | (or (and (skip-chars-backward ",; \t") | |
704 | (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:")))) | |
705 | ;; Not a valid entry, move POS after closing paren. | |
706 | (not (setq pos (match-end 0))))) | |
707 | ;; We have a candidate, make `start' record its starting | |
708 | ;; position. | |
ccf721a6 | 709 | (setq start (point)) |
d8b3b1a1 MR |
710 | ;; Otherwise look at char before POS. |
711 | (goto-char pos) | |
ccf721a6 | 712 | (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:"))) |
d8b3b1a1 MR |
713 | ;; Our candidate is just before or around POS. |
714 | (setq start (point)) | |
715 | ;; Otherwise record the current column and look backwards. | |
716 | (setq column (current-column)) | |
717 | (skip-chars-backward ",; \t") | |
c80e3b4a | 718 | ;; Record the distance traveled. |
d8b3b1a1 MR |
719 | (setq distance (- column (current-column))) |
720 | (when (looking-back | |
721 | (concat "([ \t]*\\(?:" Man-section-regexp "\\)[ \t]*)")) | |
722 | ;; Skip section number backwards. | |
723 | (goto-char (match-beginning 0)) | |
724 | (skip-chars-backward " \t")) | |
725 | (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:"))) | |
726 | (progn | |
727 | ;; We have a candidate before POS ... | |
728 | (setq start (point)) | |
729 | (goto-char pos) | |
730 | (if (and (skip-chars-forward ",; \t") | |
731 | (< (- (current-column) column) distance) | |
732 | (looking-at "[-a-zA-Z0-9._+:]")) | |
733 | ;; ... but the one after POS is better. | |
734 | (setq start (point)) | |
735 | ;; ... and anything after POS is worse. | |
736 | (goto-char start))) | |
737 | ;; No candidate before POS. | |
738 | (goto-char pos) | |
739 | (skip-chars-forward ",; \t") | |
740 | (setq start (point))))) | |
741 | ;; We have found a suitable starting point, try to skip at least | |
742 | ;; one character. | |
ccf721a6 MR |
743 | (skip-chars-forward "-a-zA-Z0-9._+:") |
744 | (setq word (buffer-substring-no-properties start (point))) | |
745 | ;; If there is a continuation at the end of line, check the | |
746 | ;; following line too, eg: | |
747 | ;; see this- | |
748 | ;; command-here(1) | |
d8b3b1a1 | 749 | ;; Note: This code gets executed iff our entry is after POS. |
ccf721a6 | 750 | (when (looking-at "[ \t\r\n]+\\([-a-zA-Z0-9._+:]+\\)([0-9])") |
d8b3b1a1 MR |
751 | (setq word (concat word (match-string-no-properties 1))) |
752 | ;; Make sure the section number gets included by the code below. | |
753 | (goto-char (match-end 1))) | |
ccf721a6 MR |
754 | (when (string-match "[._]+$" word) |
755 | (setq word (substring word 0 (match-beginning 0)))) | |
d8b3b1a1 MR |
756 | ;; The following was commented out since the preceding code |
757 | ;; should not produce a leading "*" in the first place. | |
758 | ;;; ;; If looking at something like *strcat(... , remove the '*' | |
759 | ;;; (when (string-match "^*" word) | |
760 | ;;; (setq word (substring word 1))) | |
761 | (concat | |
762 | word | |
763 | (and (not (string-equal word "")) | |
764 | ;; If looking at something like ioctl(2) or brc(1M), | |
765 | ;; include the section number in the returned value. | |
766 | (looking-at | |
767 | (concat "[ \t]*([ \t]*\\(" Man-section-regexp "\\)[ \t]*)")) | |
768 | (format "(%s)" (match-string-no-properties 1))))))) | |
55535639 PJ |
769 | |
770 | \f | |
771 | ;; ====================================================================== | |
772 | ;; Top level command and background process sentinel | |
773 | ||
774 | ;; For compatibility with older versions. | |
775 | ;;;###autoload | |
776 | (defalias 'manual-entry 'man) | |
777 | ||
e2ec6dd5 SM |
778 | (defvar Man-completion-cache nil |
779 | ;; On my machine, "man -k" is so fast that a cache makes no sense, | |
780 | ;; but apparently that's not the case in all cases, so let's add a cache. | |
781 | "Cache of completion table of the form (PREFIX . TABLE).") | |
782 | ||
327a6cca WJ |
783 | (defvar Man-man-k-use-anchor |
784 | ;; man-db or man-1.* | |
785 | (memq system-type '(gnu gnu/linux gnu/kfreebsd)) | |
786 | "If non-nil prepend ^ to the prefix passed to \"man -k\" for completion. | |
787 | The value should be nil if \"man -k ^PREFIX\" may omit some man | |
788 | pages whose names start with PREFIX. | |
789 | ||
790 | Currently, the default value depends on `system-type' and is | |
791 | non-nil where the standard man programs are known to behave | |
792 | properly. Setting the value to nil always gives correct results | |
793 | but computing the list of completions may take a bit longer.") | |
794 | ||
795 | (defun Man-parse-man-k () | |
796 | "Parse \"man -k\" output and return the list of page names. | |
797 | ||
798 | The current buffer should contain the output of a command of the | |
799 | form \"man -k keyword\", which is traditionally also available with | |
800 | apropos(1). | |
801 | ||
802 | While POSIX man(1p) is a bit vague about what to expect here, | |
803 | this function tries to parse some commonly used formats, which | |
804 | can be described in the following informal way, with square brackets | |
805 | indicating optional parts and whitespace being interpreted | |
806 | somewhat loosely. | |
807 | ||
808 | foo[, bar [, ...]] [other stuff] (sec) - description | |
809 | foo(sec)[, bar(sec) [, ...]] [other stuff] - description | |
810 | ||
811 | For more details and some regression tests, please see | |
812 | test/automated/man-tests.el in the emacs bzr repository." | |
813 | (goto-char (point-min)) | |
814 | ;; See man-tests for data about which systems use which format (hopefully we | |
815 | ;; will be able to simplify the code if/when some of those formats aren't | |
816 | ;; used any more). | |
817 | (let (table) | |
818 | (while (search-forward-regexp "^\\([^ \t,\n]+\\)\\(.*?\\)\ | |
819 | \\(?:[ \t]\\(([^ \t,\n]+?)\\)\\)?\\(?:[ \t]+- ?\\(.*\\)\\)?$" nil t) | |
820 | (let ((section (match-string 3)) | |
821 | (description (match-string 4)) | |
822 | (bound (match-end 2))) | |
823 | (goto-char (match-end 1)) | |
824 | (while | |
825 | (progn | |
826 | ;; The first regexp grouping may already match the section | |
827 | ;; tacked on to the name, which is ok since for the formats we | |
828 | ;; claim to support the third (non-shy) grouping does not | |
829 | ;; match in this case, i.e., section is nil. | |
830 | (push (propertize (concat (match-string 1) section) | |
831 | 'help-echo description) | |
832 | table) | |
833 | (search-forward-regexp "\\=, *\\([^ \t,]+\\)" bound t))))) | |
834 | (nreverse table))) | |
835 | ||
bb301b9a SM |
836 | (defun Man-completion-table (string pred action) |
837 | (cond | |
9505c3c7 SM |
838 | ;; This ends up returning t for pretty much any string, and hence leads to |
839 | ;; spurious "complete but not unique" messages. And since `man' doesn't | |
840 | ;; require-match anyway, there's not point being clever. | |
841 | ;;((eq action 'lambda) (not (string-match "([^)]*\\'" string))) | |
896114cf SM |
842 | ((equal string "-k") |
843 | ;; Let SPC (minibuffer-complete-word) insert the space. | |
844 | (complete-with-action action '("-k ") string pred)) | |
10c877fe SM |
845 | (t |
846 | (let ((table (cdr Man-completion-cache)) | |
847 | (section nil) | |
848 | (prefix string)) | |
849 | (when (string-match "\\`\\([[:digit:]].*?\\) " string) | |
850 | (setq section (match-string 1 string)) | |
851 | (setq prefix (substring string (match-end 0)))) | |
e2ec6dd5 | 852 | (unless (and Man-completion-cache |
10c877fe SM |
853 | (string-prefix-p (car Man-completion-cache) prefix)) |
854 | (with-temp-buffer | |
b58dd707 KR |
855 | (setq default-directory "/") ;; in case inherited doesn't exist |
856 | ;; Actually for my `man' the arg is a regexp. | |
857 | ;; POSIX says it must be ERE and "man-db" seems to agree, | |
10c877fe SM |
858 | ;; whereas under MacOSX it seems to be BRE-style and doesn't |
859 | ;; accept backslashes at all. Let's not bother to | |
860 | ;; quote anything. | |
861 | (let ((process-environment (copy-sequence process-environment))) | |
862 | (setenv "COLUMNS" "999") ;; don't truncate long names | |
c07ff221 SM |
863 | ;; manual-program might not even exist. And since it's |
864 | ;; run differently in Man-getpage-in-background, an error | |
865 | ;; here may not necessarily mean that we'll also get an | |
866 | ;; error later. | |
327a6cca WJ |
867 | (ignore-errors |
868 | (call-process manual-program nil '(t nil) nil | |
869 | "-k" (concat (when (or Man-man-k-use-anchor | |
870 | (string-equal prefix "")) | |
871 | "^") | |
872 | prefix)))) | |
873 | (setq table (Man-parse-man-k))) | |
874 | ;; Cache the table for later reuse. | |
875 | (setq Man-completion-cache (cons prefix table))) | |
bb301b9a SM |
876 | ;; The table may contain false positives since the match is made |
877 | ;; by "man -k" not just on the manpage's name. | |
10c877fe SM |
878 | (if section |
879 | (let ((re (concat "(" (regexp-quote section) ")\\'"))) | |
880 | (dolist (comp (prog1 table (setq table nil))) | |
881 | (if (string-match re comp) | |
882 | (push (substring comp 0 (match-beginning 0)) table))) | |
883 | (completion-table-with-context (concat section " ") table | |
884 | prefix pred action)) | |
d7117720 SM |
885 | ;; If the current text looks like a possible section name, |
886 | ;; then add a completion entry that just adds a space so SPC | |
887 | ;; can be used to insert a space. | |
888 | (if (string-match "\\`[[:digit:]]" string) | |
889 | (push (concat string " ") table)) | |
10c877fe SM |
890 | (let ((res (complete-with-action action table string pred))) |
891 | ;; In case we're completing to a single name that exists in | |
892 | ;; several sections, the longest prefix will look like "foo(". | |
893 | (if (and (stringp res) | |
894 | (string-match "([^(]*\\'" res) | |
895 | ;; In case the paren was already in `prefix', don't | |
896 | ;; remove it. | |
897 | (> (match-beginning 0) (length prefix))) | |
898 | (substring res 0 (match-beginning 0)) | |
899 | res))))))) | |
7e734986 | 900 | |
55535639 PJ |
901 | ;;;###autoload |
902 | (defun man (man-args) | |
903 | "Get a Un*x manual page and put it in a buffer. | |
91c4831e KR |
904 | This command is the top-level command in the man package. It |
905 | runs a Un*x command to retrieve and clean a manpage in the | |
906 | background and places the results in a `Man-mode' browsing | |
907 | buffer. See variable `Man-notify-method' for what happens when | |
908 | the buffer is ready. If a buffer already exists for this man | |
909 | page, it will display immediately. | |
910 | ||
911 | For a manpage from a particular section, use either of the | |
912 | following. \"cat(1)\" is how cross-references appear and is | |
913 | passed to man as \"1 cat\". | |
914 | ||
915 | cat(1) | |
916 | 1 cat | |
917 | ||
918 | To see manpages from all sections related to a subject, use an | |
919 | \"all pages\" option (which might be \"-a\" if it's not the | |
920 | default), then step through with `Man-next-manpage' (\\<Man-mode-map>\\[Man-next-manpage]) etc. | |
921 | Add to `Man-switches' to make this option permanent. | |
922 | ||
923 | -a chmod | |
924 | ||
925 | An explicit filename can be given too. Use -l if it might | |
926 | otherwise look like a page name. | |
927 | ||
928 | /my/file/name.1.gz | |
929 | -l somefile.1 | |
930 | ||
931 | An \"apropos\" query with -k gives a buffer of matching page | |
932 | names or descriptions. The pattern argument is usually an | |
933 | \"egrep\" style regexp. | |
934 | ||
935 | -k pattern" | |
936 | ||
55535639 PJ |
937 | (interactive |
938 | (list (let* ((default-entry (Man-default-man-entry)) | |
573f4575 KR |
939 | ;; ignore case because that's friendly for bizarre |
940 | ;; caps things like the X11 function names and because | |
a3827a43 | 941 | ;; "man" itself is case-insensitive on the command line |
573f4575 KR |
942 | ;; so you're accustomed not to bother about the case |
943 | ;; ("man -k" is case-insensitive similarly, so the | |
944 | ;; table has everything available to complete) | |
945 | (completion-ignore-case t) | |
327a6cca | 946 | Man-completion-cache ;Don't cache across calls. |
bb301b9a | 947 | (input (completing-read |
5b76833f | 948 | (format "Manual entry%s" |
55535639 | 949 | (if (string= default-entry "") |
5b76833f RF |
950 | ": " |
951 | (format " (default %s): " default-entry))) | |
bb301b9a SM |
952 | 'Man-completion-table |
953 | nil nil nil 'Man-topic-history default-entry))) | |
55535639 | 954 | (if (string= input "") |
72397ff1 | 955 | (error "No man args given") |
55535639 PJ |
956 | input)))) |
957 | ||
958 | ;; Possibly translate the "subject(section)" syntax into the | |
959 | ;; "section subject" syntax and possibly downcase the section. | |
960 | (setq man-args (Man-translate-references man-args)) | |
961 | ||
962 | (Man-getpage-in-background man-args)) | |
963 | ||
964 | ;;;###autoload | |
965 | (defun man-follow (man-args) | |
966 | "Get a Un*x manual page of the item under point and put it in a buffer." | |
967 | (interactive (list (Man-default-man-entry))) | |
968 | (if (or (not man-args) | |
969 | (string= man-args "")) | |
970 | (error "No item under point") | |
971 | (man man-args))) | |
972 | ||
973 | (defun Man-getpage-in-background (topic) | |
398a825b SM |
974 | "Use TOPIC to build and fire off the manpage and cleaning command. |
975 | Return the buffer in which the manpage will appear." | |
55535639 PJ |
976 | (let* ((man-args topic) |
977 | (bufname (concat "*Man " man-args "*")) | |
978 | (buffer (get-buffer bufname))) | |
979 | (if buffer | |
980 | (Man-notify-when-ready buffer) | |
981 | (require 'env) | |
982 | (message "Invoking %s %s in the background" manual-program man-args) | |
983 | (setq buffer (generate-new-buffer bufname)) | |
a88459cd | 984 | (with-current-buffer buffer |
5a0c1883 | 985 | (setq buffer-undo-list t) |
55535639 PJ |
986 | (setq Man-original-frame (selected-frame)) |
987 | (setq Man-arguments man-args)) | |
988 | (let ((process-environment (copy-sequence process-environment)) | |
989 | ;; The following is so Awk script gets \n intact | |
990 | ;; But don't prevent decoding of the outside. | |
991 | (coding-system-for-write 'raw-text-unix) | |
992 | ;; We must decode the output by a coding system that the | |
993 | ;; system's locale suggests in multibyte mode. | |
994 | (coding-system-for-read | |
597e2240 | 995 | (if (default-value 'enable-multibyte-characters) |
55535639 PJ |
996 | locale-coding-system 'raw-text-unix)) |
997 | ;; Avoid possible error by using a directory that always exists. | |
cb24638e KG |
998 | (default-directory |
999 | (if (and (file-directory-p default-directory) | |
1000 | (not (find-file-name-handler default-directory | |
1001 | 'file-directory-p))) | |
1002 | default-directory | |
1003 | "/"))) | |
55535639 PJ |
1004 | ;; Prevent any attempt to use display terminal fanciness. |
1005 | (setenv "TERM" "dumb") | |
9606309f DL |
1006 | ;; In Debian Woody, at least, we get overlong lines under X |
1007 | ;; unless COLUMNS or MANWIDTH is set. This isn't a problem on | |
1008 | ;; a tty. man(1) says: | |
1009 | ;; MANWIDTH | |
1010 | ;; If $MANWIDTH is set, its value is used as the line | |
1011 | ;; length for which manual pages should be formatted. | |
1012 | ;; If it is not set, manual pages will be formatted | |
1013 | ;; with a line length appropriate to the current ter- | |
1014 | ;; minal (using an ioctl(2) if available, the value of | |
1015 | ;; $COLUMNS, or falling back to 80 characters if nei- | |
1016 | ;; ther is available). | |
763237c4 SS |
1017 | (when (or window-system |
1018 | (not (or (getenv "MANWIDTH") (getenv "COLUMNS")))) | |
8783fe91 JL |
1019 | ;; This isn't strictly correct, since we don't know how |
1020 | ;; the page will actually be displayed, but it seems | |
1021 | ;; reasonable. | |
1022 | (setenv "COLUMNS" (number-to-string | |
1023 | (cond | |
1024 | ((and (integerp Man-width) (> Man-width 0)) | |
1025 | Man-width) | |
1026 | (Man-width (frame-width)) | |
1027 | ((window-width)))))) | |
3d56260a GM |
1028 | ;; Since man-db 2.4.3-1, man writes plain text with no escape |
1029 | ;; sequences when stdout is not a tty. In 2.5.0, the following | |
1030 | ;; env-var was added to allow control of this (see Debian Bug#340673). | |
1031 | (setenv "MAN_KEEP_FORMATTING" "1") | |
55535639 PJ |
1032 | (if (fboundp 'start-process) |
1033 | (set-process-sentinel | |
ac00945e | 1034 | (start-process manual-program buffer |
00170b0d EZ |
1035 | (if (memq system-type '(cygwin windows-nt)) |
1036 | shell-file-name | |
1037 | "sh") | |
353518de | 1038 | shell-command-switch |
55535639 PJ |
1039 | (format (Man-build-man-command) man-args)) |
1040 | 'Man-bgproc-sentinel) | |
504feff5 | 1041 | (let ((exit-status |
ac00945e EZ |
1042 | (call-process shell-file-name nil (list buffer nil) nil |
1043 | shell-command-switch | |
504feff5 KG |
1044 | (format (Man-build-man-command) man-args))) |
1045 | (msg "")) | |
1046 | (or (and (numberp exit-status) | |
1047 | (= exit-status 0)) | |
1048 | (and (numberp exit-status) | |
1049 | (setq msg | |
1050 | (format "exited abnormally with code %d" | |
1051 | exit-status))) | |
1052 | (setq msg exit-status)) | |
398a825b SM |
1053 | (Man-bgproc-sentinel bufname msg))))) |
1054 | buffer)) | |
55535639 PJ |
1055 | |
1056 | (defun Man-notify-when-ready (man-buffer) | |
1057 | "Notify the user when MAN-BUFFER is ready. | |
1058 | See the variable `Man-notify-method' for the different notification behaviors." | |
a88459cd | 1059 | (let ((saved-frame (with-current-buffer man-buffer |
55535639 | 1060 | Man-original-frame))) |
a464a6c7 SM |
1061 | (pcase Man-notify-method |
1062 | (`newframe | |
1063 | ;; Since we run asynchronously, perhaps while Emacs is waiting | |
1064 | ;; for input, we must not leave a different buffer current. We | |
1065 | ;; can't rely on the editor command loop to reselect the | |
1066 | ;; selected window's buffer. | |
1067 | (save-excursion | |
1068 | (let ((frame (make-frame Man-frame-parameters))) | |
1069 | (set-window-buffer (frame-selected-window frame) man-buffer) | |
1070 | (set-window-dedicated-p (frame-selected-window frame) t) | |
1071 | (or (display-multi-frame-p frame) | |
1072 | (select-frame frame))))) | |
1073 | (`pushy | |
1074 | (switch-to-buffer man-buffer)) | |
1075 | (`bully | |
1076 | (and (frame-live-p saved-frame) | |
1077 | (select-frame saved-frame)) | |
1078 | (pop-to-buffer man-buffer) | |
1079 | (delete-other-windows)) | |
1080 | (`aggressive | |
1081 | (and (frame-live-p saved-frame) | |
1082 | (select-frame saved-frame)) | |
1083 | (pop-to-buffer man-buffer)) | |
1084 | (`friendly | |
1085 | (and (frame-live-p saved-frame) | |
1086 | (select-frame saved-frame)) | |
1087 | (display-buffer man-buffer 'not-this-window)) | |
1088 | (`polite | |
1089 | (beep) | |
1090 | (message "Manual buffer %s is ready" (buffer-name man-buffer))) | |
1091 | (`quiet | |
1092 | (message "Manual buffer %s is ready" (buffer-name man-buffer))) | |
1093 | (_ ;; meek | |
1094 | (message "")) | |
1095 | ))) | |
55535639 PJ |
1096 | |
1097 | (defun Man-softhyphen-to-minus () | |
9606309f | 1098 | ;; \255 is SOFT HYPHEN in Latin-N. Versions of Debian man, at |
55535639 PJ |
1099 | ;; least, emit it even when not in a Latin-N locale. |
1100 | (unless (eq t (compare-strings "latin-" 0 nil | |
1101 | current-language-environment 0 6 t)) | |
1102 | (goto-char (point-min)) | |
1103 | (let ((str "\255")) | |
1104 | (if enable-multibyte-characters | |
1105 | (setq str (string-as-multibyte str))) | |
1106 | (while (search-forward str nil t) (replace-match "-"))))) | |
1107 | ||
1108 | (defun Man-fontify-manpage () | |
1109 | "Convert overstriking and underlining to the correct fonts. | |
1110 | Same for the ANSI bold and normal escape sequences." | |
1111 | (interactive) | |
aec2bd36 | 1112 | (message "Please wait: formatting the %s man page..." Man-arguments) |
55535639 | 1113 | (goto-char (point-min)) |
079c2d00 | 1114 | ;; Fontify ANSI escapes. |
456e62c2 WJ |
1115 | (let ((ansi-color-apply-face-function |
1116 | (lambda (beg end face) | |
1117 | (when face | |
1118 | (put-text-property beg end 'face face)))) | |
1119 | (ansi-color-map Man-ansi-color-map)) | |
1120 | (ansi-color-apply-on-region (point-min) (point-max))) | |
079c2d00 | 1121 | ;; Other highlighting. |
6bfb8bd6 RS |
1122 | (let ((buffer-undo-list t)) |
1123 | (if (< (buffer-size) (position-bytes (point-max))) | |
1124 | ;; Multibyte characters exist. | |
1125 | (progn | |
1126 | (goto-char (point-min)) | |
1127 | (while (search-forward "__\b\b" nil t) | |
1128 | (backward-delete-char 4) | |
456e62c2 | 1129 | (put-text-property (point) (1+ (point)) 'face 'Man-underline)) |
6bfb8bd6 RS |
1130 | (goto-char (point-min)) |
1131 | (while (search-forward "\b\b__" nil t) | |
1132 | (backward-delete-char 4) | |
456e62c2 | 1133 | (put-text-property (1- (point)) (point) 'face 'Man-underline)))) |
6bfb8bd6 RS |
1134 | (goto-char (point-min)) |
1135 | (while (search-forward "_\b" nil t) | |
1136 | (backward-delete-char 2) | |
456e62c2 | 1137 | (put-text-property (point) (1+ (point)) 'face 'Man-underline)) |
6bfb8bd6 RS |
1138 | (goto-char (point-min)) |
1139 | (while (search-forward "\b_" nil t) | |
1140 | (backward-delete-char 2) | |
456e62c2 | 1141 | (put-text-property (1- (point)) (point) 'face 'Man-underline)) |
6bfb8bd6 RS |
1142 | (goto-char (point-min)) |
1143 | (while (re-search-forward "\\(.\\)\\(\b+\\1\\)+" nil t) | |
1144 | (replace-match "\\1") | |
456e62c2 | 1145 | (put-text-property (1- (point)) (point) 'face 'Man-overstrike)) |
6bfb8bd6 RS |
1146 | (goto-char (point-min)) |
1147 | (while (re-search-forward "o\b\\+\\|\\+\bo" nil t) | |
1148 | (replace-match "o") | |
1149 | (put-text-property (1- (point)) (point) 'face 'bold)) | |
1150 | (goto-char (point-min)) | |
1151 | (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) | |
1152 | (replace-match "+") | |
1153 | (put-text-property (1- (point)) (point) 'face 'bold)) | |
91e3333f | 1154 | ;; When the header is longer than the manpage name, groff tries to |
e4769531 | 1155 | ;; condense it to a shorter line interspersed with ^H. Remove ^H with |
456e62c2 | 1156 | ;; their preceding chars (but don't put Man-overstrike). (Bug#5566) |
91e3333f JL |
1157 | (goto-char (point-min)) |
1158 | (while (re-search-forward ".\b" nil t) (backward-delete-char 2)) | |
6bfb8bd6 RS |
1159 | (goto-char (point-min)) |
1160 | ;; Try to recognize common forms of cross references. | |
1161 | (Man-highlight-references) | |
1162 | (Man-softhyphen-to-minus) | |
1163 | (goto-char (point-min)) | |
1164 | (while (re-search-forward Man-heading-regexp nil t) | |
1165 | (put-text-property (match-beginning 0) | |
1166 | (match-end 0) | |
456e62c2 | 1167 | 'face 'Man-overstrike))) |
2f5c6024 | 1168 | (message "%s man page formatted" (Man-page-from-arguments Man-arguments))) |
55535639 | 1169 | |
50071f01 | 1170 | (defun Man-highlight-references (&optional xref-man-type) |
4edd9faf | 1171 | "Highlight the references on mouse-over. |
927c60bd | 1172 | References include items in the SEE ALSO section, |
71726072 EZ |
1173 | header file (#include <foo.h>), and files in FILES. |
1174 | If optional argument XREF-MAN-TYPE is non-nil, it used as the | |
1175 | button type for items in SEE ALSO section. If it is nil, the | |
1176 | default type, `Man-xref-man-page' is used for the buttons." | |
af8308ec MY |
1177 | ;; `Man-highlight-references' is used from woman.el, too. |
1178 | ;; woman.el doesn't set `Man-arguments'. | |
1179 | (unless Man-arguments | |
1180 | (setq Man-arguments "")) | |
30abc4f4 MY |
1181 | (if (string-match "-k " Man-arguments) |
1182 | (progn | |
707f55b0 DN |
1183 | (Man-highlight-references0 nil Man-reference-regexp 1 |
1184 | 'Man-default-man-entry | |
71726072 EZ |
1185 | (or xref-man-type 'Man-xref-man-page)) |
1186 | (Man-highlight-references0 nil Man-apropos-regexp 1 | |
707f55b0 | 1187 | 'Man-default-man-entry |
71726072 | 1188 | (or xref-man-type 'Man-xref-man-page))) |
9201cc28 | 1189 | (Man-highlight-references0 Man-see-also-regexp Man-reference-regexp 1 |
707f55b0 | 1190 | 'Man-default-man-entry |
71726072 EZ |
1191 | (or xref-man-type 'Man-xref-man-page)) |
1192 | (Man-highlight-references0 Man-synopsis-regexp Man-header-regexp 0 2 | |
1193 | 'Man-xref-header-file) | |
1194 | (Man-highlight-references0 Man-files-regexp Man-normal-file-regexp 0 0 | |
1195 | 'Man-xref-normal-file))) | |
4edd9faf | 1196 | |
30abc4f4 | 1197 | (defun Man-highlight-references0 (start-section regexp button-pos target type) |
4edd9faf | 1198 | ;; Based on `Man-build-references-alist' |
30abc4f4 MY |
1199 | (when (or (null start-section) |
1200 | (Man-find-section start-section)) | |
1201 | (let ((end (if start-section | |
1202 | (progn | |
1203 | (forward-line 1) | |
1204 | (back-to-indentation) | |
1205 | (save-excursion | |
1206 | (Man-next-section 1) | |
1207 | (point))) | |
1208 | (goto-char (point-min)) | |
a88459cd | 1209 | nil))) |
4edd9faf | 1210 | (while (re-search-forward regexp end t) |
cde7e38b CY |
1211 | ;; An overlay button is preferable because the underlying text |
1212 | ;; may have text property highlights (Bug#7881). | |
1213 | (make-button | |
4edd9faf JB |
1214 | (match-beginning button-pos) |
1215 | (match-end button-pos) | |
1216 | 'type type | |
30abc4f4 | 1217 | 'Man-target-string (cond |
9201cc28 | 1218 | ((numberp target) |
30abc4f4 MY |
1219 | (match-string target)) |
1220 | ((functionp target) | |
707f55b0 | 1221 | target) |
30abc4f4 | 1222 | (t nil))))))) |
4edd9faf | 1223 | |
1d3b75d8 RS |
1224 | (defun Man-cleanup-manpage (&optional interactive) |
1225 | "Remove overstriking and underlining from the current buffer. | |
1226 | Normally skip any jobs that should have been done by the sed script, | |
1227 | but when called interactively, do those jobs even if the sed | |
1228 | script would have done them." | |
1229 | (interactive "p") | |
55535639 PJ |
1230 | (message "Please wait: cleaning up the %s man page..." |
1231 | Man-arguments) | |
1d3b75d8 | 1232 | (if (or interactive (not Man-sed-script)) |
55535639 PJ |
1233 | (progn |
1234 | (goto-char (point-min)) | |
1235 | (while (search-forward "_\b" nil t) (backward-delete-char 2)) | |
1236 | (goto-char (point-min)) | |
1237 | (while (search-forward "\b_" nil t) (backward-delete-char 2)) | |
1238 | (goto-char (point-min)) | |
1239 | (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t) | |
1240 | (replace-match "\\1")) | |
1241 | (goto-char (point-min)) | |
1242 | (while (re-search-forward "\e\\[[0-9]+m" nil t) (replace-match "")) | |
1243 | (goto-char (point-min)) | |
1244 | (while (re-search-forward "o\b\\+\\|\\+\bo" nil t) (replace-match "o")) | |
1245 | )) | |
1246 | (goto-char (point-min)) | |
1247 | (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) (replace-match "+")) | |
91e3333f | 1248 | ;; When the header is longer than the manpage name, groff tries to |
e4769531 | 1249 | ;; condense it to a shorter line interspersed with ^H. Remove ^H with |
456e62c2 | 1250 | ;; their preceding chars (but don't put Man-overstrike). (Bug#5566) |
91e3333f JL |
1251 | (goto-char (point-min)) |
1252 | (while (re-search-forward ".\b" nil t) (backward-delete-char 2)) | |
55535639 PJ |
1253 | (Man-softhyphen-to-minus) |
1254 | (message "%s man page cleaned up" Man-arguments)) | |
1255 | ||
1256 | (defun Man-bgproc-sentinel (process msg) | |
1257 | "Manpage background process sentinel. | |
1258 | When manpage command is run asynchronously, PROCESS is the process | |
1259 | object for the manpage command; when manpage command is run | |
1260 | synchronously, PROCESS is the name of the buffer where the manpage | |
1261 | command is run. Second argument MSG is the exit message of the | |
1262 | manpage command." | |
1263 | (let ((Man-buffer (if (stringp process) (get-buffer process) | |
1264 | (process-buffer process))) | |
1265 | (delete-buff nil) | |
1266 | (err-mess nil)) | |
1267 | ||
1268 | (if (null (buffer-name Man-buffer)) ;; deleted buffer | |
1269 | (or (stringp process) | |
1270 | (set-process-buffer process nil)) | |
1271 | ||
a88459cd | 1272 | (with-current-buffer Man-buffer |
55535639 PJ |
1273 | (let ((case-fold-search nil)) |
1274 | (goto-char (point-min)) | |
1275 | (cond ((or (looking-at "No \\(manual \\)*entry for") | |
1276 | (looking-at "[^\n]*: nothing appropriate$")) | |
1277 | (setq err-mess (buffer-substring (point) | |
1278 | (progn | |
1279 | (end-of-line) (point))) | |
1280 | delete-buff t)) | |
69bcb1f3 KR |
1281 | |
1282 | ;; "-k foo", successful exit, but no output (from man-db) | |
1283 | ;; ENHANCE-ME: share the check for -k with | |
1284 | ;; `Man-highlight-references'. The \\s- bits here are | |
1285 | ;; meant to allow for multiple options with -k among them. | |
1286 | ((and (string-match "\\(\\`\\|\\s-\\)-k\\s-" Man-arguments) | |
1287 | (eq (process-status process) 'exit) | |
1288 | (= (process-exit-status process) 0) | |
1289 | (= (point-min) (point-max))) | |
1290 | (setq err-mess (format "%s: no matches" Man-arguments) | |
1291 | delete-buff t)) | |
1292 | ||
55535639 PJ |
1293 | ((or (stringp process) |
1294 | (not (and (eq (process-status process) 'exit) | |
1295 | (= (process-exit-status process) 0)))) | |
1296 | (or (zerop (length msg)) | |
1297 | (progn | |
1298 | (setq err-mess | |
1299 | (concat (buffer-name Man-buffer) | |
1300 | ": process " | |
1301 | (let ((eos (1- (length msg)))) | |
1302 | (if (= (aref msg eos) ?\n) | |
1303 | (substring msg 0 eos) msg)))) | |
1304 | (goto-char (point-max)) | |
1305 | (insert (format "\nprocess %s" msg)))) | |
1306 | )) | |
1307 | (if delete-buff | |
1308 | (kill-buffer Man-buffer) | |
1309 | (if Man-fontify-manpage-flag | |
1310 | (Man-fontify-manpage) | |
1311 | (Man-cleanup-manpage)) | |
55fb4ff7 | 1312 | |
55535639 | 1313 | (run-hooks 'Man-cooked-hook) |
55fb4ff7 RS |
1314 | (Man-mode) |
1315 | ||
1316 | (if (not Man-page-list) | |
d1583c48 | 1317 | (let ((args Man-arguments)) |
55fb4ff7 | 1318 | (kill-buffer (current-buffer)) |
71873e2b SM |
1319 | (user-error "Can't find the %s manpage" |
1320 | (Man-page-from-arguments args))) | |
d1583c48 | 1321 | (set-buffer-modified-p nil)))) |
55535639 PJ |
1322 | ;; Restore case-fold-search before calling |
1323 | ;; Man-notify-when-ready because it may switch buffers. | |
1324 | ||
1325 | (if (not delete-buff) | |
1326 | (Man-notify-when-ready Man-buffer)) | |
1327 | ||
1328 | (if err-mess | |
8c16bd8c | 1329 | (error "%s" err-mess)) |
55535639 PJ |
1330 | )))) |
1331 | ||
2f5c6024 LMI |
1332 | (defun Man-page-from-arguments (args) |
1333 | ;; Skip arguments and only print the page name. | |
1334 | (mapconcat | |
1335 | 'identity | |
1336 | (delete nil | |
1337 | (mapcar | |
1338 | (lambda (elem) | |
1339 | (and (not (string-match "^-" elem)) | |
1340 | elem)) | |
1341 | (split-string args " "))) | |
1342 | " ")) | |
1343 | ||
55535639 PJ |
1344 | \f |
1345 | ;; ====================================================================== | |
1346 | ;; set up manual mode in buffer and build alists | |
1347 | ||
398a825b SM |
1348 | (defvar bookmark-make-record-function) |
1349 | ||
3a1524ed LK |
1350 | (put 'Man-mode 'mode-class 'special) |
1351 | ||
55535639 PJ |
1352 | (defun Man-mode () |
1353 | "A mode for browsing Un*x manual pages. | |
1354 | ||
1355 | The following man commands are available in the buffer. Try | |
1356 | \"\\[describe-key] <key> RET\" for more information: | |
1357 | ||
1358 | \\[man] Prompt to retrieve a new manpage. | |
1359 | \\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section. | |
1360 | \\[Man-next-manpage] Jump to next manpage in circular list. | |
1361 | \\[Man-previous-manpage] Jump to previous manpage in circular list. | |
1362 | \\[Man-next-section] Jump to next manpage section. | |
1363 | \\[Man-previous-section] Jump to previous manpage section. | |
1364 | \\[Man-goto-section] Go to a manpage section. | |
1365 | \\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section. | |
1366 | \\[Man-quit] Deletes the manpage window, bury its buffer. | |
1367 | \\[Man-kill] Deletes the manpage window, kill its buffer. | |
1368 | \\[describe-mode] Prints this help text. | |
1369 | ||
1370 | The following variables may be of some use. Try | |
1371 | \"\\[describe-variable] <variable-name> RET\" for more information: | |
1372 | ||
1373 | `Man-notify-method' What happens when manpage formatting is done. | |
1374 | `Man-downcase-section-letters-flag' Force section letters to lower case. | |
1375 | `Man-circular-pages-flag' Treat multiple manpage list as circular. | |
1376 | `Man-section-translations-alist' List of section numbers and their Un*x equiv. | |
1377 | `Man-filter-list' Background manpage filter command. | |
55535639 PJ |
1378 | `Man-mode-map' Keymap bindings for Man mode buffers. |
1379 | `Man-mode-hook' Normal hook run on entry to Man mode. | |
1380 | `Man-section-regexp' Regexp describing manpage section letters. | |
1381 | `Man-heading-regexp' Regexp describing section headers. | |
1382 | `Man-see-also-regexp' Regexp for SEE ALSO section (or your equiv). | |
1383 | `Man-first-heading-regexp' Regexp for first heading on a manpage. | |
1384 | `Man-reference-regexp' Regexp matching a references in SEE ALSO. | |
1385 | `Man-switches' Background `man' command switches. | |
1386 | ||
1387 | The following key bindings are currently in effect in the buffer: | |
1388 | \\{Man-mode-map}" | |
1389 | (interactive) | |
430663bc | 1390 | (kill-all-local-variables) |
55535639 PJ |
1391 | (setq major-mode 'Man-mode |
1392 | mode-name "Man" | |
1393 | buffer-auto-save-file-name nil | |
c7cbaf4a MB |
1394 | mode-line-buffer-identification |
1395 | (list (default-value 'mode-line-buffer-identification) | |
1396 | " {" 'Man-page-mode-string "}") | |
55535639 PJ |
1397 | truncate-lines t |
1398 | buffer-read-only t) | |
430663bc | 1399 | (buffer-disable-undo) |
55535639 PJ |
1400 | (auto-fill-mode -1) |
1401 | (use-local-map Man-mode-map) | |
1402 | (set-syntax-table man-mode-syntax-table) | |
aec2bd36 JL |
1403 | (setq imenu-generic-expression (list (list nil Man-heading-regexp 0))) |
1404 | (set (make-local-variable 'outline-regexp) Man-heading-regexp) | |
1405 | (set (make-local-variable 'outline-level) (lambda () 1)) | |
45be326a | 1406 | (set (make-local-variable 'bookmark-make-record-function) |
398a825b | 1407 | 'Man-bookmark-make-record) |
55535639 PJ |
1408 | (Man-build-page-list) |
1409 | (Man-strip-page-headers) | |
1410 | (Man-unindent) | |
55fb4ff7 | 1411 | (Man-goto-page 1 t) |
430663bc | 1412 | (run-mode-hooks 'Man-mode-hook)) |
55535639 PJ |
1413 | |
1414 | (defsubst Man-build-section-alist () | |
8b6c19f4 SM |
1415 | "Build the list of manpage sections." |
1416 | (setq Man--sections nil) | |
55535639 PJ |
1417 | (goto-char (point-min)) |
1418 | (let ((case-fold-search nil)) | |
1419 | (while (re-search-forward Man-heading-regexp (point-max) t) | |
8b6c19f4 SM |
1420 | (let ((section (match-string 1))) |
1421 | (unless (member section Man--sections) | |
1422 | (push section Man--sections))) | |
55535639 PJ |
1423 | (forward-line 1)))) |
1424 | ||
1425 | (defsubst Man-build-references-alist () | |
8b6c19f4 SM |
1426 | "Build the list of references (in the SEE ALSO section)." |
1427 | (setq Man--refpages nil) | |
55535639 PJ |
1428 | (save-excursion |
1429 | (if (Man-find-section Man-see-also-regexp) | |
1430 | (let ((start (progn (forward-line 1) (point))) | |
1431 | (end (progn | |
1432 | (Man-next-section 1) | |
1433 | (point))) | |
1434 | hyphenated | |
1435 | (runningpoint -1)) | |
1436 | (save-restriction | |
1437 | (narrow-to-region start end) | |
1438 | (goto-char (point-min)) | |
1439 | (back-to-indentation) | |
1440 | (while (and (not (eobp)) (/= (point) runningpoint)) | |
1441 | (setq runningpoint (point)) | |
1442 | (if (re-search-forward Man-hyphenated-reference-regexp end t) | |
72397ff1 | 1443 | (let* ((word (match-string 0)) |
55535639 PJ |
1444 | (len (1- (length word)))) |
1445 | (if hyphenated | |
1446 | (setq word (concat hyphenated word) | |
1447 | hyphenated nil | |
1448 | ;; Update len, in case a reference spans | |
1449 | ;; more than two lines (paranoia). | |
1450 | len (1- (length word)))) | |
327a6cca | 1451 | (if (memq (aref word len) '(?- ?)) |
55535639 | 1452 | (setq hyphenated (substring word 0 len))) |
8b6c19f4 SM |
1453 | (and (string-match Man-reference-regexp word) |
1454 | (not (member word Man--refpages)) | |
1455 | (push word Man--refpages)))) | |
55535639 | 1456 | (skip-chars-forward " \t\n,")))))) |
8b6c19f4 | 1457 | (setq Man--refpages (nreverse Man--refpages))) |
55535639 PJ |
1458 | |
1459 | (defun Man-build-page-list () | |
1460 | "Build the list of separate manpages in the buffer." | |
1461 | (setq Man-page-list nil) | |
1462 | (let ((page-start (point-min)) | |
1463 | (page-end (point-max)) | |
1464 | (header "")) | |
1465 | (goto-char page-start) | |
1466 | ;; (switch-to-buffer (current-buffer))(debug) | |
1467 | (while (not (eobp)) | |
1468 | (setq header | |
1469 | (if (looking-at Man-page-header-regexp) | |
72397ff1 | 1470 | (match-string 1) |
55535639 PJ |
1471 | nil)) |
1472 | ;; Go past both the current and the next Man-first-heading-regexp | |
1473 | (if (re-search-forward Man-first-heading-regexp nil 'move 2) | |
1474 | (let ((p (progn (beginning-of-line) (point)))) | |
1475 | ;; We assume that the page header is delimited by blank | |
1476 | ;; lines and that it contains at most one blank line. So | |
1477 | ;; if we back by three blank lines we will be sure to be | |
1478 | ;; before the page header but not before the possible | |
1479 | ;; previous page header. | |
1480 | (search-backward "\n\n" nil t 3) | |
1481 | (if (re-search-forward Man-page-header-regexp p 'move) | |
1482 | (beginning-of-line)))) | |
1483 | (setq page-end (point)) | |
1484 | (setq Man-page-list (append Man-page-list | |
1485 | (list (list (copy-marker page-start) | |
1486 | (copy-marker page-end) | |
1487 | header)))) | |
1488 | (setq page-start page-end) | |
1489 | ))) | |
1490 | ||
1491 | (defun Man-strip-page-headers () | |
1492 | "Strip all the page headers but the first from the manpage." | |
a88459cd | 1493 | (let ((inhibit-read-only t) |
55535639 | 1494 | (case-fold-search nil) |
55535639 | 1495 | (header "")) |
a88459cd | 1496 | (dolist (page Man-page-list) |
55535639 PJ |
1497 | (and (nth 2 page) |
1498 | (goto-char (car page)) | |
1499 | (re-search-forward Man-first-heading-regexp nil t) | |
1500 | (setq header (buffer-substring (car page) (match-beginning 0))) | |
1501 | ;; Since the awk script collapses all successive blank | |
1502 | ;; lines into one, and since we don't want to get rid of | |
1503 | ;; the fast awk script, one must choose between adding | |
1504 | ;; spare blank lines between pages when there were none and | |
1505 | ;; deleting blank lines at page boundaries when there were | |
1506 | ;; some. We choose the first, so we comment the following | |
1507 | ;; line. | |
1508 | ;; (setq header (concat "\n" header))) | |
1509 | (while (search-forward header (nth 1 page) t) | |
a88459cd | 1510 | (replace-match "")))))) |
55535639 PJ |
1511 | |
1512 | (defun Man-unindent () | |
1513 | "Delete the leading spaces that indent the manpage." | |
a88459cd SM |
1514 | (let ((inhibit-read-only t) |
1515 | (case-fold-search nil)) | |
1516 | (dolist (page Man-page-list) | |
1517 | (let ((indent "") | |
55535639 PJ |
1518 | (nindent 0)) |
1519 | (narrow-to-region (car page) (car (cdr page))) | |
1520 | (if Man-uses-untabify-flag | |
ebfe2597 WJ |
1521 | ;; The space characters inserted by `untabify' inherit |
1522 | ;; sticky text properties, which is unnecessary and looks | |
1523 | ;; ugly with underlining (Bug#11408). | |
1524 | (let ((text-property-default-nonsticky | |
1525 | (cons '(face . t) text-property-default-nonsticky))) | |
1526 | (untabify (point-min) (point-max)))) | |
55535639 PJ |
1527 | (if (catch 'unindent |
1528 | (goto-char (point-min)) | |
1529 | (if (not (re-search-forward Man-first-heading-regexp nil t)) | |
1530 | (throw 'unindent nil)) | |
1531 | (beginning-of-line) | |
1532 | (setq indent (buffer-substring (point) | |
1533 | (progn | |
1534 | (skip-chars-forward " ") | |
1535 | (point)))) | |
1536 | (setq nindent (length indent)) | |
1537 | (if (zerop nindent) | |
1538 | (throw 'unindent nil)) | |
1539 | (setq indent (concat indent "\\|$")) | |
1540 | (goto-char (point-min)) | |
1541 | (while (not (eobp)) | |
1542 | (if (looking-at indent) | |
1543 | (forward-line 1) | |
1544 | (throw 'unindent nil))) | |
1545 | (goto-char (point-min))) | |
1546 | (while (not (eobp)) | |
1547 | (or (eolp) | |
1548 | (delete-char nindent)) | |
1549 | (forward-line 1))) | |
55535639 PJ |
1550 | )))) |
1551 | ||
1552 | \f | |
1553 | ;; ====================================================================== | |
1554 | ;; Man mode commands | |
1555 | ||
1556 | (defun Man-next-section (n) | |
1557 | "Move point to Nth next section (default 1)." | |
1558 | (interactive "p") | |
4cb071a4 SM |
1559 | (let ((case-fold-search nil) |
1560 | (start (point))) | |
55535639 PJ |
1561 | (if (looking-at Man-heading-regexp) |
1562 | (forward-line 1)) | |
1563 | (if (re-search-forward Man-heading-regexp (point-max) t n) | |
1564 | (beginning-of-line) | |
65e07682 CY |
1565 | (goto-char (point-max)) |
1566 | ;; The last line doesn't belong to any section. | |
4cb071a4 SM |
1567 | (forward-line -1)) |
1568 | ;; But don't move back from the starting point (can happen if `start' | |
1569 | ;; is somewhere on the last line). | |
1570 | (if (< (point) start) (goto-char start)))) | |
55535639 PJ |
1571 | |
1572 | (defun Man-previous-section (n) | |
1573 | "Move point to Nth previous section (default 1)." | |
1574 | (interactive "p") | |
1575 | (let ((case-fold-search nil)) | |
1576 | (if (looking-at Man-heading-regexp) | |
1577 | (forward-line -1)) | |
1578 | (if (re-search-backward Man-heading-regexp (point-min) t n) | |
1579 | (beginning-of-line) | |
1580 | (goto-char (point-min))))) | |
1581 | ||
1582 | (defun Man-find-section (section) | |
1583 | "Move point to SECTION if it exists, otherwise don't move point. | |
1584 | Returns t if section is found, nil otherwise." | |
1585 | (let ((curpos (point)) | |
1586 | (case-fold-search nil)) | |
1587 | (goto-char (point-min)) | |
1588 | (if (re-search-forward (concat "^" section) (point-max) t) | |
1589 | (progn (beginning-of-line) t) | |
1590 | (goto-char curpos) | |
1591 | nil) | |
1592 | )) | |
1593 | ||
8b6c19f4 SM |
1594 | (defvar Man--last-section nil) |
1595 | ||
1596 | (defun Man-goto-section (section) | |
1597 | "Move point to SECTION." | |
1598 | (interactive | |
1599 | (let* ((default (if (member Man--last-section Man--sections) | |
1600 | Man--last-section | |
1601 | (car Man--sections))) | |
1602 | (completion-ignore-case t) | |
1603 | (prompt (concat "Go to section (default " default "): ")) | |
1604 | (chosen (completing-read prompt Man--sections | |
1605 | nil nil nil nil default))) | |
1606 | (list chosen))) | |
1607 | (setq Man--last-section section) | |
1608 | (unless (Man-find-section section) | |
1609 | (error "Section %s not found" section))) | |
e709e39d | 1610 | |
55535639 PJ |
1611 | |
1612 | (defun Man-goto-see-also-section () | |
110c171f | 1613 | "Move point to the \"SEE ALSO\" section. |
55535639 PJ |
1614 | Actually the section moved to is described by `Man-see-also-regexp'." |
1615 | (interactive) | |
1616 | (if (not (Man-find-section Man-see-also-regexp)) | |
8c16bd8c | 1617 | (error "%s" (concat "No " Man-see-also-regexp |
55535639 PJ |
1618 | " section found in the current manpage")))) |
1619 | ||
1620 | (defun Man-possibly-hyphenated-word () | |
1621 | "Return a possibly hyphenated word at point. | |
1622 | If the word starts at the first non-whitespace column, and the | |
1623 | previous line ends with a hyphen, return the last word on the previous | |
1624 | line instead. Thus, if a reference to \"tcgetpgrp(3V)\" is hyphenated | |
1625 | as \"tcgetp-grp(3V)\", and point is at \"grp(3V)\", we return | |
1626 | \"tcgetp-\" instead of \"grp\"." | |
1627 | (save-excursion | |
1628 | (skip-syntax-backward "w()") | |
1629 | (skip-chars-forward " \t") | |
1630 | (let ((beg (point)) | |
1631 | (word (current-word))) | |
1632 | (when (eq beg (save-excursion | |
1633 | (back-to-indentation) | |
1634 | (point))) | |
1635 | (end-of-line 0) | |
1636 | (if (eq (char-before) ?-) | |
1637 | (setq word (current-word)))) | |
1638 | word))) | |
1639 | ||
8b6c19f4 SM |
1640 | (defvar Man--last-refpage nil) |
1641 | ||
55535639 PJ |
1642 | (defun Man-follow-manual-reference (reference) |
1643 | "Get one of the manpages referred to in the \"SEE ALSO\" section. | |
1644 | Specify which REFERENCE to use; default is based on word at point." | |
1645 | (interactive | |
8b6c19f4 | 1646 | (if (not Man--refpages) |
55535639 | 1647 | (error "There are no references in the current man page") |
debe9205 JL |
1648 | (list |
1649 | (let* ((default (or | |
1650 | (car (all-completions | |
1651 | (let ((word | |
1652 | (or (Man-possibly-hyphenated-word) | |
1653 | ""))) | |
1654 | ;; strip a trailing '-': | |
1655 | (if (string-match "-$" word) | |
1656 | (substring word 0 | |
1657 | (match-beginning 0)) | |
1658 | word)) | |
8b6c19f4 SM |
1659 | Man--refpages)) |
1660 | (if (member Man--last-refpage Man--refpages) | |
1661 | Man--last-refpage | |
1662 | (car Man--refpages)))) | |
debe9205 JL |
1663 | (defaults |
1664 | (mapcar 'substring-no-properties | |
8b6c19f4 SM |
1665 | (cons default Man--refpages))) |
1666 | (prompt (concat "Refer to (default " default "): ")) | |
1667 | (chosen (completing-read prompt Man--refpages | |
1668 | nil nil nil nil defaults))) | |
1669 | chosen)))) | |
1670 | (if (not Man--refpages) | |
55535639 | 1671 | (error "Can't find any references in the current manpage") |
8b6c19f4 | 1672 | (setq Man--last-refpage reference) |
55535639 | 1673 | (Man-getpage-in-background |
8b6c19f4 | 1674 | (Man-translate-references reference)))) |
55535639 PJ |
1675 | |
1676 | (defun Man-kill () | |
1677 | "Kill the buffer containing the manpage." | |
1678 | (interactive) | |
1679 | (quit-window t)) | |
1680 | ||
1681 | (defun Man-quit () | |
1682 | "Bury the buffer containing the manpage." | |
1683 | (interactive) | |
1684 | (quit-window)) | |
1685 | ||
3ac3aabd | 1686 | (defun Man-goto-page (page &optional noerror) |
55535639 PJ |
1687 | "Go to the manual page on page PAGE." |
1688 | (interactive | |
1689 | (if (not Man-page-list) | |
55fb4ff7 | 1690 | (error "Not a man page buffer") |
55535639 PJ |
1691 | (if (= (length Man-page-list) 1) |
1692 | (error "You're looking at the only manpage in the buffer") | |
1693 | (list (read-minibuffer (format "Go to manpage [1-%d]: " | |
1694 | (length Man-page-list))))))) | |
55fb4ff7 RS |
1695 | (if (and (not Man-page-list) (not noerror)) |
1696 | (error "Not a man page buffer")) | |
1697 | (when Man-page-list | |
1698 | (if (or (< page 1) | |
1699 | (> page (length Man-page-list))) | |
71873e2b | 1700 | (user-error "No manpage %d found" page)) |
55fb4ff7 RS |
1701 | (let* ((page-range (nth (1- page) Man-page-list)) |
1702 | (page-start (car page-range)) | |
1703 | (page-end (car (cdr page-range)))) | |
1704 | (setq Man-current-page page | |
1705 | Man-page-mode-string (Man-make-page-mode-string)) | |
1706 | (widen) | |
1707 | (goto-char page-start) | |
1708 | (narrow-to-region page-start page-end) | |
1709 | (Man-build-section-alist) | |
1710 | (Man-build-references-alist) | |
1711 | (goto-char (point-min))))) | |
55535639 PJ |
1712 | |
1713 | ||
1714 | (defun Man-next-manpage () | |
1715 | "Find the next manpage entry in the buffer." | |
1716 | (interactive) | |
1717 | (if (= (length Man-page-list) 1) | |
1718 | (error "This is the only manpage in the buffer")) | |
1719 | (if (< Man-current-page (length Man-page-list)) | |
1720 | (Man-goto-page (1+ Man-current-page)) | |
1721 | (if Man-circular-pages-flag | |
1722 | (Man-goto-page 1) | |
1723 | (error "You're looking at the last manpage in the buffer")))) | |
1724 | ||
1725 | (defun Man-previous-manpage () | |
1726 | "Find the previous manpage entry in the buffer." | |
1727 | (interactive) | |
1728 | (if (= (length Man-page-list) 1) | |
1729 | (error "This is the only manpage in the buffer")) | |
1730 | (if (> Man-current-page 1) | |
1731 | (Man-goto-page (1- Man-current-page)) | |
1732 | (if Man-circular-pages-flag | |
1733 | (Man-goto-page (length Man-page-list)) | |
1734 | (error "You're looking at the first manpage in the buffer")))) | |
4edd9faf JB |
1735 | |
1736 | ;; Header file support | |
1737 | (defun Man-view-header-file (file) | |
1738 | "View a header file specified by FILE from `Man-header-file-path'." | |
1739 | (let ((path Man-header-file-path) | |
1740 | complete-path) | |
1741 | (while path | |
a88459cd | 1742 | (setq complete-path (expand-file-name file (car path)) |
4edd9faf JB |
1743 | path (cdr path)) |
1744 | (if (file-readable-p complete-path) | |
1745 | (progn (view-file complete-path) | |
1746 | (setq path nil)) | |
1747 | (setq complete-path nil))) | |
1748 | complete-path)) | |
45be326a TV |
1749 | |
1750 | ;;; Bookmark Man Support | |
e44fa724 KF |
1751 | (declare-function bookmark-make-record-default |
1752 | "bookmark" (&optional no-file no-context posn)) | |
398a825b SM |
1753 | (declare-function bookmark-prop-get "bookmark" (bookmark prop)) |
1754 | (declare-function bookmark-default-handler "bookmark" (bmk)) | |
1755 | (declare-function bookmark-get-bookmark-record "bookmark" (bmk)) | |
1756 | ||
1757 | (defun Man-default-bookmark-title () | |
1758 | "Default bookmark name for Man or WoMan pages. | |
1759 | Uses `Man-name-local-regexp'." | |
45be326a TV |
1760 | (save-excursion |
1761 | (goto-char (point-min)) | |
398a825b SM |
1762 | (when (re-search-forward Man-name-local-regexp nil t) |
1763 | (skip-chars-forward "\n\t ") | |
45be326a TV |
1764 | (buffer-substring-no-properties (point) (line-end-position))))) |
1765 | ||
398a825b | 1766 | (defun Man-bookmark-make-record () |
45be326a | 1767 | "Make a bookmark entry for a Man buffer." |
398a825b | 1768 | `(,(Man-default-bookmark-title) |
e44fa724 | 1769 | ,@(bookmark-make-record-default 'no-file) |
ebb9641f SM |
1770 | (location . ,(concat "man " Man-arguments)) |
1771 | (man-args . ,Man-arguments) | |
1772 | (handler . Man-bookmark-jump))) | |
45be326a | 1773 | |
398a825b SM |
1774 | ;;;###autoload |
1775 | (defun Man-bookmark-jump (bookmark) | |
45be326a | 1776 | "Default bookmark handler for Man buffers." |
398a825b SM |
1777 | (let* ((man-args (bookmark-prop-get bookmark 'man-args)) |
1778 | ;; Let bookmark.el do the window handling. | |
1779 | ;; This let-binding needs to be active during the call to both | |
1780 | ;; Man-getpage-in-background and accept-process-output. | |
1781 | (Man-notify-method 'meek) | |
1782 | (buf (Man-getpage-in-background man-args)) | |
1783 | (proc (get-buffer-process buf))) | |
1784 | (while (and proc (eq (process-status proc) 'run)) | |
1785 | (accept-process-output proc)) | |
45be326a TV |
1786 | (bookmark-default-handler |
1787 | `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark))))) | |
1788 | ||
55535639 PJ |
1789 | \f |
1790 | ;; Init the man package variables, if not already done. | |
1791 | (Man-init-defvars) | |
1792 | ||
55535639 PJ |
1793 | (provide 'man) |
1794 | ||
1795 | ;;; man.el ends here |