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 | ||
8c106d17 | 58 | ;; Francesco Potortì <pot@cnuce.cnr.it> cleaned it up thoroughly, |
55535639 PJ |
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 | ||
1b3b87df | 416 | (defvar Man-mode-syntax-table |
55535639 PJ |
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 | ||
958614cf | 429 | (define-key map [?\S-\ ] 'scroll-down-command) |
ce3cefcc CY |
430 | (define-key map " " 'scroll-up-command) |
431 | (define-key map "\177" 'scroll-down-command) | |
a88459cd SM |
432 | (define-key map "n" 'Man-next-section) |
433 | (define-key map "p" 'Man-previous-section) | |
434 | (define-key map "\en" 'Man-next-manpage) | |
435 | (define-key map "\ep" 'Man-previous-manpage) | |
436 | (define-key map ">" 'end-of-buffer) | |
437 | (define-key map "<" 'beginning-of-buffer) | |
438 | (define-key map "." 'beginning-of-buffer) | |
439 | (define-key map "r" 'Man-follow-manual-reference) | |
440 | (define-key map "g" 'Man-goto-section) | |
441 | (define-key map "s" 'Man-goto-see-also-section) | |
442 | (define-key map "k" 'Man-kill) | |
443 | (define-key map "q" 'Man-quit) | |
444 | (define-key map "m" 'man) | |
445 | ;; Not all the man references get buttons currently. The text in the | |
446 | ;; manual page can contain references to other man pages | |
447 | (define-key map "\r" 'man-follow) | |
448 | (define-key map "?" 'describe-mode) | |
449 | map) | |
450 | "Keymap for Man mode.") | |
55535639 | 451 | |
4edd9faf | 452 | ;; buttons |
50071f01 | 453 | (define-button-type 'Man-abstract-xref-man-page |
2bac7f17 | 454 | 'follow-link t |
50071f01 MY |
455 | 'help-echo "mouse-2, RET: display this man page" |
456 | 'func nil | |
38363db7 CY |
457 | 'action #'Man-xref-button-action) |
458 | ||
9201cc28 | 459 | (defun Man-xref-button-action (button) |
38363db7 | 460 | (let ((target (button-get button 'Man-target-string))) |
9201cc28 | 461 | (funcall |
38363db7 CY |
462 | (button-get button 'func) |
463 | (cond ((null target) | |
464 | (button-label button)) | |
465 | ((functionp target) | |
466 | (funcall target (button-start button))) | |
467 | (t target))))) | |
50071f01 | 468 | |
9201cc28 | 469 | (define-button-type 'Man-xref-man-page |
50071f01 MY |
470 | :supertype 'Man-abstract-xref-man-page |
471 | 'func 'man-follow) | |
472 | ||
4edd9faf JB |
473 | |
474 | (define-button-type 'Man-xref-header-file | |
500e05aa JB |
475 | 'action (lambda (button) |
476 | (let ((w (button-get button 'Man-target-string))) | |
477 | (unless (Man-view-header-file w) | |
478 | (error "Cannot find header file: %s" w)))) | |
2bac7f17 | 479 | 'follow-link t |
500e05aa | 480 | 'help-echo "mouse-2: display this header file") |
4edd9faf JB |
481 | |
482 | (define-button-type 'Man-xref-normal-file | |
483 | 'action (lambda (button) | |
484 | (let ((f (substitute-in-file-name | |
485 | (button-get button 'Man-target-string)))) | |
486 | (if (file-exists-p f) | |
487 | (if (file-readable-p f) | |
488 | (view-file f) | |
489 | (error "Cannot read a file: %s" f)) | |
490 | (error "Cannot find a file: %s" f)))) | |
2bac7f17 | 491 | 'follow-link t |
5bad6053 | 492 | 'help-echo "mouse-2: display this file") |
4edd9faf | 493 | |
55535639 PJ |
494 | \f |
495 | ;; ====================================================================== | |
496 | ;; utilities | |
497 | ||
498 | (defun Man-init-defvars () | |
927c60bd | 499 | "Used for initializing variables based on display's color support. |
55535639 PJ |
500 | This is necessary if one wants to dump man.el with Emacs." |
501 | ||
502 | ;; Avoid possible error in call-process by using a directory that must exist. | |
503 | (let ((default-directory "/")) | |
504 | (setq Man-sed-script | |
505 | (cond | |
506 | (Man-fontify-manpage-flag | |
507 | nil) | |
15502042 | 508 | ((eq 0 (call-process Man-sed-command nil nil nil Man-sysv-sed-script)) |
55535639 | 509 | Man-sysv-sed-script) |
15502042 | 510 | ((eq 0 (call-process Man-sed-command nil nil nil Man-berkeley-sed-script)) |
55535639 PJ |
511 | Man-berkeley-sed-script) |
512 | (t | |
513 | nil)))) | |
514 | ||
515 | (setq Man-filter-list | |
516 | ;; Avoid trailing nil which confuses customize. | |
517 | (apply 'list | |
518 | (cons | |
519 | Man-sed-command | |
1bf0da02 EZ |
520 | (if (eq system-type 'windows-nt) |
521 | ;; Windows needs ".." quoting, not '..'. | |
522 | (list | |
523 | "-e \"/Reformatting page. Wait/d\"" | |
524 | "-e \"/Reformatting entry. Wait/d\"" | |
525 | "-e \"/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d\"" | |
526 | "-e \"/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d\"" | |
527 | "-e \"/^Printed[ \t][0-9].*[0-9]$/d\"" | |
528 | "-e \"/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d\"" | |
529 | "-e \"/^[A-Za-z].*Last[ \t]change:/d\"" | |
530 | "-e \"/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d\"" | |
531 | "-e \"/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d\"") | |
532 | (list | |
533 | (if Man-sed-script | |
534 | (concat "-e '" Man-sed-script "'") | |
535 | "") | |
536 | "-e '/^[\001-\032][\001-\032]*$/d'" | |
537 | "-e '/\e[789]/s///g'" | |
538 | "-e '/Reformatting page. Wait/d'" | |
539 | "-e '/Reformatting entry. Wait/d'" | |
540 | "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'" | |
541 | "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'" | |
542 | "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'" | |
543 | "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'" | |
544 | "-e '/^Printed[ \t][0-9].*[0-9]$/d'" | |
545 | "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'" | |
546 | "-e '/^[A-Za-z].*Last[ \t]change:/d'" | |
547 | "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'" | |
548 | "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'" | |
549 | "-e '/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d'" | |
550 | ))) | |
551 | ;; Windows doesn't support multi-line commands, so don't | |
552 | ;; invoke Awk there. | |
553 | (unless (eq system-type 'windows-nt) | |
554 | (cons | |
555 | Man-awk-command | |
556 | (list | |
557 | "'\n" | |
558 | "BEGIN { blankline=0; anonblank=0; }\n" | |
559 | "/^$/ { if (anonblank==0) next; }\n" | |
560 | "{ anonblank=1; }\n" | |
561 | "/^$/ { blankline++; next; }\n" | |
562 | "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n" | |
563 | "'" | |
564 | ))) | |
55535639 PJ |
565 | (if (not Man-uses-untabify-flag) |
566 | ;; The outer list will be stripped off by apply. | |
567 | (list (cons | |
568 | Man-untabify-command | |
569 | Man-untabify-command-args)) | |
570 | ))) | |
571 | ) | |
572 | ||
55535639 PJ |
573 | (defsubst Man-make-page-mode-string () |
574 | "Formats part of the mode line for Man mode." | |
575 | (format "%s page %d of %d" | |
576 | (or (nth 2 (nth (1- Man-current-page) Man-page-list)) | |
577 | "") | |
578 | Man-current-page | |
579 | (length Man-page-list))) | |
580 | ||
581 | (defsubst Man-build-man-command () | |
582 | "Builds the entire background manpage and cleaning command." | |
583 | (let ((command (concat manual-program " " Man-switches | |
aec2bd36 JL |
584 | (cond |
585 | ;; Already has %s | |
586 | ((string-match "%s" manual-program) "") | |
587 | ;; Stock MS-DOS shells cannot redirect stderr; | |
588 | ;; `call-process' below sends it to /dev/null, | |
589 | ;; so we don't need `2>' even with DOS shells | |
590 | ;; which do support stderr redirection. | |
591 | ((not (fboundp 'start-process)) " %s") | |
592 | ((concat " %s 2>" null-device))))) | |
55535639 PJ |
593 | (flist Man-filter-list)) |
594 | (while (and flist (car flist)) | |
595 | (let ((pcom (car (car flist))) | |
596 | (pargs (cdr (car flist)))) | |
597 | (setq command | |
598 | (concat command " | " pcom " " | |
599 | (mapconcat (lambda (phrase) | |
600 | (if (not (stringp phrase)) | |
601 | (error "Malformed Man-filter-list")) | |
602 | phrase) | |
603 | pargs " "))) | |
7e734986 | 604 | (setq flist (cdr flist)))) |
55535639 PJ |
605 | command)) |
606 | ||
7e734986 JB |
607 | |
608 | (defun Man-translate-cleanup (string) | |
609 | "Strip leading, trailing and middle spaces." | |
610 | (when (stringp string) | |
611 | ;; Strip leading and trailing | |
612 | (if (string-match "^[ \t\f\r\n]*\\(.+[^ \t\f\r\n]\\)" string) | |
613 | (setq string (match-string 1 string))) | |
614 | ;; middle spaces | |
615 | (setq string (replace-regexp-in-string "[\t\r\n]" " " string)) | |
616 | (setq string (replace-regexp-in-string " +" " " string)) | |
617 | string)) | |
618 | ||
55535639 PJ |
619 | (defun Man-translate-references (ref) |
620 | "Translates REF from \"chmod(2V)\" to \"2v chmod\" style. | |
621 | Leave it as is if already in that style. Possibly downcase and | |
927c60bd JB |
622 | translate the section (see the `Man-downcase-section-letters-flag' |
623 | and the `Man-section-translations-alist' variables)." | |
55535639 | 624 | (let ((name "") |
7e734986 JB |
625 | (section "") |
626 | (slist Man-section-translations-alist)) | |
627 | (setq ref (Man-translate-cleanup ref)) | |
55535639 PJ |
628 | (cond |
629 | ;; "chmod(2V)" case ? | |
630 | ((string-match (concat "^" Man-reference-regexp "$") ref) | |
0dd8b6da LMI |
631 | (setq name (replace-regexp-in-string "[\n\t ]" "" (match-string 1 ref)) |
632 | section (match-string 3 ref))) | |
55535639 PJ |
633 | ;; "2v chmod" case ? |
634 | ((string-match (concat "^\\(" Man-section-regexp | |
635 | "\\) +\\(" Man-name-regexp "\\)$") ref) | |
72397ff1 SM |
636 | (setq name (match-string 2 ref) |
637 | section (match-string 1 ref)))) | |
55535639 PJ |
638 | (if (string= name "") |
639 | ref ; Return the reference as is | |
640 | (if Man-downcase-section-letters-flag | |
641 | (setq section (downcase section))) | |
642 | (while slist | |
643 | (let ((s1 (car (car slist))) | |
644 | (s2 (cdr (car slist)))) | |
645 | (setq slist (cdr slist)) | |
646 | (if Man-downcase-section-letters-flag | |
647 | (setq s1 (downcase s1))) | |
648 | (if (not (string= s1 section)) nil | |
649 | (setq section (if Man-downcase-section-letters-flag | |
650 | (downcase s2) | |
651 | s2) | |
652 | slist nil)))) | |
653 | (concat Man-specified-section-option section " " name)))) | |
654 | ||
aec2bd36 | 655 | (defun Man-support-local-filenames () |
3ab7ebb9 GM |
656 | "Return non-nil if the man command supports local filenames. |
657 | Different man programs support this feature in different ways. | |
658 | The default Debian man program (\"man-db\") has a `--local-file' | |
659 | \(or `-l') option for this purpose. The default Red Hat man | |
660 | program has no such option, but interprets any name containing | |
661 | a \"/\" as a local filename. The function returns either `man-db' | |
662 | `man', or nil." | |
663 | (if (eq Man-support-local-filenames 'auto-detect) | |
664 | (setq Man-support-local-filenames | |
665 | (with-temp-buffer | |
666 | (let ((default-directory | |
667 | ;; Ensure that `default-directory' exists and is readable. | |
668 | (if (and (file-directory-p default-directory) | |
669 | (file-readable-p default-directory)) | |
670 | default-directory | |
671 | (expand-file-name "~/")))) | |
672 | (ignore-errors | |
673 | (call-process manual-program nil t nil "--help"))) | |
674 | (cond ((search-backward "--local-file" nil 'move) | |
675 | 'man-db) | |
676 | ;; This feature seems to be present in at least ver 1.4f, | |
677 | ;; which is about 20 years old. | |
678 | ;; I don't know if this version has an official name? | |
679 | ((looking-at "^man, versione? [1-9]") | |
680 | 'man)))) | |
681 | Man-support-local-filenames)) | |
aec2bd36 | 682 | |
55535639 PJ |
683 | \f |
684 | ;; ====================================================================== | |
d8b3b1a1 | 685 | ;; default man entry: get word near point |
55535639 | 686 | |
d8b3b1a1 MR |
687 | (defun Man-default-man-entry (&optional pos) |
688 | "Guess default manual entry based on the text near position POS. | |
689 | POS defaults to `point'." | |
10c877fe | 690 | (let (word start column distance) |
55535639 | 691 | (save-excursion |
d8b3b1a1 MR |
692 | (when pos (goto-char pos)) |
693 | (setq pos (point)) | |
694 | ;; The default title is the nearest entry-like object before or | |
695 | ;; after POS. | |
696 | (if (and (skip-chars-backward " \ta-zA-Z0-9+") | |
697 | (not (zerop (skip-chars-backward "("))) | |
698 | ;; Try to handle the special case where POS is on a | |
699 | ;; section number. | |
700 | (looking-at | |
701 | (concat "([ \t]*\\(" Man-section-regexp "\\)[ \t]*)")) | |
702 | ;; We skipped a valid section number backwards, look at | |
703 | ;; preceding text. | |
704 | (or (and (skip-chars-backward ",; \t") | |
705 | (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:")))) | |
706 | ;; Not a valid entry, move POS after closing paren. | |
707 | (not (setq pos (match-end 0))))) | |
708 | ;; We have a candidate, make `start' record its starting | |
709 | ;; position. | |
ccf721a6 | 710 | (setq start (point)) |
d8b3b1a1 MR |
711 | ;; Otherwise look at char before POS. |
712 | (goto-char pos) | |
ccf721a6 | 713 | (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:"))) |
d8b3b1a1 MR |
714 | ;; Our candidate is just before or around POS. |
715 | (setq start (point)) | |
716 | ;; Otherwise record the current column and look backwards. | |
717 | (setq column (current-column)) | |
718 | (skip-chars-backward ",; \t") | |
c80e3b4a | 719 | ;; Record the distance traveled. |
d8b3b1a1 MR |
720 | (setq distance (- column (current-column))) |
721 | (when (looking-back | |
722 | (concat "([ \t]*\\(?:" Man-section-regexp "\\)[ \t]*)")) | |
723 | ;; Skip section number backwards. | |
724 | (goto-char (match-beginning 0)) | |
725 | (skip-chars-backward " \t")) | |
726 | (if (not (zerop (skip-chars-backward "-a-zA-Z0-9._+:"))) | |
727 | (progn | |
728 | ;; We have a candidate before POS ... | |
729 | (setq start (point)) | |
730 | (goto-char pos) | |
731 | (if (and (skip-chars-forward ",; \t") | |
732 | (< (- (current-column) column) distance) | |
733 | (looking-at "[-a-zA-Z0-9._+:]")) | |
734 | ;; ... but the one after POS is better. | |
735 | (setq start (point)) | |
736 | ;; ... and anything after POS is worse. | |
737 | (goto-char start))) | |
738 | ;; No candidate before POS. | |
739 | (goto-char pos) | |
740 | (skip-chars-forward ",; \t") | |
741 | (setq start (point))))) | |
742 | ;; We have found a suitable starting point, try to skip at least | |
743 | ;; one character. | |
ccf721a6 MR |
744 | (skip-chars-forward "-a-zA-Z0-9._+:") |
745 | (setq word (buffer-substring-no-properties start (point))) | |
746 | ;; If there is a continuation at the end of line, check the | |
747 | ;; following line too, eg: | |
748 | ;; see this- | |
749 | ;; command-here(1) | |
d8b3b1a1 | 750 | ;; Note: This code gets executed iff our entry is after POS. |
ccf721a6 | 751 | (when (looking-at "[ \t\r\n]+\\([-a-zA-Z0-9._+:]+\\)([0-9])") |
d8b3b1a1 MR |
752 | (setq word (concat word (match-string-no-properties 1))) |
753 | ;; Make sure the section number gets included by the code below. | |
754 | (goto-char (match-end 1))) | |
c1a6c0a4 | 755 | (when (string-match "[-._]+$" word) |
ccf721a6 | 756 | (setq word (substring word 0 (match-beginning 0)))) |
d8b3b1a1 MR |
757 | ;; The following was commented out since the preceding code |
758 | ;; should not produce a leading "*" in the first place. | |
759 | ;;; ;; If looking at something like *strcat(... , remove the '*' | |
760 | ;;; (when (string-match "^*" word) | |
761 | ;;; (setq word (substring word 1))) | |
762 | (concat | |
763 | word | |
764 | (and (not (string-equal word "")) | |
765 | ;; If looking at something like ioctl(2) or brc(1M), | |
766 | ;; include the section number in the returned value. | |
767 | (looking-at | |
768 | (concat "[ \t]*([ \t]*\\(" Man-section-regexp "\\)[ \t]*)")) | |
769 | (format "(%s)" (match-string-no-properties 1))))))) | |
55535639 PJ |
770 | |
771 | \f | |
772 | ;; ====================================================================== | |
773 | ;; Top level command and background process sentinel | |
774 | ||
775 | ;; For compatibility with older versions. | |
776 | ;;;###autoload | |
777 | (defalias 'manual-entry 'man) | |
778 | ||
e2ec6dd5 SM |
779 | (defvar Man-completion-cache nil |
780 | ;; On my machine, "man -k" is so fast that a cache makes no sense, | |
781 | ;; but apparently that's not the case in all cases, so let's add a cache. | |
782 | "Cache of completion table of the form (PREFIX . TABLE).") | |
783 | ||
327a6cca WJ |
784 | (defvar Man-man-k-use-anchor |
785 | ;; man-db or man-1.* | |
786 | (memq system-type '(gnu gnu/linux gnu/kfreebsd)) | |
787 | "If non-nil prepend ^ to the prefix passed to \"man -k\" for completion. | |
788 | The value should be nil if \"man -k ^PREFIX\" may omit some man | |
789 | pages whose names start with PREFIX. | |
790 | ||
791 | Currently, the default value depends on `system-type' and is | |
792 | non-nil where the standard man programs are known to behave | |
793 | properly. Setting the value to nil always gives correct results | |
794 | but computing the list of completions may take a bit longer.") | |
795 | ||
796 | (defun Man-parse-man-k () | |
797 | "Parse \"man -k\" output and return the list of page names. | |
798 | ||
799 | The current buffer should contain the output of a command of the | |
800 | form \"man -k keyword\", which is traditionally also available with | |
801 | apropos(1). | |
802 | ||
803 | While POSIX man(1p) is a bit vague about what to expect here, | |
804 | this function tries to parse some commonly used formats, which | |
805 | can be described in the following informal way, with square brackets | |
806 | indicating optional parts and whitespace being interpreted | |
807 | somewhat loosely. | |
808 | ||
809 | foo[, bar [, ...]] [other stuff] (sec) - description | |
810 | foo(sec)[, bar(sec) [, ...]] [other stuff] - description | |
811 | ||
812 | For more details and some regression tests, please see | |
813 | test/automated/man-tests.el in the emacs bzr repository." | |
814 | (goto-char (point-min)) | |
815 | ;; See man-tests for data about which systems use which format (hopefully we | |
816 | ;; will be able to simplify the code if/when some of those formats aren't | |
817 | ;; used any more). | |
818 | (let (table) | |
819 | (while (search-forward-regexp "^\\([^ \t,\n]+\\)\\(.*?\\)\ | |
820 | \\(?:[ \t]\\(([^ \t,\n]+?)\\)\\)?\\(?:[ \t]+- ?\\(.*\\)\\)?$" nil t) | |
821 | (let ((section (match-string 3)) | |
822 | (description (match-string 4)) | |
823 | (bound (match-end 2))) | |
824 | (goto-char (match-end 1)) | |
825 | (while | |
826 | (progn | |
827 | ;; The first regexp grouping may already match the section | |
828 | ;; tacked on to the name, which is ok since for the formats we | |
829 | ;; claim to support the third (non-shy) grouping does not | |
830 | ;; match in this case, i.e., section is nil. | |
831 | (push (propertize (concat (match-string 1) section) | |
832 | 'help-echo description) | |
833 | table) | |
834 | (search-forward-regexp "\\=, *\\([^ \t,]+\\)" bound t))))) | |
835 | (nreverse table))) | |
836 | ||
bb301b9a SM |
837 | (defun Man-completion-table (string pred action) |
838 | (cond | |
9505c3c7 SM |
839 | ;; This ends up returning t for pretty much any string, and hence leads to |
840 | ;; spurious "complete but not unique" messages. And since `man' doesn't | |
841 | ;; require-match anyway, there's not point being clever. | |
842 | ;;((eq action 'lambda) (not (string-match "([^)]*\\'" string))) | |
896114cf SM |
843 | ((equal string "-k") |
844 | ;; Let SPC (minibuffer-complete-word) insert the space. | |
845 | (complete-with-action action '("-k ") string pred)) | |
10c877fe SM |
846 | (t |
847 | (let ((table (cdr Man-completion-cache)) | |
848 | (section nil) | |
849 | (prefix string)) | |
850 | (when (string-match "\\`\\([[:digit:]].*?\\) " string) | |
851 | (setq section (match-string 1 string)) | |
852 | (setq prefix (substring string (match-end 0)))) | |
e2ec6dd5 | 853 | (unless (and Man-completion-cache |
10c877fe SM |
854 | (string-prefix-p (car Man-completion-cache) prefix)) |
855 | (with-temp-buffer | |
b58dd707 KR |
856 | (setq default-directory "/") ;; in case inherited doesn't exist |
857 | ;; Actually for my `man' the arg is a regexp. | |
858 | ;; POSIX says it must be ERE and "man-db" seems to agree, | |
10c877fe SM |
859 | ;; whereas under MacOSX it seems to be BRE-style and doesn't |
860 | ;; accept backslashes at all. Let's not bother to | |
861 | ;; quote anything. | |
862 | (let ((process-environment (copy-sequence process-environment))) | |
863 | (setenv "COLUMNS" "999") ;; don't truncate long names | |
c07ff221 SM |
864 | ;; manual-program might not even exist. And since it's |
865 | ;; run differently in Man-getpage-in-background, an error | |
866 | ;; here may not necessarily mean that we'll also get an | |
867 | ;; error later. | |
327a6cca WJ |
868 | (ignore-errors |
869 | (call-process manual-program nil '(t nil) nil | |
870 | "-k" (concat (when (or Man-man-k-use-anchor | |
871 | (string-equal prefix "")) | |
872 | "^") | |
873 | prefix)))) | |
874 | (setq table (Man-parse-man-k))) | |
875 | ;; Cache the table for later reuse. | |
876 | (setq Man-completion-cache (cons prefix table))) | |
bb301b9a SM |
877 | ;; The table may contain false positives since the match is made |
878 | ;; by "man -k" not just on the manpage's name. | |
10c877fe SM |
879 | (if section |
880 | (let ((re (concat "(" (regexp-quote section) ")\\'"))) | |
881 | (dolist (comp (prog1 table (setq table nil))) | |
882 | (if (string-match re comp) | |
883 | (push (substring comp 0 (match-beginning 0)) table))) | |
884 | (completion-table-with-context (concat section " ") table | |
885 | prefix pred action)) | |
d7117720 SM |
886 | ;; If the current text looks like a possible section name, |
887 | ;; then add a completion entry that just adds a space so SPC | |
888 | ;; can be used to insert a space. | |
889 | (if (string-match "\\`[[:digit:]]" string) | |
890 | (push (concat string " ") table)) | |
10c877fe SM |
891 | (let ((res (complete-with-action action table string pred))) |
892 | ;; In case we're completing to a single name that exists in | |
893 | ;; several sections, the longest prefix will look like "foo(". | |
894 | (if (and (stringp res) | |
895 | (string-match "([^(]*\\'" res) | |
896 | ;; In case the paren was already in `prefix', don't | |
897 | ;; remove it. | |
898 | (> (match-beginning 0) (length prefix))) | |
899 | (substring res 0 (match-beginning 0)) | |
900 | res))))))) | |
7e734986 | 901 | |
55535639 PJ |
902 | ;;;###autoload |
903 | (defun man (man-args) | |
904 | "Get a Un*x manual page and put it in a buffer. | |
91c4831e KR |
905 | This command is the top-level command in the man package. It |
906 | runs a Un*x command to retrieve and clean a manpage in the | |
907 | background and places the results in a `Man-mode' browsing | |
908 | buffer. See variable `Man-notify-method' for what happens when | |
909 | the buffer is ready. If a buffer already exists for this man | |
910 | page, it will display immediately. | |
911 | ||
912 | For a manpage from a particular section, use either of the | |
913 | following. \"cat(1)\" is how cross-references appear and is | |
914 | passed to man as \"1 cat\". | |
915 | ||
916 | cat(1) | |
917 | 1 cat | |
918 | ||
919 | To see manpages from all sections related to a subject, use an | |
920 | \"all pages\" option (which might be \"-a\" if it's not the | |
921 | default), then step through with `Man-next-manpage' (\\<Man-mode-map>\\[Man-next-manpage]) etc. | |
922 | Add to `Man-switches' to make this option permanent. | |
923 | ||
924 | -a chmod | |
925 | ||
926 | An explicit filename can be given too. Use -l if it might | |
927 | otherwise look like a page name. | |
928 | ||
929 | /my/file/name.1.gz | |
930 | -l somefile.1 | |
931 | ||
932 | An \"apropos\" query with -k gives a buffer of matching page | |
933 | names or descriptions. The pattern argument is usually an | |
934 | \"egrep\" style regexp. | |
935 | ||
936 | -k pattern" | |
937 | ||
55535639 PJ |
938 | (interactive |
939 | (list (let* ((default-entry (Man-default-man-entry)) | |
573f4575 KR |
940 | ;; ignore case because that's friendly for bizarre |
941 | ;; caps things like the X11 function names and because | |
a3827a43 | 942 | ;; "man" itself is case-insensitive on the command line |
573f4575 KR |
943 | ;; so you're accustomed not to bother about the case |
944 | ;; ("man -k" is case-insensitive similarly, so the | |
945 | ;; table has everything available to complete) | |
946 | (completion-ignore-case t) | |
327a6cca | 947 | Man-completion-cache ;Don't cache across calls. |
bb301b9a | 948 | (input (completing-read |
5b76833f | 949 | (format "Manual entry%s" |
55535639 | 950 | (if (string= default-entry "") |
5b76833f RF |
951 | ": " |
952 | (format " (default %s): " default-entry))) | |
bb301b9a SM |
953 | 'Man-completion-table |
954 | nil nil nil 'Man-topic-history default-entry))) | |
55535639 | 955 | (if (string= input "") |
72397ff1 | 956 | (error "No man args given") |
55535639 PJ |
957 | input)))) |
958 | ||
959 | ;; Possibly translate the "subject(section)" syntax into the | |
960 | ;; "section subject" syntax and possibly downcase the section. | |
961 | (setq man-args (Man-translate-references man-args)) | |
962 | ||
963 | (Man-getpage-in-background man-args)) | |
964 | ||
965 | ;;;###autoload | |
966 | (defun man-follow (man-args) | |
967 | "Get a Un*x manual page of the item under point and put it in a buffer." | |
968 | (interactive (list (Man-default-man-entry))) | |
969 | (if (or (not man-args) | |
970 | (string= man-args "")) | |
971 | (error "No item under point") | |
972 | (man man-args))) | |
973 | ||
974 | (defun Man-getpage-in-background (topic) | |
398a825b SM |
975 | "Use TOPIC to build and fire off the manpage and cleaning command. |
976 | Return the buffer in which the manpage will appear." | |
55535639 PJ |
977 | (let* ((man-args topic) |
978 | (bufname (concat "*Man " man-args "*")) | |
979 | (buffer (get-buffer bufname))) | |
980 | (if buffer | |
981 | (Man-notify-when-ready buffer) | |
982 | (require 'env) | |
983 | (message "Invoking %s %s in the background" manual-program man-args) | |
984 | (setq buffer (generate-new-buffer bufname)) | |
a88459cd | 985 | (with-current-buffer buffer |
5a0c1883 | 986 | (setq buffer-undo-list t) |
55535639 PJ |
987 | (setq Man-original-frame (selected-frame)) |
988 | (setq Man-arguments man-args)) | |
989 | (let ((process-environment (copy-sequence process-environment)) | |
990 | ;; The following is so Awk script gets \n intact | |
991 | ;; But don't prevent decoding of the outside. | |
992 | (coding-system-for-write 'raw-text-unix) | |
993 | ;; We must decode the output by a coding system that the | |
994 | ;; system's locale suggests in multibyte mode. | |
995 | (coding-system-for-read | |
597e2240 | 996 | (if (default-value 'enable-multibyte-characters) |
55535639 PJ |
997 | locale-coding-system 'raw-text-unix)) |
998 | ;; Avoid possible error by using a directory that always exists. | |
cb24638e KG |
999 | (default-directory |
1000 | (if (and (file-directory-p default-directory) | |
1001 | (not (find-file-name-handler default-directory | |
1002 | 'file-directory-p))) | |
1003 | default-directory | |
1004 | "/"))) | |
55535639 PJ |
1005 | ;; Prevent any attempt to use display terminal fanciness. |
1006 | (setenv "TERM" "dumb") | |
9606309f DL |
1007 | ;; In Debian Woody, at least, we get overlong lines under X |
1008 | ;; unless COLUMNS or MANWIDTH is set. This isn't a problem on | |
1009 | ;; a tty. man(1) says: | |
1010 | ;; MANWIDTH | |
1011 | ;; If $MANWIDTH is set, its value is used as the line | |
1012 | ;; length for which manual pages should be formatted. | |
1013 | ;; If it is not set, manual pages will be formatted | |
1014 | ;; with a line length appropriate to the current ter- | |
1015 | ;; minal (using an ioctl(2) if available, the value of | |
1016 | ;; $COLUMNS, or falling back to 80 characters if nei- | |
1017 | ;; ther is available). | |
763237c4 SS |
1018 | (when (or window-system |
1019 | (not (or (getenv "MANWIDTH") (getenv "COLUMNS")))) | |
8783fe91 JL |
1020 | ;; This isn't strictly correct, since we don't know how |
1021 | ;; the page will actually be displayed, but it seems | |
1022 | ;; reasonable. | |
1023 | (setenv "COLUMNS" (number-to-string | |
1024 | (cond | |
1025 | ((and (integerp Man-width) (> Man-width 0)) | |
1026 | Man-width) | |
1027 | (Man-width (frame-width)) | |
1028 | ((window-width)))))) | |
3d56260a GM |
1029 | ;; Since man-db 2.4.3-1, man writes plain text with no escape |
1030 | ;; sequences when stdout is not a tty. In 2.5.0, the following | |
1031 | ;; env-var was added to allow control of this (see Debian Bug#340673). | |
1032 | (setenv "MAN_KEEP_FORMATTING" "1") | |
55535639 PJ |
1033 | (if (fboundp 'start-process) |
1034 | (set-process-sentinel | |
ac00945e | 1035 | (start-process manual-program buffer |
00170b0d EZ |
1036 | (if (memq system-type '(cygwin windows-nt)) |
1037 | shell-file-name | |
1038 | "sh") | |
353518de | 1039 | shell-command-switch |
55535639 PJ |
1040 | (format (Man-build-man-command) man-args)) |
1041 | 'Man-bgproc-sentinel) | |
504feff5 | 1042 | (let ((exit-status |
ac00945e EZ |
1043 | (call-process shell-file-name nil (list buffer nil) nil |
1044 | shell-command-switch | |
504feff5 KG |
1045 | (format (Man-build-man-command) man-args))) |
1046 | (msg "")) | |
1047 | (or (and (numberp exit-status) | |
1048 | (= exit-status 0)) | |
1049 | (and (numberp exit-status) | |
1050 | (setq msg | |
1051 | (format "exited abnormally with code %d" | |
1052 | exit-status))) | |
1053 | (setq msg exit-status)) | |
398a825b SM |
1054 | (Man-bgproc-sentinel bufname msg))))) |
1055 | buffer)) | |
55535639 PJ |
1056 | |
1057 | (defun Man-notify-when-ready (man-buffer) | |
1058 | "Notify the user when MAN-BUFFER is ready. | |
1059 | See the variable `Man-notify-method' for the different notification behaviors." | |
a88459cd | 1060 | (let ((saved-frame (with-current-buffer man-buffer |
55535639 | 1061 | Man-original-frame))) |
a464a6c7 SM |
1062 | (pcase Man-notify-method |
1063 | (`newframe | |
1064 | ;; Since we run asynchronously, perhaps while Emacs is waiting | |
1065 | ;; for input, we must not leave a different buffer current. We | |
1066 | ;; can't rely on the editor command loop to reselect the | |
1067 | ;; selected window's buffer. | |
1068 | (save-excursion | |
1069 | (let ((frame (make-frame Man-frame-parameters))) | |
1070 | (set-window-buffer (frame-selected-window frame) man-buffer) | |
1071 | (set-window-dedicated-p (frame-selected-window frame) t) | |
1072 | (or (display-multi-frame-p frame) | |
1073 | (select-frame frame))))) | |
1074 | (`pushy | |
1075 | (switch-to-buffer man-buffer)) | |
1076 | (`bully | |
1077 | (and (frame-live-p saved-frame) | |
1078 | (select-frame saved-frame)) | |
1079 | (pop-to-buffer man-buffer) | |
1080 | (delete-other-windows)) | |
1081 | (`aggressive | |
1082 | (and (frame-live-p saved-frame) | |
1083 | (select-frame saved-frame)) | |
1084 | (pop-to-buffer man-buffer)) | |
1085 | (`friendly | |
1086 | (and (frame-live-p saved-frame) | |
1087 | (select-frame saved-frame)) | |
1088 | (display-buffer man-buffer 'not-this-window)) | |
1089 | (`polite | |
1090 | (beep) | |
1091 | (message "Manual buffer %s is ready" (buffer-name man-buffer))) | |
1092 | (`quiet | |
1093 | (message "Manual buffer %s is ready" (buffer-name man-buffer))) | |
1094 | (_ ;; meek | |
1095 | (message "")) | |
1096 | ))) | |
55535639 PJ |
1097 | |
1098 | (defun Man-softhyphen-to-minus () | |
9606309f | 1099 | ;; \255 is SOFT HYPHEN in Latin-N. Versions of Debian man, at |
55535639 PJ |
1100 | ;; least, emit it even when not in a Latin-N locale. |
1101 | (unless (eq t (compare-strings "latin-" 0 nil | |
1102 | current-language-environment 0 6 t)) | |
1103 | (goto-char (point-min)) | |
1104 | (let ((str "\255")) | |
1105 | (if enable-multibyte-characters | |
1106 | (setq str (string-as-multibyte str))) | |
1107 | (while (search-forward str nil t) (replace-match "-"))))) | |
1108 | ||
1109 | (defun Man-fontify-manpage () | |
1110 | "Convert overstriking and underlining to the correct fonts. | |
1111 | Same for the ANSI bold and normal escape sequences." | |
1112 | (interactive) | |
aec2bd36 | 1113 | (message "Please wait: formatting the %s man page..." Man-arguments) |
55535639 | 1114 | (goto-char (point-min)) |
079c2d00 | 1115 | ;; Fontify ANSI escapes. |
456e62c2 WJ |
1116 | (let ((ansi-color-apply-face-function |
1117 | (lambda (beg end face) | |
1118 | (when face | |
1119 | (put-text-property beg end 'face face)))) | |
1120 | (ansi-color-map Man-ansi-color-map)) | |
1121 | (ansi-color-apply-on-region (point-min) (point-max))) | |
079c2d00 | 1122 | ;; Other highlighting. |
6bfb8bd6 RS |
1123 | (let ((buffer-undo-list t)) |
1124 | (if (< (buffer-size) (position-bytes (point-max))) | |
1125 | ;; Multibyte characters exist. | |
1126 | (progn | |
1127 | (goto-char (point-min)) | |
1128 | (while (search-forward "__\b\b" nil t) | |
1129 | (backward-delete-char 4) | |
456e62c2 | 1130 | (put-text-property (point) (1+ (point)) 'face 'Man-underline)) |
6bfb8bd6 RS |
1131 | (goto-char (point-min)) |
1132 | (while (search-forward "\b\b__" nil t) | |
1133 | (backward-delete-char 4) | |
456e62c2 | 1134 | (put-text-property (1- (point)) (point) 'face 'Man-underline)))) |
6bfb8bd6 RS |
1135 | (goto-char (point-min)) |
1136 | (while (search-forward "_\b" nil t) | |
1137 | (backward-delete-char 2) | |
456e62c2 | 1138 | (put-text-property (point) (1+ (point)) 'face 'Man-underline)) |
6bfb8bd6 RS |
1139 | (goto-char (point-min)) |
1140 | (while (search-forward "\b_" nil t) | |
1141 | (backward-delete-char 2) | |
456e62c2 | 1142 | (put-text-property (1- (point)) (point) 'face 'Man-underline)) |
6bfb8bd6 RS |
1143 | (goto-char (point-min)) |
1144 | (while (re-search-forward "\\(.\\)\\(\b+\\1\\)+" nil t) | |
1145 | (replace-match "\\1") | |
456e62c2 | 1146 | (put-text-property (1- (point)) (point) 'face 'Man-overstrike)) |
6bfb8bd6 RS |
1147 | (goto-char (point-min)) |
1148 | (while (re-search-forward "o\b\\+\\|\\+\bo" nil t) | |
1149 | (replace-match "o") | |
1150 | (put-text-property (1- (point)) (point) 'face 'bold)) | |
1151 | (goto-char (point-min)) | |
1152 | (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) | |
1153 | (replace-match "+") | |
1154 | (put-text-property (1- (point)) (point) 'face 'bold)) | |
91e3333f | 1155 | ;; When the header is longer than the manpage name, groff tries to |
e4769531 | 1156 | ;; condense it to a shorter line interspersed with ^H. Remove ^H with |
456e62c2 | 1157 | ;; their preceding chars (but don't put Man-overstrike). (Bug#5566) |
91e3333f JL |
1158 | (goto-char (point-min)) |
1159 | (while (re-search-forward ".\b" nil t) (backward-delete-char 2)) | |
6bfb8bd6 RS |
1160 | (goto-char (point-min)) |
1161 | ;; Try to recognize common forms of cross references. | |
1162 | (Man-highlight-references) | |
1163 | (Man-softhyphen-to-minus) | |
1164 | (goto-char (point-min)) | |
1165 | (while (re-search-forward Man-heading-regexp nil t) | |
1166 | (put-text-property (match-beginning 0) | |
1167 | (match-end 0) | |
456e62c2 | 1168 | 'face 'Man-overstrike))) |
2f5c6024 | 1169 | (message "%s man page formatted" (Man-page-from-arguments Man-arguments))) |
55535639 | 1170 | |
50071f01 | 1171 | (defun Man-highlight-references (&optional xref-man-type) |
4edd9faf | 1172 | "Highlight the references on mouse-over. |
927c60bd | 1173 | References include items in the SEE ALSO section, |
71726072 EZ |
1174 | header file (#include <foo.h>), and files in FILES. |
1175 | If optional argument XREF-MAN-TYPE is non-nil, it used as the | |
1176 | button type for items in SEE ALSO section. If it is nil, the | |
1177 | default type, `Man-xref-man-page' is used for the buttons." | |
af8308ec MY |
1178 | ;; `Man-highlight-references' is used from woman.el, too. |
1179 | ;; woman.el doesn't set `Man-arguments'. | |
1180 | (unless Man-arguments | |
1181 | (setq Man-arguments "")) | |
30abc4f4 MY |
1182 | (if (string-match "-k " Man-arguments) |
1183 | (progn | |
707f55b0 DN |
1184 | (Man-highlight-references0 nil Man-reference-regexp 1 |
1185 | 'Man-default-man-entry | |
71726072 EZ |
1186 | (or xref-man-type 'Man-xref-man-page)) |
1187 | (Man-highlight-references0 nil Man-apropos-regexp 1 | |
707f55b0 | 1188 | 'Man-default-man-entry |
71726072 | 1189 | (or xref-man-type 'Man-xref-man-page))) |
9201cc28 | 1190 | (Man-highlight-references0 Man-see-also-regexp Man-reference-regexp 1 |
707f55b0 | 1191 | 'Man-default-man-entry |
71726072 EZ |
1192 | (or xref-man-type 'Man-xref-man-page)) |
1193 | (Man-highlight-references0 Man-synopsis-regexp Man-header-regexp 0 2 | |
1194 | 'Man-xref-header-file) | |
1195 | (Man-highlight-references0 Man-files-regexp Man-normal-file-regexp 0 0 | |
1196 | 'Man-xref-normal-file))) | |
4edd9faf | 1197 | |
30abc4f4 | 1198 | (defun Man-highlight-references0 (start-section regexp button-pos target type) |
4edd9faf | 1199 | ;; Based on `Man-build-references-alist' |
30abc4f4 MY |
1200 | (when (or (null start-section) |
1201 | (Man-find-section start-section)) | |
1202 | (let ((end (if start-section | |
1203 | (progn | |
1204 | (forward-line 1) | |
1205 | (back-to-indentation) | |
1206 | (save-excursion | |
1207 | (Man-next-section 1) | |
1208 | (point))) | |
1209 | (goto-char (point-min)) | |
a88459cd | 1210 | nil))) |
4edd9faf | 1211 | (while (re-search-forward regexp end t) |
cde7e38b CY |
1212 | ;; An overlay button is preferable because the underlying text |
1213 | ;; may have text property highlights (Bug#7881). | |
1214 | (make-button | |
4edd9faf JB |
1215 | (match-beginning button-pos) |
1216 | (match-end button-pos) | |
1217 | 'type type | |
30abc4f4 | 1218 | 'Man-target-string (cond |
9201cc28 | 1219 | ((numberp target) |
30abc4f4 MY |
1220 | (match-string target)) |
1221 | ((functionp target) | |
707f55b0 | 1222 | target) |
30abc4f4 | 1223 | (t nil))))))) |
4edd9faf | 1224 | |
1d3b75d8 RS |
1225 | (defun Man-cleanup-manpage (&optional interactive) |
1226 | "Remove overstriking and underlining from the current buffer. | |
1227 | Normally skip any jobs that should have been done by the sed script, | |
1228 | but when called interactively, do those jobs even if the sed | |
1229 | script would have done them." | |
1230 | (interactive "p") | |
55535639 PJ |
1231 | (message "Please wait: cleaning up the %s man page..." |
1232 | Man-arguments) | |
1d3b75d8 | 1233 | (if (or interactive (not Man-sed-script)) |
55535639 PJ |
1234 | (progn |
1235 | (goto-char (point-min)) | |
1236 | (while (search-forward "_\b" nil t) (backward-delete-char 2)) | |
1237 | (goto-char (point-min)) | |
1238 | (while (search-forward "\b_" nil t) (backward-delete-char 2)) | |
1239 | (goto-char (point-min)) | |
1240 | (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t) | |
1241 | (replace-match "\\1")) | |
1242 | (goto-char (point-min)) | |
1243 | (while (re-search-forward "\e\\[[0-9]+m" nil t) (replace-match "")) | |
1244 | (goto-char (point-min)) | |
1245 | (while (re-search-forward "o\b\\+\\|\\+\bo" nil t) (replace-match "o")) | |
1246 | )) | |
1247 | (goto-char (point-min)) | |
1248 | (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) (replace-match "+")) | |
91e3333f | 1249 | ;; When the header is longer than the manpage name, groff tries to |
e4769531 | 1250 | ;; condense it to a shorter line interspersed with ^H. Remove ^H with |
456e62c2 | 1251 | ;; their preceding chars (but don't put Man-overstrike). (Bug#5566) |
91e3333f JL |
1252 | (goto-char (point-min)) |
1253 | (while (re-search-forward ".\b" nil t) (backward-delete-char 2)) | |
55535639 PJ |
1254 | (Man-softhyphen-to-minus) |
1255 | (message "%s man page cleaned up" Man-arguments)) | |
1256 | ||
1257 | (defun Man-bgproc-sentinel (process msg) | |
1258 | "Manpage background process sentinel. | |
1259 | When manpage command is run asynchronously, PROCESS is the process | |
1260 | object for the manpage command; when manpage command is run | |
1261 | synchronously, PROCESS is the name of the buffer where the manpage | |
1262 | command is run. Second argument MSG is the exit message of the | |
1263 | manpage command." | |
1264 | (let ((Man-buffer (if (stringp process) (get-buffer process) | |
1265 | (process-buffer process))) | |
1266 | (delete-buff nil) | |
1267 | (err-mess nil)) | |
1268 | ||
1269 | (if (null (buffer-name Man-buffer)) ;; deleted buffer | |
1270 | (or (stringp process) | |
1271 | (set-process-buffer process nil)) | |
1272 | ||
a88459cd | 1273 | (with-current-buffer Man-buffer |
55535639 PJ |
1274 | (let ((case-fold-search nil)) |
1275 | (goto-char (point-min)) | |
1276 | (cond ((or (looking-at "No \\(manual \\)*entry for") | |
1277 | (looking-at "[^\n]*: nothing appropriate$")) | |
1278 | (setq err-mess (buffer-substring (point) | |
1279 | (progn | |
1280 | (end-of-line) (point))) | |
1281 | delete-buff t)) | |
69bcb1f3 KR |
1282 | |
1283 | ;; "-k foo", successful exit, but no output (from man-db) | |
1284 | ;; ENHANCE-ME: share the check for -k with | |
1285 | ;; `Man-highlight-references'. The \\s- bits here are | |
1286 | ;; meant to allow for multiple options with -k among them. | |
1287 | ((and (string-match "\\(\\`\\|\\s-\\)-k\\s-" Man-arguments) | |
1288 | (eq (process-status process) 'exit) | |
1289 | (= (process-exit-status process) 0) | |
1290 | (= (point-min) (point-max))) | |
1291 | (setq err-mess (format "%s: no matches" Man-arguments) | |
1292 | delete-buff t)) | |
1293 | ||
55535639 PJ |
1294 | ((or (stringp process) |
1295 | (not (and (eq (process-status process) 'exit) | |
1296 | (= (process-exit-status process) 0)))) | |
1297 | (or (zerop (length msg)) | |
1298 | (progn | |
1299 | (setq err-mess | |
1300 | (concat (buffer-name Man-buffer) | |
1301 | ": process " | |
1302 | (let ((eos (1- (length msg)))) | |
1303 | (if (= (aref msg eos) ?\n) | |
1304 | (substring msg 0 eos) msg)))) | |
1305 | (goto-char (point-max)) | |
1306 | (insert (format "\nprocess %s" msg)))) | |
1307 | )) | |
1308 | (if delete-buff | |
1309 | (kill-buffer Man-buffer) | |
1310 | (if Man-fontify-manpage-flag | |
1311 | (Man-fontify-manpage) | |
1312 | (Man-cleanup-manpage)) | |
55fb4ff7 | 1313 | |
55535639 | 1314 | (run-hooks 'Man-cooked-hook) |
55fb4ff7 RS |
1315 | (Man-mode) |
1316 | ||
1317 | (if (not Man-page-list) | |
d1583c48 | 1318 | (let ((args Man-arguments)) |
55fb4ff7 | 1319 | (kill-buffer (current-buffer)) |
71873e2b SM |
1320 | (user-error "Can't find the %s manpage" |
1321 | (Man-page-from-arguments args))) | |
d1583c48 | 1322 | (set-buffer-modified-p nil)))) |
55535639 PJ |
1323 | ;; Restore case-fold-search before calling |
1324 | ;; Man-notify-when-ready because it may switch buffers. | |
1325 | ||
1326 | (if (not delete-buff) | |
1327 | (Man-notify-when-ready Man-buffer)) | |
1328 | ||
1329 | (if err-mess | |
8c16bd8c | 1330 | (error "%s" err-mess)) |
55535639 PJ |
1331 | )))) |
1332 | ||
2f5c6024 LMI |
1333 | (defun Man-page-from-arguments (args) |
1334 | ;; Skip arguments and only print the page name. | |
1335 | (mapconcat | |
1336 | 'identity | |
1337 | (delete nil | |
1338 | (mapcar | |
1339 | (lambda (elem) | |
1340 | (and (not (string-match "^-" elem)) | |
1341 | elem)) | |
1342 | (split-string args " "))) | |
1343 | " ")) | |
1344 | ||
55535639 PJ |
1345 | \f |
1346 | ;; ====================================================================== | |
1347 | ;; set up manual mode in buffer and build alists | |
1348 | ||
398a825b SM |
1349 | (defvar bookmark-make-record-function) |
1350 | ||
3a1524ed LK |
1351 | (put 'Man-mode 'mode-class 'special) |
1352 | ||
1b3b87df | 1353 | (define-derived-mode Man-mode fundamental-mode "Man" |
55535639 PJ |
1354 | "A mode for browsing Un*x manual pages. |
1355 | ||
1356 | The following man commands are available in the buffer. Try | |
1357 | \"\\[describe-key] <key> RET\" for more information: | |
1358 | ||
1359 | \\[man] Prompt to retrieve a new manpage. | |
1360 | \\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section. | |
1361 | \\[Man-next-manpage] Jump to next manpage in circular list. | |
1362 | \\[Man-previous-manpage] Jump to previous manpage in circular list. | |
1363 | \\[Man-next-section] Jump to next manpage section. | |
1364 | \\[Man-previous-section] Jump to previous manpage section. | |
1365 | \\[Man-goto-section] Go to a manpage section. | |
1366 | \\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section. | |
1367 | \\[Man-quit] Deletes the manpage window, bury its buffer. | |
1368 | \\[Man-kill] Deletes the manpage window, kill its buffer. | |
1369 | \\[describe-mode] Prints this help text. | |
1370 | ||
1371 | The following variables may be of some use. Try | |
1372 | \"\\[describe-variable] <variable-name> RET\" for more information: | |
1373 | ||
1374 | `Man-notify-method' What happens when manpage formatting is done. | |
1375 | `Man-downcase-section-letters-flag' Force section letters to lower case. | |
1376 | `Man-circular-pages-flag' Treat multiple manpage list as circular. | |
1377 | `Man-section-translations-alist' List of section numbers and their Un*x equiv. | |
1378 | `Man-filter-list' Background manpage filter command. | |
55535639 PJ |
1379 | `Man-mode-map' Keymap bindings for Man mode buffers. |
1380 | `Man-mode-hook' Normal hook run on entry to Man mode. | |
1381 | `Man-section-regexp' Regexp describing manpage section letters. | |
1382 | `Man-heading-regexp' Regexp describing section headers. | |
1383 | `Man-see-also-regexp' Regexp for SEE ALSO section (or your equiv). | |
1384 | `Man-first-heading-regexp' Regexp for first heading on a manpage. | |
1385 | `Man-reference-regexp' Regexp matching a references in SEE ALSO. | |
1386 | `Man-switches' Background `man' command switches. | |
1387 | ||
1388 | The following key bindings are currently in effect in the buffer: | |
1389 | \\{Man-mode-map}" | |
1b3b87df | 1390 | (setq buffer-auto-save-file-name nil |
c7cbaf4a MB |
1391 | mode-line-buffer-identification |
1392 | (list (default-value 'mode-line-buffer-identification) | |
1393 | " {" 'Man-page-mode-string "}") | |
55535639 PJ |
1394 | truncate-lines t |
1395 | buffer-read-only t) | |
430663bc | 1396 | (buffer-disable-undo) |
55535639 | 1397 | (auto-fill-mode -1) |
aec2bd36 JL |
1398 | (setq imenu-generic-expression (list (list nil Man-heading-regexp 0))) |
1399 | (set (make-local-variable 'outline-regexp) Man-heading-regexp) | |
1400 | (set (make-local-variable 'outline-level) (lambda () 1)) | |
45be326a | 1401 | (set (make-local-variable 'bookmark-make-record-function) |
398a825b | 1402 | 'Man-bookmark-make-record) |
55535639 PJ |
1403 | (Man-build-page-list) |
1404 | (Man-strip-page-headers) | |
1405 | (Man-unindent) | |
1b3b87df | 1406 | (Man-goto-page 1 t)) |
55535639 PJ |
1407 | |
1408 | (defsubst Man-build-section-alist () | |
8b6c19f4 SM |
1409 | "Build the list of manpage sections." |
1410 | (setq Man--sections nil) | |
55535639 PJ |
1411 | (goto-char (point-min)) |
1412 | (let ((case-fold-search nil)) | |
1413 | (while (re-search-forward Man-heading-regexp (point-max) t) | |
8b6c19f4 SM |
1414 | (let ((section (match-string 1))) |
1415 | (unless (member section Man--sections) | |
1416 | (push section Man--sections))) | |
55535639 PJ |
1417 | (forward-line 1)))) |
1418 | ||
1419 | (defsubst Man-build-references-alist () | |
8b6c19f4 SM |
1420 | "Build the list of references (in the SEE ALSO section)." |
1421 | (setq Man--refpages nil) | |
55535639 PJ |
1422 | (save-excursion |
1423 | (if (Man-find-section Man-see-also-regexp) | |
1424 | (let ((start (progn (forward-line 1) (point))) | |
1425 | (end (progn | |
1426 | (Man-next-section 1) | |
1427 | (point))) | |
1428 | hyphenated | |
1429 | (runningpoint -1)) | |
1430 | (save-restriction | |
1431 | (narrow-to-region start end) | |
1432 | (goto-char (point-min)) | |
1433 | (back-to-indentation) | |
1434 | (while (and (not (eobp)) (/= (point) runningpoint)) | |
1435 | (setq runningpoint (point)) | |
1436 | (if (re-search-forward Man-hyphenated-reference-regexp end t) | |
72397ff1 | 1437 | (let* ((word (match-string 0)) |
55535639 PJ |
1438 | (len (1- (length word)))) |
1439 | (if hyphenated | |
1440 | (setq word (concat hyphenated word) | |
1441 | hyphenated nil | |
1442 | ;; Update len, in case a reference spans | |
1443 | ;; more than two lines (paranoia). | |
1444 | len (1- (length word)))) | |
327a6cca | 1445 | (if (memq (aref word len) '(?- ?)) |
55535639 | 1446 | (setq hyphenated (substring word 0 len))) |
8b6c19f4 SM |
1447 | (and (string-match Man-reference-regexp word) |
1448 | (not (member word Man--refpages)) | |
1449 | (push word Man--refpages)))) | |
55535639 | 1450 | (skip-chars-forward " \t\n,")))))) |
8b6c19f4 | 1451 | (setq Man--refpages (nreverse Man--refpages))) |
55535639 PJ |
1452 | |
1453 | (defun Man-build-page-list () | |
1454 | "Build the list of separate manpages in the buffer." | |
1455 | (setq Man-page-list nil) | |
1456 | (let ((page-start (point-min)) | |
1457 | (page-end (point-max)) | |
1458 | (header "")) | |
1459 | (goto-char page-start) | |
1460 | ;; (switch-to-buffer (current-buffer))(debug) | |
1461 | (while (not (eobp)) | |
1462 | (setq header | |
1463 | (if (looking-at Man-page-header-regexp) | |
72397ff1 | 1464 | (match-string 1) |
55535639 PJ |
1465 | nil)) |
1466 | ;; Go past both the current and the next Man-first-heading-regexp | |
1467 | (if (re-search-forward Man-first-heading-regexp nil 'move 2) | |
1468 | (let ((p (progn (beginning-of-line) (point)))) | |
1469 | ;; We assume that the page header is delimited by blank | |
1470 | ;; lines and that it contains at most one blank line. So | |
1471 | ;; if we back by three blank lines we will be sure to be | |
1472 | ;; before the page header but not before the possible | |
1473 | ;; previous page header. | |
1474 | (search-backward "\n\n" nil t 3) | |
1475 | (if (re-search-forward Man-page-header-regexp p 'move) | |
1476 | (beginning-of-line)))) | |
1477 | (setq page-end (point)) | |
1478 | (setq Man-page-list (append Man-page-list | |
1479 | (list (list (copy-marker page-start) | |
1480 | (copy-marker page-end) | |
1481 | header)))) | |
1482 | (setq page-start page-end) | |
1483 | ))) | |
1484 | ||
1485 | (defun Man-strip-page-headers () | |
1486 | "Strip all the page headers but the first from the manpage." | |
a88459cd | 1487 | (let ((inhibit-read-only t) |
55535639 | 1488 | (case-fold-search nil) |
55535639 | 1489 | (header "")) |
a88459cd | 1490 | (dolist (page Man-page-list) |
55535639 PJ |
1491 | (and (nth 2 page) |
1492 | (goto-char (car page)) | |
1493 | (re-search-forward Man-first-heading-regexp nil t) | |
1494 | (setq header (buffer-substring (car page) (match-beginning 0))) | |
1495 | ;; Since the awk script collapses all successive blank | |
1496 | ;; lines into one, and since we don't want to get rid of | |
1497 | ;; the fast awk script, one must choose between adding | |
1498 | ;; spare blank lines between pages when there were none and | |
1499 | ;; deleting blank lines at page boundaries when there were | |
1500 | ;; some. We choose the first, so we comment the following | |
1501 | ;; line. | |
1502 | ;; (setq header (concat "\n" header))) | |
1503 | (while (search-forward header (nth 1 page) t) | |
a88459cd | 1504 | (replace-match "")))))) |
55535639 PJ |
1505 | |
1506 | (defun Man-unindent () | |
1507 | "Delete the leading spaces that indent the manpage." | |
a88459cd SM |
1508 | (let ((inhibit-read-only t) |
1509 | (case-fold-search nil)) | |
1510 | (dolist (page Man-page-list) | |
1511 | (let ((indent "") | |
55535639 PJ |
1512 | (nindent 0)) |
1513 | (narrow-to-region (car page) (car (cdr page))) | |
1514 | (if Man-uses-untabify-flag | |
ebfe2597 WJ |
1515 | ;; The space characters inserted by `untabify' inherit |
1516 | ;; sticky text properties, which is unnecessary and looks | |
1517 | ;; ugly with underlining (Bug#11408). | |
1518 | (let ((text-property-default-nonsticky | |
1519 | (cons '(face . t) text-property-default-nonsticky))) | |
1520 | (untabify (point-min) (point-max)))) | |
55535639 PJ |
1521 | (if (catch 'unindent |
1522 | (goto-char (point-min)) | |
1523 | (if (not (re-search-forward Man-first-heading-regexp nil t)) | |
1524 | (throw 'unindent nil)) | |
1525 | (beginning-of-line) | |
1526 | (setq indent (buffer-substring (point) | |
1527 | (progn | |
1528 | (skip-chars-forward " ") | |
1529 | (point)))) | |
1530 | (setq nindent (length indent)) | |
1531 | (if (zerop nindent) | |
1532 | (throw 'unindent nil)) | |
1533 | (setq indent (concat indent "\\|$")) | |
1534 | (goto-char (point-min)) | |
1535 | (while (not (eobp)) | |
1536 | (if (looking-at indent) | |
1537 | (forward-line 1) | |
1538 | (throw 'unindent nil))) | |
1539 | (goto-char (point-min))) | |
1540 | (while (not (eobp)) | |
1541 | (or (eolp) | |
1542 | (delete-char nindent)) | |
1543 | (forward-line 1))) | |
55535639 PJ |
1544 | )))) |
1545 | ||
1546 | \f | |
1547 | ;; ====================================================================== | |
1548 | ;; Man mode commands | |
1549 | ||
1550 | (defun Man-next-section (n) | |
1551 | "Move point to Nth next section (default 1)." | |
1552 | (interactive "p") | |
4cb071a4 SM |
1553 | (let ((case-fold-search nil) |
1554 | (start (point))) | |
55535639 PJ |
1555 | (if (looking-at Man-heading-regexp) |
1556 | (forward-line 1)) | |
1557 | (if (re-search-forward Man-heading-regexp (point-max) t n) | |
1558 | (beginning-of-line) | |
65e07682 CY |
1559 | (goto-char (point-max)) |
1560 | ;; The last line doesn't belong to any section. | |
4cb071a4 SM |
1561 | (forward-line -1)) |
1562 | ;; But don't move back from the starting point (can happen if `start' | |
1563 | ;; is somewhere on the last line). | |
1564 | (if (< (point) start) (goto-char start)))) | |
55535639 PJ |
1565 | |
1566 | (defun Man-previous-section (n) | |
1567 | "Move point to Nth previous section (default 1)." | |
1568 | (interactive "p") | |
1569 | (let ((case-fold-search nil)) | |
1570 | (if (looking-at Man-heading-regexp) | |
1571 | (forward-line -1)) | |
1572 | (if (re-search-backward Man-heading-regexp (point-min) t n) | |
1573 | (beginning-of-line) | |
1574 | (goto-char (point-min))))) | |
1575 | ||
1576 | (defun Man-find-section (section) | |
1577 | "Move point to SECTION if it exists, otherwise don't move point. | |
1578 | Returns t if section is found, nil otherwise." | |
1579 | (let ((curpos (point)) | |
1580 | (case-fold-search nil)) | |
1581 | (goto-char (point-min)) | |
1582 | (if (re-search-forward (concat "^" section) (point-max) t) | |
1583 | (progn (beginning-of-line) t) | |
1584 | (goto-char curpos) | |
1585 | nil) | |
1586 | )) | |
1587 | ||
8b6c19f4 SM |
1588 | (defvar Man--last-section nil) |
1589 | ||
1590 | (defun Man-goto-section (section) | |
1591 | "Move point to SECTION." | |
1592 | (interactive | |
1593 | (let* ((default (if (member Man--last-section Man--sections) | |
1594 | Man--last-section | |
1595 | (car Man--sections))) | |
1596 | (completion-ignore-case t) | |
1597 | (prompt (concat "Go to section (default " default "): ")) | |
1598 | (chosen (completing-read prompt Man--sections | |
1599 | nil nil nil nil default))) | |
1600 | (list chosen))) | |
1601 | (setq Man--last-section section) | |
1602 | (unless (Man-find-section section) | |
1603 | (error "Section %s not found" section))) | |
e709e39d | 1604 | |
55535639 PJ |
1605 | |
1606 | (defun Man-goto-see-also-section () | |
110c171f | 1607 | "Move point to the \"SEE ALSO\" section. |
55535639 PJ |
1608 | Actually the section moved to is described by `Man-see-also-regexp'." |
1609 | (interactive) | |
1610 | (if (not (Man-find-section Man-see-also-regexp)) | |
8c16bd8c | 1611 | (error "%s" (concat "No " Man-see-also-regexp |
55535639 PJ |
1612 | " section found in the current manpage")))) |
1613 | ||
1614 | (defun Man-possibly-hyphenated-word () | |
1615 | "Return a possibly hyphenated word at point. | |
1616 | If the word starts at the first non-whitespace column, and the | |
1617 | previous line ends with a hyphen, return the last word on the previous | |
1618 | line instead. Thus, if a reference to \"tcgetpgrp(3V)\" is hyphenated | |
1619 | as \"tcgetp-grp(3V)\", and point is at \"grp(3V)\", we return | |
1620 | \"tcgetp-\" instead of \"grp\"." | |
1621 | (save-excursion | |
1622 | (skip-syntax-backward "w()") | |
1623 | (skip-chars-forward " \t") | |
1624 | (let ((beg (point)) | |
1625 | (word (current-word))) | |
1626 | (when (eq beg (save-excursion | |
1627 | (back-to-indentation) | |
1628 | (point))) | |
1629 | (end-of-line 0) | |
1630 | (if (eq (char-before) ?-) | |
1631 | (setq word (current-word)))) | |
1632 | word))) | |
1633 | ||
8b6c19f4 SM |
1634 | (defvar Man--last-refpage nil) |
1635 | ||
55535639 PJ |
1636 | (defun Man-follow-manual-reference (reference) |
1637 | "Get one of the manpages referred to in the \"SEE ALSO\" section. | |
1638 | Specify which REFERENCE to use; default is based on word at point." | |
1639 | (interactive | |
8b6c19f4 | 1640 | (if (not Man--refpages) |
55535639 | 1641 | (error "There are no references in the current man page") |
debe9205 JL |
1642 | (list |
1643 | (let* ((default (or | |
1644 | (car (all-completions | |
1645 | (let ((word | |
1646 | (or (Man-possibly-hyphenated-word) | |
1647 | ""))) | |
1648 | ;; strip a trailing '-': | |
1649 | (if (string-match "-$" word) | |
1650 | (substring word 0 | |
1651 | (match-beginning 0)) | |
1652 | word)) | |
8b6c19f4 SM |
1653 | Man--refpages)) |
1654 | (if (member Man--last-refpage Man--refpages) | |
1655 | Man--last-refpage | |
1656 | (car Man--refpages)))) | |
debe9205 JL |
1657 | (defaults |
1658 | (mapcar 'substring-no-properties | |
8b6c19f4 SM |
1659 | (cons default Man--refpages))) |
1660 | (prompt (concat "Refer to (default " default "): ")) | |
1661 | (chosen (completing-read prompt Man--refpages | |
1662 | nil nil nil nil defaults))) | |
1663 | chosen)))) | |
1664 | (if (not Man--refpages) | |
55535639 | 1665 | (error "Can't find any references in the current manpage") |
8b6c19f4 | 1666 | (setq Man--last-refpage reference) |
55535639 | 1667 | (Man-getpage-in-background |
8b6c19f4 | 1668 | (Man-translate-references reference)))) |
55535639 PJ |
1669 | |
1670 | (defun Man-kill () | |
1671 | "Kill the buffer containing the manpage." | |
1672 | (interactive) | |
1673 | (quit-window t)) | |
1674 | ||
1675 | (defun Man-quit () | |
1676 | "Bury the buffer containing the manpage." | |
1677 | (interactive) | |
1678 | (quit-window)) | |
1679 | ||
3ac3aabd | 1680 | (defun Man-goto-page (page &optional noerror) |
55535639 PJ |
1681 | "Go to the manual page on page PAGE." |
1682 | (interactive | |
1683 | (if (not Man-page-list) | |
55fb4ff7 | 1684 | (error "Not a man page buffer") |
55535639 PJ |
1685 | (if (= (length Man-page-list) 1) |
1686 | (error "You're looking at the only manpage in the buffer") | |
1687 | (list (read-minibuffer (format "Go to manpage [1-%d]: " | |
1688 | (length Man-page-list))))))) | |
55fb4ff7 RS |
1689 | (if (and (not Man-page-list) (not noerror)) |
1690 | (error "Not a man page buffer")) | |
1691 | (when Man-page-list | |
1692 | (if (or (< page 1) | |
1693 | (> page (length Man-page-list))) | |
71873e2b | 1694 | (user-error "No manpage %d found" page)) |
55fb4ff7 RS |
1695 | (let* ((page-range (nth (1- page) Man-page-list)) |
1696 | (page-start (car page-range)) | |
1697 | (page-end (car (cdr page-range)))) | |
1698 | (setq Man-current-page page | |
1699 | Man-page-mode-string (Man-make-page-mode-string)) | |
1700 | (widen) | |
1701 | (goto-char page-start) | |
1702 | (narrow-to-region page-start page-end) | |
1703 | (Man-build-section-alist) | |
1704 | (Man-build-references-alist) | |
1705 | (goto-char (point-min))))) | |
55535639 PJ |
1706 | |
1707 | ||
1708 | (defun Man-next-manpage () | |
1709 | "Find the next manpage entry in the buffer." | |
1710 | (interactive) | |
1711 | (if (= (length Man-page-list) 1) | |
1712 | (error "This is the only manpage in the buffer")) | |
1713 | (if (< Man-current-page (length Man-page-list)) | |
1714 | (Man-goto-page (1+ Man-current-page)) | |
1715 | (if Man-circular-pages-flag | |
1716 | (Man-goto-page 1) | |
1717 | (error "You're looking at the last manpage in the buffer")))) | |
1718 | ||
1719 | (defun Man-previous-manpage () | |
1720 | "Find the previous manpage entry in the buffer." | |
1721 | (interactive) | |
1722 | (if (= (length Man-page-list) 1) | |
1723 | (error "This is the only manpage in the buffer")) | |
1724 | (if (> Man-current-page 1) | |
1725 | (Man-goto-page (1- Man-current-page)) | |
1726 | (if Man-circular-pages-flag | |
1727 | (Man-goto-page (length Man-page-list)) | |
1728 | (error "You're looking at the first manpage in the buffer")))) | |
4edd9faf JB |
1729 | |
1730 | ;; Header file support | |
1731 | (defun Man-view-header-file (file) | |
1732 | "View a header file specified by FILE from `Man-header-file-path'." | |
1733 | (let ((path Man-header-file-path) | |
1734 | complete-path) | |
1735 | (while path | |
a88459cd | 1736 | (setq complete-path (expand-file-name file (car path)) |
4edd9faf JB |
1737 | path (cdr path)) |
1738 | (if (file-readable-p complete-path) | |
1739 | (progn (view-file complete-path) | |
1740 | (setq path nil)) | |
1741 | (setq complete-path nil))) | |
1742 | complete-path)) | |
45be326a TV |
1743 | |
1744 | ;;; Bookmark Man Support | |
e44fa724 KF |
1745 | (declare-function bookmark-make-record-default |
1746 | "bookmark" (&optional no-file no-context posn)) | |
398a825b SM |
1747 | (declare-function bookmark-prop-get "bookmark" (bookmark prop)) |
1748 | (declare-function bookmark-default-handler "bookmark" (bmk)) | |
1749 | (declare-function bookmark-get-bookmark-record "bookmark" (bmk)) | |
1750 | ||
1751 | (defun Man-default-bookmark-title () | |
1752 | "Default bookmark name for Man or WoMan pages. | |
1753 | Uses `Man-name-local-regexp'." | |
45be326a TV |
1754 | (save-excursion |
1755 | (goto-char (point-min)) | |
398a825b SM |
1756 | (when (re-search-forward Man-name-local-regexp nil t) |
1757 | (skip-chars-forward "\n\t ") | |
45be326a TV |
1758 | (buffer-substring-no-properties (point) (line-end-position))))) |
1759 | ||
398a825b | 1760 | (defun Man-bookmark-make-record () |
45be326a | 1761 | "Make a bookmark entry for a Man buffer." |
398a825b | 1762 | `(,(Man-default-bookmark-title) |
e44fa724 | 1763 | ,@(bookmark-make-record-default 'no-file) |
ebb9641f SM |
1764 | (location . ,(concat "man " Man-arguments)) |
1765 | (man-args . ,Man-arguments) | |
1766 | (handler . Man-bookmark-jump))) | |
45be326a | 1767 | |
398a825b SM |
1768 | ;;;###autoload |
1769 | (defun Man-bookmark-jump (bookmark) | |
45be326a | 1770 | "Default bookmark handler for Man buffers." |
398a825b SM |
1771 | (let* ((man-args (bookmark-prop-get bookmark 'man-args)) |
1772 | ;; Let bookmark.el do the window handling. | |
1773 | ;; This let-binding needs to be active during the call to both | |
1774 | ;; Man-getpage-in-background and accept-process-output. | |
1775 | (Man-notify-method 'meek) | |
1776 | (buf (Man-getpage-in-background man-args)) | |
1777 | (proc (get-buffer-process buf))) | |
1778 | (while (and proc (eq (process-status proc) 'run)) | |
1779 | (accept-process-output proc)) | |
45be326a TV |
1780 | (bookmark-default-handler |
1781 | `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark))))) | |
1782 | ||
55535639 PJ |
1783 | \f |
1784 | ;; Init the man package variables, if not already done. | |
1785 | (Man-init-defvars) | |
1786 | ||
55535639 PJ |
1787 | (provide 'man) |
1788 | ||
1789 | ;;; man.el ends here |