diff options
| author | Richard M. Stallman | 1993-05-29 04:53:57 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1993-05-29 04:53:57 +0000 |
| commit | effdc6a22650a28fa7994356e10c7fa53aba0bc9 (patch) | |
| tree | 96cead6ee1b6d09728da83a705bd49bcfc9dee95 /lisp/man.el | |
| parent | 83317c079a1d06bbfd4d0005db9dc30febc82e10 (diff) | |
| download | emacs-effdc6a22650a28fa7994356e10c7fa53aba0bc9.tar.gz emacs-effdc6a22650a28fa7994356e10c7fa53aba0bc9.zip | |
entered into RCS
Diffstat (limited to 'lisp/man.el')
| -rw-r--r-- | lisp/man.el | 879 |
1 files changed, 731 insertions, 148 deletions
diff --git a/lisp/man.el b/lisp/man.el index 6461f3f2447..03437445d9f 100644 --- a/lisp/man.el +++ b/lisp/man.el | |||
| @@ -1,9 +1,12 @@ | |||
| 1 | ;;; man.el --- read in and display parts of Unix manual. | 1 | ;;; man.el --- browse UNIX manual pages |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1993 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Maintainer: FSF | 5 | ;; Author: Barry A. Warsaw <bwarsaw@cen.com> |
| 6 | ;; Keywords: unix | 6 | ;; Last-Modified: 31-Jul-1991 |
| 7 | ;; Version: 1.1 | ||
| 8 | ;; Keywords: help | ||
| 9 | ;; Adapted-By: ESR | ||
| 7 | 10 | ||
| 8 | ;; This file is part of GNU Emacs. | 11 | ;; This file is part of GNU Emacs. |
| 9 | 12 | ||
| @@ -23,159 +26,739 @@ | |||
| 23 | 26 | ||
| 24 | ;;; Commentary: | 27 | ;;; Commentary: |
| 25 | 28 | ||
| 26 | ;; This package provides an equivalent of the UNIX man(1) command within | 29 | ;; This code provides a function, manual-entry, with which you can |
| 27 | ;; Emacs. The single entry point is `manual-entry'. | 30 | ;; browse UNIX manual pages. Formatting is done in background so that |
| 31 | ;; you can continue to use your Emacs while processing is going on. | ||
| 32 | ;; | ||
| 33 | ;; The mode also supports hypertext-like following of manual page SEE | ||
| 34 | ;; ALSO references, and other features. See below or do `?' in a | ||
| 35 | ;; manual page buffer for details. | ||
| 36 | |||
| 37 | ;; ========== Credits and History ========== | ||
| 38 | ;; In mid 1991, several people posted some interesting improvements to | ||
| 39 | ;; man.el from the standard emacs 18.57 distribution. I liked many of | ||
| 40 | ;; these, but wanted everthing in one single package, so I decided | ||
| 41 | ;; to encorporate them into a single manual browsing mode. While | ||
| 42 | ;; much of the code here has been rewritten, and some features added, | ||
| 43 | ;; these folks deserve lots of credit for providing the initial | ||
| 44 | ;; excellent packages on which this one is based. | ||
| 45 | |||
| 46 | ;; Nick Duffek <duffek@chaos.cs.brandeis.edu>, posted a very nice | ||
| 47 | ;; improvement which retrieved and cleaned the manpages in a | ||
| 48 | ;; background process, and which correctly deciphered such options as | ||
| 49 | ;; man -k. | ||
| 50 | |||
| 51 | ;; Eric Rose <erose@jessica.stanford.edu>, submitted manual.el which | ||
| 52 | ;; provided a very nice manual browsing mode. | ||
| 53 | |||
| 54 | ;; This package was available as `superman.el' from the LCD paackage | ||
| 55 | ;; for some time before it was accepted into Emacs 19. The entry | ||
| 56 | ;; point and some other names have been changed to make it a drop-in | ||
| 57 | ;; replacement for the old man.el package. | ||
| 58 | |||
| 59 | ;; ========== Features ========== | ||
| 60 | ;; + Runs "man" in the background and pipes the results through a | ||
| 61 | ;; series of sed and awk scripts so that all retrieving and cleaning | ||
| 62 | ;; is done in the background. The cleaning commands are configurable. | ||
| 63 | ;; + Syntax is the same as Un*x man | ||
| 64 | ;; + Functionality is the same as Un*x man, including "man -k" and | ||
| 65 | ;; "man <section>, etc. | ||
| 66 | ;; + Provides a manual browsing mode with keybindings for traversing | ||
| 67 | ;; the sections of a manpage, following references in the SEE ALSO | ||
| 68 | ;; section, and more. | ||
| 69 | ;; + Multiple manpages created with the same man command are put into | ||
| 70 | ;; a narrowed buffer circular list. | ||
| 28 | 71 | ||
| 29 | ;;; Code: | 72 | ;;; Code: |
| 30 | 73 | ||
| 31 | ;;;###autoload | 74 | (require 'assoc) |
| 32 | (defun manual-entry (topic &optional section) | 75 | (provide 'man) |
| 33 | "Display the Unix manual entry for TOPIC. | 76 | |
| 34 | TOPIC is either the title of the entry, or has the form TITLE(SECTION) | 77 | ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv |
| 35 | where SECTION is the desired section of the manual, as in \"tty(4)\"." | 78 | ;; user variables |
| 36 | (interactive "sManual entry (topic): ") | 79 | |
| 37 | (if (= (length topic) 0) | 80 | (defvar Man-notify 'friendly |
| 38 | (error "Must specify topic")) | 81 | "*Selects the behavior when manpage is ready. |
| 39 | (if (and (null section) | 82 | This variable may have one of the following values: |
| 40 | (string-match "\\`[ \t]*\\([^( \t]+\\)[ \t]*(\\(.+\\))[ \t]*\\'" topic)) | 83 | |
| 41 | (setq section (substring topic (match-beginning 2) | 84 | bully -- make the manpage the current buffer and only window |
| 42 | (match-end 2)) | 85 | aggressive -- make the manpage the current buffer in the other window |
| 43 | topic (substring topic (match-beginning 1) | 86 | friendly -- display manpage in other window but don't make current |
| 44 | (match-end 1)))) | 87 | polite -- don't display manpage, but prints message when ready (beeps) |
| 45 | (with-output-to-temp-buffer (concat "*" topic " Manual Entry*") | 88 | quiet -- like `polite', but don't beep |
| 46 | (buffer-disable-undo standard-output) | 89 | meek -- make no indication that manpage is ready |
| 90 | |||
| 91 | Any other value of `Man-notify' is equivalent to `meek'.") | ||
| 92 | |||
| 93 | (defvar Man-reuse-okay-p t | ||
| 94 | "*Reuse a manpage buffer if possible. | ||
| 95 | When t, and a manpage buffer already exists with the same invocation, | ||
| 96 | man just indicates the manpage is ready according to the value of | ||
| 97 | Man-notify. When nil, it always fires off a background process, putting | ||
| 98 | the results in a uniquely named buffer.") | ||
| 99 | |||
| 100 | (defvar Man-downcase-section-letters-p t | ||
| 101 | "*Letters in sections are converted to lower case. | ||
| 102 | Some Un*x man commands can't handle uppercase letters in sections, for | ||
| 103 | example \"man 2V chmod\", but they are often displayed in the manpage | ||
| 104 | with the upper case letter. When this variable is t, the section | ||
| 105 | letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before | ||
| 106 | being sent to the man background process.") | ||
| 107 | |||
| 108 | (defvar Man-circular-pages-p t | ||
| 109 | "*If t, the manpage list is treated as circular for traversal.") | ||
| 110 | |||
| 111 | (defvar Man-auto-section-alist | ||
| 112 | '((c-mode . ("2" "3")) | ||
| 113 | (c++-mode . ("2" "3")) | ||
| 114 | (shell-mode . ("1" "8")) | ||
| 115 | (cmushell-mode . ("1" "8")) | ||
| 116 | (text-mode . "1") | ||
| 117 | ) | ||
| 118 | "*Association list of major modes and their default section numbers. | ||
| 119 | List is of the form: (MAJOR-MODE . [SECTION | (SECTION*)]). If current | ||
| 120 | major mode is not in list, then the default is to check for manpages | ||
| 121 | in all sections.") | ||
| 122 | |||
| 123 | (defvar Man-section-translations-alist | ||
| 124 | '(("3C++" . "3") | ||
| 125 | ("1-UCB" . "")) | ||
| 126 | "*Association list of bogus sections to real section numbers. | ||
| 127 | Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in | ||
| 128 | their references which Un*x man(1) does not recognize. This | ||
| 129 | assocation list is used to translate those sections, when found, to | ||
| 130 | the associated section number.") | ||
| 131 | |||
| 132 | (defvar Man-filter-list | ||
| 133 | '(("sed " | ||
| 134 | ("-e 's/.\010//g'" | ||
| 135 | "-e '/[Nn]o such file or directory/d'" | ||
| 136 | "-e '/Reformatting page. Wait... done/d'" | ||
| 137 | "-e '/^\\([A-Z][A-Z.]*([0-9A-Za-z][-0-9A-Za-z+]*)\\).*\\1$/d'" | ||
| 138 | "-e '/^[ \\t]*Hewlett-Packard Company[ \\t]*- [0-9]* -.*$/d'" | ||
| 139 | "-e '/^[ \\t]*Hewlett-Packard[ \\t]*- [0-9]* -.*$/d'" | ||
| 140 | "-e '/^ *Page [0-9]*.*(printed [0-9\\/]*)$/d'" | ||
| 141 | "-e '/^Printed [0-9].*[0-9]$/d'" | ||
| 142 | "-e '/^Sun Microsystems.*Last change:/d'" | ||
| 143 | "-e '/^Sun Release [0-9].*[0-9]$/d'" | ||
| 144 | "-e '/^\\n$/D'" | ||
| 145 | )) | ||
| 146 | ("awk '" | ||
| 147 | ("BEGIN { blankline=0; anonblank=0; }" | ||
| 148 | "/^$/ { if (anonblank==0) next; }" | ||
| 149 | "{ anonblank=1; }" | ||
| 150 | "/^$/ { blankline++; next; }" | ||
| 151 | "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }" | ||
| 152 | "'" | ||
| 153 | )) | ||
| 154 | ) | ||
| 155 | "*Manpage cleaning filter command phrases. | ||
| 156 | This variable contains an association list of the following form: | ||
| 157 | |||
| 158 | '((command-string (phrase-string*))*) | ||
| 159 | |||
| 160 | Each phrase-string is concatenated onto the command-string to form a | ||
| 161 | command filter. The (standard) output (and standard error) of the Un*x | ||
| 162 | man command is piped through each command filter in the order the | ||
| 163 | commands appear in the association list. The final output is placed in | ||
| 164 | the manpage buffer.") | ||
| 165 | |||
| 166 | (defvar Man-mode-line-format | ||
| 167 | '("" mode-line-modified | ||
| 168 | mode-line-buffer-identification " " | ||
| 169 | global-mode-string | ||
| 170 | Man-page-mode-string | ||
| 171 | " %[(" mode-name minor-mode-alist mode-line-process ")%]----" | ||
| 172 | (-3 . "%p") "-%-") | ||
| 173 | "*Mode line format for manual mode buffer.") | ||
| 174 | |||
| 175 | (defvar Man-mode-map nil | ||
| 176 | "*Keymap for Man-mode.") | ||
| 177 | |||
| 178 | (defvar Man-mode-hooks nil | ||
| 179 | "*Hooks for Man-mode.") | ||
| 180 | |||
| 181 | (defvar Man-section-regexp "[0-9][a-zA-Z+]*" | ||
| 182 | "*Regular expression describing a manpage section within parentheses.") | ||
| 183 | |||
| 184 | (defvar Man-heading-regexp "^[A-Z]" | ||
| 185 | "*Regular expression describing a manpage heading entry.") | ||
| 186 | |||
| 187 | (defvar Man-see-also-regexp "SEE ALSO" | ||
| 188 | "*Regular expression for SEE ALSO heading (or your equivalent). | ||
| 189 | This regexp should not start with a `^' character.") | ||
| 190 | |||
| 191 | (defvar Man-first-heading-regexp "^NAME$\\|^No manual entry for .*$" | ||
| 192 | "*Regular expression describing first heading on a manpage. | ||
| 193 | This regular expression should start with a `^' character.") | ||
| 194 | |||
| 195 | (defvar Man-reference-regexp "[-a-zA-Z0-9_.]+\\(([0-9][a-zA-Z+]*)\\)?" | ||
| 196 | "*Regular expression describing a reference in the SEE ALSO section.") | ||
| 197 | |||
| 198 | |||
| 199 | ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 200 | ;; end user variables | ||
| 201 | |||
| 202 | (defconst Man-version-number "1.1" | ||
| 203 | "man's version number.") | ||
| 204 | |||
| 205 | |||
| 206 | ;; other variables and keymap initializations | ||
| 207 | (make-variable-buffer-local 'Man-sections-alist) | ||
| 208 | (make-variable-buffer-local 'Man-refpages-alist) | ||
| 209 | (make-variable-buffer-local 'Man-page-list) | ||
| 210 | (make-variable-buffer-local 'Man-current-page) | ||
| 211 | (make-variable-buffer-local 'Man-page-mode-string) | ||
| 212 | |||
| 213 | (setq-default Man-sections-alist nil) | ||
| 214 | (setq-default Man-refpages-alist nil) | ||
| 215 | (setq-default Man-page-list nil) | ||
| 216 | (setq-default Man-current-page 0) | ||
| 217 | (setq-default Man-page-mode-string "1 (of 1)") | ||
| 218 | |||
| 219 | (if Man-mode-map | ||
| 220 | nil | ||
| 221 | (setq Man-mode-map (make-keymap)) | ||
| 222 | (suppress-keymap Man-mode-map) | ||
| 223 | (define-key Man-mode-map " " 'scroll-up) | ||
| 224 | (define-key Man-mode-map "\177" 'scroll-down) | ||
| 225 | (define-key Man-mode-map "n" 'Man-next-section) | ||
| 226 | (define-key Man-mode-map "p" 'Man-previous-section) | ||
| 227 | (define-key Man-mode-map "\en" 'Man-next-manpage) | ||
| 228 | (define-key Man-mode-map "\ep" 'Man-previous-manpage) | ||
| 229 | (define-key Man-mode-map "," 'beginning-of-buffer) | ||
| 230 | (define-key Man-mode-map "." 'end-of-buffer) | ||
| 231 | (define-key Man-mode-map "r" 'Man-follow-manual-reference) | ||
| 232 | (define-key Man-mode-map "t" 'toggle-truncate-lines) | ||
| 233 | (define-key Man-mode-map "g" 'Man-goto-section) | ||
| 234 | (define-key Man-mode-map "s" 'Man-goto-see-also-section) | ||
| 235 | (define-key Man-mode-map "q" 'Man-quit) | ||
| 236 | (define-key Man-mode-map "m" 'manual-entry) | ||
| 237 | (define-key Man-mode-map "v" 'Man-version) | ||
| 238 | (define-key Man-mode-map "?" 'describe-mode) | ||
| 239 | ) | ||
| 240 | |||
| 241 | |||
| 242 | ;; ====================================================================== | ||
| 243 | ;; utilities | ||
| 244 | |||
| 245 | (defun Man-page-mode-string () | ||
| 246 | "Formats part of the mode line for manual mode." | ||
| 247 | (format "%d (of %d)" Man-current-page (length Man-page-list))) | ||
| 248 | |||
| 249 | (defun Man-delete-trailing-newline (str) | ||
| 250 | (if (string= (substring str (1- (length str))) "\n") | ||
| 251 | (substring str 0 (1- (length str))) | ||
| 252 | str)) | ||
| 253 | |||
| 254 | (defun Man-build-man-command () | ||
| 255 | "Builds the entire background manpage and cleaning command." | ||
| 256 | (let ((command "man %s 2>&1 | ") | ||
| 257 | (flist Man-filter-list)) | ||
| 258 | (while flist | ||
| 259 | (let ((pcom (car (car flist))) | ||
| 260 | (pargs (car (cdr (car flist))))) | ||
| 261 | (setq flist (cdr flist)) | ||
| 262 | (if (or (not (stringp pcom)) | ||
| 263 | (not (listp pargs))) | ||
| 264 | (error "malformed Man-filter-list.")) | ||
| 265 | (setq command (concat command pcom | ||
| 266 | (mapconcat '(lambda (phrase) phrase) | ||
| 267 | pargs " ")))) | ||
| 268 | (if flist | ||
| 269 | (setq command (concat command " | " )))) | ||
| 270 | command)) | ||
| 271 | |||
| 272 | (defun Man-downcase (man-args) | ||
| 273 | "Downcases section letters in MAN-ARGS." | ||
| 274 | (let ((newargs "") | ||
| 275 | (s 0) | ||
| 276 | mstart mend | ||
| 277 | (len (length man-args))) | ||
| 278 | (while (and (< s len) | ||
| 279 | (setq mstart (string-match Man-section-regexp man-args s))) | ||
| 280 | (setq mend (match-end 0) | ||
| 281 | newargs (concat newargs (substring man-args s mstart))) | ||
| 282 | (setq newargs (concat newargs (downcase | ||
| 283 | (substring man-args mstart mend))) | ||
| 284 | s mend)) | ||
| 285 | (concat newargs (substring man-args s len)))) | ||
| 286 | |||
| 287 | (defun Man-translate-references (ref) | ||
| 288 | "Translates REF from \"chmod(2V)\" to \"2v chmod\" style." | ||
| 289 | (if (string-match (concat "(" Man-section-regexp ")$") ref) | ||
| 290 | (let* ((word (progn (string-match "(" ref) | ||
| 291 | (substring ref 0 (1- (match-end 0))))) | ||
| 292 | (section-re (concat "(\\(" Man-section-regexp "\\))")) | ||
| 293 | (section (if (string-match section-re ref) | ||
| 294 | (substring ref (match-beginning 1) (match-end 1)) | ||
| 295 | "")) | ||
| 296 | (slist Man-section-translations-alist) | ||
| 297 | ) | ||
| 298 | (if Man-downcase-section-letters-p | ||
| 299 | (setq section (Man-downcase section))) | ||
| 300 | (while slist | ||
| 301 | (let ((s1 (car (car slist))) | ||
| 302 | (s2 (cdr (car slist)))) | ||
| 303 | (setq slist (cdr slist)) | ||
| 304 | (if Man-downcase-section-letters-p | ||
| 305 | (setq s1 (Man-downcase s1))) | ||
| 306 | (if (not (string= s1 section)) nil | ||
| 307 | (setq section (if Man-downcase-section-letters-p | ||
| 308 | (Man-downcase s2) | ||
| 309 | s2) | ||
| 310 | slist nil)))) | ||
| 311 | (concat section " " word)) | ||
| 312 | ref)) | ||
| 313 | |||
| 314 | (defun Man-linepos (&optional position col-p) | ||
| 315 | "Return the character position at various line/buffer positions. | ||
| 316 | Preserves the state of point, mark, etc. Optional POSITION can be one | ||
| 317 | of the following symbols: | ||
| 318 | bol == beginning of line | ||
| 319 | boi == beginning of indentation | ||
| 320 | eol == end of line [default] | ||
| 321 | bob == beginning of buffer | ||
| 322 | eob == end of buffer | ||
| 323 | |||
| 324 | Optional COL-P non-nil returns current-column instead of character position." | ||
| 325 | (let ((tpnt (point)) | ||
| 326 | rval) | ||
| 327 | (cond | ||
| 328 | ((eq position 'bol) (beginning-of-line)) | ||
| 329 | ((eq position 'boi) (back-to-indentation)) | ||
| 330 | ((eq position 'bob) (goto-char (point-min))) | ||
| 331 | ((eq position 'eob) (goto-char (point-max))) | ||
| 332 | (t (end-of-line))) | ||
| 333 | (setq rval (if col-p (current-column) (point))) | ||
| 334 | (goto-char tpnt) | ||
| 335 | rval)) | ||
| 336 | |||
| 337 | |||
| 338 | ;; ====================================================================== | ||
| 339 | ;; default man entry and get word under point | ||
| 340 | |||
| 341 | (defun Man-default-man-args (manword) | ||
| 342 | "Build the default man args from MANWORD and major-mode." | ||
| 343 | (let ((mode major-mode) | ||
| 344 | (slist Man-auto-section-alist)) | ||
| 345 | (while (and slist | ||
| 346 | (not (eq (car (car slist)) mode))) | ||
| 347 | (setq slist (cdr slist))) | ||
| 348 | (if (not slist) | ||
| 349 | manword | ||
| 350 | (let ((sections (cdr (car slist)))) | ||
| 351 | (if (not (listp sections)) | ||
| 352 | (concat sections " " manword) | ||
| 353 | (let ((manarg "")) | ||
| 354 | (while sections | ||
| 355 | (setq manarg (concat manarg " " (car sections) " " manword)) | ||
| 356 | (setq sections (cdr sections))) | ||
| 357 | manarg) | ||
| 358 | ))))) | ||
| 359 | |||
| 360 | (defun Man-default-man-entry () | ||
| 361 | "Make a guess at a default manual entry. | ||
| 362 | This guess is based on the text surrounding the cursor, and the | ||
| 363 | default section number is selected from Man-auto-section-alist." | ||
| 364 | (let ((default-section nil) | ||
| 365 | default-title) | ||
| 47 | (save-excursion | 366 | (save-excursion |
| 48 | (set-buffer standard-output) | 367 | |
| 49 | (message "Looking for formatted entry for %s%s..." | 368 | ;; Default man entry title is any word the cursor is on, |
| 50 | topic (if section (concat "(" section ")") "")) | 369 | ;; or if cursor not on a word, then nearest preceding |
| 51 | (let ((dirlist manual-formatted-dirlist) | 370 | ;; word. |
| 52 | (case-fold-search nil) | 371 | (and (not (looking-at "[a-zA-Z_]")) |
| 53 | name) | 372 | (skip-chars-backward "^a-zA-Z_")) |
| 54 | (if (and section (or (file-exists-p | 373 | (skip-chars-backward "(a-zA-Z_0-9") |
| 55 | (setq name (concat manual-formatted-dir-prefix | 374 | (and (looking-at "(") (forward-char 1)) |
| 56 | (substring section 0 1) | 375 | (setq default-title |
| 57 | "/" | 376 | (buffer-substring |
| 58 | topic "." section))) | 377 | (point) |
| 59 | (file-exists-p | 378 | (progn (skip-chars-forward "a-zA-Z0-9_") (point)))) |
| 60 | (setq name (concat manual-formatted-dir-prefix | 379 | |
| 61 | section | 380 | ;; If looking at something like ioctl(2) or brc(1M), include |
| 62 | "/" | 381 | ;; section number in default-entry |
| 63 | topic "." section))))) | 382 | (if (looking-at "[ \t]*([ \t]*[0-9][a-zA-Z]?[ \t]*)") |
| 64 | (insert-man-file name) | 383 | (progn (skip-chars-forward "^0-9") |
| 65 | (while dirlist | 384 | (setq default-section |
| 66 | (let* ((dir (car dirlist)) | 385 | (buffer-substring |
| 67 | (name1 (concat dir "/" topic "." | 386 | (point) |
| 68 | (or section | 387 | (progn |
| 69 | (substring | 388 | (skip-chars-forward "0-9a-zA-Z") |
| 70 | dir | 389 | (point))))) |
| 71 | (1+ (or (string-match "\\.[^./]*$" dir) | 390 | |
| 72 | -2)))))) | 391 | ;; Otherwise, assume section number to be 2 if we're |
| 73 | completions) | 392 | ;; in C code |
| 74 | (if (file-exists-p name1) | 393 | (and (eq major-mode 'c-mode) |
| 75 | (insert-man-file name1) | 394 | (setq default-section "2"))) |
| 76 | (condition-case () | 395 | (if default-section |
| 77 | (progn | 396 | (format "%s %s" default-section default-title) |
| 78 | (setq completions (file-name-all-completions | 397 | default-title)))) |
| 79 | (concat topic "." (or section "")) | 398 | |
| 80 | dir)) | 399 | |
| 81 | (while completions | 400 | ;; ====================================================================== |
| 82 | (insert-man-file (concat dir "/" (car completions))) | 401 | ;; top level command and background process sentinel |
| 83 | (setq completions (cdr completions)))) | ||
| 84 | (file-error nil))) | ||
| 85 | (goto-char (point-max))) | ||
| 86 | (setq dirlist (cdr dirlist))))) | ||
| 87 | |||
| 88 | (if (= (buffer-size) 0) | ||
| 89 | (progn | ||
| 90 | (message "No formatted entry, invoking man %s%s..." | ||
| 91 | (if section (concat section " ") "") topic) | ||
| 92 | (if section | ||
| 93 | (call-process manual-program nil t nil section topic) | ||
| 94 | (call-process manual-program nil t nil topic)) | ||
| 95 | (if (< (buffer-size) 80) | ||
| 96 | (progn | ||
| 97 | (goto-char (point-min)) | ||
| 98 | (end-of-line) | ||
| 99 | (error (buffer-substring 1 (point))))))) | ||
| 100 | |||
| 101 | (message "Cleaning manual entry for %s..." topic) | ||
| 102 | (nuke-nroff-bs) | ||
| 103 | (set-buffer-modified-p nil) | ||
| 104 | (setq buffer-read-only t) | ||
| 105 | (view-mode nil 'bury-buffer) | ||
| 106 | (message "")))) | ||
| 107 | |||
| 108 | ;; Hint: BS stands for more things than "back space" | ||
| 109 | (defun nuke-nroff-bs () | ||
| 110 | (interactive "*") | ||
| 111 | ;; Nuke headers: "MORE(1) UNIX Programmer's Manual MORE(1)" | ||
| 112 | ;; We expext to find a footer just before the header except at the beginning. | ||
| 113 | (goto-char (point-min)) | ||
| 114 | (while (re-search-forward "^ *\\([A-Za-z][-_.A-Za-z0-9]*([0-9A-Z]+)\\).*\\1$" nil t) | ||
| 115 | (let (start end) | ||
| 116 | ;; Put START and END around footer and header and garbage blank lines. | ||
| 117 | ;; Fixed line counts are risky, but allow us to preserve | ||
| 118 | ;; significant blank lines. | ||
| 119 | ;; These numbers are correct for MORE BSD, at least. | ||
| 120 | (setq start (save-excursion (forward-line -9) (point))) | ||
| 121 | (setq end (save-excursion (forward-line 3) (point))) | ||
| 122 | (delete-region start end))) | ||
| 123 | ;; Catch the final footer. | ||
| 124 | (goto-char (point-max)) | ||
| 125 | (delete-region (point) (save-excursion (forward-line -7) (point))) | ||
| 126 | |||
| 127 | ;; Nuke underlining and overstriking (only by the same letter) | ||
| 128 | (goto-char (point-min)) | ||
| 129 | (while (search-forward "\b" nil t) | ||
| 130 | (let* ((preceding (char-after (- (point) 2))) | ||
| 131 | (following (following-char))) | ||
| 132 | (cond ((= preceding following) | ||
| 133 | ;; x\bx | ||
| 134 | (delete-char -2)) | ||
| 135 | ((and (= preceding ?o) (= following ?\+)) | ||
| 136 | ;; o\b+ | ||
| 137 | (delete-char -2)) | ||
| 138 | ((= preceding ?\_) | ||
| 139 | ;; _\b | ||
| 140 | (delete-char -2)) | ||
| 141 | ((= following ?\_) | ||
| 142 | ;; \b_ | ||
| 143 | (delete-region (1- (point)) (1+ (point))))))) | ||
| 144 | |||
| 145 | ;; Zap ESC7, ESC8, and ESC9. | ||
| 146 | ;; This is for Sun man pages like "man 1 csh" | ||
| 147 | (goto-char (point-min)) | ||
| 148 | (while (re-search-forward "\e[789]" nil t) | ||
| 149 | (replace-match "")) | ||
| 150 | 402 | ||
| 151 | ;; Convert o^H+ into o. | 403 | ;;;###autoload |
| 152 | (goto-char (point-min)) | 404 | (defun manual-entry (arg) |
| 153 | (while (re-search-forward "o\010\\+" nil t) | 405 | "Get a Un*x manual page and put it in a buffer. |
| 154 | (replace-match "o")) | 406 | This command is the top-level command in the man package. It runs a Un*x |
| 407 | command to retrieve and clean a manpage in the background and places the | ||
| 408 | results in a Man-mode (manpage browsing) buffer. See variable | ||
| 409 | Man-notify for what happens when the buffer is ready. | ||
| 410 | Universal argument ARG, is passed to Man-getpage-in-background." | ||
| 411 | (interactive "P") | ||
| 412 | (let* ((default-entry (Man-default-man-entry)) | ||
| 413 | (man-args | ||
| 414 | (read-string (format "Manual-entry: %s" | ||
| 415 | (if (string= default-entry "") "" | ||
| 416 | (format "(default: %s) " | ||
| 417 | default-entry)))))) | ||
| 418 | (and (string= man-args "") | ||
| 419 | (if (string= default-entry "") | ||
| 420 | (error "No man args given.") | ||
| 421 | (setq man-args default-entry))) | ||
| 422 | (if Man-downcase-section-letters-p | ||
| 423 | (setq man-args (Man-downcase man-args))) | ||
| 424 | (Man-getpage-in-background man-args (consp arg)) | ||
| 425 | )) | ||
| 155 | 426 | ||
| 156 | ;; Nuke the dumb reformatting message | 427 | (defun Man-getpage-in-background (man-args &optional override-reuse-p) |
| 157 | (goto-char (point-min)) | 428 | "Uses MAN-ARGS to build and fire off the manpage and cleaning command. |
| 158 | (while (re-search-forward "Reformatting page. Wait... done\n\n" nil t) | 429 | Optional OVERRIDE-REUSE-P, when supplied non-nil forces man to |
| 159 | (replace-match "")) | 430 | start a background process even if a buffer already exists and |
| 431 | Man-reuse-okay-p is non-nil." | ||
| 432 | (let* ((bufname (concat "*man " man-args "*")) | ||
| 433 | (buffer (get-buffer bufname))) | ||
| 434 | (if (and Man-reuse-okay-p | ||
| 435 | (not override-reuse-p) | ||
| 436 | buffer) | ||
| 437 | (Man-notify-when-ready buffer) | ||
| 438 | (message "Invoking man %s in background..." man-args) | ||
| 439 | (setq buffer (generate-new-buffer bufname)) | ||
| 440 | (set-process-sentinel | ||
| 441 | (start-process "man" buffer "sh" "-c" | ||
| 442 | (format (Man-build-man-command) man-args)) | ||
| 443 | 'Man-bgproc-sentinel)) | ||
| 444 | )) | ||
| 445 | |||
| 446 | (defun Man-notify-when-ready (man-buffer) | ||
| 447 | "Notify the user when MAN-BUFFER is ready. | ||
| 448 | See the variable Man-notify for the different notification behaviors." | ||
| 449 | (cond | ||
| 450 | ((eq man-notify 'bully) | ||
| 451 | (pop-to-buffer man-buffer) | ||
| 452 | (delete-other-windows)) | ||
| 453 | ((eq man-notify 'aggressive) | ||
| 454 | (pop-to-buffer man-buffer)) | ||
| 455 | ((eq man-notify 'friendly) | ||
| 456 | (display-buffer man-buffer 'not-this-window)) | ||
| 457 | ((eq man-notify 'polite) | ||
| 458 | (beep) | ||
| 459 | (message "Manual buffer %s is ready." (buffer-name man-buffer))) | ||
| 460 | ((eq man-notify 'quiet) | ||
| 461 | (message "Manual buffer %s is ready." (buffer-name man-buffer))) | ||
| 462 | ((or (eq man-notify 'meek) | ||
| 463 | t) | ||
| 464 | (message "")) | ||
| 465 | )) | ||
| 466 | |||
| 467 | (defun Man-bgproc-sentinel (process msg) | ||
| 468 | "Manpage background process sentinel." | ||
| 469 | (let ((Man-buffer (process-buffer process)) | ||
| 470 | (delete-buff nil) | ||
| 471 | (err-mess nil)) | ||
| 472 | (if (null (buffer-name Man-buffer)) ;; deleted buffer | ||
| 473 | (set-process-buffer process nil) | ||
| 474 | (save-excursion | ||
| 475 | (set-buffer Man-buffer) | ||
| 476 | (goto-char (point-min)) | ||
| 477 | (cond ((or (looking-at "No \\(manual \\)*entry for") | ||
| 478 | (looking-at "[^\n]*: nothing appropriate$")) | ||
| 479 | (setq err-mess (buffer-substring (point) (Man-linepos 'eol)) | ||
| 480 | delete-buff t) | ||
| 481 | ) | ||
| 482 | ((not (and (eq (process-status process) 'exit) | ||
| 483 | (= (process-exit-status process) 0))) | ||
| 484 | (setq err-mess | ||
| 485 | (concat (buffer-name Man-buffer) | ||
| 486 | ": process " | ||
| 487 | (let ((eos (1- (length msg)))) | ||
| 488 | (if (= (aref msg eos) ?\n) | ||
| 489 | (substring msg 0 eos) msg)))) | ||
| 490 | (goto-char (point-max)) | ||
| 491 | (insert (format "\nprocess %s" msg)) | ||
| 492 | ))) | ||
| 493 | (if delete-buff | ||
| 494 | (kill-buffer Man-buffer) | ||
| 495 | (save-window-excursion | ||
| 496 | (save-excursion | ||
| 497 | (set-buffer Man-buffer) | ||
| 498 | (Man-mode) | ||
| 499 | (set-buffer-modified-p nil))) | ||
| 500 | (Man-notify-when-ready Man-buffer)) | ||
| 501 | |||
| 502 | (if err-mess | ||
| 503 | (error err-mess)) | ||
| 504 | ))) | ||
| 505 | |||
| 506 | |||
| 507 | ;; ====================================================================== | ||
| 508 | ;; set up manual mode in buffer and build alists | ||
| 509 | |||
| 510 | (defun Man-mode () | ||
| 511 | "SUPERMAN 1.1: A mode for browsing Un*x manual pages. | ||
| 512 | |||
| 513 | The following man commands are available in the buffer. Try | ||
| 514 | \"\\[describe-key] <key> RET\" for more information: | ||
| 160 | 515 | ||
| 161 | ;; Crunch blank lines | 516 | \\[manual-entry] Prompt to retrieve a new manpage. |
| 517 | \\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section. | ||
| 518 | \\[Man-next-manpage] Jump to next manpage in circular list. | ||
| 519 | \\[Man-previous-manpage] Jump to previous manpage in circular list. | ||
| 520 | \\[Man-next-section] Jump to next manpage section. | ||
| 521 | \\[Man-previous-section] Jump to previous manpage section. | ||
| 522 | \\[Man-goto-section] Go to a manpage section. | ||
| 523 | \\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section. | ||
| 524 | \\[Man-quit] Deletes the manpage, its buffer, and window. | ||
| 525 | \\[Man-version] Prints man's version number. | ||
| 526 | \\[describe-mode] Prints this help text. | ||
| 527 | |||
| 528 | The following variables may be of some use. Try | ||
| 529 | \"\\[describe-variable] <variable-name> RET\" for more information: | ||
| 530 | |||
| 531 | Man-notify What happens when manpage formatting is done. | ||
| 532 | Man-reuse-okay-p Okay to reuse already formatted buffer? | ||
| 533 | Man-downcase-section-letters-p Force section letters to lower case? | ||
| 534 | Man-circular-pages-p Multiple manpage list treated as circular? | ||
| 535 | Man-auto-section-alist List of major modes and their section numbers. | ||
| 536 | Man-section-translations-alist List of section numbers and their Un*x equiv. | ||
| 537 | Man-filter-list Background manpage filter command. | ||
| 538 | Man-mode-line-format Mode line format for Man-mode buffers. | ||
| 539 | Man-mode-map Keymap bindings for Man-mode buffers. | ||
| 540 | Man-mode-hooks Hooks for Man-mode. | ||
| 541 | Man-section-regexp Regexp describing manpage section letters. | ||
| 542 | Man-heading-regexp Regexp describing section headers. | ||
| 543 | Man-see-also-regexp Regexp for SEE ALSO section (or your equiv). | ||
| 544 | Man-first-heading-regexp Regexp for first heading on a manpage. | ||
| 545 | Man-reference-regexp Regexp matching a references in SEE ALSO. | ||
| 546 | Man-version-number Superman version number. | ||
| 547 | |||
| 548 | The following key bindings are currently in effect in the buffer: | ||
| 549 | \\{Man-mode-map}" | ||
| 550 | (interactive) | ||
| 551 | (setq major-mode 'Man-mode | ||
| 552 | mode-name "Manual" | ||
| 553 | buffer-auto-save-file-name nil | ||
| 554 | mode-line-format Man-mode-line-format | ||
| 555 | truncate-lines t | ||
| 556 | buffer-read-only t) | ||
| 557 | (buffer-disable-undo (current-buffer)) | ||
| 558 | (auto-fill-mode -1) | ||
| 559 | (use-local-map Man-mode-map) | ||
| 162 | (goto-char (point-min)) | 560 | (goto-char (point-min)) |
| 163 | (while (re-search-forward "\n\n\n\n*" nil t) | 561 | (Man-build-page-list) |
| 164 | (replace-match "\n\n")) | 562 | (Man-goto-page 1)) |
| 165 | 563 | ||
| 166 | ;; Nuke blanks lines at start. | 564 | (defun Man-build-section-alist () |
| 565 | "Build the association list of manpage sections." | ||
| 566 | (setq Man-sections-alist nil) | ||
| 167 | (goto-char (point-min)) | 567 | (goto-char (point-min)) |
| 168 | (skip-chars-forward "\n") | 568 | (while (re-search-forward Man-heading-regexp (point-max) t) |
| 169 | (delete-region (point-min) (point))) | 569 | (aput 'Man-sections-alist |
| 170 | 570 | (buffer-substring (Man-linepos 'bol) (Man-linepos))) | |
| 171 | 571 | (forward-line 1) | |
| 172 | (defun insert-man-file (name) | 572 | )) |
| 173 | ;; Insert manual file (unpacked as necessary) into buffer | 573 | |
| 174 | (if (or (equal (substring name -2) ".Z") | 574 | (defun Man-build-references-alist () |
| 175 | (string-match "/cat[0-9][a-z]?\\.Z/" name)) | 575 | "Build the association list of references (in the SEE ALSO section)." |
| 176 | (call-process "zcat" name t nil) | 576 | (setq Man-refpages-alist nil) |
| 177 | (if (equal (substring name -2) ".z") | 577 | (save-excursion |
| 178 | (call-process "pcat" nil t nil name) | 578 | (if (Man-find-section Man-see-also-regexp) |
| 179 | (insert-file-contents name)))) | 579 | (let ((start (progn (forward-line 1) (point))) |
| 580 | (end (progn | ||
| 581 | (Man-next-section 1) | ||
| 582 | (point))) | ||
| 583 | hyphenated | ||
| 584 | (runningpoint -1)) | ||
| 585 | (narrow-to-region start end) | ||
| 586 | (goto-char (point-min)) | ||
| 587 | (back-to-indentation) | ||
| 588 | (while (and (not (eobp)) (/= (point) runningpoint)) | ||
| 589 | (setq runningpoint (point)) | ||
| 590 | (let* ((bow (point)) | ||
| 591 | (eow (re-search-forward Man-reference-regexp end t)) | ||
| 592 | (word (buffer-substring bow (match-end 0))) | ||
| 593 | (len (1- (length word)))) | ||
| 594 | (if (not eow) nil | ||
| 595 | (if hyphenated | ||
| 596 | (setq word (concat hyphenated word) | ||
| 597 | hyphenated nil)) | ||
| 598 | (if (= (aref word len) ?-) | ||
| 599 | (setq hyphenated (substring word 0 len)) | ||
| 600 | (aput 'Man-refpages-alist word)))) | ||
| 601 | (skip-chars-forward " \t\n,")) | ||
| 602 | )))) | ||
| 603 | |||
| 604 | (defun Man-build-page-list () | ||
| 605 | "Build the list of separate manpages in the buffer." | ||
| 606 | (setq Man-page-list nil) | ||
| 607 | (save-excursion | ||
| 608 | (let ((page-start (Man-linepos 'bob)) | ||
| 609 | (page-end (Man-linepos 'eob)) | ||
| 610 | (regexp Man-first-heading-regexp)) | ||
| 611 | (goto-char (point-min)) | ||
| 612 | (re-search-forward regexp (point-max) t) | ||
| 613 | (while (not (eobp)) | ||
| 614 | (if (re-search-forward regexp (point-max) t) | ||
| 615 | (progn | ||
| 616 | (setq page-end (Man-linepos 'bol)) | ||
| 617 | (end-of-line)) | ||
| 618 | (goto-char (point-max)) | ||
| 619 | (setq page-end (point))) | ||
| 620 | (setq Man-page-list (append Man-page-list | ||
| 621 | (list (cons page-start page-end))) | ||
| 622 | page-start page-end) | ||
| 623 | )))) | ||
| 624 | |||
| 625 | |||
| 626 | ;; ====================================================================== | ||
| 627 | ;; Man-mode commands | ||
| 628 | |||
| 629 | (defun Man-next-section (n) | ||
| 630 | "Move point to Nth next section (default 1)." | ||
| 631 | (interactive "p") | ||
| 632 | (if (looking-at Man-heading-regexp) | ||
| 633 | (forward-line 1)) | ||
| 634 | (if (re-search-forward Man-heading-regexp (point-max) t n) | ||
| 635 | (beginning-of-line) | ||
| 636 | (goto-char (point-max)))) | ||
| 637 | |||
| 638 | (defun Man-previous-section (n) | ||
| 639 | "Move point to Nth previous section (default 1)." | ||
| 640 | (interactive "p") | ||
| 641 | (if (looking-at Man-heading-regexp) | ||
| 642 | (forward-line -1)) | ||
| 643 | (if (re-search-backward Man-heading-regexp (point-min) t n) | ||
| 644 | (beginning-of-line) | ||
| 645 | (goto-char (point-min)))) | ||
| 646 | |||
| 647 | (defun Man-find-section (section) | ||
| 648 | "Move point to SECTION if it exists, otherwise don't move point. | ||
| 649 | Returns t if section is found, nil otherwise." | ||
| 650 | (let ((curpos (point))) | ||
| 651 | (goto-char (point-min)) | ||
| 652 | (if (re-search-forward (concat "^" section) (point-max) t) | ||
| 653 | (progn (beginning-of-line) t) | ||
| 654 | (goto-char curpos) | ||
| 655 | nil) | ||
| 656 | )) | ||
| 657 | |||
| 658 | (defun Man-goto-section () | ||
| 659 | "Query for section to move point to." | ||
| 660 | (interactive) | ||
| 661 | (aput 'Man-sections-alist | ||
| 662 | (let* ((default (aheadsym Man-sections-alist)) | ||
| 663 | (completion-ignore-case t) | ||
| 664 | chosen | ||
| 665 | (prompt (concat "Go to section: (default " default ") "))) | ||
| 666 | (setq chosen (completing-read prompt Man-sections-alist)) | ||
| 667 | (if (or (not chosen) | ||
| 668 | (string= chosen "")) | ||
| 669 | default | ||
| 670 | chosen))) | ||
| 671 | (Man-find-section (aheadsym Man-sections-alist))) | ||
| 672 | |||
| 673 | (defun Man-goto-see-also-section () | ||
| 674 | "Move point the the \"SEE ALSO\" section. | ||
| 675 | Actually the section moved to is described by Man-see-also-regexp." | ||
| 676 | (interactive) | ||
| 677 | (if (not (Man-find-section Man-see-also-regexp)) | ||
| 678 | (error (concat "No " Man-see-also-regexp | ||
| 679 | " section found in current manpage.")))) | ||
| 680 | |||
| 681 | (defun Man-follow-manual-reference (arg) | ||
| 682 | "Get one of the manpages referred to in the \"SEE ALSO\" section. | ||
| 683 | Queries you for the page to retrieve. Of course it does this in the | ||
| 684 | background. Universal argument ARG is passed to Man-getpage-in-background." | ||
| 685 | (interactive "P") | ||
| 686 | (if (not Man-refpages-alist) | ||
| 687 | (error (concat "No references found in current manpage.")) | ||
| 688 | (aput 'Man-refpages-alist | ||
| 689 | (let* ((default (aheadsym Man-refpages-alist)) | ||
| 690 | chosen | ||
| 691 | (prompt (concat "Refer to: (default " default ") "))) | ||
| 692 | (setq chosen (completing-read prompt Man-refpages-alist nil t)) | ||
| 693 | (if (or (not chosen) | ||
| 694 | (string= chosen "")) | ||
| 695 | default | ||
| 696 | chosen))) | ||
| 697 | (Man-getpage-in-background | ||
| 698 | (Man-translate-references (aheadsym Man-refpages-alist)) | ||
| 699 | (consp arg)))) | ||
| 700 | |||
| 701 | (defun Man-quit () | ||
| 702 | "Kill the buffer containing the manpage." | ||
| 703 | (interactive) | ||
| 704 | (let ((buff (current-buffer))) | ||
| 705 | (delete-windows-on buff) | ||
| 706 | (kill-buffer buff))) | ||
| 707 | |||
| 708 | (defun Man-goto-page (page) | ||
| 709 | "Go to the manual page on page PAGE." | ||
| 710 | (interactive | ||
| 711 | (if (not Man-page-list) | ||
| 712 | (error "You're looking at the only manpage in the buffer.") | ||
| 713 | (format "nGo to manpage [1-%d]: " (length Man-page-list)))) | ||
| 714 | (if (or (< page 1) | ||
| 715 | (> page (length Man-page-list))) | ||
| 716 | (error "No manpage %d found" page)) | ||
| 717 | (let* ((page-range (nth (1- page) Man-page-list)) | ||
| 718 | (page-start (car page-range)) | ||
| 719 | (page-end (cdr page-range))) | ||
| 720 | (setq Man-current-page page | ||
| 721 | Man-page-mode-string (Man-page-mode-string)) | ||
| 722 | (widen) | ||
| 723 | (goto-char page-start) | ||
| 724 | (narrow-to-region page-start page-end) | ||
| 725 | (Man-build-section-alist) | ||
| 726 | (Man-build-references-alist) | ||
| 727 | (widen) | ||
| 728 | (narrow-to-region page-start page-end) | ||
| 729 | (goto-char (point-min)))) | ||
| 730 | |||
| 731 | |||
| 732 | (defun Man-next-manpage () | ||
| 733 | "Find the next manpage entry in the buffer." | ||
| 734 | (interactive) | ||
| 735 | (if (= (length Man-page-list) 1) | ||
| 736 | (error "This is the only manpage in the buffer.")) | ||
| 737 | (if (< Man-current-page (length Man-page-list)) | ||
| 738 | (Man-goto-page (1+ Man-current-page)) | ||
| 739 | (if Man-circular-pages-p | ||
| 740 | (Man-goto-page 1) | ||
| 741 | (error "You're looking at the last manpage in the buffer.")))) | ||
| 742 | |||
| 743 | (defun Man-previous-manpage () | ||
| 744 | "Find the previous manpage entry in the buffer." | ||
| 745 | (interactive) | ||
| 746 | (if (= (length Man-page-list) 1) | ||
| 747 | (error "This is the only manpage in the buffer.")) | ||
| 748 | (if (> Man-current-page 1) | ||
| 749 | (Man-goto-page (1- Man-current-page)) | ||
| 750 | (if Man-circular-pages-p | ||
| 751 | (Man-goto-page (length Man-page-list)) | ||
| 752 | (error "You're looking at the first manpage in the buffer.")))) | ||
| 753 | |||
| 754 | (defun Man-version (arg) | ||
| 755 | "Show man's version. | ||
| 756 | Universal argument (\\[universal-argument]) ARG inserts version | ||
| 757 | information in the current buffer instead of printing the message in | ||
| 758 | the echo area." | ||
| 759 | (interactive "P") | ||
| 760 | (if (consp arg) | ||
| 761 | (insert "Using Superman version " Man-version-number ".") | ||
| 762 | (message "Using Superman version %s." Man-version-number))) | ||
| 180 | 763 | ||
| 181 | ;;; man.el ends here | 764 | ;;; man.el ends here |