0
|
1 ;;; man.el --- browse UNIX manual pages
|
74
|
2 ;; Keywords: help
|
0
|
3
|
181
|
4 ;; Copyright (C) 1985, 1993, 1994, 1996, 1997 Free Software Foundation, Inc.
|
74
|
5 ;;
|
0
|
6 ;; This file is part of XEmacs.
|
|
7
|
|
8 ;; XEmacs is free software; you can redistribute it and/or modify it
|
|
9 ;; under the terms of the GNU General Public License as published by
|
|
10 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
11 ;; any later version.
|
|
12
|
|
13 ;; XEmacs is distributed in the hope that it will be useful, but
|
|
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
16 ;; General Public License for more details.
|
|
17
|
|
18 ;; You should have received a copy of the GNU General Public License
|
72
|
19 ;; along with XEmacs; see the file COPYING. If not, write to the Free
|
74
|
20 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
0
|
21
|
74
|
22 ;; This file defines "manual-entry", and the remaining definitions all
|
|
23 ;; begin with "Manual-". This makes the autocompletion on "M-x man" work.
|
100
|
24 ;;
|
|
25 ;; Eviscerated 26-Jun-96 by Jamie Zawinski <jwz@netscape.com>.
|
|
26 ;; All that stuff about looking at $MANPATH and building up lists of
|
|
27 ;; directories was bullshit. Now we just invoke "man" and format the
|
|
28 ;; output, end of story.
|
74
|
29 ;;
|
100
|
30 ;; [ older changelog entries removed, since they're all about code that
|
|
31 ;; I've deleted. ]
|
0
|
32
|
120
|
33 (defgroup man nil
|
189
|
34 "Browse Unix manual pages"
|
120
|
35 :group 'help)
|
70
|
36
|
120
|
37 (defcustom Manual-program "man" "\
|
|
38 *Name of the program to invoke in order to format the source man pages."
|
|
39 :type 'string
|
|
40 :group 'man)
|
|
41
|
|
42 (defcustom Manual-buffer-view-mode t "\
|
74
|
43 *Whether manual buffers should be placed in view-mode.
|
|
44 nil means leave the buffer in fundamental-mode in another window.
|
|
45 t means use `view-buffer' to display the man page in the current window.
|
120
|
46 Any other value means use `view-buffer-other-window'."
|
189
|
47 :type '(radio (const :tag "Fundamental-mode other window" nil)
|
|
48 (const :tag "View-mode current window" t)
|
|
49 (sexp :format "%t\n" :tag "View-mode other window" other))
|
120
|
50 :group 'man)
|
70
|
51
|
120
|
52 (defcustom Manual-mode-hook nil
|
|
53 "Function or functions run on entry to Manual-mode."
|
|
54 :type 'hook
|
|
55 :group 'man)
|
0
|
56
|
74
|
57 (defvar Manual-page-history nil "\
|
|
58 A list of names of previously visited man page buffers.")
|
70
|
59
|
173
|
60 (defvar Manual-page-minibuffer-history nil "\
|
|
61 Minibuffer completion history for `manual-entry'.")
|
70
|
62
|
74
|
63 ;; New variables.
|
70
|
64
|
144
|
65 (defcustom Manual-snip-subchapter
|
|
66 (not (string-match "solaris" system-configuration))
|
|
67 "*Should man look in chapter 3 for ctime(3c)?
|
|
68 This is relavent for Solaris and, perhaps, other systems which have
|
|
69 `man -s 3' not find things in chapter 3c, or other such sub-chapters"
|
|
70 :type 'boolean
|
|
71 :group 'man)
|
|
72
|
189
|
73 ;;Here is information on RosettaMan, from Neal.Becker@comsat.com (Neal Becker):
|
|
74
|
|
75 ;;RosettaMan is a filter for UNIX manual pages. It takes as input man
|
|
76 ;;pages formatted for a variety of UNIX flavors (not [tn]roff source)
|
|
77 ;;and produces as output a variety of file formats. Currently
|
|
78 ;;RosettaMan accepts man pages as formatted by the following flavors of
|
|
79 ;;UNIX: Hewlett-Packard HP-UX, AT&T System V, SunOS, Sun Solaris, OSF/1,
|
|
80 ;;DEC Ultrix, SGI IRIX, Linux, SCO; and produces output for the following
|
|
81 ;;formats: printable ASCII only (stripping page headers and footers),
|
|
82 ;;section and subsection headers only, TkMan, [tn]roff, Ensemble, RTF,
|
|
83 ;;SGML (soon--I finally found a DTD), HTML, MIME, LaTeX, LaTeX 2e, Perl 5's pod.
|
|
84
|
|
85 ;;RosettaMan improves on other man page filters in several ways: (1) its
|
|
86 ;;analysis recognizes the structural pieces of man pages, enabling high
|
|
87 ;;quality output, (2) its modular structure permits easy augmentation of
|
|
88 ;;output formats, (3) it accepts man pages formatted with the varient
|
|
89 ;;macros of many different flavors of UNIX, and (4) it doesn't require
|
|
90 ;;modification or cooperation with any other program.
|
|
91
|
|
92 ;;RosettaMan is a rewrite of TkMan's man page filter, called bs2tk. (If
|
|
93 ;;you haven't heard about TkMan, a hypertext man page browser, you
|
|
94 ;;should grab it via anonymous ftp from ftp.cs.berkeley.edu:
|
|
95 ;;/ucb/people/phelps/tkman.tar.Z.) Whereas bs2tk generated output only for
|
|
96 ;;TkMan, RosettaMan generalizes the process so that the analysis can be
|
|
97 ;;leveraged to new output formats. A single analysis engine recognizes
|
|
98 ;;section heads, subsection heads, body text, lists, references to other
|
|
99 ;;man pages, boldface, italics, bold italics, special characters (like
|
|
100 ;;bullets), tables (to a degree) and strips out page headers and
|
|
101 ;;footers. The engine sends signals to the selected output functions so
|
|
102 ;;that an enhancement in the engine improves the quality of output of
|
|
103 ;;all of them. Output format functions are easy to add, and thus far
|
|
104 ;;average about about 75 lines of C code each.
|
|
105
|
|
106
|
|
107
|
|
108 ;;*** NOTES ON CURRENT VERSION ***
|
|
109
|
|
110 ;;Help! I'm looking for people to help with the following projects.
|
|
111 ;;\(1) Better RTF output format. The current one works, but could be
|
|
112 ;;made better. (2) Roff macros that produce text that is easily
|
|
113 ;;parsable. RosettaMan handles a great variety, but some things, like
|
|
114 ;;H-P's tables, are intractable. If you write an output format or
|
|
115 ;;otherwise improve RosettaMan, please send in your code so that I may
|
|
116 ;;share the wealth in future releases.
|
|
117
|
|
118 ;;This version can try to identify tables (turn this on with the -T
|
|
119 ;;switch) by looking for lines with a large amount of interword spacing,
|
|
120 ;;reasoning that this is space between columns of a table. This
|
|
121 ;;heuristic doesn't always work and sometimes misidentifies ordinary
|
|
122 ;;text as tables. In general I think it is impossible to perfectly
|
|
123 ;;identify tables from nroff formatted text. However, I do think the
|
|
124 ;;heuristics can be tuned, so if you have a collection of manual pages
|
|
125 ;;with unrecognized tables, send me the lot, in formatted form (i.e.,
|
|
126 ;;after formatting with nroff -man), and uuencode them to preserve the
|
|
127 ;;control characters. Better, if you can think of heuristics that
|
|
128 ;;distinguish tables from ordinary text, I'd like to hear them.
|
|
129
|
|
130 ;;Notes for HTML consumers: This filter does real (heuristic)
|
|
131 ;;parsing--no <PRE>! Man page references are turned into hypertext links.
|
|
132
|
120
|
133 (defcustom Manual-use-rosetta-man (not (null (locate-file "rman" exec-path))) "\
|
76
|
134 If non-nil, use RosettaMan (rman) to filter man pages.
|
|
135 This makes man-page cleanup virtually instantaneous, instead of
|
189
|
136 potentially taking a long time."
|
120
|
137 :type 'boolean
|
|
138 :group 'man)
|
76
|
139
|
120
|
140 (defface man-italic '((t (:italic t)))
|
|
141 "Manual italics face"
|
|
142 :group 'man)
|
10
|
143
|
120
|
144 (defface man-bold '((t (:bold t)))
|
|
145 "Manual bold face"
|
|
146 :group 'man)
|
0
|
147
|
120
|
148 (defface man-heading '((t (:bold t)))
|
|
149 "Manual headings face"
|
|
150 :group 'man)
|
0
|
151
|
120
|
152 (defface man-xref '((t (:underline t)))
|
|
153 "Manual xrefs face"
|
|
154 :group 'man)
|
|
155
|
74
|
156
|
100
|
157 (defvar Manual-mode-map
|
|
158 (let ((m (make-sparse-keymap)))
|
|
159 (set-keymap-name m 'Manual-mode-map)
|
|
160 (define-key m "l" 'Manual-last-page)
|
|
161 (define-key m 'button2 'Manual-follow-xref)
|
|
162 (define-key m 'button3 'Manual-popup-menu)
|
|
163 m))
|
0
|
164
|
|
165 ;;;###autoload
|
74
|
166 (defun manual-entry (topic &optional arg silent)
|
100
|
167 "Display the Unix manual entry (or entries) for TOPIC."
|
74
|
168 (interactive
|
173
|
169 (list (let* ((fmh "-A-Za-z0-9_.:")
|
74
|
170 (default (save-excursion
|
|
171 (buffer-substring
|
|
172 (progn
|
|
173 (re-search-backward "\\sw" nil t)
|
|
174 (skip-chars-backward fmh) (point))
|
|
175 (progn (skip-chars-forward fmh) (point)))))
|
|
176 (thing (read-string
|
|
177 (if (equal default "") "Manual entry: "
|
173
|
178 (concat "Manual entry: (default " default ") "))
|
|
179 nil 'Manual-page-minibuffer-history)))
|
74
|
180 (if (equal thing "") default thing))
|
|
181 (prefix-numeric-value current-prefix-arg)))
|
|
182 (or arg (setq arg 1))
|
100
|
183 (let (section apropos-mode)
|
74
|
184 (let ((case-fold-search nil))
|
|
185 (if (and (null section)
|
100
|
186 (string-match "\\`[ \t]*\\([^( \t]+\\)[ \t]*(\\(.+\\))[ \t]*\\'"
|
|
187 topic))
|
181
|
188 (setq section (match-string 2 topic)
|
|
189 topic (match-string 1 topic))
|
74
|
190 (if (string-match "\\`[ \t]*-k[ \t]+\\([^ \t]+\\)\\'" topic)
|
|
191 (setq section "-k"
|
181
|
192 topic (match-string 1 topic)))))
|
100
|
193
|
144
|
194 (when Manual-snip-subchapter
|
|
195 ;; jwz: turn section "3x11" and "3n" into "3".
|
|
196 (if (and section (string-match "\\`\\([0-9]+\\)[^0-9]" section))
|
181
|
197 (setq section (match-string 1 section))))
|
|
198
|
74
|
199 (if (equal section "-k")
|
100
|
200 (setq apropos-mode t))
|
74
|
201
|
195
|
202 (let ((bufname (concat "Man"
|
|
203 (when apropos-mode " apropos")
|
|
204 ": " topic
|
207
|
205 (when section (concat "(" section ")"))))
|
100
|
206 (temp-buffer-show-function
|
|
207 (cond ((eq 't Manual-buffer-view-mode)
|
|
208 'view-buffer)
|
|
209 ((eq 'nil Manual-buffer-view-mode)
|
|
210 temp-buffer-show-function)
|
|
211 (t
|
|
212 'view-buffer-other-window))))
|
74
|
213
|
100
|
214 (cond ((get-buffer bufname)
|
|
215 ;; reselect an old man page buffer if it exists already.
|
|
216 (save-excursion
|
|
217 (set-buffer (get-buffer bufname))
|
|
218 (Manual-mode))
|
|
219 (if temp-buffer-show-function
|
|
220 (funcall temp-buffer-show-function (get-buffer bufname))
|
|
221 (display-buffer bufname)))
|
|
222 (t
|
|
223 (with-output-to-temp-buffer bufname
|
|
224 (buffer-disable-undo standard-output)
|
|
225 (save-excursion
|
|
226 (set-buffer standard-output)
|
|
227 (setq buffer-read-only nil)
|
|
228 (erase-buffer)
|
74
|
229
|
100
|
230 (let ((args (list topic))
|
|
231 args-string)
|
|
232 (if section
|
|
233 (setq args
|
|
234 (if (eq system-type 'usg-unix-v)
|
|
235 (cons "-s" (cons section args))
|
|
236 (cons section args))))
|
|
237 (setq args-string
|
|
238 (mapconcat 'identity (cons Manual-program args) " "))
|
|
239 (if (string-match "\\`\\([^ \t/]*/\\)+" args-string)
|
|
240 (setq args-string
|
|
241 (substring args-string (match-end 0))))
|
74
|
242
|
100
|
243 (message "%s (running...)" args-string)
|
149
|
244 (apply 'call-process Manual-program nil '(t nil) nil args)
|
70
|
245
|
100
|
246 (if (< (buffer-size) 200)
|
|
247 (progn
|
163
|
248 (kill-buffer (current-buffer))
|
|
249 (error "%s not found" args-string)))
|
74
|
250
|
100
|
251 (message "%s (cleaning...)" args-string)
|
|
252 (Manual-nuke-nroff-bs apropos-mode)
|
181
|
253 (message "%s (done.)" args-string))
|
100
|
254 (set-buffer-modified-p nil)
|
181
|
255 (Manual-mode)))))
|
183
|
256 (let ((page (if section
|
|
257 (concat topic "(" section ")")
|
|
258 topic)))
|
181
|
259 (setq Manual-page-history
|
|
260 (cons (buffer-name)
|
|
261 (delete (buffer-name) Manual-page-history))
|
|
262 Manual-page-minibuffer-history
|
|
263 (cons page (delete page Manual-page-minibuffer-history))))))
|
|
264
|
100
|
265 (message nil)
|
|
266 t)
|
74
|
267
|
|
268 (defun Manual-mode ()
|
|
269 (kill-all-local-variables)
|
|
270 (setq buffer-read-only t)
|
|
271 (use-local-map Manual-mode-map)
|
|
272 (setq major-mode 'Manual-mode
|
|
273 mode-name "Manual")
|
|
274 ;; man pages with long lines are buggy!
|
|
275 ;; This looks slightly better if they only
|
|
276 ;; overran by a couple of chars.
|
|
277 (setq truncate-lines t)
|
|
278 ;; turn off horizontal scrollbars in this buffer
|
104
|
279 (when (featurep 'scrollbar)
|
|
280 (set-specifier scrollbar-height (cons (current-buffer) 0)))
|
181
|
281 (make-local-hook 'kill-buffer-hook)
|
|
282 (add-hook 'kill-buffer-hook #'(lambda ()
|
|
283 (setq Manual-page-history
|
|
284 (delete (buffer-name)
|
|
285 Manual-page-history)))
|
|
286 nil t)
|
74
|
287 (run-hooks 'Manual-mode-hook))
|
|
288
|
|
289 (defun Manual-last-page ()
|
|
290 (interactive)
|
181
|
291 (if Manual-page-history
|
|
292 (let ((page (pop Manual-page-history)))
|
|
293 (if page
|
|
294 (progn
|
|
295 (get-buffer page)
|
|
296 (cons Manual-page-history page)
|
|
297 (switch-to-buffer page))))
|
|
298 (error "No manual page buffers found. Use `M-x manual-entry'")))
|
74
|
299
|
|
300
|
|
301 (defmacro Manual-delete-char (n)
|
|
302 ;; in v19, delete-char is compiled as a function call, but delete-region
|
|
303 ;; is byte-coded, so it's much faster. (We were spending 40% of our time
|
|
304 ;; in delete-char alone.)
|
|
305 (list 'delete-region '(point) (list '+ '(point) n)))
|
|
306
|
195
|
307 ;; Hint: BS stands for more things than "back space"
|
74
|
308 (defun Manual-nuke-nroff-bs (&optional apropos-mode)
|
|
309 (interactive "*")
|
76
|
310 (if Manual-use-rosetta-man
|
|
311 (call-process-region (point-min) (point-max) "rman" t t nil)
|
|
312 ;;
|
|
313 ;; turn underlining into italics
|
|
314 ;;
|
|
315 (goto-char (point-min))
|
|
316 (while (search-forward "_\b" nil t)
|
100
|
317 ;; searching for underscore-backspace and then comparing the
|
|
318 ;; following chars until the sequence ends turns out to be much
|
|
319 ;; faster than searching for a regexp which matches the whole
|
|
320 ;; sequence.
|
76
|
321 (let ((s (match-beginning 0)))
|
|
322 (goto-char s)
|
|
323 (while (and (= (following-char) ?_)
|
|
324 (= (char-after (1+ (point))) ?\b))
|
|
325 (Manual-delete-char 2)
|
|
326 (forward-char 1))
|
|
327 (set-extent-face (make-extent s (point)) 'man-italic)))
|
|
328 ;;
|
|
329 ;; turn overstriking into bold
|
|
330 ;;
|
|
331 (goto-char (point-min))
|
|
332 (while (re-search-forward "\\([^\n]\\)\\(\b\\1\\)" nil t)
|
|
333 ;; Surprisingly, searching for the above regexp is faster than searching
|
|
334 ;; for a backspace and then comparing the preceding and following chars,
|
|
335 ;; I presume because there are many false matches, meaning more funcalls
|
|
336 ;; to re-search-forward.
|
|
337 (let ((s (match-beginning 0)))
|
|
338 (goto-char s)
|
|
339 ;; Some systems (SGI) overstrike multiple times, eg, "M\bM\bM\bM".
|
|
340 (while (looking-at "\\([^\n]\\)\\(\b\\1\\)+")
|
|
341 (delete-region (+ (point) 1) (match-end 0))
|
|
342 (forward-char 1))
|
|
343 (set-extent-face (make-extent s (point)) 'man-bold)))
|
|
344 ;;
|
|
345 ;; hack bullets: o^H+ --> +
|
|
346 (goto-char (point-min))
|
|
347 (while (search-forward "\b" nil t)
|
|
348 (Manual-delete-char -2))
|
70
|
349
|
76
|
350 (if (> (buffer-size) 100) ; minor kludge
|
|
351 (Manual-nuke-nroff-bs-footers))
|
|
352 ) ;; not Manual-use-rosetta-man
|
74
|
353 ;;
|
|
354 ;; turn subsection header lines into bold
|
|
355 ;;
|
|
356 (goto-char (point-min))
|
|
357 (if apropos-mode
|
|
358 (while (re-search-forward "[a-zA-Z0-9] ([0-9]" nil t)
|
|
359 (forward-char -2)
|
|
360 (delete-backward-char 1))
|
70
|
361
|
74
|
362 ;; (while (re-search-forward "^[^ \t\n]" nil t)
|
|
363 ;; (set-extent-face (make-extent (match-beginning 0)
|
|
364 ;; (progn (end-of-line) (point)))
|
|
365 ;; 'man-heading))
|
72
|
366
|
74
|
367 ;; boldface the first line
|
|
368 (if (looking-at "[^ \t\n].*$")
|
|
369 (set-extent-face (make-extent (match-beginning 0) (match-end 0))
|
|
370 'man-bold))
|
70
|
371
|
74
|
372 ;; boldface subsequent title lines
|
|
373 ;; Regexp to match section headers changed to match a non-indented
|
|
374 ;; line preceded by a blank line and followed by an indented line.
|
|
375 ;; This seems to work ok for manual pages but gives better results
|
|
376 ;; with other nroff'd files
|
|
377 (while (re-search-forward "\n\n\\([^ \t\n].*\\)\n[ \t]+[^ \t\n]" nil t)
|
|
378 (goto-char (match-end 1))
|
|
379 (set-extent-face (make-extent (match-beginning 1) (match-end 1))
|
|
380 'man-heading)
|
|
381 (forward-line 1))
|
|
382 )
|
70
|
383
|
76
|
384 (if Manual-use-rosetta-man
|
|
385 nil
|
|
386 ;; Zap ESC7, ESC8, and ESC9
|
|
387 ;; This is for Sun man pages like "man 1 csh"
|
|
388 (goto-char (point-min))
|
|
389 (while (re-search-forward "\e[789]" nil t)
|
|
390 (replace-match "")))
|
100
|
391
|
74
|
392 ;; Nuke blanks lines at start.
|
|
393 ;; (goto-char (point-min))
|
|
394 ;; (skip-chars-forward "\n")
|
|
395 ;; (delete-region (point-min) (point))
|
|
396
|
|
397 (Manual-mouseify-xrefs)
|
|
398 )
|
|
399
|
|
400 (fset 'nuke-nroff-bs 'Manual-nuke-nroff-bs) ; use old name
|
|
401
|
70
|
402
|
74
|
403 (defun Manual-nuke-nroff-bs-footers ()
|
181
|
404 "For info see comments in packages/man.el"
|
74
|
405 ;; Nuke headers and footers.
|
|
406 ;;
|
|
407 ;; nroff assumes pages are 66 lines high. We assume that, and that the
|
|
408 ;; first and last line on each page is expendible. There is no way to
|
|
409 ;; tell the difference between a page break in the middle of a paragraph
|
|
410 ;; and a page break between paragraphs (the amount of extra whitespace
|
|
411 ;; that nroff inserts is the same in both cases) so this might strip out
|
|
412 ;; a blank line were one should remain. I think that's better than
|
|
413 ;; leaving in a blank line where there shouldn't be one. (Need I say
|
|
414 ;; it: FMH.)
|
|
415 ;;
|
|
416 ;; Note that if nroff spits out error messages, pages will be more than
|
|
417 ;; 66 lines high, and we'll lose badly. That's ok because standard
|
|
418 ;; nroff doesn't do any diagnostics, and the "gnroff" wrapper for groff
|
|
419 ;; turns off error messages for compatibility. (At least, it's supposed
|
|
420 ;; to.)
|
|
421 ;;
|
|
422 (goto-char (point-min))
|
|
423 ;; first lose the status output
|
|
424 (let ((case-fold-search t))
|
|
425 (if (and (not (looking-at "[^\n]*warning"))
|
|
426 (looking-at "Reformatting.*\n"))
|
|
427 (delete-region (match-beginning 0) (match-end 0))))
|
72
|
428
|
74
|
429 ;; kludge around a groff bug where it won't keep quiet about some
|
|
430 ;; warnings even with -Wall or -Ww.
|
|
431 (cond ((looking-at "grotty:")
|
|
432 (while (looking-at "grotty:")
|
|
433 (delete-region (point) (progn (forward-line 1) (point))))
|
|
434 (if (looking-at " *done\n")
|
|
435 (delete-region (point) (match-end 0)))))
|
70
|
436
|
74
|
437 (let ((pages '())
|
|
438 p)
|
|
439 ;; collect the page boundary markers before we start deleting, to make
|
|
440 ;; it easier to strip things out without changing the page sizes.
|
|
441 (while (not (eobp))
|
|
442 (forward-line 66)
|
|
443 (setq pages (cons (point-marker) pages)))
|
|
444 (setq pages (nreverse pages))
|
|
445 (while pages
|
|
446 (goto-char (car pages))
|
|
447 (set-marker (car pages) nil)
|
|
448 ;;
|
|
449 ;; The lines are: 3 blank; footer; 6 blank; header; 3 blank.
|
|
450 ;; We're in between the previous footer and the following header,
|
|
451 ;;
|
|
452 ;; First lose 3 blank lines, the header, and then 3 more.
|
|
453 ;;
|
|
454 (setq p (point))
|
|
455 (skip-chars-forward "\n")
|
|
456 (delete-region p (point))
|
|
457 (and (looking-at "[^\n]+\n\n?\n?\n?")
|
|
458 (delete-region (match-beginning 0) (match-end 0)))
|
|
459 ;;
|
|
460 ;; Next lose the footer, and the 3 blank lines after, and before it.
|
|
461 ;; But don't lose the last footer of the manual entry; that contains
|
|
462 ;; the "last change" date, so it's not completely uninteresting.
|
|
463 ;; (Actually lose all blank lines before it; sh(1) needs this.)
|
|
464 ;;
|
|
465 (skip-chars-backward "\n")
|
|
466 (beginning-of-line)
|
|
467 (if (null (cdr pages))
|
|
468 nil
|
|
469 (and (looking-at "[^\n]+\n\n?\n?\n?")
|
|
470 (delete-region (match-beginning 0) (match-end 0))))
|
|
471 (setq p (point))
|
|
472 (skip-chars-backward "\n")
|
|
473 (if (> (- p (point)) 4)
|
|
474 (delete-region (+ 2 (point)) p)
|
|
475 (delete-region (1+ (point)) p))
|
|
476 ; (and (looking-at "\n\n?\n?")
|
|
477 ; (delete-region (match-beginning 0) (match-end 0)))
|
72
|
478
|
74
|
479 (setq pages (cdr pages)))
|
|
480 ;;
|
|
481 ;; Now nuke the extra blank lines at the beginning and end.
|
72
|
482 (goto-char (point-min))
|
74
|
483 (if (looking-at "\n+")
|
|
484 (delete-region (match-beginning 0) (match-end 0)))
|
|
485 (forward-line 1)
|
|
486 (if (looking-at "\n\n+")
|
|
487 (delete-region (1+ (match-beginning 0)) (match-end 0)))
|
|
488 (goto-char (point-max))
|
|
489 (skip-chars-backward "\n")
|
|
490 (delete-region (point) (point-max))
|
|
491 (beginning-of-line)
|
|
492 (forward-char -1)
|
|
493 (setq p (point))
|
|
494 (skip-chars-backward "\n")
|
|
495 (if (= ?\n (following-char)) (forward-char 1))
|
|
496 (if (> (point) (1+ p))
|
|
497 (delete-region (point) p))
|
72
|
498 ))
|
|
499
|
74
|
500 (defun Manual-mouseify-xrefs ()
|
|
501 (goto-char (point-min))
|
183
|
502 ;; skip the top line of manual pages, but not apropos listings.
|
|
503 (unless apropos-mode (forward-line 1))
|
74
|
504 (let ((case-fold-search nil)
|
181
|
505 s e name splitp extent)
|
74
|
506 ;; possibly it would be faster to rewrite this expression to search for
|
|
507 ;; a less common sequence first (like "([0-9]") and then back up to see
|
|
508 ;; if it's really a match. This function is 15% of the total time, 13%
|
|
509 ;; of which is this call to re-search-forward.
|
173
|
510 (while (re-search-forward "[a-zA-Z_][-a-zA-Z0-9_.:]*([0-9][a-zA-Z0-9]*)"
|
74
|
511 nil t)
|
|
512 (setq s (match-beginning 0)
|
|
513 e (match-end 0)
|
181
|
514 name (buffer-substring s e)
|
|
515 splitp nil)
|
|
516
|
74
|
517 (goto-char s)
|
181
|
518 ;; if this is a hyphenated xref, we're on the second line, 1st char now.
|
|
519 (when (progn
|
|
520 (beginning-of-line)
|
|
521 (and (looking-at (concat "^[ \t]+" (regexp-quote name)))
|
|
522 (progn
|
|
523 (backward-char 1)
|
|
524 (or (equal (char-before) ?-)
|
|
525 (equal (char-before) ?\255)))
|
|
526 (setq s (progn
|
|
527 (skip-chars-backward "-\255_a-zA-Z0-9")
|
|
528 (point))
|
|
529 name (buffer-substring s e))))
|
|
530 (setq splitp t)
|
|
531 ;; delete the spaces and dash from `name'
|
|
532 (let (i)
|
|
533 (while (setq i (string-match "[-\255 \n\t]+" name i))
|
|
534 (setq name (concat (substring name 0 i)
|
|
535 (substring name (match-end 0)))
|
|
536 i (1+ i)))))
|
|
537
|
74
|
538 ;; if there are upper case letters in the section, downcase them.
|
|
539 (if (string-match "(.*[A-Z]+.*)$" name)
|
|
540 (setq name (concat (substring name 0 (match-beginning 0))
|
|
541 (downcase (substring name (match-beginning 0))))))
|
181
|
542
|
|
543 ;; if the xref was hyphenated, don't highlight the indention spaces.
|
|
544 (if splitp
|
|
545 (progn
|
|
546 (setq extent (make-extent s (progn (goto-char s) (end-of-line) (point))))
|
|
547 (set-extent-property extent 'man (list 'Manual-follow-xref name))
|
|
548 (set-extent-property extent 'highlight t)
|
|
549 (set-extent-face extent 'man-xref)
|
|
550 (goto-char e)
|
|
551 (skip-chars-backward "-_a-zA-Z0-9()")
|
|
552 (setq extent (make-extent (point) e)))
|
|
553 (setq extent (make-extent s e)))
|
74
|
554 (set-extent-property extent 'man (list 'Manual-follow-xref name))
|
|
555 (set-extent-property extent 'highlight t)
|
|
556 (set-extent-face extent 'man-xref)
|
|
557 (goto-char e))))
|
72
|
558
|
74
|
559 (defun Manual-follow-xref (&optional name-or-event)
|
|
560 "Invoke `manual-entry' on the cross-reference under the mouse.
|
|
561 When invoked noninteractively, the arg may be an xref string to parse instead."
|
|
562 (interactive "e")
|
|
563 (if (eventp name-or-event)
|
|
564 (let* ((p (event-point name-or-event))
|
|
565 (extent (and p (extent-at p
|
|
566 (event-buffer name-or-event)
|
|
567 'highlight)))
|
|
568 (data (and extent (extent-property extent 'man))))
|
|
569 (if (eq (car-safe data) 'Manual-follow-xref)
|
|
570 (eval data)
|
|
571 (error "no manual cross-reference there.")))
|
100
|
572 (or (manual-entry name-or-event)
|
|
573 ;; If that didn't work, maybe it's in a different section than the
|
|
574 ;; man page writer expected. For example, man pages tend assume
|
|
575 ;; that all user programs are in section 1, but X tends to generate
|
|
576 ;; makefiles that put things in section "n" instead...
|
|
577 (and (string-match "[ \t]*([^)]+)\\'" name-or-event)
|
|
578 (progn
|
|
579 (message "No entries found for %s; checking other sections..."
|
|
580 name-or-event)
|
|
581 (manual-entry
|
|
582 (substring name-or-event 0 (match-beginning 0))
|
|
583 nil t))))))
|
72
|
584
|
74
|
585 (defun Manual-popup-menu (&optional event)
|
|
586 "Pops up a menu of cross-references in this manual page.
|
|
587 If there is a cross-reference under the mouse button which invoked this
|
|
588 command, it will be the first item on the menu. Otherwise, they are
|
|
589 on the menu in the order in which they appear in the buffer."
|
|
590 (interactive "e")
|
|
591 (let ((buffer (current-buffer))
|
|
592 (sep "---")
|
|
593 xref items)
|
|
594 (cond (event
|
|
595 (setq buffer (event-buffer event))
|
|
596 (let* ((p (event-point event))
|
|
597 (extent (and p (extent-at p buffer 'highlight)))
|
|
598 (data (and extent (extent-property extent 'man))))
|
|
599 (if (eq (car-safe data) 'Manual-follow-xref)
|
|
600 (setq xref (nth 1 data))))))
|
|
601 (if xref (setq items (list sep xref)))
|
|
602 (map-extents #'(lambda (extent ignore)
|
|
603 (let ((data (extent-property extent 'man)))
|
|
604 (if (and (eq (car-safe data) 'Manual-follow-xref)
|
|
605 (not (member (nth 1 data) items)))
|
|
606 (setq items (cons (nth 1 data) items)))
|
|
607 nil))
|
|
608 buffer)
|
|
609 (if (eq sep (car items)) (setq items (cdr items)))
|
181
|
610 (let ((popup-menu-titles t))
|
|
611 (and (null items) (setq popup-menu-titles nil))
|
74
|
612 (popup-menu
|
|
613 (cons "Manual Entry"
|
|
614 (mapcar #'(lambda (item)
|
|
615 (if (eq item sep)
|
|
616 item
|
181
|
617 (vector item
|
74
|
618 (list 'Manual-follow-xref item) t)))
|
|
619 (nreverse items)))))))
|
70
|
620
|
74
|
621 (defun pager-cleanup-hook ()
|
|
622 "cleanup man page if called via $PAGER"
|
|
623 (let ((buf-name (or buffer-file-name (buffer-name))))
|
100
|
624 (if (or (string-match "^/tmp/man[0-9]+" buf-name)
|
|
625 (string-match ".*/man/\\(man\\|cat\\)[1-9a-z]/" buf-name))
|
74
|
626 (let (buffer manpage)
|
|
627 (require 'man)
|
|
628 (goto-char (point-min))
|
|
629 (setq buffer-read-only nil)
|
|
630 (Manual-nuke-nroff-bs)
|
|
631 (goto-char (point-min))
|
|
632 (if (re-search-forward "[^ \t]")
|
|
633 (goto-char (- (point) 1)))
|
|
634 (if (looking-at "\\([a-zA-Z0-9]+\\)[ \t]*(")
|
100
|
635 (setq manpage (buffer-substring (match-beginning 1)
|
|
636 (match-end 1)))
|
74
|
637 (setq manpage "???"))
|
195
|
638 (setq buffer
|
|
639 (rename-buffer (generate-new-buffer-name
|
|
640 (concat "Man: " manpage)))
|
|
641 buffer-file-name nil)
|
74
|
642 (goto-char (point-min))
|
|
643 (insert (format "%s\n" buf-name))
|
|
644 (goto-char (point-min))
|
|
645 (buffer-disable-undo buffer)
|
|
646 (set-buffer-modified-p nil)
|
|
647 (Manual-mode)
|
|
648 ))))
|
4
|
649
|
74
|
650 (add-hook 'server-visit-hook 'pager-cleanup-hook)
|
72
|
651 (provide 'man)
|