;;; man.el --- browse UNIX manual pages -*- coding: iso-8859-1 -*-
-;; Copyright (C) 1993, 1994, 1996, 1997, 2001, 2003, 2004 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994, 1996, 1997, 2001, 2002, 2003,
+;; 2004, 2005 Free Software Foundation, Inc.
;; Author: Barry A. Warsaw <bwarsaw@cen.com>
;; Maintainer: FSF
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
\f
;;; Code:
+(eval-when-compile (require 'cl))
(require 'assoc)
(require 'button)
:type 'face
:group 'man)
+(defcustom Man-reverse-face 'highlight
+ "*Face to use when fontifying reverse video."
+ :type 'face
+ :group 'man)
+
;; Use the value of the obsolete user option Man-notify, if set.
(defcustom Man-notify-method (if (boundp 'Man-notify) Man-notify 'friendly)
"*Selects the behavior when manpage is ready.
(defvar Man-cooked-hook nil
"Hook run after removing backspaces but before `Man-mode' processing.")
-(defvar Man-name-regexp "[-a-zA-Z0-9_+][-a-zA-Z0-9_.+]*"
+(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]"
+(defvar Man-section-regexp "[0-9][a-zA-Z0-9+]*\\|[LNln]"
"Regular expression describing a manpage section within parentheses.")
(defvar Man-page-header-regexp
This regular expression should start with a `^' character.")
(defvar Man-reference-regexp
- (concat "\\(" Man-name-regexp "\\)(\\(" Man-section-regexp "\\))")
+ (concat "\\(" Man-name-regexp "\\)[ \t]*(\\(" Man-section-regexp "\\))")
"Regular expression describing a reference to another manpage.")
+(defvar Man-apropos-regexp
+ (concat "\\\[\\(" Man-name-regexp "\\)\\\][ \t]*(\\(" Man-section-regexp "\\))")
+ "Regular expression describing a reference to manpages in \"man -k output\".")
+
(defvar Man-synopsis-regexp "SYNOPSIS"
"Regular expression for SYNOPSIS heading (or your equivalent).
This regexp should not start with a `^' character.")
(make-variable-buffer-local 'Man-page-mode-string)
(make-variable-buffer-local 'Man-original-frame)
(make-variable-buffer-local 'Man-arguments)
+(put 'Man-arguments 'permanent-local t)
(setq-default Man-sections-alist nil)
(setq-default Man-refpages-alist nil)
(let ((table (copy-syntax-table (standard-syntax-table))))
(modify-syntax-entry ?. "w" table)
(modify-syntax-entry ?_ "w" table)
+ (modify-syntax-entry ?: "w" table) ; for PDL::Primitive in Perl man pages
table)
"Syntax table used in Man mode buffers.")
-(if Man-mode-map
- nil
- (setq Man-mode-map (copy-keymap button-buffer-map))
+(unless Man-mode-map
+ (setq Man-mode-map (make-sparse-keymap))
(suppress-keymap Man-mode-map)
+ (set-keymap-parent Man-mode-map button-buffer-map)
+
(define-key Man-mode-map " " 'scroll-up)
(define-key Man-mode-map "\177" 'scroll-down)
(define-key Man-mode-map "n" 'Man-next-section)
(define-key Man-mode-map "k" 'Man-kill)
(define-key Man-mode-map "q" 'Man-quit)
(define-key Man-mode-map "m" 'man)
- (define-key Man-mode-map "?" 'describe-mode)
- )
+ (define-key Man-mode-map "?" 'describe-mode))
;; buttons
-(define-button-type 'Man-xref-man-page
- 'action (lambda (button) (man-follow (button-label button)))
- 'help-echo "RET, mouse-2: display this man page")
+(define-button-type 'Man-abstract-xref-man-page
+ 'follow-link t
+ 'help-echo "mouse-2, RET: display this man page"
+ 'func nil
+ 'action (lambda (button) (funcall
+ (button-get button 'func)
+ (or (button-get button 'Man-target-string)
+ (button-label button)))))
+
+(define-button-type 'Man-xref-man-page
+ :supertype 'Man-abstract-xref-man-page
+ 'func 'man-follow)
+
(define-button-type 'Man-xref-header-file
'action (lambda (button)
(let ((w (button-get button 'Man-target-string)))
(unless (Man-view-header-file w)
(error "Cannot find header file: %s" w))))
+ 'follow-link t
'help-echo "mouse-2: display this header file")
(define-button-type 'Man-xref-normal-file
(view-file f)
(error "Cannot read a file: %s" f))
(error "Cannot find a file: %s" f))))
+ 'follow-link t
'help-echo "mouse-2: display this file")
\f
;; utilities
(defun Man-init-defvars ()
- "Used for initialising variables based on display's color support.
+ "Used for initializing variables based on display's color support.
This is necessary if one wants to dump man.el with Emacs."
;; Avoid possible error in call-process by using a directory that must exist.
(defun Man-translate-references (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)."
+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))
as local filenames.
Return the value of the variable `Man-support-local-filenames'
if it was set to nil or t before the call of this function.
-If t, the man command supports `-l' option. If nil, it don't.
+If t, the man command supports `-l' option. If nil, it doesn't.
Otherwise, if the value of `Man-support-local-filenames'
is neither t nor nil, then determine a new value, set it
to the variable `Man-support-local-filenames' and return
(interactive
(list (let* ((default-entry (Man-default-man-entry))
(input (read-string
- (format "Manual entry%s: "
+ (format "Manual entry%s"
(if (string= default-entry "")
- ""
- (format " (default %s)" default-entry)))
+ ": "
+ (format " (default %s): " default-entry)))
nil nil default-entry)))
(if (string= input "")
(error "No man args given")
(setq buffer (generate-new-buffer bufname))
(save-excursion
(set-buffer buffer)
+ (setq buffer-undo-list t)
(setq Man-original-frame (selected-frame))
(setq Man-arguments man-args))
(let ((process-environment (copy-sequence process-environment))
(interactive)
(message "Please wait: formatting 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 Man-overstrike-face))
- (if (< (buffer-size) (position-bytes (point-max)))
- ;; Multibyte characters exist.
- (progn
- (goto-char (point-min))
- (while (search-forward "__\b\b" nil t)
- (backward-delete-char 4)
- (put-text-property (point) (1+ (point)) 'face Man-underline-face))
- (goto-char (point-min))
- (while (search-forward "\b\b__" nil t)
- (backward-delete-char 4)
- (put-text-property (1- (point)) (point) 'face Man-underline-face))))
- (goto-char (point-min))
- (while (search-forward "_\b" nil t)
- (backward-delete-char 2)
- (put-text-property (point) (1+ (point)) 'face Man-underline-face))
- (goto-char (point-min))
- (while (search-forward "\b_" nil t)
- (backward-delete-char 2)
- (put-text-property (1- (point)) (point) 'face Man-underline-face))
- (goto-char (point-min))
- (while (re-search-forward "\\(.\\)\\(\b+\\1\\)+" nil t)
- (replace-match "\\1")
- (put-text-property (1- (point)) (point) 'face Man-overstrike-face))
- (goto-char (point-min))
- (while (re-search-forward "o\b\\+\\|\\+\bo" nil t)
- (replace-match "o")
- (put-text-property (1- (point)) (point) 'face 'bold))
- (goto-char (point-min))
- (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t)
- (replace-match "+")
- (put-text-property (1- (point)) (point) 'face 'bold))
- (goto-char (point-min))
- ;; Try to recognize common forms of cross references.
- (Man-highlight-references)
- (Man-softhyphen-to-minus)
- (goto-char (point-min))
- (while (re-search-forward Man-heading-regexp nil t)
- (put-text-property (match-beginning 0)
- (match-end 0)
- 'face Man-overstrike-face))
+ ;; Fontify ANSI escapes.
+ (let ((faces nil)
+ (buffer-undo-list t)
+ (start (point)))
+ ;; http://www.isthe.com/chongo/tech/comp/ansi_escapes.html
+ ;; suggests many codes, but we only handle:
+ ;; ESC [ 00 m reset to normal display
+ ;; ESC [ 01 m bold
+ ;; ESC [ 04 m underline
+ ;; ESC [ 07 m reverse-video
+ ;; ESC [ 22 m no-bold
+ ;; ESC [ 24 m no-underline
+ ;; ESC [ 27 m no-reverse-video
+ (while (re-search-forward "\e\\[0?\\([1470]\\|2\\([247]\\)\\)m" nil t)
+ (if faces (put-text-property start (match-beginning 0) 'face
+ (if (cdr faces) faces (car faces))))
+ (setq faces
+ (cond
+ ((match-beginning 2)
+ (delq (case (char-after (match-beginning 2))
+ (?2 Man-overstrike-face)
+ (?4 Man-underline-face)
+ (?7 Man-reverse-face))
+ faces))
+ ((eq (char-after (match-beginning 1)) ?0) nil)
+ (t
+ (cons (case (char-after (match-beginning 1))
+ (?1 Man-overstrike-face)
+ (?4 Man-underline-face)
+ (?7 Man-reverse-face))
+ faces))))
+ (delete-region (match-beginning 0) (match-end 0))
+ (setq start (point))))
+ ;; Other highlighting.
+ (let ((buffer-undo-list t))
+ (if (< (buffer-size) (position-bytes (point-max)))
+ ;; Multibyte characters exist.
+ (progn
+ (goto-char (point-min))
+ (while (search-forward "__\b\b" nil t)
+ (backward-delete-char 4)
+ (put-text-property (point) (1+ (point)) 'face Man-underline-face))
+ (goto-char (point-min))
+ (while (search-forward "\b\b__" nil t)
+ (backward-delete-char 4)
+ (put-text-property (1- (point)) (point) 'face Man-underline-face))))
+ (goto-char (point-min))
+ (while (search-forward "_\b" nil t)
+ (backward-delete-char 2)
+ (put-text-property (point) (1+ (point)) 'face Man-underline-face))
+ (goto-char (point-min))
+ (while (search-forward "\b_" nil t)
+ (backward-delete-char 2)
+ (put-text-property (1- (point)) (point) 'face Man-underline-face))
+ (goto-char (point-min))
+ (while (re-search-forward "\\(.\\)\\(\b+\\1\\)+" nil t)
+ (replace-match "\\1")
+ (put-text-property (1- (point)) (point) 'face Man-overstrike-face))
+ (goto-char (point-min))
+ (while (re-search-forward "o\b\\+\\|\\+\bo" nil t)
+ (replace-match "o")
+ (put-text-property (1- (point)) (point) 'face 'bold))
+ (goto-char (point-min))
+ (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t)
+ (replace-match "+")
+ (put-text-property (1- (point)) (point) 'face 'bold))
+ (goto-char (point-min))
+ ;; Try to recognize common forms of cross references.
+ (Man-highlight-references)
+ (Man-softhyphen-to-minus)
+ (goto-char (point-min))
+ (while (re-search-forward Man-heading-regexp nil t)
+ (put-text-property (match-beginning 0)
+ (match-end 0)
+ 'face Man-overstrike-face)))
(message "%s man page formatted" Man-arguments))
-(defun Man-highlight-references ()
+(defun Man-highlight-references (&optional xref-man-type)
"Highlight the references on mouse-over.
-references include items in the SEE ALSO section,
-header file(#include <foo.h>) and files in FILES"
- (let ((dummy 0))
- (Man-highlight-references0
- Man-see-also-regexp Man-reference-regexp 1 dummy
- 'Man-xref-man-page)
- (Man-highlight-references0
- Man-synopsis-regexp Man-header-regexp 0 2
- 'Man-xref-header-file)
- (Man-highlight-references0
- Man-files-regexp Man-normal-file-regexp 0 0
- 'Man-xref-normal-file)))
-
-(defun Man-highlight-references0 (start-section regexp button-pos target-pos type)
+References include items in the SEE ALSO section,
+header file (#include <foo.h>), and files in FILES.
+If optional argument XREF-MAN-TYPE is non-nil, it used as the
+button type for items in SEE ALSO section. If it is nil, the
+default type, `Man-xref-man-page' is used for the buttons."
+ ;; `Man-highlight-references' is used from woman.el, too.
+ ;; woman.el doesn't set `Man-arguments'.
+ (unless Man-arguments
+ (setq Man-arguments ""))
+ (if (string-match "-k " Man-arguments)
+ (progn
+ (Man-highlight-references0 nil Man-reference-regexp 1 nil
+ (or xref-man-type 'Man-xref-man-page))
+ (Man-highlight-references0 nil Man-apropos-regexp 1
+ (lambda ()
+ (format "%s(%s)"
+ (match-string 1)
+ (match-string 2)))
+ (or xref-man-type 'Man-xref-man-page)))
+ (Man-highlight-references0 Man-see-also-regexp Man-reference-regexp 1 nil
+ (or xref-man-type 'Man-xref-man-page))
+ (Man-highlight-references0 Man-synopsis-regexp Man-header-regexp 0 2
+ 'Man-xref-header-file)
+ (Man-highlight-references0 Man-files-regexp Man-normal-file-regexp 0 0
+ 'Man-xref-normal-file)))
+
+(defun Man-highlight-references0 (start-section regexp button-pos target type)
;; Based on `Man-build-references-alist'
- (when (Man-find-section start-section)
- (forward-line 1)
- (let ((end (save-excursion
- (Man-next-section 1)
- (point))))
- (back-to-indentation)
+ (when (or (null start-section)
+ (Man-find-section start-section))
+ (let ((end (if start-section
+ (progn
+ (forward-line 1)
+ (back-to-indentation)
+ (save-excursion
+ (Man-next-section 1)
+ (point)))
+ (goto-char (point-min))
+ (point-max))))
(while (re-search-forward regexp end t)
(make-text-button
(match-beginning button-pos)
(match-end button-pos)
'type type
- 'Man-target-string (match-string target-pos)
- )))))
+ 'Man-target-string (cond
+ ((numberp target)
+ (match-string target))
+ ((functionp target)
+ (funcall target))
+ (t nil)))))))
(defun Man-cleanup-manpage (&optional interactive)
"Remove overstriking and underlining from the current buffer.
(if Man-fontify-manpage-flag
(Man-fontify-manpage)
(Man-cleanup-manpage))
+
(run-hooks 'Man-cooked-hook)
- (Man-mode)
+ (Man-mode)
+
+ (if (not Man-page-list)
+ (let ((args Man-arguments))
+ (kill-buffer (current-buffer))
+ (error "Can't find the %s manpage" args)))
+
(set-buffer-modified-p nil)
))
;; Restore case-fold-search before calling
;; ======================================================================
;; set up manual mode in buffer and build alists
+(put 'Man-mode 'mode-class 'special)
+
(defun Man-mode ()
"A mode for browsing Un*x manual pages.
The following key bindings are currently in effect in the buffer:
\\{Man-mode-map}"
(interactive)
+ (kill-all-local-variables)
(setq major-mode 'Man-mode
mode-name "Man"
buffer-auto-save-file-name nil
" {" 'Man-page-mode-string "}")
truncate-lines t
buffer-read-only t)
- (buffer-disable-undo (current-buffer))
+ (buffer-disable-undo)
(auto-fill-mode -1)
(use-local-map Man-mode-map)
(set-syntax-table man-mode-syntax-table)
(Man-build-page-list)
(Man-strip-page-headers)
(Man-unindent)
- (Man-goto-page 1)
- (run-hooks 'Man-mode-hook))
+ (Man-goto-page 1 t)
+ (run-mode-hooks 'Man-mode-hook))
(defsubst Man-build-section-alist ()
"Build the association list of manpage sections."
(let* ((default (aheadsym Man-sections-alist))
(completion-ignore-case t)
chosen
- (prompt (concat "Go to section: (default " default ") ")))
+ (prompt (concat "Go to section (default " default "): ")))
(setq chosen (completing-read prompt Man-sections-alist))
(if (or (not chosen)
(string= chosen ""))
(error "There are no references in the current man page")
(list (let* ((default (or
(car (all-completions
- (let ((word (Man-possibly-hyphenated-word)))
+ (let ((word
+ (or (Man-possibly-hyphenated-word)
+ "")))
;; strip a trailing '-':
(if (string-match "-$" word)
(substring word 0
Man-refpages-alist))
(aheadsym Man-refpages-alist)))
chosen
- (prompt (concat "Refer to: (default " default ") ")))
+ (prompt (concat "Refer to (default " default "): ")))
(setq chosen (completing-read prompt Man-refpages-alist))
(if (or (not chosen)
(string= chosen ""))
(interactive)
(quit-window))
-(defun Man-goto-page (page)
+(defun Man-goto-page (page &optional noerror)
"Go to the manual page on page PAGE."
(interactive
(if (not Man-page-list)
- (let ((args Man-arguments))
- (kill-buffer (current-buffer))
- (error "Can't find the %s manpage" args))
+ (error "Not a man page buffer")
(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 (car (cdr page-range))))
- (setq Man-current-page page
- 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)
- (Man-build-references-alist)
- (goto-char (point-min))))
+ (if (and (not Man-page-list) (not noerror))
+ (error "Not a man page buffer"))
+ (when Man-page-list
+ (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 (car (cdr page-range))))
+ (setq Man-current-page page
+ 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)
+ (Man-build-references-alist)
+ (goto-char (point-min)))))
(defun Man-next-manpage ()
(provide 'man)
-;;; arch-tag: 587cda76-8e23-4594-b1f3-89b6b09a0d47
+;; arch-tag: 587cda76-8e23-4594-b1f3-89b6b09a0d47
;;; man.el ends here