Mercurial > emacs
changeset 8970:c0a15f821bd8
* man.el (Man-notify-flag): replaces the old Man-notify
variable, use the old one if it is bound as initial value.
(Man-reuse-okay-flag): replaces Man-reuse-okay.
(Man-downcase-section-letters-flag): replaces
Man-downcase-section-letters.
(Man-circular-pages-flag): replaces Man-circular-pages.
(Man-auto-section-alist): variable deleted.
(Man-section-translations-alist): removed the "3x" translation.
(Man-untabify-command, Man-untabify-command-args): new vars.
(Man-sed-command, Man-awk-command): new variables.
(Man-sysv-sed-script, Man-berkeley-sed-script): new constants.
(Man-name-regexp, Man-page-header-regexp): new variable.
(Man-heading-regexp): changed default value.
(Man-reference-regexp): now refers to previous regexps.
(Man-arguments): new buffer-local variable.
(Man-page-mode-string): changed default value.
(Man-mode-map): changed the meanings of ",", ".", "q". Added new
keys ">", "<", "k".
(Man-page-mode-string): function deleted.
(Man-init-defvars): New function used for initialising the system
and environment dependent variables Man-fontify-manpage-flag,
Man-uses-untabify, Man-sed-script, Man-filter-list.
(Man-delete-trailing-newlines): function deleted.
(Man-make-page-mode-string): new subst.
(Man-build-man-command): now subst instead of function. Modified
to comply with the new format of Man-filter-list.
(Man-downcase): function deleted.
(Man-translate-references): complete rewrite.
(Man-linepos): function deleted.
(Man-match-substring): new function.
(Man-default-man-args): function deleted.
(Man-default-man-entry): complete rewrite.
(man, manual-entry): function-alias relationship reversed.
(man): prompt changed, prompt using interactive, call
Man-init-defvars, set Man-arguments.
(Man-notify-when-ready): manage the 'pushy value.
(Man-fontify-manpage): substitute Man-set-fonts.
(Man-cleanup-manpage): new function.
(Man-bgproc-sentinel): cleanup, call Man-fontify-manpage and
Man-cleanup-page when necessary.
(Man-mode): call Man-strip-page-headers and Man-unindent.
(Man-build-section-alist, Man-build-references-alist,
Man-build-page-list): substs instead of functions.
(Man-build-references-alist): cleanup.
(Man-build-page-list): new algorithm.
(Man-strip-page-headers, Man-unindent): new substs.
(Man-find-section): assume section names start in column 1.
(Man-quit): bury the buffer instead of killing it, delete the
frame when necessary.
(Man-kill): new function.
(Man-goto-page): do the right thing when the manpage is not found,
do not assume that Man-build-references-alist is broken.
author | Francesco Potortì <pot@gnu.org> |
---|---|
date | Wed, 21 Sep 1994 16:15:42 +0000 |
parents | d62a9fd47cb2 |
children | 69ac91e85d1e |
files | lisp/man.el |
diffstat | 1 files changed, 548 insertions(+), 334 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/man.el Wed Sep 21 09:35:35 1994 +0000 +++ b/lisp/man.el Wed Sep 21 16:15:42 1994 +0000 @@ -3,10 +3,10 @@ ;; Copyright (C) 1993, 1994 Free Software Foundation, Inc. ;; Author: Barry A. Warsaw <bwarsaw@cen.com> -;; Last-Modified: 31-Jul-1991 -;; Version: 1.1 +;; Last-Modified: $Date: $ +;; Version: $Revision: $ ;; Keywords: help -;; Adapted-By: ESR +;; Adapted-By: ESR, pot ;; This file is part of GNU Emacs. @@ -26,9 +26,9 @@ ;;; Commentary: -;; This code provides a function, manual-entry, with which you can -;; browse UNIX manual pages. Formatting is done in background so that -;; you can continue to use your Emacs while processing is going on. +;; This code provides a function, `man', with which you can browse +;; UNIX manual pages. Formatting is done in background so that you +;; can continue to use your Emacs while processing is going on. ;; ;; The mode also supports hypertext-like following of manual page SEE ;; ALSO references, and other features. See below or do `?' in a @@ -38,7 +38,7 @@ ;; In mid 1991, several people posted some interesting improvements to ;; man.el from the standard emacs 18.57 distribution. I liked many of ;; these, but wanted everthing in one single package, so I decided -;; to encorporate them into a single manual browsing mode. While +;; to incorporate them into a single manual browsing mode. While ;; much of the code here has been rewritten, and some features added, ;; these folks deserve lots of credit for providing the initial ;; excellent packages on which this one is based. @@ -56,6 +56,10 @@ ;; point and some other names have been changed to make it a drop-in ;; replacement for the old man.el package. +;; Francesco Potorti` <pot@cnuce.cnr.it> cleaned it up thoroughly, +;; making it faster, more robust and more tolerant of different +;; systems' man idiosynchrasies. + ;; ========== Features ========== ;; + Runs "man" in the background and pipes the results through a ;; series of sed and awk scripts so that all retrieving and cleaning @@ -69,6 +73,25 @@ ;; + Multiple manpages created with the same man command are put into ;; a narrowed buffer circular list. +;; ============= TODO =========== +;; - Add a command for printing. +;; - The awk script deletes multiple blank lines. This behaviour does +;; not allow to understand if there was indeed a blank line at the +;; end or beginning of a page (after the header, or before the +;; footer). A different algorithm should be used. It is easy to +;; compute how many blank lines there are before and after the page +;; headers, and after the page footer. But it is possible to compute +;; the number of blank lines before the page footer by euristhics +;; only. Is it worth doing? +;; - Allow the Man-reuse-okay-flag to be set to 'always, meaning that all +;; the manpages should go in the same buffer, where they can be browsed +;; with M-n and M-p. +;; - Allow completion on the manpage name when calling man. This +;; requires a reliable list of places where manpages can be found. The +;; drawback would be that if the list is not complete, the user might +;; be led to believe that the manpages in the missing directories do +;; not exist. + ;;; Code: (require 'assoc) @@ -79,31 +102,35 @@ (defvar manual-program "man" "The name of the program that produces man pages.") -(defvar Man-notify 'friendly +;; Use the value of the obsolete user option Man-notify, if set. +(defvar Man-notify-flag (if (boundp 'Man-notify) Man-notify 'friendly) "*Selects the behavior when manpage is ready. -This variable may have one of the following values: +This variable may have one of the following values, where (sf) means +that the frames are switched, so the manpage is displayed in the frame +where the man command was called from: newframe -- put the manpage in its own frame (see `Man-frame-parameters') -bully -- make the manpage the current buffer and only window -aggressive -- make the manpage the current buffer in the other window -friendly -- display manpage in other window but don't make current -polite -- don't display manpage, but prints message when ready (beeps) +pushy -- make the manpage the current buffer in the current window +bully -- make the manpage the current buffer and only window (sf) +aggressive -- make the manpage the current buffer in the other window (sf) +friendly -- display manpage in the other window but don't make current (sf) +polite -- don't display manpage, but prints message and beep when ready quiet -- like `polite', but don't beep -meek -- make no indication that manpage is ready +meek -- make no indication that the manpage is ready -Any other value of `Man-notify' is equivalent to `meek'.") +Any other value of `Man-notify-flag' is equivalent to `meek'.") (defvar Man-frame-parameters nil "*Frame parameter list for creating a new frame for a manual page.") -(defvar Man-reuse-okay-p t +(defvar Man-reuse-okay-flag t "*Reuse a manpage buffer if possible. If non-nil, and a manpage buffer already exists with the same invocation, man just indicates the manpage is ready according to the -value of `Man-notify'. When nil, it always fires off a background -process, putting the results in a uniquely named buffer.") +value of `Man-notify-flag'. When nil, it always fires off a +background process,putting the results in a uniquely named buffer.") -(defvar Man-downcase-section-letters-p t +(defvar Man-downcase-section-letters-flag t "*Letters in sections are converted to lower case. Some Un*x man commands can't handle uppercase letters in sections, for example \"man 2V chmod\", but they are often displayed in the manpage @@ -111,79 +138,62 @@ letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before being sent to the man background process.") -(defvar Man-circular-pages-p t +(defvar Man-circular-pages-flag t "*If t, the manpage list is treated as circular for traversal.") -;; I changed this to nil because it is a bad idea -;; to fail to recognize things in other sections. -(defvar Man-auto-section-alist - nil -;; '((c-mode . ("2" "3")) -;; (c++-mode . ("2" "3")) -;; (c++-c-mode . ("2" "3")) -;; (shell-mode . ("1" "8")) -;; (cmushell-mode . ("1" "8")) -;; (text-mode . "1") -;; ) - "*Association list of major modes and their default section numbers. -List is of the form: (MAJOR-MODE . [SECTION | (SECTION*)]). If current -major mode is not in list, then the default is to check for manpages -in all sections.") - (defvar Man-section-translations-alist - '(("3C++" . "3") - ("3X" . "3") ; Xlib man pages - ("3X11" . "3") - ("1-UCB" . "")) + (list + '("3C++" . "3") + ;; Some systems have a real 3x man section, so let's comment this. + ;; '("3X" . "3") ; Xlib man pages + '("3X11" . "3") + '("1-UCB" . "")) "*Association list of bogus sections to real section numbers. Some manpages (e.g. the Sun C++ 2.1 manpages) have section numbers in their references which Un*x `man' does not recognize. This association list is used to translate those sections, when found, to the associated section number.") -(defvar Man-filter-list - '(("sed " - (;;"-e 's/.\010//g'" - "-e '/[Nn]o such file or directory/d'" - "-e '/Reformatting page. Wait/d'" - "-e '/Reformatting entry. Wait/d'" - "-e '/^ *\\([A-Za-z][A-Za-z.]*([0-9A-Za-z][-0-9A-Za-z+]*)\\).*\\1$/d'" - "-e '/^[ \t]*Hewlett-Packard Company[ \t]*- [0-9]* -.*$/d'" - "-e '/^[ \t]*Hewlett-Packard[ \t]*- [0-9]* -.*$/d'" - "-e '/^ *- [0-9]* - *Formatted:.*[0-9]$/d'" - "-e '/^[ \t]*Page [0-9]*.*(printed [0-9\\/]*)$/d'" - "-e '/^Printed [0-9].*[0-9]$/d'" - "-e '/^[ \t]*X Version 1[01].*Release [0-9]/d'" - "-e '/^[A-za-z].*Last change:/d'" - "-e '/^Sun Release [0-9].*[0-9]$/d'" - "-e '/^\\n$/D'" - )) - ("awk '\n" - ("BEGIN { blankline=0; anonblank=0; }\n" - "/^$/ { if (anonblank==0) next; }\n" - "{ anonblank=1; }\n" - "/^$/ { blankline++; next; }\n" - "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n" - "'" - )) - ) - "*Manpage cleaning filter command phrases. -This variable contains an association list of the following form: +(defvar Man-untabify-command "pr" + "*Command used for untabifying.") + +(defvar Man-untabify-command-args (list "-t" "-e") + "*List of arguments to be passed to Man-untabify-command (which see).") + +(defvar Man-sed-command "sed" + "*Command used for processing sed scripts.") + +(defvar Man-awk-command "awk" + "*Command used for processing awk scripts.") -'((command-string (phrase-string*))*) +(defconst Man-sysv-sed-script "\ +/\b/ { s/_\b//g + s/\b_//g + s/o\b+/o/g + :ovstrk + s/\\(.\\)\b\\1/\\1/g + t ovstrk + } +/\e\\[[0-9][0-9]*m/ s///g" + "Script for sysV-like sed to nuke backspaces and ANSI codes from manpages.") -Each phrase-string is concatenated onto the command-string to form a -command filter. The (standard) output (and standard error) of the Un*x -man command is piped through each command filter in the order the -commands appear in the association list. The final output is placed in -the manpage buffer.") +(defconst Man-berkeley-sed-script "\ +/\b/ { s/_\b//g\\ + s/\b_//g\\ + s/o\b+/o/g\\ + :ovstrk\\ + s/\\(.\\)\b\\1/\\1/g\\ + t ovstrk\\ + }\\ +/\e\\[[0-9][0-9]*m/ s///g" + "Script for berkeley-like sed to nuke backspaces and ANSI codes from manpages.") (defvar Man-mode-line-format '("" mode-line-modified - mode-line-buffer-identification " " + mode-line-buffer-identification " " global-mode-string " " Man-page-mode-string - " %[(" mode-name mode-line-process minor-mode-alist ")%]----" + " %[(" mode-name mode-line-process minor-mode-alist ")%]----" (-3 . "%p") "-%-") "*Mode line format for manual mode buffer.") @@ -191,17 +201,23 @@ "*Keymap for Man mode.") (defvar Man-mode-hook nil - "*Normal hook run when Man mode is enabled.") + "*Hook run when Man mode is enabled.") (defvar Man-cooked-hook nil - "*Normal hook run after removing backspaces but before Man-mode processing.") + "*Hook run after removing backspaces but before Man-mode processing.") + +(defvar Man-name-regexp "[-a-zA-Z0-9_][-a-zA-Z0-9_.]*" + "*Regular expression describing the name of a manpage (without section).") (defvar Man-section-regexp "[0-9][a-zA-Z+]*\\|[LNln]" "*Regular expression describing a manpage section within parentheses.") -;; Unless some system actually adds leading whitespace other than one space, -;; it is more reliable not to accept any other leading whitespace. -(defvar Man-heading-regexp "^[ \t]*\\([A-Z][A-Z \t]+\\)$" +(defvar Man-page-header-regexp + (concat "^[ \t]*\\(" Man-name-regexp + "(\\(" Man-section-regexp "\\))\\).*\\1") + "*Regular expression describing the heading of a page.") + +(defvar Man-heading-regexp "^\\([A-Z][A-Z ]+\\)$" "*Regular expression describing a manpage heading entry.") (defvar Man-see-also-regexp "SEE ALSO" @@ -213,7 +229,7 @@ This regular expression should start with a `^' character.") (defvar Man-reference-regexp - "[-a-zA-Z0-9_][-a-zA-Z0-9_.]*\\(([0-9][a-zA-Z+]*)\\)?" + (concat "\\(" Man-name-regexp "\\)(\\(" Man-section-regexp "\\))") "*Regular expression describing a reference in the SEE ALSO section.") (defvar Man-switches "" @@ -238,12 +254,13 @@ (make-variable-buffer-local 'Man-current-page) (make-variable-buffer-local 'Man-page-mode-string) (make-variable-buffer-local 'Man-original-frame) +(make-variable-buffer-local 'Man-arguments) (setq-default Man-sections-alist nil) (setq-default Man-refpages-alist nil) (setq-default Man-page-list nil) (setq-default Man-current-page 0) -(setq-default Man-page-mode-string "1 (of 1)") +(setq-default Man-page-mode-string "1 of 1") (if Man-mode-map nil @@ -255,14 +272,16 @@ (define-key Man-mode-map "p" 'Man-previous-section) (define-key Man-mode-map "\en" 'Man-next-manpage) (define-key Man-mode-map "\ep" 'Man-previous-manpage) - (define-key Man-mode-map "," 'beginning-of-buffer) - (define-key Man-mode-map "." 'end-of-buffer) + (define-key Man-mode-map ">" 'end-of-buffer) + (define-key Man-mode-map "<" 'beginning-of-buffer) + (define-key Man-mode-map "." 'beginning-of-buffer) (define-key Man-mode-map "r" 'Man-follow-manual-reference) (define-key Man-mode-map "t" 'toggle-truncate-lines) (define-key Man-mode-map "g" 'Man-goto-section) (define-key Man-mode-map "s" 'Man-goto-see-also-section) + (define-key Man-mode-map "k" 'Man-kill) (define-key Man-mode-map "q" 'Man-quit) - (define-key Man-mode-map "m" 'manual-entry) + (define-key Man-mode-map "m" 'man) (define-key Man-mode-map "?" 'describe-mode) ) @@ -270,205 +289,255 @@ ;; ====================================================================== ;; utilities -(defun Man-page-mode-string () - "Formats part of the mode line for Man mode." - (format "%d (of %d)" Man-current-page (length Man-page-list))) +(defsubst Man-init-defvars () + "Used for initialising variables based on the value of window-system. +This is necessary if one wants to dump man.el with emacs." + + (defvar Man-fontify-manpage-flag t + "*Make up the manpage with fonts.") + ;; The following is necessary until fonts are implemented on + ;; terminals. + (setq Man-fontify-manpage-flag (and Man-fontify-manpage-flag + window-system)) + + (defconst Man-uses-untabify-flag t + ;; don't use pr: it is buggy + ;; (or (not (file-readable-p "/etc/passwd")) + ;; (/= 0 (apply 'call-process + ;; Man-untabify-command nil nil nil + ;; (append Man-untabify-command-args + ;; (list "/etc/passwd"))))) + "Use `untabify', because Man-untabify-command cannot do that.") + + (defconst Man-sed-script + (cond + (Man-fontify-manpage-flag + nil) + ((= 0 (call-process Man-sed-command nil nil nil Man-sysv-sed-script)) + Man-sysv-sed-script) + ((= 0 (call-process Man-sed-command nil nil nil Man-berkeley-sed-script)) + Man-berkeley-sed-script) + (t + nil)) + "Script for sed to nuke backspaces and ANSI codes from manpages.") -(defun Man-delete-trailing-newline (str) - (if (string= (substring str (1- (length str))) "\n") - (substring str 0 (1- (length str))) - str)) + (defvar Man-filter-list + (list + (cons + Man-sed-command + (list + (if Man-sed-script + (concat "-e '" Man-sed-script "'") + "") + "-e '/\e[789]/s///g'" + "-e '/o\b+/s//o/g'" + "-e '/^\\n$/D'" + "-e '/[Nn]o such file or directory/d'" + "-e '/Reformatting page. Wait/d'" + "-e '/Reformatting entry. Wait/d'" + "-e '/^[ \t]*Hewlett-Packard[ \t]Company[ \t]*-[ \t][0-9]*[ \t]-/d'" + "-e '/^[ \t]*Hewlett-Packard[ \t]*-[ \t][0-9]*[ \t]-.*$/d'" + "-e '/^[ \t][ \t]*-[ \t][0-9]*[ \t]-[ \t]*Formatted:.*[0-9]$/d'" + "-e '/^[ \t]*Page[ \t][0-9]*.*(printed[ \t][0-9\\/]*)$/d'" + "-e '/^Printed[ \t][0-9].*[0-9]$/d'" + "-e '/^[ \t]*X[ \t]Version[ \t]1[01].*Release[ \t][0-9]/d'" + "-e '/^[A-za-z].*Last[ \t]change:/d'" + "-e '/^Sun[ \t]Release[ \t][0-9].*[0-9]$/d'" + )) + (cons + Man-awk-command + (list + "'\n" + "BEGIN { blankline=0; anonblank=0; }\n" + "/^$/ { if (anonblank==0) next; }\n" + "{ anonblank=1; }\n" + "/^$/ { blankline++; next; }\n" + "{ if (blankline>0) { print \"\"; blankline=0; } print $0; }\n" + "'" + )) + (if (not Man-uses-untabify-flag) + (cons + Man-untabify-command + Man-untabify-command-args) + )) + "*Manpage cleaning filter command phrases. +This variable contains a list of the following form: -(defun Man-build-man-command () +'((command-string phrase-string*)*) + +Each phrase-string is concatenated onto the command-string to form a +command filter. The (standard) output (and standard error) of the Un*x +man command is piped through each command filter in the order the +commands appear in the association list. The final output is placed in +the manpage buffer.") +) + +(defsubst Man-make-page-mode-string () + "Formats part of the mode line for Man mode." + (format "%s page %d of %d" + (or (nth 2 (nth (1- Man-current-page) Man-page-list)) + "") + Man-current-page + (length Man-page-list))) + +(defsubst Man-build-man-command () "Builds the entire background manpage and cleaning command." (let ((command (concat manual-program " " Man-switches " %s 2>/dev/null")) (flist Man-filter-list)) - (while flist + (while (and flist (car flist)) (let ((pcom (car (car flist))) - (pargs (car (cdr (car flist))))) - (setq flist (cdr flist)) - (if (or (not (stringp pcom)) - (not (listp pargs))) - (error "Malformed Man-filter-list")) - (setq command (concat command " | " pcom - (mapconcat '(lambda (phrase) phrase) - pargs " "))))) + (pargs (cdr (car flist)))) + (setq command + (concat command " | " pcom " " + (mapconcat '(lambda (phrase) + (if (not (stringp phrase)) + (error "Malformed Man-filter-list")) + phrase) + pargs " "))) + (setq flist (cdr flist)))) command)) -(defun Man-downcase (man-args) - "Downcases section letters in MAN-ARGS." - (let ((newargs "") - (s 0) - mstart mend - (len (length man-args))) - (while (and (< s len) - (setq mstart (string-match Man-section-regexp man-args s))) - (setq mend (match-end 0) - newargs (concat newargs (substring man-args s mstart))) - (setq newargs (concat newargs (downcase - (substring man-args mstart mend))) - s mend)) - (concat newargs (substring man-args s len)))) - (defun Man-translate-references (ref) - "Translates REF from \"chmod(2V)\" to \"2v chmod\" style." - (if (string-match (concat "(" Man-section-regexp ")$") ref) - (let* ((word (progn (string-match "(" ref) - (substring ref 0 (1- (match-end 0))))) - (section-re (concat "(\\(" Man-section-regexp "\\))")) - (section (if (string-match section-re ref) - (substring ref (match-beginning 1) (match-end 1)) - "")) - (slist Man-section-translations-alist) - ) - (if Man-downcase-section-letters-p - (setq section (Man-downcase section))) - (while slist - (let ((s1 (car (car slist))) - (s2 (cdr (car slist)))) - (setq slist (cdr slist)) - (if Man-downcase-section-letters-p - (setq s1 (Man-downcase s1))) - (if (not (string= s1 section)) nil - (setq section (if Man-downcase-section-letters-p - (Man-downcase s2) - s2) - slist nil)))) - (concat Man-specified-section-option section " " word)) - ref)) + "Translates REF from \"chmod(2V)\" to \"2v chmod\" style. +Leave it as is if already in that style. Possibly downcase and +translate the section (see the Man-downcase-section-letters-flag +and the Man-section-translations-alist variables)." + (let ((name "") + (section "") + (slist Man-section-translations-alist)) + (cond + ;; "chmod(2V)" case ? + ((string-match (concat Man-reference-regexp "$") ref) + (setq name (Man-match-substring 1 ref) + section (Man-match-substring 2 ref))) + ;; "2v chmod" case ? + ((string-match (concat "\\(" Man-section-regexp + "\\) +\\(" Man-name-regexp "\\)$") ref) + (setq name (Man-match-substring 2 ref) + section (Man-match-substring 1 ref)))) + (if (string= name "") + ref ; Return the reference as is + (if Man-downcase-section-letters-flag + (setq section (downcase section))) + (while slist + (let ((s1 (car (car slist))) + (s2 (cdr (car slist)))) + (setq slist (cdr slist)) + (if Man-downcase-section-letters-flag + (setq s1 (downcase s1))) + (if (not (string= s1 section)) nil + (setq section (if Man-downcase-section-letters-flag + (downcase s2) + s2) + slist nil)))) + (concat Man-specified-section-option section " " name)))) -(defun Man-linepos (&optional position col-p) - "Return the character position at various line/buffer positions. -Preserves the state of point, mark, etc. Optional arg POSITION can be one -of the following symbols: - bol == beginning of line - boi == beginning of indentation - eol == end of line [default] - bob == beginning of buffer - eob == end of buffer - -Optional arg COL-P, if non-nil, means to return -the current column instead of character position." - (let ((tpnt (point)) - rval) - (cond - ((eq position 'bol) (beginning-of-line)) - ((eq position 'boi) (back-to-indentation)) - ((eq position 'bob) (goto-char (point-min))) - ((eq position 'eob) (goto-char (point-max))) - (t (end-of-line))) - (setq rval (if col-p (current-column) (point))) - (goto-char tpnt) - rval)) +(defsubst Man-match-substring (&optional n string) + "Return the substring matched by the last search. +Optional arg N means return the substring matched by the Nth paren +grouping. Optinal second arg STRING means return a substring from +that string instead of from the current buffer." + (if (null n) (setq n 0)) + (if string + (substring string (match-beginning n) (match-end n)) + (buffer-substring (match-beginning n) (match-end n)))) ;; ====================================================================== -;; default man entry and get word under point +;; default man entry: get word under point -(defun Man-default-man-args (manword) - "Build the default man args from MANWORD and buffer's major mode." - (let ((sections (cdr (assq major-mode Man-auto-section-alist)))) - (cond - ((null sections) manword) - ((consp sections) - (mapconcat (lambda (n) (concat Man-specified-section-option - n " " manword)) - sections " ")) - (t - (concat sections " " manword))))) - -(defun Man-default-man-entry () +(defsubst Man-default-man-entry () "Make a guess at a default manual entry. This guess is based on the text surrounding the cursor, and the default section number is selected from `Man-auto-section-alist'." (let (default-title) (save-excursion - ;; Default man entry title is any word the cursor is on, - ;; or if cursor not on a word, then nearest preceding - ;; word. - (and (not (looking-at "[a-zA-Z_]")) - (skip-chars-backward "^a-zA-Z_")) - (skip-chars-backward "(a-zA-Z_0-9") - (and (looking-at "(") (forward-char 1)) + ;; Default man entry title is any word the cursor is on, or if + ;; cursor not on a word, then nearest preceding word. Cannot + ;; use the current-word function because it skips the dots. + (if (not (looking-at "[-a-zA-Z_.]")) + (skip-chars-backward "^a-zA-Z")) + (skip-chars-backward "-(a-zA-Z_0-9_.") + (if (looking-at "(") (forward-char 1)) (setq default-title (buffer-substring (point) - (progn (skip-chars-forward "a-zA-Z0-9_") (point)))) + (progn (skip-chars-forward "-a-zA-Z0-9_.") (point)))) - ;; If looking at something like ioctl(2) or brc(1M), include - ;; section number in default-entry - (if (looking-at "[ \t]*([ \t]*[0-9][a-zA-Z]?[ \t]*)") - (progn (skip-chars-forward "^0-9") - (setq default-title - (concat Man-specified-section-option - (buffer-substring - (point) - (progn - (skip-chars-forward "0-9a-zA-Z") - (point))) - " " - default-title))) - (setq default-title (Man-default-man-args default-title))) - default-title))) - + ;; If looking at something like ioctl(2) or brc(1M), include the + ;; section number in the returned value. + (concat + default-title + (if (looking-at + (concat "[ \t]*([ \t]*\\(" Man-section-regexp "\\)[ \t]*)")) + (format "(%s)" (Man-match-substring 1))) + )))) + ;; ====================================================================== -;; top level command and background process sentinel +;; Top level command and background process sentinel -;;; This alias makes completion more predictable if ignoring case. +;; For compatibility with older versions. ;;;###autoload -(defalias 'man 'manual-entry) +(defalias 'manual-entry 'man) ;;;###autoload -(defun manual-entry (arg) +(defun man (man-args prefix-arg) "Get a Un*x manual page and put it in a buffer. This command is the top-level command in the man package. It runs a Un*x command to retrieve and clean a manpage in the background and places the results in a Man mode (manpage browsing) buffer. See variable -`Man-notify' for what happens when the buffer is ready. +`Man-notify-flag' for what happens when the buffer is ready. Normally, if a buffer already exists for this man page, it will display -immediately; either a prefix argument or a nil value to `Man-reuse-okay-p' +immediately; either a prefix argument or a nil value to `Man-reuse-okay-flag' overrides this and forces the man page to be regenerated." - (interactive "P") - (let* ((default-entry (Man-default-man-entry)) - (man-args - (read-string (format "Manual-entry: %s" - (if (string= default-entry "") "" - (format "(default: %s) " - default-entry)))))) - (and (string= man-args "") - (if (string= default-entry "") - (error "No man args given") - (setq man-args default-entry))) + (interactive + (list + ;; first argument + (let* ((default-entry (Man-default-man-entry)) + (input (read-string + (format "Manual entry%s: " + (if (string= default-entry "") + "" + (format " (default %s)" default-entry)))))) + (if (string= input "") + (if (string= default-entry "") + (error "No man args given") + default-entry) + input)) + ;; second argument + current-prefix-arg)) - ;; Recognize the subject(section) syntax. - (setq man-args (Man-translate-references man-args)) + ;; Init the man package variables, if not already done. + (Man-init-defvars) - ;; This is apparently already done correctly via Man-translate-references. - ;; (if Man-downcase-section-letters-p - ;; (setq man-args (Man-downcase man-args))) - (Man-getpage-in-background man-args (consp arg)) - )) + ;; Possibly translate the "subject(section)" syntax into the + ;; "section subject" syntax and possibly downcase the section. + (setq man-args (Man-translate-references man-args)) + + (Man-getpage-in-background man-args (consp prefix-arg))) + (defun Man-getpage-in-background (topic &optional override-reuse-p) "Uses TOPIC to build and fire off the manpage and cleaning command. Optional OVERRIDE-REUSE-P, when non-nil, means to start a background process even if a buffer already exists and -`Man-reuse-okay-p' is non-nil." +`Man-reuse-okay-flag' is non-nil." (let* ((man-args topic) (bufname (concat "*man " man-args "*")) (buffer (get-buffer bufname))) - (if (and Man-reuse-okay-p + (if (and Man-reuse-okay-flag (not override-reuse-p) buffer) (Man-notify-when-ready buffer) (require 'env) - (message "Invoking %s %s in background" manual-program man-args) + (message "Invoking %s %s in the background" manual-program man-args) (setq buffer (generate-new-buffer bufname)) (save-excursion (set-buffer buffer) - (setq Man-original-frame (selected-frame))) + (setq Man-original-frame (selected-frame)) + (setq Man-arguments man-args)) (let ((process-environment (copy-sequence process-environment))) ;; Prevent any attempt to use display terminal fanciness. (setenv "TERM" "dumb") @@ -480,71 +549,119 @@ (defun Man-notify-when-ready (man-buffer) "Notify the user when MAN-BUFFER is ready. -See the variable `Man-notify' for the different notification behaviors." +See the variable `Man-notify-flag' for the different notification behaviors." (let ((saved-frame (save-excursion (set-buffer man-buffer) Man-original-frame))) (cond - ((eq Man-notify 'newframe) - ;; Since we run asynchronously, perhaps while Emacs is waiting for input, - ;; we must not leave a different buffer current. - ;; We can't rely on the editor command loop to reselect - ;; the selected window's buffer. + ((eq Man-notify-flag 'newframe) + ;; Since we run asynchronously, perhaps while Emacs is waiting + ;; for input, we must not leave a different buffer current. We + ;; can't rely on the editor command loop to reselect the + ;; selected window's buffer. (save-excursion (set-buffer man-buffer) (make-frame Man-frame-parameters))) - ((eq Man-notify 'bully) + ((eq Man-notify-flag 'pushy) + (switch-to-buffer man-buffer)) + ((eq Man-notify-flag 'bully) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (pop-to-buffer man-buffer) (delete-other-windows)) - ((eq Man-notify 'aggressive) + ((eq Man-notify-flag 'aggressive) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (pop-to-buffer man-buffer)) - ((eq Man-notify 'friendly) + ((eq Man-notify-flag 'friendly) (and window-system (frame-live-p saved-frame) (select-frame saved-frame)) (display-buffer man-buffer 'not-this-window)) - ((eq Man-notify 'polite) + ((eq Man-notify-flag 'polite) (beep) - (message "Manual buffer %s is ready." (buffer-name man-buffer))) - ((eq Man-notify 'quiet) - (message "Manual buffer %s is ready." (buffer-name man-buffer))) - ((or (eq Man-notify 'meek) + (message "Manual buffer %s is ready" (buffer-name man-buffer))) + ((eq Man-notify-flag 'quiet) + (message "Manual buffer %s is ready" (buffer-name man-buffer))) + ((or (eq Man-notify-flag 'meek) t) (message "")) ))) -(defun Man-set-fonts () +(defun Man-fontify-manpage () + "Convert overstriking and underlining to the correct fonts. +Same for the ANSI bold and normal escape sequences." + (interactive) + (message "Please wait: making up the %s man page..." Man-arguments) + (goto-char (point-min)) + (while (search-forward "\e[1m" nil t) + (delete-backward-char 4) + (put-text-property (point) + (progn (if (search-forward "\e[0m" nil 'move) + (delete-backward-char 4)) + (point)) + 'face 'bold)) + (goto-char (point-min)) + (while (search-forward "_\b" nil t) + (backward-delete-char 2) + (put-text-property (point) (1+ (point)) 'face 'underline)) + (goto-char (point-min)) + (while (search-forward "\b_" nil t) + (backward-delete-char 2) + (put-text-property (1- (point)) (point) 'face 'underline)) + (goto-char (point-min)) + (while (re-search-forward "\e[789]" nil t) + (backward-delete-char 2)) (goto-char (point-min)) - (while (re-search-forward "\\(.\b\\)+" nil t) - (let ((st (match-beginning 0)) (en (match-end 0))) - (goto-char st) - (if window-system - (put-text-property st (if (= en (point-max)) en (1+ en)) 'face - (if (looking-at "_") 'underline 'bold))) - (while (and (< (point) en) (looking-at ".\b")) - (replace-match ""))))) + (while (search-forward "o\b+" nil t) + (backward-delete-char 2) + (put-text-property (1- (point)) (point) 'face 'bold)) + (goto-char (point-min)) + (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t) + (replace-match "\\1") + (put-text-property (1- (point)) (point) 'face 'bold)) + (message "%s man page made up" Man-arguments)) + +(defun Man-cleanup-manpage () + "Remove overstriking and underlining from the current buffer." + (interactive) + (message "Please wait: cleaning up the %s man page..." Man-arguments) + (goto-char (point-min)) + (while (re-search-forward "\e\\[[0-9]+m" nil t) (replace-match "")) + (goto-char (point-min)) + (while (search-forward "_\b" nil t) (backward-delete-char 2)) + (goto-char (point-min)) + (while (search-forward "\b_" nil t) (backward-delete-char 2)) + (goto-char (point-min)) + (while (re-search-forward "\e[789]" nil t) (backward-delete-char 2)) + (goto-char (point-min)) + (while (search-forward "o\b+" nil t) (backward-delete-char 2)) + (goto-char (point-min)) + (while (re-search-forward "\\(.\\)\\(\b\\1\\)+" nil t) + (replace-match "\\1")) + (message "%s man page cleaned up" Man-arguments)) (defun Man-bgproc-sentinel (process msg) "Manpage background process sentinel." (let ((Man-buffer (process-buffer process)) (delete-buff nil) (err-mess nil)) + (if (null (buffer-name Man-buffer)) ;; deleted buffer (set-process-buffer process nil) - (save-match-data - (save-excursion - (set-buffer Man-buffer) - (goto-char (point-min)) + + (save-excursion + (set-buffer Man-buffer) + (save-match-data (let ((case-fold-search nil)) + (goto-char (point-min)) (cond ((or (looking-at "No \\(manual \\)*entry for") (looking-at "[^\n]*: nothing appropriate$")) - (setq err-mess (buffer-substring (point) (Man-linepos 'eol)) + (setq err-mess (buffer-substring (point) + (progn + (end-of-line) (point))) delete-buff t)) ((not (and (eq (process-status process) 'exit) (= (process-exit-status process) 0))) @@ -556,21 +673,26 @@ (substring msg 0 eos) msg)))) (goto-char (point-max)) (insert (format "\nprocess %s" msg)) - )))) - (if delete-buff - (kill-buffer Man-buffer) - (save-window-excursion - (save-excursion - (set-buffer Man-buffer) - (let ((case-fold-search nil)) - (Man-set-fonts) - (run-hooks 'Man-cooked-hook) - (Man-mode)) - (set-buffer-modified-p nil))) - (Man-notify-when-ready Man-buffer)) + )) + (if delete-buff + (kill-buffer Man-buffer) + (if Man-fontify-manpage-flag + (Man-fontify-manpage) + (if (not Man-sed-script) + (Man-cleanup-manpage))) + (run-hooks 'Man-cooked-hook) + (Man-mode) + (set-buffer-modified-p nil) + )) + ;; Restore case-fold-search before calling + ;; Man-notify-when-ready because it may switch buffers. - (if err-mess - (error err-mess)))))) + (if (not delete-buff) + (Man-notify-when-ready Man-buffer)) + + (if err-mess + (error err-mess)) + ))))) ;; ====================================================================== @@ -582,7 +704,7 @@ The following man commands are available in the buffer. Try \"\\[describe-key] <key> RET\" for more information: -\\[manual-entry] Prompt to retrieve a new manpage. +\\[man] Prompt to retrieve a new manpage. \\[Man-follow-manual-reference] Retrieve reference in SEE ALSO section. \\[Man-next-manpage] Jump to next manpage in circular list. \\[Man-previous-manpage] Jump to previous manpage in circular list. @@ -590,16 +712,17 @@ \\[Man-previous-section] Jump to previous manpage section. \\[Man-goto-section] Go to a manpage section. \\[Man-goto-see-also-section] Jumps to the SEE ALSO manpage section. -\\[Man-quit] Deletes the manpage, its buffer, and window. +\\[Man-quit] Deletes the manpage window, bury its buffer. +\\[Man-kill] Deletes the manpage window, kill its buffer. \\[describe-mode] Prints this help text. The following variables may be of some use. Try \"\\[describe-variable] <variable-name> RET\" for more information: -Man-notify What happens when manpage formatting is done. -Man-reuse-okay-p Okay to reuse already formatted buffer? -Man-downcase-section-letters-p Force section letters to lower case? -Man-circular-pages-p Multiple manpage list treated as circular? +Man-notify-flag What happens when manpage formatting is done. +Man-reuse-okay-flag Reuse already formatted buffer. +Man-downcase-section-letters-flag Force section letters to lower case. +Man-circular-pages-flag Treat multiple manpage list as circular. Man-auto-section-alist List of major modes and their section numbers. Man-section-translations-alist List of section numbers and their Un*x equiv. Man-filter-list Background manpage filter command. @@ -625,22 +748,22 @@ (buffer-disable-undo (current-buffer)) (auto-fill-mode -1) (use-local-map Man-mode-map) - (goto-char (point-min)) (Man-build-page-list) + (Man-strip-page-headers) + (Man-unindent) (Man-goto-page 1) (run-hooks 'Man-mode-hook)) -(defun Man-build-section-alist () +(defsubst Man-build-section-alist () "Build the association list of manpage sections." (setq Man-sections-alist nil) (goto-char (point-min)) (let ((case-fold-search nil)) (while (re-search-forward Man-heading-regexp (point-max) t) - (aput 'Man-sections-alist - (buffer-substring (match-beginning 1) (match-end 1))) + (aput 'Man-sections-alist (Man-match-substring 1)) (forward-line 1)))) -(defun Man-build-references-alist () +(defsubst Man-build-references-alist () "Build the association list of references (in the SEE ALSO section)." (setq Man-refpages-alist nil) (save-excursion @@ -657,38 +780,110 @@ (back-to-indentation) (while (and (not (eobp)) (/= (point) runningpoint)) (setq runningpoint (point)) - (let* ((eow (re-search-forward Man-reference-regexp end t)) - (word (buffer-substring - (match-beginning 0) (match-end 0))) - (len (1- (length word)))) - (if (not eow) nil - (if hyphenated - (setq word (concat hyphenated word) - hyphenated nil)) - (if (= (aref word len) ?-) - (setq hyphenated (substring word 0 len)) - (aput 'Man-refpages-alist word)))) + (if (re-search-forward Man-reference-regexp end t) + (let* ((word (Man-match-substring 0)) + (len (1- (length word)))) + (if hyphenated + (setq word (concat hyphenated word) + hyphenated nil)) + (if (= (aref word len) ?-) + (setq hyphenated (substring word 0 len)) + (aput 'Man-refpages-alist word)))) (skip-chars-forward " \t\n,"))))))) -(defun Man-build-page-list () +(defsubst Man-build-page-list () "Build the list of separate manpages in the buffer." (setq Man-page-list nil) - (save-excursion - (let ((page-start (Man-linepos 'bob)) - (page-end (Man-linepos 'eob)) - (regexp Man-first-heading-regexp)) - (goto-char (point-min)) - (re-search-forward regexp (point-max) t) - (while (not (eobp)) - (if (re-search-forward regexp (point-max) t) - (progn - (setq page-end (Man-linepos 'bol)) - (end-of-line)) - (goto-char (point-max)) - (setq page-end (point))) - (setq Man-page-list (append Man-page-list - (list (cons page-start page-end))) - page-start page-end) + (let ((page-start (point-min)) + (page-end (point-max)) + (header "")) + (goto-char page-start) + ;; (switch-to-buffer (current-buffer))(debug) + (while (not (eobp)) + (setq header + (if (looking-at Man-page-header-regexp) + (Man-match-substring 1) + nil)) + ;; Go past both the current and the next Man-first-heading-regexp + (if (re-search-forward Man-first-heading-regexp nil 'move 2) + (let ((p (progn (beginning-of-line) (point)))) + ;; We assume that the page header is delimited by blank + ;; lines and that it contains at most one blank line. So + ;; if we back by three blank lines we will be sure to be + ;; before the page header but not before the possible + ;; previous page header. + (search-backward "\n\n" nil t 3) + (if (re-search-forward Man-page-header-regexp p 'move) + (beginning-of-line)))) + (setq page-end (point)) + (setq Man-page-list (append Man-page-list + (list (list (copy-marker page-start) + (copy-marker page-end) + header)))) + (setq page-start page-end) + ))) + +(defsubst Man-strip-page-headers () + "Strip all the page headers but the first from the manpage." + (let ((buffer-read-only nil) + (case-fold-search nil) + (page-list Man-page-list) + (page ()) + (header "")) + (while page-list + (setq page (car page-list)) + (and (nth 2 page) + (goto-char (car page)) + (re-search-forward Man-first-heading-regexp nil t) + (setq header (buffer-substring (car page) (match-beginning 0))) + ;; Since the awk script collapses all successive blank + ;; lines into one, and since we don't want to get rid of + ;; the fast awk script, one must choose between adding + ;; spare blank lines between pages when there were none and + ;; deleting blank lines at page boundaries when there were + ;; some. We choose the first, so we comment the following + ;; line. + ;; (setq header (concat "\n" header))) + (while (search-forward header (nth 1 page) t) + (replace-match ""))) + (setq page-list (cdr page-list))))) + +(defsubst Man-unindent () + "Delete the leading spaces that indent the manpage." + (let ((buffer-read-only nil) + (case-fold-search nil) + (page-list Man-page-list)) + (while page-list + (let ((page (car page-list)) + (indent "") + (nindent 0)) + (narrow-to-region (car page) (car (cdr page))) + (if Man-uses-untabify-flag + (untabify (point-min) (point-max))) + (if (catch 'unindent + (goto-char (point-min)) + (if (not (re-search-forward Man-first-heading-regexp nil t)) + (throw 'unindent nil)) + (beginning-of-line) + (setq indent (buffer-substring (point) + (progn + (skip-chars-forward " ") + (point)))) + (setq nindent (length indent)) + (if (zerop nindent) + (throw 'unindent nil)) + (setq indent (concat indent "\\|$")) + (goto-char (point-min)) + (while (not (eobp)) + (if (looking-at indent) + (forward-line 1) + (throw 'unindent nil))) + (goto-char (point-min))) + (while (not (eobp)) + (or (eolp) + (delete-char nindent)) + (forward-line 1))) + (setq page-list (cdr page-list)) )))) @@ -721,7 +916,7 @@ (let ((curpos (point)) (case-fold-search nil)) (goto-char (point-min)) - (if (re-search-forward (concat "^[ \t]*" section) (point-max) t) + (if (re-search-forward (concat "^" section) (point-max) t) (progn (beginning-of-line) t) (goto-char curpos) nil) @@ -748,7 +943,7 @@ (interactive) (if (not (Man-find-section Man-see-also-regexp)) (error (concat "No " Man-see-also-regexp - " section found in current manpage")))) + " section found in the current manpage")))) (defun Man-follow-manual-reference (arg reference) "Get one of the manpages referred to in the \"SEE ALSO\" section. @@ -756,7 +951,7 @@ Prefix argument ARG is passed to `Man-getpage-in-background'." (interactive (if (not Man-refpages-alist) - (error "No references in current man page") + (error "There are no references in the current man page") (list current-prefix-arg (let* ((default (or (car (all-completions @@ -779,45 +974,64 @@ default chosen))))) (if (not Man-refpages-alist) - (error "No references found in current manpage") + (error "Can't find any references in the current manpage") (aput 'Man-refpages-alist reference) (Man-getpage-in-background (Man-translate-references (aheadsym Man-refpages-alist)) arg))) -(defun Man-quit () +(defun Man-kill () "Kill the buffer containing the manpage." (interactive) (let ((buff (current-buffer))) (delete-windows-on buff) - (kill-buffer buff))) + (kill-buffer buff)) + (if (and window-system + (or (eq Man-notify-flag 'newframe) + (and pop-up-frames + (eq Man-notify-flag 'bully)))) + (delete-frame))) + +(defun Man-quit () + "Bury the buffer containing the manpage." + (interactive) + (let ((buff (current-buffer))) + (delete-windows-on buff) + (bury-buffer buff)) + (if (and window-system + (or (eq Man-notify-flag 'newframe) + (and pop-up-frames + (eq Man-notify-flag 'bully)))) + (delete-frame))) (defun Man-goto-page (page) "Go to the manual page on page PAGE." (interactive - (if (= (length Man-page-list) 1) - (error "You're looking at the only manpage in the buffer.") - (list (read-minibuffer (format "Go to manpage [1-%d]: " - (length Man-page-list)))))) + (if (not Man-page-list) + (let ((args Man-arguments)) + (kill-buffer (current-buffer)) + (error "Can't find the %s manpage" args)) + (if (= (length Man-page-list) 1) + (error "You're looking at the only manpage in the buffer") + (list (read-minibuffer (format "Go to manpage [1-%d]: " + (length Man-page-list))))))) + (if (not Man-page-list) + (let ((args Man-arguments)) + (kill-buffer (current-buffer)) + (error "Can't find the %s manpage" args))) (if (or (< page 1) (> page (length Man-page-list))) (error "No manpage %d found" page)) (let* ((page-range (nth (1- page) Man-page-list)) (page-start (car page-range)) - (page-end (cdr page-range))) + (page-end (car (cdr page-range)))) (setq Man-current-page page - Man-page-mode-string (Man-page-mode-string)) + Man-page-mode-string (Man-make-page-mode-string)) (widen) (goto-char page-start) (narrow-to-region page-start page-end) (Man-build-section-alist) - ;; Don't let bugs in Man-build-references-alist - ;; interfere with ordinary use of this package. - (condition-case nil - (Man-build-references-alist) - (error)) - (widen) - (narrow-to-region page-start page-end) + (Man-build-references-alist) (goto-char (point-min)))) @@ -828,7 +1042,7 @@ (error "This is the only manpage in the buffer")) (if (< Man-current-page (length Man-page-list)) (Man-goto-page (1+ Man-current-page)) - (if Man-circular-pages-p + (if Man-circular-pages-flag (Man-goto-page 1) (error "You're looking at the last manpage in the buffer")))) @@ -839,7 +1053,7 @@ (error "This is the only manpage in the buffer")) (if (> Man-current-page 1) (Man-goto-page (1- Man-current-page)) - (if Man-circular-pages-p + (if Man-circular-pages-flag (Man-goto-page (length Man-page-list)) (error "You're looking at the first manpage in the buffer"))))