;;; woman.el --- browse UN*X manual pages `wo (without) man'
-;; Copyright (C) 2000, 2002, 2003, 2004, 2005,
-;; 2006 Free Software Foundation, Inc.
+;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005,
+;; 2006, 2007 Free Software Foundation, Inc.
;; Author: Francis J. Wright <F.J.Wright@qmul.ac.uk>
;; Maintainer: FSF
(defun woman-Cyg-to-Win (file)
"Convert an absolute filename FILE from Cygwin to Windows form."
- ;; Code taken from w32-symlinks.el
- (if (eq (aref file 0) ?/)
- ;; Try to use Cygwin mount table via `cygpath.exe'.
- (condition-case nil
- (with-temp-buffer
- ;; cygpath -m file
- (call-process "cygpath" nil t nil "-m" file)
- (buffer-substring 1 (buffer-size)))
- (error
- ;; Assume no `cygpath' program available.
- ;; Hack /cygdrive/x/ or /x/ or (obsolete) //x/ to x:/
- (when (string-match "\\`\\(/cygdrive\\|/\\)?/./" file)
- (if (match-string 1) ; /cygdrive/x/ or //x/ -> /x/
- (setq file (substring file (match-end 1))))
- (aset file 0 (aref file 1)) ; /x/ -> xx/
- (aset file 1 ?:)) ; xx/ -> x:/
- file))
- file))
+ ;; MANPATH_MAP conses are not converted since they presumably map
+ ;; Cygwin to Cygwin form.
+ (if (consp file)
+ file
+ ;; Code taken from w32-symlinks.el
+ (if (eq (aref file 0) ?/)
+ ;; Try to use Cygwin mount table via `cygpath.exe'.
+ (condition-case nil
+ (with-temp-buffer
+ ;; cygpath -m file
+ (call-process "cygpath" nil t nil "-m" file)
+ (buffer-substring 1 (buffer-size)))
+ (error
+ ;; Assume no `cygpath' program available.
+ ;; Hack /cygdrive/x/ or /x/ or (obsolete) //x/ to x:/
+ (when (string-match "\\`\\(/cygdrive\\|/\\)?/./" file)
+ (if (match-string 1) ; /cygdrive/x/ or //x/ -> /x/
+ (setq file (substring file (match-end 1))))
+ (aset file 0 (aref file 1)) ; /x/ -> xx/
+ (aset file 1 ?:)) ; xx/ -> x:/
+ file))
+ file)))
\f
;;; User options:
(mapcar 'woman-Cyg-to-Win path)
path))
"*List of dirs to search and/or files to try for man config file.
-A trailing separator (`/' for UNIX etc.) on directories is optional,
-and the filename is used if a directory specified is the first to
-contain the strings \"man\" and \".conf\" (in that order).
-If MANPATH is not set but a config file is found then it is parsed
-instead to provide a default value for `woman-manpath'."
+A trailing separator (`/' for UNIX etc.) on directories is
+optional, and the filename is used if a directory specified is
+the first to start with \"man\" and has an extension starting
+with \".conf\". If MANPATH is not set but a config file is found
+then it is parsed instead to provide a default value for
+`woman-manpath'."
:type '(repeat string)
:group 'woman-interface)
or
MANDATORY_MANPATH /usr/man
or
- OPTIONAL_MANPATH /usr/man"
+ OPTIONAL_MANPATH /usr/man
+or
+ MANPATH_MAP /opt/bin /opt/man"
;; Functionality suggested by Charles Curley.
(let ((path woman-man.conf-path)
file manpath)
(or (not (file-directory-p file))
(and
(setq file
- (directory-files file t "man.*\\.conf" t))
+ (directory-files file t "\\`man.*\\.conf[a-z]*\\'" t))
(file-readable-p (setq file (car file)))))
;; Parse the file -- if no MANPATH data ignore it:
(with-temp-buffer
(while (re-search-forward
;; `\(?: ... \)' is a "shy group"
"\
-^[ \t]*\\(?:MANDATORY_\\|OPTIONAL_\\)?MANPATH[ \t]+\\(\\S-+\\)" nil t)
- (setq manpath (cons (match-string 1) manpath)))
+^[ \t]*\\(?:\\(?:MANDATORY_\\|OPTIONAL_\\)?MANPATH[ \t]+\\(\\S-+\\)\\|\
+MANPATH_MAP[ \t]+\\(\\S-+\\)[ \t]+\\(\\S-+\\)\\)" nil t)
+ (add-to-list 'manpath
+ (if (match-beginning 1)
+ (match-string 1)
+ (cons (match-string 2)
+ (match-string 3)))))
manpath))
))
(setq path (cdr path)))
selected by the value of `woman-manpath-man-regexp'. Non-directory
and unreadable files are ignored.
+Elements can also be a cons cell indicating a mapping from PATH
+to manual trees: if such an element's car is equal to a path
+element of the environment variable PATH, the cdr of the cons
+cell is included in the directory tree search.
+
If not set then the environment variable MANPATH is used. If no such
environment variable is found, the default list is determined by
consulting the man configuration file if found, which is determined by
The MANPATH environment variable may be set using DOS semi-colon-
separated or UN*X/Cygwin colon-separated syntax (but not mixed)."
- :type '(repeat string)
+ :type '(repeat (choice string (cons string string)))
:group 'woman-interface)
(defcustom woman-manpath-man-regexp "[Mm][Aa][Nn]"
Called both to generate and to check the cache!"
;; Must use substituted paths because values of env vars may change!
(list woman-cache-level
- (mapcar 'substitute-in-file-name woman-manpath)
+ (let (lst path)
+ (dolist (dir woman-manpath (nreverse lst))
+ (when (consp dir)
+ (unless path
+ (setq path
+ (split-string (getenv "PATH") path-separator t)))
+ (setq dir (and (member (car dir) path) (cdr dir))))
+ (when dir (add-to-list 'lst (substitute-in-file-name dir)))))
(mapcar 'substitute-in-file-name woman-path)))
(defun woman-read-directory-cache ()
;; Allow each path to be a single string or a list of strings:
(if (not (listp woman-manpath)) (setq woman-manpath (list woman-manpath)))
(if (not (listp woman-path)) (setq woman-path (list woman-path)))
- (let (dir head dirs)
+ (let (dir head dirs path)
(while woman-manpath
(setq dir (car woman-manpath)
woman-manpath (cdr woman-manpath))
+ (when (consp dir)
+ (unless path
+ (setq path (split-string (getenv "PATH") path-separator t)))
+ (setq dir (and (member (car dir) path)
+ (cdr dir))))
(if (and dir (woman-file-readable-p dir))
;; NB: `parse-colon-path' creates null elements for
;; redundant (semi-)colons and trailing `/'s!
;; conditionals and switch source requests:
(woman0-roff-buffer from)
+ ;; Check for macro sets that woman cannot handle. We can only
+ ;; because do this after processing source-switch directives.
+ (goto-char (point-min))
+ (let ((case-fold-search nil))
+ (unless (and (re-search-forward "^\\.SH[ \n]" (point-max) t)
+ (progn (goto-char (point-min))
+ (re-search-forward "^\\.TH[ \n]" (point-max) t))
+ (progn (goto-char (point-min))
+ (not (re-search-forward "^\\.\\([pnil]p\\|sh\\)[ \n]"
+ (point-max) t))))
+ (error "WoMan can only format man pages written with the usual `-man' macros")))
+
;; Process \k escapes BEFORE changing tab width (?):
(goto-char from)
(woman-mark-horizonal-position)
;; Find font requests, paragraph macros and font escapes:
(re-search-forward
"^[.'][ \t]*\\(\\(\\ft\\)\\|\\(.P\\)\\)\\|\\(\\\\f\\)" nil 1)
- (let (font beg notfont)
+ (let (font beg notfont fescape)
;; Match font indicator and leave point at end of sequence:
(cond ((match-string 2)
;; .ft request found
(setq font 'default))
((match-string 4)
;; \f escape found
- (setq beg (match-beginning 0))
+ (setq beg (match-beginning 0)
+ fescape t)
(woman-match-name))
(t (setq notfont t)))
(if notfont
;; Delete font control line or escape sequence:
(cond (beg (delete-region beg (point))
(if (eq font 'previous) (setq font previous-font))))
+ ;; Deal with things like \fB.cvsrc\fR at the start of a line.
+ ;; After removing the font control codes, this would
+ ;; otherwise match woman-request-regexp. The "\\&" which is
+ ;; inserted to prevent this is removed by woman2-process-escapes.
+ (and fescape
+ (looking-at woman-request-regexp)
+ (insert "\\&"))
(woman-set-face previous-pos (point) current-font)
(if beg
;; Explicit font control
(unwind-protect
(while
;; Find next control line:
- (re-search-forward woman-request-regexp nil t)
- (cond
- ;; Construct woman function to call:
- ((setq fn (intern-soft
- (concat "woman2-"
- (setq request (match-string 1)))))
- ;; Delete request or macro name:
- (woman-delete-match 0))
- ;; Unrecognised request:
- ((prog1 nil
- ;; (WoMan-warn ".%s request ignored!" request)
- (WoMan-warn-ignored request "ignored!")
- ;; (setq fn 'woman2-LP)
- ;; AVOID LEAVING A BLANK LINE!
- ;; (setq fn 'woman2-format-paragraphs)
- ))
- ;; .LP assumes it is at eol and leaves a (blank) line,
- ;; so leave point at end of line before paragraph:
- ((or (looking-at "[ \t]*$") ; no argument
- woman-ignore) ; ignore all
- ;; (beginning-of-line) (kill-line)
- ;; AVOID LEAVING A BLANK LINE!
- (beginning-of-line) (woman-delete-line 1))
- (t (end-of-line) (insert ?\n))
- )
- (if (not (or fn
- (and (not (memq (following-char) '(?. ?')))
- (setq fn 'woman2-format-paragraphs))))
- ()
- ;; Find next control line:
- (set-marker to (woman-find-next-control-line))
- ;; Call the appropriate function:
- (funcall fn to)))
+ (re-search-forward woman-request-regexp nil t)
+ (cond
+ ;; Construct woman function to call:
+ ((setq fn (intern-soft
+ (concat "woman2-"
+ (setq request (match-string 1)))))
+ ;; Delete request or macro name:
+ (woman-delete-match 0))
+ ;; Unrecognised request:
+ ((prog1 nil
+ ;; (WoMan-warn ".%s request ignored!" request)
+ (WoMan-warn-ignored request "ignored!")
+ ;; (setq fn 'woman2-LP)
+ ;; AVOID LEAVING A BLANK LINE!
+ ;; (setq fn 'woman2-format-paragraphs)
+ ))
+ ;; .LP assumes it is at eol and leaves a (blank) line,
+ ;; so leave point at end of line before paragraph:
+ ((or (looking-at "[ \t]*$") ; no argument
+ woman-ignore) ; ignore all
+ ;; (beginning-of-line) (kill-line)
+ ;; AVOID LEAVING A BLANK LINE!
+ (beginning-of-line) (woman-delete-line 1))
+ (t (end-of-line) (insert ?\n))
+ )
+ (if (not (or fn
+ (and (not (memq (following-char) '(?. ?')))
+ (setq fn 'woman2-format-paragraphs))))
+ ()
+ ;; Find next control line:
+ (set-marker to (woman-find-next-control-line))
+ ;; Call the appropriate function:
+ (funcall fn to)))
(if (not (eobp)) ; This should not happen, but ...
(woman2-format-paragraphs (copy-marker (point-max) t)
woman-left-margin))
(let ((i (woman2-get-prevailing-indent 'leave-eol)))
(beginning-of-line)
(woman-leave-blank-lines) ; must be here,
- (woman2-tagged-paragraph to i))))
+ ;;
+ ;; The cvs.1 manpage contains some (possibly buggy) syntax that
+ ;; confuses woman, although the man program displays it ok.
+ ;; Most problems are caused by IP followed by another request on
+ ;; the next line. Without the following hack, the second request
+ ;; gets displayed raw in the output. Note that
+ ;; woman2-tagged-paragraph also contains a hack for similar
+ ;; issues (eg IP followed by SP).
+ ;;
+ ;; i) For IP followed by one or more IPs, we ignore all but the
+ ;; last (mimic man). The hack in w-t-p would only work for two
+ ;; consecutive IPs, and would use the first.
+ ;; ii) For IP followed by SP followed by one or more requests,
+ ;; do nothing. At least in cvs.1, there is usually another IP in
+ ;; there somewhere.
+ (unless (or (looking-at "^\\.IP")
+ (and (looking-at "^\\.sp")
+ (save-excursion
+ (and (zerop (forward-line 1))
+ (looking-at woman-request-regexp)))))
+ (woman2-tagged-paragraph to i)))))
(defun woman-find-next-control-line-carefully ()
"Find and return start of next control line, even if already there!"
(if (not (looking-at "\\s *$")) ; non-empty tag
(setq woman-leave-blank-lines nil))
- ;; Temporary hack for bash.1 and groff_mmse.7 until code is revised
- ;; to process all requests uniformly:
- (cond ((and (= (point) to) (looking-at "^[.'][ \t]*\\(PD\\|br\\|ta\\) *"))
- (if (string= (match-string 1) "br")
- (woman-delete-line 1)
- (woman-delete-match 0)
- (if (string= (match-string 1) "ta") ; for GetInt.3
- (woman2-ta to)
- (woman-set-interparagraph-distance)))
- (set-marker to (woman-find-next-control-line-carefully))
- ))
+ ;; Temporary hack for bash.1, cvs.1 and groff_mmse.7 until code is revised
+ ;; to process all requests uniformly.
+ ;; This hack deals with IP requests followed by other requests (eg
+ ;; SP) on the very next line. We skip over the SP, otherwise it gets
+ ;; inserted raw in the rendered output.
+ (cond ((and (= (point) to)
+ (looking-at "^[.'][ \t]*\\(PD\\|br\\|ta\\|sp\\) *"))
+ (if (member (match-string 1) '("br" "sp"))
+ (woman-delete-line 1)
+ (woman-delete-match 0)
+ (if (string= (match-string 1) "ta") ; for GetInt.3
+ (woman2-ta to)
+ (woman-set-interparagraph-distance)))
+ (set-marker to (woman-find-next-control-line-carefully))
+ ))
(let ((tag (point)))
(woman-reset-nospace)
(let ((i (woman-get-numeric-arg)))
(woman-delete-line) (or leave-eol (delete-char 1))
;; i = 0 if the argument was not a number
+ ;; FIXME should this be >= 0? How else to reset to 0 indent?
(if (> i 0) (setq woman-prevailing-indent i))))
woman-prevailing-indent)