;;; woman.el --- browse UN*X manual pages `wo (without) man'
-;; Copyright (C) 2000, 2002 Free Software Foundation, Inc.
+;; Copyright (C) 2000, 2002, 2004, 2005 Free Software Foundation, Inc.
;; Author: Francis J. Wright <F.J.Wright@qmul.ac.uk>
;; Maintainer: Francis J. Wright <F.J.Wright@qmul.ac.uk>
;; Keywords: help, unix
-;; Adapted-By: Eli Zaretskii <eliz@is.elta.co.il>
+;; Adapted-By: Eli Zaretskii <eliz@gnu.org>
;; Version: see `woman-version'
;; URL: http://centaur.maths.qmul.ac.uk/Emacs/WoMan/
;; 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:
;; Alexander Hinds <ahinds@thegrid.net>
;; Stefan Hornburg <sth@hacon.de>
;; Theodore Jump <tjump@cais.com>
+;; David Kastrup <dak@gnu.org>
;; Paul Kinnucan <paulk@mathworks.com>
;; Jonas Linde <jonas@init.se>
;; Andrew McRae <andrewm@optimation.co.nz>
"Return concatenated list of FN applied to successive `car' elements of X.
FN must return a list, cons or nil. Useful for splicing into a list."
;; Based on the Standard Lisp function MAPCAN but with args swapped!
- (and x (nconc (funcall fn (car x)) (woman-mapcan fn (cdr x)))))
+ ;; More concise implementation than the recursive one. -- dak
+ (apply #'nconc (mapcar fn x)))
(defun woman-parse-colon-path (paths)
"Explode search path string PATHS into a list of directory names.
(defcustom woman-bold-headings t
"*If non-nil then embolden section and subsection headings. Default is t.
-Heading emboldening is NOT standard `man' behaviour."
+Heading emboldening is NOT standard `man' behavior."
:type 'boolean
:group 'woman-formatting)
(defcustom woman-ignore t
- "*If non-nil then unrecognised requests etc. are ignored. Default is t.
-This gives the standard ?roff behaviour. If nil then they are left in
+ "*If non-nil then unrecognized requests etc. are ignored. Default is t.
+This gives the standard ?roff behavior. If nil then they are left in
the buffer, which may aid debugging."
:type 'boolean
:group 'woman-formatting)
;; This is overkill! Troff uses just italic; Nroff uses just underline.
;; You should probably select either italic or underline as you prefer, but
;; not both, although italic and underline work together perfectly well!
-(defface woman-italic-face
- `((((background light)) (:slant italic :underline t :foreground "red"))
+(defface woman-italic
+ `((((min-colors 88) (background light))
+ (:slant italic :underline t :foreground "red1"))
+ (((background light)) (:slant italic :underline t :foreground "red"))
(((background dark)) (:slant italic :underline t)))
"Face for italic font in man pages."
:group 'woman-faces)
+;; backward-compatibility alias
+(put 'woman-italic-face 'face-alias 'woman-italic)
-(defface woman-bold-face
- '((((background light)) (:weight bold :foreground "blue"))
+(defface woman-bold
+ '((((min-colors 88) (background light)) (:weight bold :foreground "blue1"))
+ (((background light)) (:weight bold :foreground "blue"))
(((background dark)) (:weight bold :foreground "green2")))
"Face for bold font in man pages."
:group 'woman-faces)
+;; backward-compatibility alias
+(put 'woman-bold-face 'face-alias 'woman-bold)
;; Brown is a good compromise: it is distinguishable from the default
;; but not enough so to make font errors look terrible. (Files that use
;; non-standard fonts seem to do so badly or in idiosyncratic ways!)
-(defface woman-unknown-face
+(defface woman-unknown
'((((background light)) (:foreground "brown"))
+ (((min-colors 88) (background dark)) (:foreground "cyan1"))
(((background dark)) (:foreground "cyan")))
"Face for all unknown fonts in man pages."
:group 'woman-faces)
+;; backward-compatibility alias
+(put 'woman-unknown-face 'face-alias 'woman-unknown)
-(defface woman-addition-face
+(defface woman-addition
'((t (:foreground "orange")))
"Face for all WoMan additions to man pages."
:group 'woman-faces)
+;; backward-compatibility alias
+(put 'woman-addition-face 'face-alias 'woman-addition)
(defun woman-default-faces ()
- "Set foreground colours of italic and bold faces to their default values."
+ "Set foreground colors of italic and bold faces to their default values."
(interactive)
- (face-spec-set 'woman-italic-face
- (face-user-default-spec 'woman-italic-face))
- (face-spec-set 'woman-bold-face (face-user-default-spec 'woman-bold-face)))
+ (face-spec-set 'woman-italic (face-user-default-spec 'woman-italic))
+ (face-spec-set 'woman-bold (face-user-default-spec 'woman-bold)))
(defun woman-monochrome-faces ()
- "Set foreground colours of italic and bold faces to that of the default face.
+ "Set foreground colors of italic and bold faces to that of the default face.
This is usually either black or white."
(interactive)
- (set-face-foreground 'woman-italic-face 'unspecified)
- (set-face-foreground 'woman-bold-face 'unspecified))
+ (set-face-foreground 'woman-italic 'unspecified)
+ (set-face-foreground 'woman-bold 'unspecified))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Experimental font support, initially only for MS-Windows.
symbol-fonts))
(when woman-font-support
- (make-face 'woman-symbol-face)
+ (make-face 'woman-symbol)
;; Set the symbol font only if `woman-use-symbol-font' is true, to
;; avoid unnecessarily upsetting the line spacing in NTEmacs 20.5!
"Set `woman-nospace' to nil."
(setq woman-nospace nil))
-(defconst woman-mode-line-format
- ;; This is essentially the Man-mode format with page numbers removed
- ;; and line numbers added. (Online documents do not have pages, but
- ;; they do have lines!)
- '("-" mode-line-mule-info mode-line-modified
- mode-line-frame-identification mode-line-buffer-identification
- " " global-mode-string
- " %[(WoMan" mode-line-process minor-mode-alist ")%]--"
- (line-number-mode "L%l--")
- (-3 . "%p") "-%-")
- "Mode line format for WoMan buffer.")
-
(defconst woman-request-regexp "^[.'][ \t]*\\(\\S +\\) *"
;; Was "^\\.[ \t]*\\([a-z0-9]+\\) *" but cvs.1 uses a macro named
;; "`" and CGI.man uses a macro named "''"!
should be a topic string and non-nil RE-CACHE forces re-caching."
(interactive (list nil current-prefix-arg))
;; The following test is for non-interactive calls via gnudoit etc.
- (if (or (interactive-p) (not (stringp topic)) (string-match "\\S " topic))
+ (if (or (not (stringp topic)) (string-match "\\S " topic))
(let ((file-name (woman-file-name topic re-cache)))
(if file-name
(woman-find-file file-name)
;; is re-processed by `woman-topic-all-completions-merge'.
(let (dir files (path-index 0)) ; indexing starts at zero
(while path
- (setq dir (car path)
- path (cdr path))
+ (setq dir (pop path))
(if (woman-not-member dir path) ; use each directory only once!
- (setq files
- (nconc files
- (woman-topic-all-completions-1 dir path-index))))
+ (push (woman-topic-all-completions-1 dir path-index)
+ files))
(setq path-index (1+ path-index)))
;; Uniquefy topics:
- (woman-topic-all-completions-merge files)))
+ ;; Concate all lists with a single nconc call to
+ ;; avoid retraversing the first lists repeatedly -- dak
+ (woman-topic-all-completions-merge
+ (apply #'nconc files))))
(defun woman-topic-all-completions-1 (dir path-index)
"Return an alist of the man topics in directory DIR with index PATH-INDEX.
;; unnecessary. So let us assume that `woman-file-regexp' will
;; filter out any directories, which probably should not be there
;; anyway, i.e. it is a user error!
- (mapcar
- (lambda (file)
- (cons
- (file-name-sans-extension
- (if (string-match woman-file-compression-regexp file)
- (file-name-sans-extension file)
- file))
- (if (> woman-cache-level 1)
- (cons
- path-index
- (if (> woman-cache-level 2)
- (cons file nil))))))
- (directory-files dir nil woman-file-regexp)))
+ ;;
+ ;; Don't sort files: we do that when merging, anyway. -- dak
+ (let (newlst (lst (directory-files dir nil woman-file-regexp t))
+ ;; Make an explicit regexp for stripping extension and
+ ;; compression extension: file-name-sans-extension is a
+ ;; far too costly function. -- dak
+ (ext (format "\\(\\.[^.\\/]*\\)?\\(%s\\)?\\'"
+ woman-file-compression-regexp)))
+ ;; Use a loop instead of mapcar in order to avoid the speed
+ ;; penalty of binding function arguments. -- dak
+ (dolist (file lst newlst)
+ (push
+ (cons
+ (if (string-match ext file)
+ (substring file 0 (match-beginning 0))
+ file)
+ (and (> woman-cache-level 1)
+ (cons
+ path-index
+ (and (> woman-cache-level 2)
+ (list file)))))
+ newlst))))
(defun woman-topic-all-completions-merge (alist)
"Merge the alist ALIST so that the keys are unique.
Also make each path-info component into a list.
\(Note that this function changes the value of ALIST.)"
- ;; Intended to be fast by avoiding recursion and list copying.
- (if (> woman-cache-level 1)
- (let ((newalist alist))
- (while newalist
- (let ((tail newalist) (topic (car (car newalist))))
- ;; Make the path-info into a list:
- (setcdr (car newalist) (list (cdr (car newalist))))
- (while tail
- (while (and tail (not (string= topic (car (car (cdr tail))))))
- (setq tail (cdr tail)))
- (if tail ; merge path-info into (car newalist)
- (let ((path-info (cdr (car (cdr tail)))))
- (if (member path-info (cdr (car newalist)))
- ()
- ;; Make the path-info into a list:
- (nconc (car newalist) (list path-info)))
- (setcdr tail (cdr (cdr tail))))
- ))
- (setq newalist (cdr newalist))))
- alist)
+ ;; Replaces unreadably "optimized" O(n^2) implementation.
+ ;; Instead we use sorting to merge stuff efficiently. -- dak
+ (let (elt newalist)
+ ;; Sort list into reverse order
+ (setq alist (sort alist (lambda(x y) (string< (car y) (car x)))))
+ ;; merge duplicate keys.
+ (if (> woman-cache-level 1)
+ (while alist
+ (setq elt (pop alist))
+ (if (equal (car elt) (caar newalist))
+ (unless (member (cdr elt) (cdar newalist))
+ (setcdr (car newalist) (cons (cdr elt)
+ (cdar newalist))))
+ (setcdr elt (list (cdr elt)))
+ (push elt newalist)))
;; woman-cache-level = 1 => elements are single-element lists ...
- (while (and alist (member (car alist) (cdr alist)))
- (setq alist (cdr alist)))
- (if alist
- (let ((newalist alist) cdr_alist)
- (while (setq cdr_alist (cdr alist))
- (if (not (member (car cdr_alist) (cdr cdr_alist)))
- (setq alist cdr_alist)
- (setcdr alist (cdr cdr_alist)))
- )
- newalist))))
+ (while alist
+ (setq elt (pop alist))
+ (unless (equal (car elt) (caar newalist))
+ (push elt newalist))))
+ newalist))
(defun woman-file-name-all-completions (topic)
"Return an alist of the files in all man directories that match TOPIC."
(defsubst woman-dired-define-key-maybe (key)
"If KEY is undefined in Dired, bind it to command `woman-dired-find-file'."
- (if (eq (lookup-key dired-mode-map key) 'undefined)
+ (if (or (eq (lookup-key dired-mode-map key) 'undefined)
+ (null (lookup-key dired-mode-map key)))
(woman-dired-define-key key)))
(defun woman-dired-define-keys ()
(goto-char (point-min))
(while (search-forward "__\b\b" nil t)
(backward-delete-char 4)
- (woman-set-face (point) (1+ (point)) 'woman-italic-face))
+ (woman-set-face (point) (1+ (point)) 'woman-italic))
(goto-char (point-min))
(while (search-forward "\b\b__" nil t)
(backward-delete-char 4)
- (woman-set-face (1- (point)) (point) 'woman-italic-face))))
+ (woman-set-face (1- (point)) (point) 'woman-italic))))
;; Interpret overprinting to indicate bold face:
(goto-char (point-min))
(while (re-search-forward "\\(.\\)\\(\\(\b+\\1\\)+\\)" nil t)
(woman-delete-match 2)
- (woman-set-face (1- (point)) (point) 'woman-bold-face))
+ (woman-set-face (1- (point)) (point) 'woman-bold))
;; Interpret underlining to indicate italic face:
;; (Must be AFTER emboldening to interpret bold _ correctly!)
(goto-char (point-min))
(while (search-forward "_\b" nil t)
(delete-char -2)
- (woman-set-face (point) (1+ (point)) 'woman-italic-face))
+ (woman-set-face (point) (1+ (point)) 'woman-italic))
;; Leave any other uninterpreted ^H's in the buffer for now! (They
;; might indicate composite special characters, which could be
(goto-char (point-min))
(forward-line)
(while (re-search-forward "^\\( \\)?\\([A-Z].*\\)" nil t)
- (woman-set-face (match-beginning 2) (match-end 2) 'woman-bold-face))))
+ (woman-set-face (match-beginning 2) (match-end 2) 'woman-bold))))
)
(defun woman-insert-file-contents (filename compressed)
(defvar woman-mode-map nil "Keymap for woman mode.")
-(if woman-mode-map
- ()
- ;; Set up the keymap, mostly inherited from Man-mode-map. Normally
- ;; button-buffer-map is used as a parent keymap, but we can't have two
- ;; parents, so we just copy it.
- (setq woman-mode-map (copy-keymap button-buffer-map))
+(unless woman-mode-map
+ (setq woman-mode-map (make-sparse-keymap))
(set-keymap-parent woman-mode-map Man-mode-map)
- ;; Above two lines were
- ;; (setq woman-mode-map (cons 'keymap Man-mode-map))
+
(define-key woman-mode-map "R" 'woman-reformat-last-file)
(define-key woman-mode-map "w" 'woman)
(define-key woman-mode-map "\en" 'WoMan-next-manpage)
(setq woman-emulation value)
(woman-reformat-last-file))
+(put 'woman-mode 'mode-class 'special)
+
(defun woman-mode ()
"Turn on (most of) Man mode to browse a buffer formatted by WoMan.
WoMan is an ELisp emulation of much of the functionality of the Emacs
(fset 'Man-unindent 'ignore)
(fset 'Man-goto-page 'ignore)
(unwind-protect
- (progn
- (set (make-local-variable 'Man-mode-map) woman-mode-map)
- ;; Install Man mode:
- (Man-mode)
- ;; Reset inappropriate definitions:
- (setq mode-line-format woman-mode-line-format)
- (put 'Man-mode 'mode-class 'special))
+ (delay-mode-hooks (Man-mode))
;; Restore the status quo:
(fset 'Man-build-page-list Man-build-page-list)
(fset 'Man-strip-page-headers Man-strip-page-headers)
(fset 'Man-unindent Man-unindent)
- (fset 'Man-goto-page Man-goto-page)
- )
- ;; Imenu support:
- (set (make-local-variable 'imenu-generic-expression)
- ;; `make-local-variable' in case imenu not yet loaded!
- woman-imenu-generic-expression)
- (set (make-local-variable 'imenu-space-replacement) " ")
- ;; For reformat ...
- ;; necessary when reformatting a file in its old buffer:
- (setq imenu--last-menubar-index-alist nil)
- ;; necessary to avoid re-installing the same imenu:
- (setq woman-imenu-done nil)
- (if woman-imenu (woman-imenu))
- (setq buffer-read-only nil)
- (Man-highlight-references)
- (setq buffer-read-only t)
- (set-buffer-modified-p nil)))
+ (fset 'Man-goto-page Man-goto-page)))
+ (setq major-mode 'woman-mode
+ mode-name "WoMan")
+ ;; Don't show page numbers like Man-mode does. (Online documents do
+ ;; not have pages)
+ (kill-local-variable 'mode-line-buffer-identification)
+ (use-local-map woman-mode-map)
+ ;; Imenu support:
+ (set (make-local-variable 'imenu-generic-expression)
+ ;; `make-local-variable' in case imenu not yet loaded!
+ woman-imenu-generic-expression)
+ (set (make-local-variable 'imenu-space-replacement) " ")
+ ;; For reformat ...
+ ;; necessary when reformatting a file in its old buffer:
+ (setq imenu--last-menubar-index-alist nil)
+ ;; necessary to avoid re-installing the same imenu:
+ (setq woman-imenu-done nil)
+ (if woman-imenu (woman-imenu))
+ (let (buffer-read-only)
+ (Man-highlight-references))
+ (set-buffer-modified-p nil)
+ (run-mode-hooks 'woman-mode-hook))
(defun woman-imenu (&optional redraw)
"Add a \"Contents\" menu to the menubar.
(around Man-getpage-in-background-advice (topic) activate)
"Use WoMan unless invoked outside a WoMan buffer or invoked explicitly.
Otherwise use Man and record start of formatting time."
- (if (and (eq mode-line-format woman-mode-line-format)
+ (if (and (eq major-mode 'woman-mode)
(not (eq (caar command-history) 'man)))
(WoMan-getpage-in-background topic)
;; Initiates man processing
;; Prepare non-underlined versions of underlined faces:
(woman-non-underline-faces)
- ;; Set font of `woman-symbol-face' to `woman-symbol-font' if
+ ;; Set font of `woman-symbol' face to `woman-symbol-font' if
;; `woman-symbol-font' is well defined.
(and woman-use-symbol-font
(stringp woman-symbol-font)
- (set-face-font 'woman-symbol-face woman-symbol-font
+ (set-face-font 'woman-symbol woman-symbol-font
(and (frame-live-p woman-frame) woman-frame)))
;; Set syntax and display tables:
"^" "_")))
(cond (first
(replace-match repl nil t)
- (put-text-property (1- (point)) (point)
- 'face 'woman-addition-face)
+ (put-text-property (1- (point)) (point) 'face 'woman-addition)
(WoMan-warn
"Initial vertical motion escape \\%s simulated" esc)
(WoMan-log
(setq c (memq (following-char) woman-if-conditions-true)))
;; Unrecognised letter so reject:
((looking-at "[A-Za-z]") (setq c nil)
- (WoMan-warn "%s %s -- unrecognised condition name rejected!"
+ (WoMan-warn "%s %s -- unrecognized condition name rejected!"
request (match-string 0)))
;; Accept strings if identical:
((save-restriction
Set NEWTEXT in face FACE if specified."
(woman-delete-match 0)
(insert-before-markers newtext)
- (if face (put-text-property (1- (point)) (point)
- 'face 'woman-symbol-face))
+ (if face (put-text-property (1- (point)) (point) 'face 'woman-symbol))
t)
(defun woman-special-characters (to)
;; Need symbol font:
(if woman-use-symbol-font
(woman-replace-match (nth 2 replacement)
- 'woman-symbol-face))
+ 'woman-symbol))
;; Need extended font:
(if woman-use-extended-font
(woman-replace-match (nth 2 replacement))))))
(while (< i 256)
(insert (format "\\%03o " i) (string i) " " (string i))
(put-text-property (1- (point)) (point)
- 'face 'woman-symbol-face)
+ 'face 'woman-symbol)
(insert " ")
(setq i (1+ i))
(when (= i 128) (setq i 160) (insert "\n"))
(defconst woman-font-alist
'(("R" . default)
- ("I" . woman-italic-face)
- ("B" . woman-bold-face)
+ ("I" . woman-italic)
+ ("B" . woman-bold)
("P" . previous)
("1" . default)
- ("2" . woman-italic-face)
- ("3" . woman-bold-face) ; used in bash.1
+ ("2" . woman-italic)
+ ("3" . woman-bold) ; used in bash.1
)
"Alist of ?roff font indicators and woman font variables and names.")
;; Paragraph .LP/PP/HP/IP/TP and font .B/.BI etc. macros reset font.
;; Should .SH/.SS reset font?
;; Font size setting macros (?) should reset font.
- (let ((woman-font-alist woman-font-alist) ; for local updating
+ (let ((font-alist woman-font-alist) ; for local updating
(previous-pos (point))
(previous-font 'default)
(current-font 'default))
;; Get font name:
(or font
(let ((fontstring (match-string 0)))
- (setq font (assoc fontstring woman-font-alist)
- ;; NB: woman-font-alist contains VARIABLE NAMES.
+ (setq font (assoc fontstring font-alist)
+ ;; NB: font-alist contains VARIABLE NAMES.
font (if font
(cdr font)
(WoMan-warn "Unknown font %s." fontstring)
;; Output this message once only per call ...
- (setq woman-font-alist
- (cons (cons fontstring 'woman-unknown-face)
- woman-font-alist))
- 'woman-unknown-face)
+ (setq font-alist
+ (cons (cons fontstring 'woman-unknown)
+ font-alist))
+ 'woman-unknown)
)))
;; Delete font control line or escape sequence:
(cond (beg (delete-region beg (point))
))
;; Embolden heading (point is at end of heading):
(woman-set-face
- (save-excursion (beginning-of-line) (point)) (point) 'woman-bold-face)
+ (save-excursion (beginning-of-line) (point)) (point) 'woman-bold)
(forward-line)
(delete-blank-lines)
(setq woman-left-margin woman-default-indent)
;; Optionally embolden heading (point is at beginning of heading):
(if woman-bold-headings
(woman-set-face
- (point) (save-excursion (end-of-line) (point)) 'woman-bold-face))
+ (point) (save-excursion (end-of-line) (point)) 'woman-bold))
(forward-line)
(setq woman-left-margin woman-default-indent
woman-nofill nil) ; fill output lines