;;; i18n-man.el --- browse UNIX English manual pages
;;; $Id: i18n-man.el,v 1.8 1998/09/20 16:26:35 iwasaki Exp $:

;;;  This file core of i18n-man - i18nized man,
;;;  a GNU Emacs front end to browse the UNIX manual pages in various languages.

;;; Everyone is granted permission to copy, modify and redistribute
;;; this program.

;;; HISTORY
;;;
;;;	Written by Mitsuru IWASAKI 10 Sep, 1998 (FreeBSD Japanese man Project)
;;;                Yoshishige Arai (FreeBSD Japanese man Project)
;;;
;;;     Added i18n-man-de (German) code by IWASAKI 17 Sep, 1998
;;;

;;; Suggested USAGE
;;;
;;;  In your ~/.emacs
;;;(autoload 'jman "i18n-man-ja" nil t)
;;;(autoload 'eman "i18n-man-en" nil t)
;;;(autoload 'dman "i18n-man-de" nil t)
;;;  then
;;;  M-x jman
;;;    to get a Japanese manual page thru jman(1) and put it in a buffer.
;;;  M-x eman
;;;    to get a English manual page thru man(1) and put it in a buffer.
;;;  M-x dman
;;;    to get a German manual page thru man(1) and put it in a buffer.

;;; This program was developed based on man.el package of emacs-20.3.
;;; See also Copyright documentation attached below.

;;; man.el --- browse UNIX manual pages

;; Copyright (C) 1993, 1994, 1996, 1997 Free Software Foundation, Inc.

;; Author:		Barry A. Warsaw <bwarsaw@cen.com>
;; Keywords:		help
;; Adapted-By:		ESR, pot

;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; 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.

;;; Commentary:

;; 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
;; manual page buffer for details.

;; ========== Credits and History ========== 
;; 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 everything in one single package, so I decided
;; 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.

;; Nick Duffek <duffek@chaos.cs.brandeis.edu>, posted a very nice
;; improvement which retrieved and cleaned the manpages in a
;; background process, and which correctly deciphered such options as
;; man -k.

;; Eric Rose <erose@jessica.stanford.edu>, submitted manual.el which
;; provided a very nice manual browsing mode.

;; This package was available as `superman.el' from the LCD package
;; for some time before it was accepted into Emacs 19.  The entry
;; 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 idiosyncrasies.

;; ========== Features ==========
;; + Runs "man" in the background and pipes the results through a
;;   series of sed and awk scripts so that all retrieving and cleaning
;;   is done in the background. The cleaning commands are configurable.
;; + Syntax is the same as Un*x man
;; + Functionality is the same as Un*x man, including "man -k" and
;;   "man <section>", etc.
;; + Provides a manual browsing mode with keybindings for traversing
;;   the sections of a manpage, following references in the SEE ALSO
;;   section, and more.
;; + 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 a user option to mean 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)

;; For older versions of Emacs without defgroup and defcustom features
(eval-and-compile
  (condition-case ()
      (require 'custom)
    (error nil))
  (if (and (featurep 'custom) (fboundp 'custom-declare-variable))
      nil ;; We've got what we needed
    ;; We have the old custom-library, hack around it!
    (defmacro defgroup (&rest args)
      nil)
    (defmacro defcustom (var value doc &rest args) 
      (` (defvar (, var) (, value) (, doc))))))

;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
;; empty defvars (keep the compiler quiet)

(defgroup i18n-man nil
  "Browse UNIX manual pages."
  :prefix "i18n-Man-"
  :group 'help)


(defvar i18n-Man-notify)
(defvar i18n-Man-current-page)
(defvar i18n-Man-page-list)
(defcustom i18n-Man-filter-list nil
  "*Manpage cleaning filter command phrases.
This variable contains a list of the following form:

'((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."
  :type '(repeat (list (string :tag "Command String")
		       (repeat :inline t
			       (string :tag "Phrase String"))))
  :group 'i18n-man)

(defvar i18n-Man-original-frame)
(defvar i18n-Man-arguments)
(defvar i18n-Man-sections-alist)
(defvar i18n-Man-refpages-alist)
(defvar i18n-Man-uses-untabify-flag t
  "When non-nil use `untabify' instead of i18n-Man-untabify-command.")
(defvar i18n-Man-page-mode-string)
(defvar i18n-Man-sed-script nil
  "Script for sed to nuke backspaces and ANSI codes from manpages.")

;; for switching environment between different language manpages.
(defvar i18n-manual-lang-depend-program "man")
(defvar i18n-Man-lang-depend-heading-regexp "^\\([^ \t\n_][^0-9\n]+\\)$")
(defvar i18n-Man-lang-depend-see-also-regexp "SEE ALSO")
(defvar i18n-Man-lang-depend-first-heading-regexp "^[ \t]*NAME$\\|^[ \t]*No manual entry fo.*$")
(defvar i18n-Man-lang-depend-cooked-hook nil)
(defvar i18n-Man-lang-depend-process-starting-hook nil)
(defvar i18n-Man-lang-depend-process-started-hook nil)
(defvar i18n-Man-lang-depend-language-string "English")

;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
;; user variables

(defcustom i18n-Man-fontify-manpage-flag t
  "*Make up the manpage with fonts."
  :type 'boolean
  :group 'i18n-man)

(defcustom i18n-Man-overstrike-face 'bold
  "*Face to use when fontifying overstrike."
  :type 'face
  :group 'i18n-man)

(defcustom i18n-Man-underline-face 'underline
  "*Face to use when fontifying underlining."
  :type 'face
  :group 'i18n-man)

;; Use the value of the obsolete user option i18n-Man-notify, if set.
(defcustom i18n-Man-notify-method (if (boundp 'i18n-Man-notify) i18n-Man-notify 'friendly)
  "*Selects the behavior when manpage is ready.
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 `i18n-Man-frame-parameters')
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 the manpage is ready

Any other value of `i18n-Man-notify-method' is equivalent to `meek'."
  :type '(radio (const newframe) (const pushy) (const bully)
		(const aggressive) (const friendly)
		(const polite) (const quiet) (const meek))
  :group 'i18n-man)

(defcustom i18n-Man-frame-parameters nil
  "*Frame parameter list for creating a new frame for a manual page."
  :type 'sexp
  :group 'i18n-man)

(defcustom i18n-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
with the upper case letter.  When this variable is t, the section
letter (e.g., \"2V\") is converted to lowercase (e.g., \"2v\") before
being sent to the man background process."
  :type 'boolean
  :group 'i18n-man)

(defcustom i18n-Man-circular-pages-flag t
  "*If t, the manpage list is treated as circular for traversal."
  :type 'boolean
  :group 'i18-man)

(defcustom i18n-Man-section-translations-alist
  (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."
  :type '(repeat (cons (string :tag "Bogus Section")
		       (string :tag "Real Section")))
  :group 'i18n-man)

(defvar i18n-manual-program "man"
  "The name of the program that produces man pages.")

(defvar i18n-Man-untabify-command "pr"
  "Command used for untabifying.")

(defvar i18n-Man-untabify-command-args (list "-t" "-e")
  "List of arguments to be passed to i18n-Man-untabify-command (which see).")

(defvar i18n-Man-sed-command "sed"
  "Command used for processing sed scripts.")

(defvar i18n-Man-awk-command "awk"
  "Command used for processing awk scripts.")

(defvar i18n-Man-mode-line-format
  '("-"
    mode-line-mule-info
    mode-line-modified
    mode-line-frame-identification
    mode-line-buffer-identification "  "
    global-mode-string
    " " i18n-Man-page-mode-string
    "  %[(" mode-name mode-line-process minor-mode-alist "%n)%]--"
    (line-number-mode "L%l--")
    (column-number-mode "C%c--")
    (-3 . "%p") "-%-")
  "Mode line format for manual mode buffer.")

(defvar i18n-Man-mode-map nil
  "Keymap for i18n-Man mode.")

(defvar i18n-Man-mode-hook nil
  "Hook run when i18n-Man mode is enabled.")

(defvar i18n-Man-cooked-hook nil
  "Hook run after removing backspaces but before i18n-Man-mode processing.")

(defvar i18n-Man-process-starting-hook nil
  "Hook run before starting man process..")

(defvar i18n-Man-process-started-hook nil
  "Hook run after starting man process. Process object is provided as ARG1.")

(defvar i18n-Man-name-regexp "[-a-zA-Z0-9_][-a-zA-Z0-9_.]*"
  "Regular expression describing the name of a manpage (without section).")

(defvar i18n-Man-section-regexp "[0-9][a-zA-Z+]*\\|[LNln]"
  "Regular expression describing a manpage section within parentheses.")

(defvar i18n-Man-page-header-regexp
  (concat "^[ \t]*\\(" i18n-Man-name-regexp
	  "(\\(" i18n-Man-section-regexp "\\))\\).*\\1")
  "Regular expression describing the heading of a page.")

(defvar i18n-Man-heading-regexp "^\\([A-Z][A-Z ]+\\)$"
  "Regular expression describing a manpage heading entry.")

(defvar i18n-Man-see-also-regexp "SEE ALSO"
  "Regular expression for SEE ALSO heading (or your equivalent).
This regexp should not start with a `^' character.")

(defvar i18n-Man-first-heading-regexp "^[ \t]*NAME$\\|^[ \t]*No manual entry fo.*$"
  "Regular expression describing first heading on a manpage.
This regular expression should start with a `^' character.")

(defvar i18n-Man-reference-regexp
  (concat "\\(" i18n-Man-name-regexp "\\)(\\(" i18n-Man-section-regexp "\\))")
  "Regular expression describing a reference in the SEE ALSO section.")

(defvar i18n-Man-switches ""
  "Switches passed to the man command, as a single string.")

(defvar i18n-Man-specified-section-option
  (if (string-match "-solaris[0-9.]*$" system-configuration)
      "-s"
    "")
  "Option that indicates a specified a manual section name.")

(defvar i18n-Man-lanugage-string "English"
  "String describing the language to browse manpage.")

;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;; end user variables

;; other variables and keymap initializations
(make-variable-buffer-local 'i18n-Man-sections-alist)
(make-variable-buffer-local 'i18n-Man-refpages-alist)
(make-variable-buffer-local 'i18n-Man-page-list)
(make-variable-buffer-local 'i18n-Man-current-page)
(make-variable-buffer-local 'i18n-Man-page-mode-string)
(make-variable-buffer-local 'i18n-Man-original-frame)
(make-variable-buffer-local 'i18n-Man-arguments)

(setq-default i18n-Man-sections-alist nil)
(setq-default i18n-Man-refpages-alist nil)
(setq-default i18n-Man-page-list nil)
(setq-default i18n-Man-current-page 0)
(setq-default i18n-Man-page-mode-string "1 of 1")

(defconst i18n-Man-sysv-sed-script "\
/\b/ {	s/_\b//g
	s/\b_//g
        s/o\b+/o/g
        s/+\bo/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.")

(defconst i18n-Man-berkeley-sed-script "\
/\b/ {	s/_\b//g\\
	s/\b_//g\\
        s/o\b+/o/g\\
        s/+\bo/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 i18n-man-mode-syntax-table
  (let ((table (copy-syntax-table (standard-syntax-table))))
    (modify-syntax-entry ?. "w" table)
    (modify-syntax-entry ?_ "w" table)
    table)
  "Syntax table used in i18n-Man mode buffers.")

(if i18n-Man-mode-map
    nil
  (setq i18n-Man-mode-map (make-keymap))
  (suppress-keymap i18n-Man-mode-map)
  (define-key i18n-Man-mode-map " "    'scroll-up)
  (define-key i18n-Man-mode-map "\177" 'scroll-down)
  (define-key i18n-Man-mode-map "n"    'i18n-Man-next-section)
  (define-key i18n-Man-mode-map "p"    'i18n-Man-previous-section)
  (define-key i18n-Man-mode-map "\en"  'i18n-Man-next-manpage)
  (define-key i18n-Man-mode-map "\ep"  'i18n-Man-previous-manpage)
  (define-key i18n-Man-mode-map ">"    'end-of-buffer)
  (define-key i18n-Man-mode-map "<"    'beginning-of-buffer)
  (define-key i18n-Man-mode-map "."    'beginning-of-buffer)
  (define-key i18n-Man-mode-map "r"    'i18n-Man-follow-manual-reference)
  (define-key i18n-Man-mode-map "g"    'i18n-Man-goto-section)
  (define-key i18n-Man-mode-map "s"    'i18n-Man-goto-see-also-section)
  (define-key i18n-Man-mode-map "k"    'i18n-Man-kill)
  (define-key i18n-Man-mode-map "q"    'i18n-Man-quit)
  (define-key i18n-Man-mode-map "m"    'i18n-man-with-current-environment)
  (define-key i18n-Man-mode-map "\r"   'i18n-man-follow)
  (define-key i18n-Man-mode-map "?"    'describe-mode)

  (if (string= window-system "x")
      (if (featurep 'xemacs)
	  (and (not (featurep 'infodock))
	       (define-key i18n-Man-mode-map 'button2 'i18n-Manual-follow-by-click-xemacs))
	(define-key i18n-Man-mode-map [mouse-2] 'i18n-Manual-follow-by-click-emacs)))

  (if (string= window-system "x")
      (if (featurep 'xemacs)
	  (and (not (featurep 'infodock))
	       (define-key i18n-Man-mode-map 'button3 'i18n-Manual-popup-xemacs-menu))
	(define-key i18n-Man-mode-map [down-mouse-3] 'i18n-Manual-popup-emacs-menu)))
  )


;; ======================================================================
;; utilities

(defun i18n-Man-init-defvars ()
  "Used for initialising variables based on the value of window-system.
This is necessary if one wants to dump i18n-man.el with emacs."

  ;; The following is necessary until fonts are implemented on
  ;; terminals.
  (setq i18n-Man-fontify-manpage-flag (and i18n-Man-fontify-manpage-flag
				      window-system))

  ;; Avoid possible error in call-process by using a directory that must exist.
  (let ((default-directory "/"))
    (setq i18n-Man-sed-script
	  (cond
	   (i18n-Man-fontify-manpage-flag
	    nil)
	   ((= 0 (call-process i18n-Man-sed-command nil nil nil i18n-Man-sysv-sed-script))
	    i18n-Man-sysv-sed-script)
	   ((= 0 (call-process i18n-Man-sed-command nil nil nil i18n-Man-berkeley-sed-script))
	    i18n-Man-berkeley-sed-script)
	   (t
	    nil))))

  (setq i18n-Man-filter-list
	(list
	 (cons
	  i18n-Man-sed-command
	  (list
	   (if i18n-Man-sed-script
	       (concat "-e '" i18n-Man-sed-script "'")
	     "")
	   "-e '/^[\001-\032][\001-\032]*$/d'"
	   "-e '/\e[789]/s///g'"
	   "-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'"
	   "-e '/[ \t]*Copyright [0-9]* UNIX System Laboratories, Inc.$/d'"
	   "-e '/^[ \t]*Rev\\..*Page [0-9][0-9]*$/d'"
	   ))
	 (cons
	  i18n-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 i18n-Man-uses-untabify-flag)
	     (cons
	      i18n-Man-untabify-command
	      i18n-Man-untabify-command-args)
	   )))
)

(defsubst i18n-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.  Optional 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))))

(defsubst i18n-Man-make-page-mode-string ()
  "Formats part of the mode line for i18n-Man mode."
  (format "%s page %d of %d"
	  (or (nth 2 (nth (1- i18n-Man-current-page) i18n-Man-page-list))
	      "")
	  i18n-Man-current-page
	  (length i18n-Man-page-list)))

(defsubst i18n-Man-build-man-command ()
  "Builds the entire background manpage and cleaning command."
  (let ((command (concat i18n-manual-program " " i18n-Man-switches
			 ; Stock MS-DOS shells cannot redirect stderr;
			 ; `call-process' below sends it to /dev/null,
			 ; so we don't need `2>' even with DOS shells
			 ; which do support stderr redirection.
			 (if (not (fboundp 'start-process))
			     " %s"
			   " %s 2>/dev/null")))
	(flist i18n-Man-filter-list))
    (while (and flist (car flist))
      (let ((pcom (car (car flist)))
	    (pargs (cdr (car flist))))
	(setq command
	      (concat command " | " pcom " "
		      (mapconcat '(lambda (phrase)
				    (if (not (stringp phrase))
					(error "Malformed i18n-Man-filter-list"))
				    phrase)
				 pargs " ")))
	(setq flist (cdr flist))))
    command))

(defun i18n-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 i18n-Man-downcase-section-letters-flag
and the i18n-Man-section-translations-alist variables)."
  (let ((name "")
	(section "")
	(slist i18n-Man-section-translations-alist))
    (cond
     ;; "chmod(2V)" case ?
     ((string-match (concat "^" i18n-Man-reference-regexp "$") ref)
      (setq name (i18n-Man-match-substring 1 ref)
	    section (i18n-Man-match-substring 2 ref)))
     ;; "2v chmod" case ?
     ((string-match (concat "^\\(" i18n-Man-section-regexp
			    "\\) +\\(" i18n-Man-name-regexp "\\)$") ref)
      (setq name (i18n-Man-match-substring 2 ref)
	    section (i18n-Man-match-substring 1 ref))))
    (if (string= name "")
	ref				; Return the reference as is
      (if i18n-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 i18n-Man-downcase-section-letters-flag
	      (setq s1 (downcase s1)))
	  (if (not (string= s1 section)) nil
	    (setq section (if i18n-Man-downcase-section-letters-flag
			      (downcase s2)
			    s2)
		  slist nil))))
      (concat i18n-Man-specified-section-option section " " name))))

(defun i18n-Man-multibyteb-characters-enablep ()
  (or (and (boundp 'enable-multibyte-characters) enable-multibyte-characters)
      (featurep 'mule)))


;; ======================================================================
;; default i18n-man entry: get word under point

(defsubst i18n-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 `i18n-Man-auto-section-alist'."
  (let (word)
    (save-excursion
      ;; Default i18n-man entry title is any word the cursor is on, or if
      ;; cursor not on a word, then nearest preceding word.
      (setq word (current-word))
      (if (string-match "[._]+$" word)
	  (setq word (substring word 0 (match-beginning 0))))
      ;; If looking at something like ioctl(2) or brc(1M), include the
      ;; section number in the returned value.  Remove text properties.
      (forward-word 1)
      ;; Use `format' here to clear any text props from `word'.
      (format "%s%s"
	      word
	      (if (looking-at
		   (concat "[ \t]*([ \t]*\\(" i18n-Man-section-regexp "\\)[ \t]*)"))
		  (format "(%s)" (i18n-Man-match-substring 1))
		"")))))


;; ======================================================================
;; Top level command and background process sentinel

;; For compatibility with older versions.
;;;###autoload
(defalias 'i18n-manual-entry 'i18n-man)

;;;###autoload
(defun i18n-man (man-args)
  "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 i18n-Man mode (manpage browsing) buffer.  See variable
`i18n-Man-notify-method' for what happens when the buffer is ready.
If a buffer already exists for this man page, it will display immediately."
  (interactive
   (list (let* ((default-entry (i18n-Man-default-man-entry))
		(input (read-string
			(format "i18n-Man 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))))

  ;; Possibly translate the "subject(section)" syntax into the
  ;; "section subject" syntax and possibly downcase the section.
  (setq man-args (i18n-Man-translate-references man-args))

  (i18n-Man-getpage-in-background man-args))

;;;###autoload
(defun i18n-man-follow (man-args)
  "Get a Un*x manual page of the item under point and put it in a buffer."
  (interactive (list (i18n-Man-default-man-entry)))
  (if (or (not man-args)
	  (string= man-args ""))
      (error "No item under point")
    (i18n-man-with-current-environment man-args)))

(defun i18n-man-with-current-environment (&optional man-args)
  "Get a Un*x manual page in the same environment of the current buffer."
  (interactive)
  (i18n-Man-restore-lang-depend-variables)
  (call-interactively 'i18n-man man-args))

(defun i18n-Man-getpage-in-background (topic)
  "Uses TOPIC to build and fire off the manpage and cleaning command."
  (let* ((man-args topic)
	 (bufname (concat "*i18n-Man " man-args " (" i18n-Man-lang-depend-language-string ")*"))
	 (buffer  (get-buffer bufname)))
    (if buffer
	(i18n-Man-notify-when-ready buffer)
      (require 'env)
      (message "Invoking %s %s in the background" i18n-manual-lang-depend-program man-args)
      (setq buffer (generate-new-buffer bufname))
      (save-excursion
	(set-buffer buffer)
	(i18n-Man-save-lang-depend-variables)
	(setq i18n-Man-original-frame (selected-frame))
	(setq i18n-Man-arguments man-args)
;;;XXX
	(let ((process-environment (copy-sequence process-environment)))
;       (let ((process-environment (copy-sequence process-environment))
;	    ;; The following is so Awk script gets \n intact
;	    ;; But don't prevent decoding of the outside.
;	    (coding-system-for-write 'raw-text-unix)
;	    ;; Avoid possible error by using a directory that always exists.
;	    (default-directory "/"))
	  ;; Prevent any attempt to use display terminal fanciness.
	  (setenv "TERM" "dumb")

;;;XXX
	  (run-hooks 'i18n-Man-process-starting-hook)
	  (setq process (start-process i18n-manual-program buffer "sh" "-c"
				       (format (i18n-Man-build-man-command) man-args)))

	  (set-process-sentinel process 'i18n-Man-bgproc-sentinel)
	  ;; Process object is needed to be passed for hook, 
	  ;; so cannot to use run-hooks...
	  (if (or (and (boundp 'functionp)
		       (functionp i18n-Man-process-started-hook))
		  (not (null i18n-Man-process-started-hook)))
	      (funcall (eval 'i18n-Man-process-started-hook) process))))
      )))
;       (set-process-sentinel
;	   (start-process i18n-manual-program buffer "sh" "-c"
;			  (format (i18n-Man-build-man-command) man-args))
;	 'i18n-Man-bgproc-sentinel)))))
;	(if (fboundp 'start-process)
;	    (set-process-sentinel
;	     (start-process i18n-manual-program buffer "sh" "-c"
;			    (format (i18n-Man-build-man-command) man-args))
;	     'i18n-Man-bgproc-sentinel)
;	  (progn
;	    (let ((exit-status
;		   (call-process shell-file-name nil (list buffer nil) nil "-c"
;				 (format (i18n-Man-build-man-command) man-args)))
;		  (msg ""))
;	      (or (and (numberp exit-status)
;		       (= exit-status 0))
;		  (and (numberp exit-status)
;		       (setq msg
;			     (format "exited abnormally with code %d"
;				     exit-status)))
;		  (setq msg exit-status))
;	      (i18n-Man-bgproc-sentinel bufname msg))))))))

(defun i18n-Man-notify-when-ready (man-buffer)
  "Notify the user when MAN-BUFFER is ready.
See the variable `i18n-Man-notify-method' for the different notification behaviors."
  (let ((saved-frame (save-excursion
		       (set-buffer man-buffer)
		       i18n-Man-original-frame)))
    (cond
     ((eq i18n-Man-notify-method '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
	(let ((frame (make-frame i18n-Man-frame-parameters)))
	  (set-window-buffer (frame-selected-window frame) man-buffer)
          (set-window-dedicated-p (frame-selected-window frame) t))))
     ((eq i18n-Man-notify-method 'pushy)
      (switch-to-buffer man-buffer))
     ((eq i18n-Man-notify-method 'bully)
      (and window-system
	   (frame-live-p saved-frame)
	   (select-frame saved-frame))
      (pop-to-buffer man-buffer)
      (delete-other-windows))
     ((eq i18n-Man-notify-method 'aggressive)
      (and window-system
	   (frame-live-p saved-frame)
	   (select-frame saved-frame))
      (pop-to-buffer man-buffer))
     ((eq i18n-Man-notify-method 'friendly)
      (and window-system
	   (frame-live-p saved-frame)
	   (select-frame saved-frame))
      (display-buffer man-buffer 'not-this-window))
     ((eq i18n-Man-notify-method 'polite)
      (beep)
      (message "i18n-Man buffer %s is ready" (buffer-name man-buffer)))
     ((eq i18n-Man-notify-method 'quiet)
      (message "i18n-Man buffer %s is ready" (buffer-name man-buffer)))
     ((or (eq i18n-Man-notify-method 'meek)
	  t)
      (message ""))
     )))

(defun i18n-Man-softhyphen-to-minus ()
  ;; \255 is some kind of dash in Latin-1.
  (goto-char (point-min))
  (if (i18n-Man-multibyteb-characters-enablep)
      (while (search-forward "\255" nil t)
	(if (= (preceding-char) ?\255)
	    (replace-match "-")))
;; non-multibyteb-characters-enable Emacs can destroy the text...
;    (while (search-forward "\255" nil t) (replace-match "-"))
    ))

(defun i18n-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..." i18n-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 i18n-Man-overstrike-face))
;; 1998/9/15 by iwasaki
;; On Mule 2.3, Emacs 20.3, original underlining code does not process some of the 
;; laten-1 characters correctlly in German manpage (looks like \201\337).
;; After eliminating '_\b' from the region, new code calls put-text-property for the region.
;; This is not for German specific problem I think, so original code had been replaced with
;; my new underlining code.

  (goto-char (point-min))
  (while (search-forward "_\b" nil t)
    (goto-char (- (point) 2))
    (while (looking-at "\\(_\b\\(.\\)\\)+")
      (let ((mb (match-beginning 0))
	    (me (match-end 0))
	    (dc 0))
	(while (and (search-forward "_\b" nil t) (<= (point) (- me dc)))
	  (backward-delete-char 2)
	  (setq dc (+ dc 2)))
	(put-text-property mb (- me dc) 'face i18n-Man-underline-face)
	(goto-char (- me dc)))))
;; Original underlining code...
;  (goto-char (point-min))
;  (while (search-forward "_\b" nil t)
;    (backward-delete-char 2)
;    (put-text-property (point) (1+ (point)) 'face i18n-Man-underline-face))
  (goto-char (point-min))
  (while (search-forward "\b_" nil t)
    (backward-delete-char 2)
    (put-text-property (1- (point)) (point) 'face i18n-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 i18n-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))
  (i18n-Man-softhyphen-to-minus)
  (message "%s man page made up" i18n-Man-arguments))

(defun i18n-Man-cleanup-manpage ()
  "Remove overstriking and underlining from the current buffer."
  (interactive)
  (message "Please wait: cleaning up the %s man page..."
	   i18n-Man-arguments)
  (if (or (interactive-p) (not i18n-Man-sed-script))
      (progn
	(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 "\\(.\\)\\(\b\\1\\)+" nil t)
	  (replace-match "\\1"))
	(goto-char (point-min))
	(while (re-search-forward "\e\\[[0-9]+m" nil t) (replace-match ""))
	(goto-char (point-min))
	(while (re-search-forward "o\b\\+\\|\\+\bo" nil t) (replace-match "o"))
	))
  (goto-char (point-min))
  (while (re-search-forward "[-|]\\(\b[-|]\\)+" nil t) (replace-match "+"))
  (i18n-Man-softhyphen-to-minus)
  (message "%s man page cleaned up" i18n-Man-arguments))

(defun i18n-Man-bgproc-sentinel (process msg)
  "Manpage background process sentinel.
When manpage command is run asynchronously, PROCESS is the process 
object for the manpage command; when manpage command is run
synchronously, PROCESS is the name of the buffer where the manpage
command is run.  Second argument MSG is the exit message of the
manpage command."
  (let ((i18n-Man-buffer (if (stringp process) (get-buffer process)
		      (process-buffer process)))
	(delete-buff nil)
	(err-mess nil))

    (if (null (buffer-name i18n-Man-buffer)) ;; deleted buffer
	(or (stringp process)
	    (set-process-buffer process nil))

      (save-excursion
	(set-buffer i18n-Man-buffer)
	(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)
						  (progn
						    (end-of-line) (point)))
		       delete-buff t))
		((or (stringp process)
		     (not (and (eq (process-status process) 'exit)
			       (= (process-exit-status process) 0))))
		 (or (zerop (length msg))
		     (progn
		       (setq err-mess
			     (concat (buffer-name i18n-Man-buffer)
				     ": process "
				     (let ((eos (1- (length msg))))
				       (if (= (aref msg eos) ?\n)
					   (substring msg 0 eos) msg))))
		       (goto-char (point-max))
		       (insert (format "\nprocess %s" msg))))
		 ))
        (if delete-buff
            (kill-buffer i18n-Man-buffer)
          (if i18n-Man-fontify-manpage-flag
              (i18n-Man-fontify-manpage)
            (i18n-Man-cleanup-manpage))
          (run-hooks 'i18n-Man-cooked-hook)
          (i18n-Man-mode)
          (set-buffer-modified-p nil)
          ))
	;; Restore case-fold-search before calling
	;; i18n-Man-notify-when-ready because it may switch buffers.

	(if (not delete-buff)
	    (i18n-Man-notify-when-ready i18n-Man-buffer))

	(if err-mess
	    (error err-mess))
	))))

(defun i18n-Manual-popup-xemacs-menu (event)
  "Pops up a menu of cross-references in this manual page."
  (interactive "e")
  (let ((sep "---")
	items item)
    (save-excursion
      (set-buffer (event-buffer event))
      (i18n-Man-restore-lang-depend-variables)
      (setq items (mapcar '(lambda (item) (car item))
			  i18n-Man-refpages-alist))
      (or (null items)
	  (popup-menu
	   (cons "Manual Entry"
		 (mapcar '(lambda (item)
			    (if (eq item sep) item
			      (vector item (list 'i18n-man item) t)))
			 (nreverse items))))))))

(defun i18n-Manual-popup-emacs-menu (event)
  "Pops up a menu of cross-references in this manual page."
  (interactive "e")
  (let (items item)
    (save-excursion
      (set-buffer (window-buffer
		   (posn-window (event-start event))))
      (i18n-Man-restore-lang-depend-variables)
      (setq items (mapcar '(lambda (item) (car item))
			  i18n-Man-refpages-alist))
      (or (null items)
	  (let ((manpage (x-popup-menu
			  event
			  (cons "Manual Entry"
				(list (cons "Manual entry"
					    (mapcar '(lambda (item)
						       (cons item (list 'i18n-man item)))
						    (nreverse items))))))))
	    (or (null manpage) (eval manpage)))))))

(defun i18n-Manual-follow-by-click-xemacs (event)
  "Invoke `manual-entry' on the string under the mouse."
  (interactive "e")
  (save-excursion
    (set-buffer (event-buffer event))
    (i18n-Man-restore-lang-depend-variables)
    (goto-char (event-point event))
    (i18n-man (i18n-Man-default-man-entry))))

(defun i18n-Manual-follow-by-click-emacs (event)
  "Invoke `manual-entry' on the string under the mouse."
  (interactive "e")
  (save-excursion
    (set-buffer (window-buffer
		 (posn-window (event-start event))))
    (i18n-Man-restore-lang-depend-variables)
    (goto-char (posn-point (event-start event)))
    (i18n-man (i18n-Man-default-man-entry))))


;; ======================================================================
;; set up manual mode in buffer and build alists

(defun i18n-Man-mode ()
  "A mode for browsing Un*x manual pages.

The following man commands are available in the buffer. Try
\"\\[describe-key] <key> RET\" for more information:

\\[i18n-man]       Prompt to retrieve a new manpage.
\\[i18n-Man-follow-manual-reference]       Retrieve reference in SEE ALSO section.
\\[i18n-Man-next-manpage]   Jump to next manpage in circular list.
\\[i18n-Man-previous-manpage]   Jump to previous manpage in circular list.
\\[i18n-Man-next-section]       Jump to next manpage section.
\\[i18n-Man-previous-section]       Jump to previous manpage section.
\\[i18n-Man-goto-section]       Go to a manpage section.
\\[i18n-Man-goto-see-also-section]       Jumps to the SEE ALSO manpage section.
\\[i18n-Man-quit]       Deletes the manpage window, bury its buffer.
\\[i18n-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:

i18n-Man-notify-method               What happens when manpage formatting is done.
i18n-Man-downcase-section-letters-flag  Force section letters to lower case.
i18n-Man-circular-pages-flag         Treat multiple manpage list as circular.
i18n-Man-auto-section-alist          List of major modes and their section numbers.
i18n-Man-section-translations-alist  List of section numbers and their Un*x equiv.
i18n-Man-filter-list                 Background manpage filter command.
i18n-Man-mode-line-format            Mode line format for i18n-Man mode buffers.
i18n-Man-mode-map                    Keymap bindings for i18n-Man mode buffers.
i18n-Man-mode-hook                   Normal hook run on entry to i18n-Man mode.
i18n-Man-section-regexp              Regexp describing manpage section letters.
i18n-Man-heading-regexp              Regexp describing section headers.
i18n-Man-see-also-regexp             Regexp for SEE ALSO section (or your equiv).
i18n-Man-first-heading-regexp        Regexp for first heading on a manpage.
i18n-Man-reference-regexp            Regexp matching a references in SEE ALSO.
i18n-Man-switches			Background `man' command switches.

The following key bindings are currently in effect in the buffer:
\\{i18n-Man-mode-map}"
  (interactive)
  (setq major-mode 'i18n-Man-mode
	mode-name "i18n-Man"
	buffer-auto-save-file-name nil
	mode-line-format i18n-Man-mode-line-format
	truncate-lines t
	buffer-read-only t)
  (buffer-disable-undo (current-buffer))
  (auto-fill-mode -1)
  (use-local-map i18n-Man-mode-map)
  (set-syntax-table i18n-man-mode-syntax-table)
  (i18n-Man-build-page-list)
  (i18n-Man-strip-page-headers)
  (i18n-Man-unindent)
  (i18n-Man-goto-page 1)
  (run-hooks 'i18n-Man-mode-hook))

(defsubst i18n-Man-build-section-alist ()
  "Build the association list of manpage sections."
  (setq i18n-Man-sections-alist nil)
  (goto-char (point-min))
  (let ((case-fold-search nil))
    (while (re-search-forward i18n-Man-heading-regexp (point-max) t)
      (aput 'i18n-Man-sections-alist (i18n-Man-match-substring 1))
      (forward-line 1))))

(defsubst i18n-Man-build-references-alist ()
  "Build the association list of references (in the SEE ALSO section)."
  (setq i18n-Man-refpages-alist nil)
  (save-excursion
    (if (i18n-Man-find-section i18n-Man-see-also-regexp)
	(let ((start (progn (forward-line 1) (point)))
	      (end (progn
		     (i18n-Man-next-section 1)
		     (point)))
	      hyphenated
	      (runningpoint -1))
	  (save-restriction
	    (narrow-to-region start end)
	    (goto-char (point-min))
	    (back-to-indentation)
	    (while (and (not (eobp)) (/= (point) runningpoint))
	      (setq runningpoint (point))
	      (if (re-search-forward i18n-Man-reference-regexp end t)
		  (let* ((word (i18n-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 'i18n-Man-refpages-alist word))))
	      (skip-chars-forward " \t\n,")))))))

(defun i18n-Man-build-page-list ()
  "Build the list of separate manpages in the buffer."
  (setq i18n-Man-page-list nil)
  (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 i18n-Man-page-header-regexp)
		(i18n-Man-match-substring 1)
	      nil))
      ;; Go past both the current and the next i18n-Man-first-heading-regexp
      (if (re-search-forward i18n-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 i18n-Man-page-header-regexp p 'move)
		(beginning-of-line))))
      (setq page-end (point))
      (setq i18n-Man-page-list (append i18n-Man-page-list
				  (list (list (copy-marker page-start)
					      (copy-marker page-end)
					      header))))
      (setq page-start page-end)
      )))

(defun i18n-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 i18n-Man-page-list)
	(page ())
	(header ""))
    (while page-list
      (setq page (car page-list))
      (and (nth 2 page)
	   (goto-char (car page))
	   (re-search-forward i18n-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)))))

(defun i18n-Man-unindent ()
  "Delete the leading spaces that indent the manpage."
  (let ((buffer-read-only nil)
	(case-fold-search nil)
	(page-list i18n-Man-page-list))
    (while page-list
      (let ((page (car page-list))
	    (indent "")
	    (nindent 0))
	(narrow-to-region (car page) (car (cdr page)))
	(if i18n-Man-uses-untabify-flag
	    (untabify (point-min) (point-max)))
	(if (catch 'unindent
	      (goto-char (point-min))
	      (if (not (re-search-forward i18n-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))
	))))

(defun i18n-Man-init-lang-depend-variables ()
  "Initialize language depend variables."
  (setq i18n-manual-lang-depend-program nil)
  (setq i18n-Man-lang-depend-heading-regexp nil)
  (setq i18n-Man-lang-depend-see-also-regexp nil)
  (setq i18n-Man-lang-depend-first-heading-regexp nil)
  (setq i18n-Man-lang-depend-cooked-hook nil)
  (setq i18n-Man-lang-depend-process-starting-hook nil)
  (setq i18n-Man-lang-depend-process-started-hook nil)
  (setq i18n-Man-lang-depend-language-string nil)
)

(defun i18n-Man-save-lang-depend-variables ()
  "Save Language depend variables into the manpage buffer as buffer-local variables."
  (kill-local-variable 'i18n-manual-program)
  (kill-local-variable 'i18n-Man-heading-regexp)
  (kill-local-variable 'i18n-Man-see-also-regexp)
  (kill-local-variable 'i18n-Man-first-heading-regexp)
  (kill-local-variable 'i18n-Man-cooked-hook)
  (kill-local-variable 'i18n-Man-process-starting-hook)
  (kill-local-variable 'i18n-Man-process-started-hook)
  (kill-local-variable 'i18n-Man-language-string)

  (make-local-variable 'i18n-manual-program)
  (make-local-variable 'i18n-Man-heading-regexp)
  (make-local-variable 'i18n-Man-see-also-regexp)
  (make-local-variable 'i18n-Man-first-heading-regexp)
  (make-local-variable 'i18n-Man-cooked-hook)
  (make-local-variable 'i18n-Man-process-starting-hook)
  (make-local-variable 'i18n-Man-process-started-hook)
  (make-local-variable 'i18n-Man-language-string)

  (setq i18n-manual-program i18n-manual-lang-depend-program)
  (setq i18n-Man-heading-regexp i18n-Man-lang-depend-heading-regexp)
  (setq i18n-Man-see-also-regexp i18n-Man-lang-depend-see-also-regexp)
  (setq i18n-Man-first-heading-regexp i18n-Man-lang-depend-first-heading-regexp)
  (setq i18n-Man-cooked-hook i18n-Man-lang-depend-cooked-hook)
  (setq i18n-Man-process-starting-hook i18n-Man-lang-depend-process-starting-hook)
  (setq i18n-Man-process-started-hook i18n-Man-lang-depend-process-started-hook)
  (setq i18n-Man-language-string i18n-Man-lang-depend-language-string)
)

(defun i18n-Man-restore-lang-depend-variables ()
  "Restore language depend variables from the manpage buffer to invoke i18n-man."
  (setq i18n-manual-lang-depend-program i18n-manual-program)
  (setq i18n-Man-lang-depend-heading-regexp i18n-Man-heading-regexp)
  (setq i18n-Man-lang-depend-see-also-regexp i18n-Man-see-also-regexp)
  (setq i18n-Man-lang-depend-first-heading-regexp i18n-Man-first-heading-regexp)
  (setq i18n-Man-lang-depend-cooked-hook i18n-Man-cooked-hook)
  (setq i18n-Man-lang-depend-process-starting-hook i18n-Man-process-starting-hook)
  (setq i18n-Man-lang-depend-process-started-hook i18n-Man-process-started-hook)
  (setq i18n-Man-lang-depend-language-string i18n-Man-language-string)
)

(defun i18n-Man-debug (var)
  "For debugging :-)"
  (save-excursion
    (setq buf (current-buffer))
    (set-buffer (get-buffer "*scratch*"))
    (goto-char (point-max))
    (insert var (format "(%s)\n" buf))
    (goto-char (point-max))
    ))


;; ======================================================================
;; i18n-Man mode commands

(defun i18n-Man-next-section (n)
  "Move point to Nth next section (default 1)."
  (interactive "p")
  (let ((case-fold-search nil))
    (if (looking-at i18n-Man-heading-regexp)
	(forward-line 1))
    (if (re-search-forward i18n-Man-heading-regexp (point-max) t n)
	(beginning-of-line)
      (goto-char (point-max)))))

(defun i18n-Man-previous-section (n)
  "Move point to Nth previous section (default 1)."
  (interactive "p")
  (let ((case-fold-search nil))
    (if (looking-at i18n-Man-heading-regexp)
	(forward-line -1))
    (if (re-search-backward i18n-Man-heading-regexp (point-min) t n)
	(beginning-of-line)
      (goto-char (point-min)))))

(defun i18n-Man-find-section (section)
  "Move point to SECTION if it exists, otherwise don't move point.
Returns t if section is found, nil otherwise."
  (let ((curpos (point))
	(case-fold-search nil))
    (goto-char (point-min))
    (if (re-search-forward (concat "^" section) (point-max) t)
	(progn (beginning-of-line) t)
      (goto-char curpos)
      nil)
    ))

(defun i18n-Man-goto-section ()
  "Query for section to move point to."
  (interactive)
  (aput 'i18n-Man-sections-alist
	(let* ((default (aheadsym i18n-Man-sections-alist))
	       (completion-ignore-case t)
	       chosen
	       (prompt (concat "Go to section: (default " default ") ")))
	  (setq chosen (completing-read prompt i18n-Man-sections-alist))
	  (if (or (not chosen)
		  (string= chosen ""))
	      default
	    chosen)))
  (i18n-Man-find-section (aheadsym i18n-Man-sections-alist)))

(defun i18n-Man-goto-see-also-section ()
  "Move point the the \"SEE ALSO\" section.
Actually the section moved to is described by `i18n-Man-see-also-regexp'."
  (interactive)
  (if (not (i18n-Man-find-section i18n-Man-see-also-regexp))
      (error (concat "No " i18n-Man-see-also-regexp
		     " section found in the current manpage"))))

(defun i18n-Man-follow-manual-reference (reference)
  "Get one of the manpages referred to in the \"SEE ALSO\" section.
Specify which reference to use; default is based on word at point."
  (interactive
   (if (not i18n-Man-refpages-alist)
       (error "There are no references in the current man page")
     (list (let* ((default (or
			     (car (all-completions
				   (save-excursion
				     (skip-syntax-backward "w()")
				     (skip-chars-forward " \t")
				     (let ((word (current-word)))
				       ;; strip a trailing '-':
				       (if (string-match "-$" word)
					   (substring word 0
						      (match-beginning 0))
					 word)))
				   i18n-Man-refpages-alist))
			     (aheadsym i18n-Man-refpages-alist)))
		   chosen
		   (prompt (concat "Refer to: (default " default ") ")))
	      (setq chosen (completing-read prompt i18n-Man-refpages-alist nil t))
	      (if (or (not chosen)
		      (string= chosen ""))
		  default
		chosen)))))
  (if (not i18n-Man-refpages-alist)
      (error "Can't find any references in the current manpage")
    (aput 'i18n-Man-refpages-alist reference)
    (i18n-Man-restore-lang-depend-variables)
    (i18n-Man-getpage-in-background
     (i18n-Man-translate-references (aheadsym i18n-Man-refpages-alist)))))

(defun i18n-Man-delete-frame ()
  (if (and window-system
	   (or (eq i18n-Man-notify-method 'newframe)
	       (and pop-up-frames
		    (eq i18n-Man-notify-method 'bully))))
      (delete-frame)))

(defun i18n-Man-kill ()
  "Kill the buffer containing the manpage."
  (interactive)
  (if (fboundp 'quit-window)
      (quit-window t)
    (progn
      (let ((buff (current-buffer)))
	(delete-windows-on buff)
	(kill-buffer buff))
      (i18n-Man-delete-frame))))

(defun i18n-Man-quit ()
  "Bury the buffer containing the manpage."
  (interactive)
  (if (fboundp 'quit-window)
      (quit-window)
    (progn
      (let ((buff (current-buffer)))
	(delete-windows-on buff)
	(bury-buffer buff))
      (i18n-Man-delete-frame))))

(defun i18n-Man-goto-page (page)
  "Go to the manual page on page PAGE."
  (interactive
   (if (not i18n-Man-page-list)
       (let ((args i18n-Man-arguments))
	 (kill-buffer (current-buffer))
	 (error "Can't find the %s manpage" args))
     (if (= (length i18n-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 i18n-Man-page-list)))))))
  (if (not i18n-Man-page-list)
      (let ((args i18n-Man-arguments))
	(kill-buffer (current-buffer))
	(error "Can't find the %s manpage" args)))
  (if (or (< page 1)
	  (> page (length i18n-Man-page-list)))
      (error "No manpage %d found" page))
  (let* ((page-range (nth (1- page) i18n-Man-page-list))
	 (page-start (car page-range))
	 (page-end (car (cdr page-range))))
    (setq i18n-Man-current-page page
	  i18n-Man-page-mode-string (i18n-Man-make-page-mode-string))
    (widen)
    (goto-char page-start)
    (narrow-to-region page-start page-end)
    (i18n-Man-build-section-alist)
    (i18n-Man-build-references-alist)
    (goto-char (point-min))))


(defun i18n-Man-next-manpage ()
  "Find the next manpage entry in the buffer."
  (interactive)
  (if (= (length i18n-Man-page-list) 1)
      (error "This is the only manpage in the buffer"))
  (if (< i18n-Man-current-page (length i18n-Man-page-list))
      (i18n-Man-goto-page (1+ i18n-Man-current-page))
    (if i18n-Man-circular-pages-flag
	(i18n-Man-goto-page 1)
      (error "You're looking at the last manpage in the buffer"))))

(defun i18n-Man-previous-manpage ()
  "Find the previous manpage entry in the buffer."
  (interactive)
  (if (= (length i18n-Man-page-list) 1)
      (error "This is the only manpage in the buffer"))
  (if (> i18n-Man-current-page 1)
      (i18n-Man-goto-page (1- i18n-Man-current-page))
    (if i18n-Man-circular-pages-flag
	(i18n-Man-goto-page (length i18n-Man-page-list))
      (error "You're looking at the first manpage in the buffer"))))

;; Init the man package variables, if not already done.
(i18n-Man-init-defvars)

(provide 'i18n-man)

;;; i18n-man.el ends here
