[Python-checkins] python/dist/src/Doc/tools py2texi.el,NONE,1.1.2.1 mkinfo,1.2,1.2.30.1

fdrake@sourceforge.net fdrake@sourceforge.net
Thu, 02 May 2002 21:54:22 -0700


Update of /cvsroot/python/python/dist/src/Doc/tools
In directory usw-pr-cvs1:/tmp/cvs-serv18139/tools

Modified Files:
      Tag: release22-maint
	mkinfo 
Added Files:
      Tag: release22-maint
	py2texi.el 
Log Message:
Integrated SF patch #539487 by Matthias Klose:
This patch adds Milan Zamazal's conversion script and
modifies the mkinfo script to build the info doc files
from the LaTeX sources.  Currently, the mac, doc and
inst TeX files are not handled.
Explicitly checks for GNU Emacs 21.


--- NEW FILE: py2texi.el ---
;;; py2texi.el -- Conversion of Python LaTeX documentation to Texinfo

;; Copyright (C) 1998, 1999, 2001, 2002 Milan Zamazal

;; Author: Milan Zamazal <pdm@zamazal.org>
;; Version: $Id: py2texi.el,v 1.1.2.1 2002/05/03 04:54:20 fdrake Exp $
;; Keywords: python

;; COPYRIGHT NOTICE
;;
;; This program 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.
;;
;; This program 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 can find the GNU General Public License at
;; http://www.gnu.org/copyleft/gpl.html
;; or you can write to the Free Software Foundation, Inc., 59 Temple Place,
;; Suite 330, Boston, MA 02111-1307, USA.

;;; Commentary:

;; This is a Q&D hack for conversion of Python manuals to on-line help format.
;; I desperately needed usable online documenta for Python, so I wrote this.
;; The result code is ugly and need not contain complete information from
;; Python manuals.  I apologize for my ignorance, especially ignorance to
;; python.sty.  Improvements of this convertor are welcomed.

;; How to use it:
;; Load this file and apply `M-x py2texi'.  You will be asked for name of a
;; file to be converted.

;; Where to find it:
;; New versions of this code might be found at
;; http://www.zamazal.org/software/python/py2texi/ .

;;; Code:


(require 'texinfo)
(eval-when-compile
  (require 'cl))


(defvar py2texi-python-version "2.2"
  "What to substitute for the \\version macro.")

(defvar py2texi-python-short-version
  (progn
    (string-match "[0-9]+\\.[0-9]+" py2texi-python-version)
    (match-string 0 py2texi-python-version))
  "Short version number, usually set by the LaTeX commands.")

(defvar py2texi-stop-on-problems nil
  "*If non-nil, stop when you encouter soft problem.")

(defconst py2texi-environments
  '(("abstract" 0 "@quotation" "@end quotation\n")
    ("center" 0 "" "")
    ("cfuncdesc" 3
     (progn (setq findex t)
	    "\n@table @code\n@item \\1 \\2(\\3)\n@findex \\2\n")
     "@end table")
    ("classdesc" 2
     (progn (setq obindex t)
	    "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
     "@end table")
    ("classdesc*" 1
     (progn (setq obindex t)
	    "\n@table @code\n@item \\1\n@obindex \\1\n")
     "@end table")
    ("csimplemacrodesc" 1
     (progn (setq cindex t)
	    "\n@table @code\n@item \\1\n@cindex \\1\n")
     "@end table")
    ("ctypedesc" 1
     (progn (setq cindex t)
	    "\n@table @code\n@item \\1\n@cindex \\1\n")
     "@end table")
    ("cvardesc" 2
     (progn (setq findex t)
	    "\n@table @code\n@item \\1 \\2\n@findex \\2\n")
     "@end table")
    ("datadesc" 1
     (progn (setq findex t)
	    "\n@table @code\n@item \\1\n@findex \\1\n")
     "@end table")
    ("datadescni" 1 "\n@table @code\n@item \\1\n" "@end table")
    ("definitions" 0 "@table @dfn" "@end table\n")
    ("description" 0 "@table @samp" "@end table\n")
    ("displaymath" 0 "" "")
    ("document" 0
     (concat "@defcodeindex mo\n"
	     "@defcodeindex ob\n"
	     "@titlepage\n"
	     (format "@title " title "\n")
	     (format "@author " author "\n")
	     "@page\n"
	     author-address
	     "@end titlepage\n"
	     "@node Top, , , (dir)\n")
     (concat "@indices\n"
	     "@contents\n"
	     "@bye\n"))
    ("enumerate" 0 "@enumerate" "@end enumerate")
    ("excdesc" 1
     (progn (setq obindex t)
	    "\n@table @code\n@item \\1\n@obindex \\1\n")
     "@end table")
    ("excclassdesc" 2
     (progn (setq obindex t)
	    "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n")
     "@end table")
    ("flushleft" 0 "" "")
    ("fulllineitems" 0 "\n@table @code\n" "@end table")
    ("funcdesc" 2
     (progn (setq findex t)
	    "\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
     "@end table")
    ("funcdescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table")
    ("itemize" 0 "@itemize @bullet" "@end itemize\n")
    ("list" 2 "\n@table @code\n" "@end table")
    ("longtableii" 4 (concat "@multitable @columnfractions .5 .5\n"
			     "@item \\3 @tab \\4\n"
			     "@item ------- @tab ------ \n")
     "@end multitable\n")
    ("longtableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
			      "@item \\3 @tab \\4 @tab \\5\n"
			      "@item ------- @tab ------ @tab ------\n")
     "@end multitable\n")
    ("memberdesc" 1
     (progn (setq findex t)
	    "\n@table @code\n@item \\1\n@findex \\1\n")
     "@end table")
    ("memberdescni" 1 "\n@table @code\n@item \\1\n" "@end table")
    ("methoddesc" 2
     (progn (setq findex t)
	    "\n@table @code\n@item \\1(\\2)\n@findex \\1\n")
     "@end table")
    ("methoddescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table")
    ("notice" 0 "@emph{Notice:} " "")
    ("opcodedesc" 2
     (progn (setq findex t)
	    "\n@table @code\n@item \\1 \\2\n@findex \\1\n")
     "@end table")
    ("productionlist" 0 "\n@table @code\n" "@end table")
    ("quotation" 0 "@quotation" "@end quotation")
    ("seealso" 0 "See also:\n@table @emph\n" "@end table")
    ("seealso*" 0 "@table @emph\n" "@end table")
    ("sloppypar" 0 "" "")
    ("small" 0 "" "")
    ("tableii" 4 (concat "@multitable @columnfractions .5 .5\n"
			 "@item \\3 @tab \\4\n"
			 "@item ------- @tab ------ \n")
     "@end multitable\n")
    ("tableiii" 5 (concat "@multitable @columnfractions .33 .33 .33\n"
			 "@item \\3 @tab \\4 @tab \\5\n"
			 "@item ------- @tab ------ @tab ------\n")
     "@end multitable\n")
    ("tableiv" 6 (concat
		  "@multitable @columnfractions .25 .25 .25 .25\n"
		  "@item \\3 @tab \\4 @tab \\5 @tab \\6\n"
		  "@item ------- @tab ------- @tab ------- @tab -------\n")
     "@end multitable\n")
    ("tablev" 7 (concat
		 "@multitable @columnfractions .20 .20 .20 .20 .20\n"
		 "@item \\3 @tab \\4 @tab \\5 @tab \\6 @tab \\7\n"
		 "@item ------- @tab ------- @tab ------- @tab ------- @tab -------\n")
     "@end multitable\n"))
  "Associative list defining substitutions for environments.
Each list item is of the form (ENVIRONMENT ARGNUM BEGIN END) where:
- ENVIRONMENT is LaTeX environment name
- ARGNUM is number of (required) macro arguments
- BEGIN is substitution for \begin{ENVIRONMENT}
- END is substitution for \end{ENVIRONMENT}
Both BEGIN and END are evaled.  Moreover, you can reference arguments through
\N regular expression notation in strings of BEGIN.")

(defconst py2texi-commands
  '(("ABC" 0 "ABC")
    ("appendix" 0 (progn (setq appendix t) ""))
    ("ASCII" 0 "ASCII")
    ("author" 1 (progn (setq author (match-string 1 string)) ""))
    ("authoraddress" 1
     (progn (setq author-address (match-string 1 string)) ""))
    ("b" 1 "@w{\\1}")
    ("bf" 0 "@destroy")
    ("bifuncindex" 1 (progn (setq findex t) "@findex{\\1}"))
    ("C" 0 "C")
    ("c" 0 "@,")
    ("catcode" 0 "")
    ("cdata" 1 "@code{\\1}")
    ("centerline" 1 "@center \\1")
    ("cfunction" 1 "@code{\\1}")
    ("chapter" 1 (format "@node \\1\n@%s \\1\n"
			 (if appendix "appendix" "chapter")))
    ("chapter*" 1 "@node \\1\n@unnumbered \\1\n")
    ("character" 1 "@samp{\\1}")
    ("citetitle" 1 "@ref{Top,,,\\1}")
    ("class" 1 "@code{\\1}")
    ("code" 1 "@code{\\1}")
    ("command" 1 "@command{\\1}")
    ("constant" 1 "@code{\\1}")
    ("copyright" 1 "@copyright{}")
    ("Cpp" 0 "C++")
    ("ctype" 1 "@code{\\1}")
    ("dataline" 1 (progn (setq findex t) "@item \\1\n@findex \\1\n"))
    ("date" 1 "\\1")
    ("declaremodule" 2 (progn (setq cindex t) "@label{\\2}@cindex{\\2}"))
    ("deprecated" 2 "@emph{This is deprecated in Python \\1.  \\2}")
    ("dfn" 1 "@dfn{\\1}")
    ("documentclass" 1 py2texi-magic)
    ("e" 0 "@backslash{}")
    ("else" 0 (concat "@end ifinfo\n@" (setq last-if "iftex")))
    ("EOF" 0 "@code{EOF}")
    ("email" 1 "@email{\\1}")
    ("emph" 1 "@emph{\\1}")
    ("envvar" 1 "@samp{\\1}")
    ("exception" 1 "@code{\\1}")
    ("exindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
    ("fi" 0 (concat "@end " last-if))
    ("file" 1 "@file{\\1}")
    ("filevar" 1 "@file{@var{\\1}}")
    ("footnote" 1 "@footnote{\\1}")
    ("frac" 0 "")
    ("funcline" 2 (progn (setq findex t) "@item \\1 \\2\n@findex \\1"))
    ("funclineni" 2 "@item \\1 \\2")
    ("function" 1 "@code{\\1}")
    ("grammartoken" 1 "@code{\\1}")
    ("hline" 0 "")
    ("ifhtml" 0 (concat "@" (setq last-if "ifinfo")))
    ("iftexi" 0 (concat "@" (setq last-if "ifinfo")))
    ("index" 1 (progn (setq cindex t) "@cindex{\\1}"))
    ("indexii" 2 (progn (setq cindex t) "@cindex{\\1 \\2}"))
    ("indexiii" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3}"))
    ("indexiv" 3 (progn (setq cindex t) "@cindex{\\1 \\2 \\3 \\4}"))
    ("infinity" 0 "@emph{infinity}")
    ("it" 0 "@destroy")
    ("kbd" 1 "@key{\\1}")
    ("keyword" 1 "@code{\\1}")
    ("kwindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
    ("label" 1 "@label{\\1}")
    ("Large" 0 "")
    ("LaTeX" 0 "La@TeX{}")
    ("large" 0 "")
    ("ldots" 0 "@dots{}")
    ("leftline" 1 "\\1")
    ("lineii" 2 "@item \\1 @tab \\2")
    ("lineiii" 3 "@item \\1 @tab \\2 @tab \\3")
    ("lineiv" 4 "@item \\1 @tab \\2 @tab \\3 @tab \\4")
    ("linev" 5 "@item \\1 @tab \\2 @tab \\3 @tab \\4 @tab \\5")
    ("localmoduletable" 0 "")
    ("longprogramopt" 1 "@option{--\\1}")
    ("mailheader" 1 "@code{\\1}")
    ("makeindex" 0 "")
    ("makemodindex" 0 "")
    ("maketitle" 0 (concat "@top " title "\n"))
    ("makevar" 1 "@code{\\1}")
    ("manpage" 2 "@samp{\\1(\\2)}")
    ("mbox" 1 "@w{\\1}")
    ("member" 1 "@code{\\1}")
    ("memberline" 1 "@item \\1\n@findex \\1\n")
    ("menuselection" 1 "@samp{\\1}")
    ("method" 1 "@code{\\1}")
    ("methodline" 2 (progn (setq moindex t) "@item \\1(\\2)\n@moindex \\1\n"))
    ("methodlineni" 2 "@item \\1(\\2)\n")
    ("mimetype" 1 "@samp{\\1}")
    ("module" 1 "@samp{\\1}")
    ("moduleauthor" 2 "This module was written by \\1 @email{\\2}.@*")
    ("modulesynopsis" 1 "\\1")
    ("moreargs" 0 "@dots{}")
    ("n" 0 "@backslash{}n")
    ("newcommand" 2 "")
    ("newsgroup" 1 "@samp{\\1}")
    ("nodename" 1
     (save-excursion
       (save-match-data
	 (re-search-backward "^@node "))
       (delete-region (point) (save-excursion (end-of-line) (point)))
       (insert "@node " (match-string 1 string))
       ""))
    ("noindent" 0 "@noindent ")
    ("note" 1 "@emph{Note:} \\1")
    ("NULL" 0 "@code{NULL}")
    ("obindex" 1 (progn (setq obindex t) "@obindex{\\1}"))
    ("opindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
    ("option" 1 "@option{\\1}")
    ("optional" 1 "[\\1]")
    ("pep" 1 (progn (setq cindex t) "PEP@ \\1@cindex PEP \\1\n"))
    ("pi" 0 "pi")
    ("platform" 1 "")
    ("plusminus" 0 "+-")
    ("POSIX" 0 "POSIX")
    ("production" 2 "@item \\1 \\2")
    ("program" 1 "@command{\\1}")
    ("programopt" 1 "@option{\\1}")
    ("protect" 0 "")
    ("pytype" 1 "@code{\\1}")
    ("ref" 1 "@ref{\\1}")
    ("refbimodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
    ("refmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
    ("refmodule" 1 "@samp{\\1}")
    ("refstmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
    ("regexp" 1 "\"\\1\"")
    ("release" 1
     (progn (setq py2texi-python-version (match-string 1 string)) ""))
    ("renewcommand" 2 "")
    ("rfc" 1 (progn (setq cindex t) "RFC@ \\1@cindex RFC \\1\n"))
    ("rm" 0 "@destroy")
    ("samp" 1 "@samp{\\1}")
    ("section" 1 (let ((str (match-string 1 string)))
		   (save-match-data
		     (if (string-match "\\(.*\\)[ \t\n]*---[ \t\n]*\\(.*\\)"
				       str)
			 (format
			  "@node %s\n@section %s\n"
			  (py2texi-backslash-quote (match-string 1 str))
			  (py2texi-backslash-quote (match-string 2 str)))
		       "@node \\1\n@section \\1\n"))))
    ("sectionauthor" 2
     "\nThis manual section was written by \\1 @email{\\2}.@*")
    ("seemodule" 2 "@ref{\\1} \\2")
    ("seepep" 3 "\n@table @strong\n@item PEP\\1 \\2\n\\3\n@end table\n")
    ("seerfc" 3 "\n@table @strong\n@item RFC\\1 \\2\n\\3\n@end table\n")
    ("seetext" 1 "\\1")
    ("seetitle" 1 "@cite{\\1}")
    ("seeurl" 2 "\n@table @url\n@item \\1\n\\2\n@end table\n")
    ("setindexsubitem" 1 (progn (setq cindex t) "@cindex \\1"))
    ("setreleaseinfo" 1 (progn (setq py2texi-releaseinfo "")))
    ("setshortversion" 1
     (progn (setq py2texi-python-short-version (match-string 1 string)) ""))
    ("shortversion" 0 py2texi-python-short-version)
    ("sqrt" 0 "")
    ("stindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
    ("stmodindex" 1 (progn (setq moindex t) "@moindex{\\1}"))
    ("strong" 1 "@strong{\\1}")
    ("sub" 0 "/")
    ("subsection" 1 "@node \\1\n@subsection \\1\n")
    ("subsubsection" 1 "@node \\1\n@subsubsection \\1\n")
    ("sum" 0 "")
    ("tableofcontents" 0 "")
    ("term" 1 "@item \\1")
    ("textasciitilde" 0 "~")
    ("textasciicircum" 0 "^")
    ("textbackslash" 0 "@backslash{}")
    ("textrm" 1 "\\1")
    ("texttt" 1 "@code{\\1}")
    ("textunderscore" 0 "_")
    ("title" 1 (progn (setq title (match-string 1 string)) "@settitle \\1"))
    ("today" 0 "@today{}")
    ("token" 1 "@code{\\1}")
    ("tt" 0 "@destroy")
    ("ttindex" 1 (progn (setq cindex t) "@cindex{\\1}"))
    ("u" 0 "@backslash{}u")
    ("ulink" 2 "\\1")
    ("UNIX" 0 "UNIX")
    ("unspecified" 0 "@dots{}")
    ("url" 1 "@url{\\1}")
    ("usepackage" 1 "")
    ("var" 1 "@var{\\1}")
    ("verbatiminput" 1 "@code{\\1}")
    ("version" 0 py2texi-python-version)
    ("versionadded" 1 "@emph{Added in Python version \\1}")
    ("versionchanged" 1 "@emph{Changed in Python version \\1}")
    ("vskip" 1 "")
    ("vspace" 1 "")
    ("warning" 1 "@emph{\\1}")
    ("withsubitem" 2 "\\2")
    ("XXX" 1 "@strong{\\1}"))
  "Associative list of command substitutions.
Each list item is of the form (COMMAND ARGNUM SUBSTITUTION) where:
- COMMAND is LaTeX command name
- ARGNUM is number of (required) command arguments
- SUBSTITUTION substitution for the command.  It is evaled and you can
  reference command arguments through the \\N regexp notation in strings.")

(defvar py2texi-magic "@documentclass\n"
  "\"Magic\" string for auxiliary insertion at the beginning of document.")

(defvar py2texi-dirs '("./" "../texinputs/")
  "Where to search LaTeX input files.")

(defvar py2texi-buffer "*py2texi*"
  "The name of a buffer where Texinfo is generated.")

(defconst py2texi-xemacs (string-match "^XEmacs" (emacs-version))
  "Running under XEmacs?")


(defmacro py2texi-search (regexp &rest body)
  `(progn
     (goto-char (point-min))
     (while (re-search-forward ,regexp nil t)
       ,@body)))

(defmacro py2texi-search-safe (regexp &rest body)
  `(py2texi-search ,regexp
    (unless (py2texi-protected)
      ,@body)))


(defun py2texi-message (message)
  "Report message and stop if `py2texi-stop-on-problems' is non-nil."
  (if py2texi-stop-on-problems
      (error message)
    (message message)))


(defun py2texi-backslash-quote (string)
  "Double backslahes in STRING."
  (let ((i 0))
    (save-match-data
      (while (setq i (string-match "\\\\" string i))
	(setq string (replace-match "\\\\\\\\" t nil string))
	(setq i (+ i 2))))
    string))


(defun py2texi (file)
  "Convert Python LaTeX documentation FILE to Texinfo."
  (interactive "fFile to convert: ")
  (switch-to-buffer (get-buffer-create py2texi-buffer))
  (erase-buffer)
  (insert-file file)
  (let ((case-fold-search nil)
	(title "")
	(author "")
	(author-address "")
	(appendix nil)
	(findex nil)
	(obindex nil)
	(cindex nil)
	(moindex nil)
	last-if)
    (py2texi-process-verbatims)
    (py2texi-process-comments)
    (py2texi-process-includes)
    (py2texi-process-funnyas)
    (py2texi-process-environments)
    (py2texi-process-commands)
    (py2texi-fix-indentation)
    (py2texi-fix-nodes)
    (py2texi-fix-references)
    (py2texi-fix-indices)
    (py2texi-process-simple-commands)
    (py2texi-fix-fonts)
    (py2texi-fix-braces)
    (py2texi-fix-backslashes)
    (py2texi-destroy-empties)
    (py2texi-fix-newlines)
    (py2texi-adjust-level))
  (let* ((filename (concat "./"
			   (file-name-nondirectory file)
			   (if (string-match "\\.tex$" file) "i" ".texi")))
	 (infofilename (py2texi-info-file-name filename)))
    (goto-char (point-min))
    (when (looking-at py2texi-magic)
      (delete-region (point) (progn (beginning-of-line 2) (point)))
      (insert "\\input texinfo @c -*-texinfo-*-\n")
      (insert "@setfilename " (file-name-nondirectory infofilename)))
    (when (re-search-forward "@chapter" nil t)
      (texinfo-all-menus-update t))
    (goto-char (point-min))
    (write-file filename)
    (message (format "You can apply `makeinfo %s' now." filename))))


(defun py2texi-info-file-name (filename)
  "Generate name of info file from original file name FILENAME."
  (setq filename (expand-file-name filename))
  (let ((directory (file-name-directory filename))
	(basename (file-name-nondirectory filename)))
    (concat directory "python-"
	    (substring basename 0 (- (length basename) 4)) "info")))


(defun py2texi-process-verbatims ()
  "Process and protect verbatim environments."
  (let (delimiter
	beg
	end)
    (py2texi-search-safe "\\\\begin{\\(verbatim\\|displaymath\\)}"
      (replace-match "@example")
      (setq beg (copy-marker (point) nil))
      (re-search-forward "\\\\end{\\(verbatim\\|displaymath\\)}")
      (setq end (copy-marker (match-beginning 0) nil))
      (replace-match "@end example")
      (py2texi-texinfo-escape beg end)
      (put-text-property (- beg (length "@example"))
			 (+ end (length "@end example"))
			 'py2texi-protected t))
    (py2texi-search-safe "\\\\verb\\([^a-z]\\)"
      (setq delimiter (match-string 1))
      (replace-match "@code{")
      (setq beg (copy-marker (point) nil))
      (re-search-forward (regexp-quote delimiter))
      (setq end (copy-marker (match-beginning 0) nil))
      (replace-match "}")
      (put-text-property (- beg (length "@code{")) (+ end (length "}"))
			 'py2texi-protected t)
      (py2texi-texinfo-escape beg end))))


(defun py2texi-process-comments ()
  "Remove comments."
  (let (point)
    (py2texi-search-safe "%"
      (setq point (point))
      (when (save-excursion
	      (re-search-backward "\\(^\\|[^\\]\\(\\\\\\\\\\)*\\)%\\=" nil t))
	(delete-region (1- point)
		       (save-excursion (beginning-of-line 2) (point)))))))


(defun py2texi-process-includes ()
  "Include LaTeX input files.
Do not include .ind files."
  (let ((path (file-name-directory file))
	filename
	dirs
	includefile)
    (py2texi-search-safe "\\\\input{\\([^}]+\\)}"
      (setq filename (match-string 1))
      (unless (save-match-data (string-match "\\.tex$" filename))
	(setq filename (concat filename ".tex")))
      (setq includefile (save-match-data
			  (string-match "\\.ind\\.tex$" filename)))
      (setq dirs py2texi-dirs)
      (while (and (not includefile) dirs)
	(setq includefile (concat path (car dirs) filename))
	(unless (file-exists-p includefile)
	  (setq includefile nil)
	  (setq dirs (cdr dirs))))
      (if includefile
	  (save-restriction
	    (narrow-to-region (match-beginning 0) (match-end 0))
	    (delete-region (point-min) (point-max))
	    (when (stringp includefile)
	      (insert-file-contents includefile)
	      (goto-char (point-min))
	      (insert "\n")
	      (py2texi-process-verbatims)
	      (py2texi-process-comments)
	      (py2texi-process-includes)))
	(replace-match (format "\\\\emph{Included file %s}" filename))
	(py2texi-message (format "Input file %s not found" filename))))))


(defun py2texi-process-funnyas ()
  "Convert @s."
  (py2texi-search-safe "@"
    (replace-match "@@")))


(defun py2texi-process-environments ()
  "Process LaTeX environments."
  (let ((stack ())
	kind
	environment
	parameter
	arguments
	n
	string
	description)
    (py2texi-search-safe (concat "\\\\\\(begin\\|end\\|item\\)"
				 "\\({\\([^}]*\\)}\\|[[]\\([^]]*\\)[]]\\|\\)")
      (setq kind (match-string 1)
	    environment (match-string 3)
	    parameter (match-string 4))
      (replace-match "")
      (cond
       ((string= kind "begin")
	(setq description (assoc environment py2texi-environments))
	(if description
	    (progn
	      (setq n (cadr description))
	      (setq description (cddr description))
	      (setq string (py2texi-tex-arguments n))
	      (string-match (py2texi-regexp n) string)
				      ; incorrect but sufficient
	      (insert (replace-match (eval (car description))
				     t nil string))
	      (setq stack (cons (cadr description) stack)))
	  (py2texi-message (format "Unknown environment: %s" environment))
	  (setq stack (cons "" stack))))
       ((string= kind "end")
	(insert (eval (car stack)))
	(setq stack (cdr stack)))
       ((string= kind "item")
	(insert "\n@item " (or parameter "") "\n"))))
    (when stack
      (py2texi-message (format "Unclosed environment: %s" (car stack))))))


(defun py2texi-process-commands ()
  "Process LaTeX commands."
  (let (done
	command
	command-info
	string
	n)
    (while (not done)
      (setq done t)
      (py2texi-search-safe "\\\\\\([a-zA-Z*]+\\)\\(\\[[^]]*\\]\\)?"
	(setq command (match-string 1))
	(setq command-info (assoc command py2texi-commands))
	(if command-info
	    (progn
	      (setq done nil)
	      (replace-match "")
	      (setq command-info (cdr command-info))
	      (setq n (car command-info))
	      (setq string (py2texi-tex-arguments n))
	      (string-match (py2texi-regexp n) string)
				      ; incorrect but sufficient
	      (insert (replace-match (eval (cadr command-info))
				     t nil string)))
	  (py2texi-message (format "Unknown command: %s (not processed)"
				   command)))))))


(defun py2texi-argument-pattern (count)
  (let ((filler "\\(?:[^{}]\\|\\\\{\\|\\\\}\\)*"))
    (if (<= count 0)
	filler
      (concat filler "\\(?:{"
	      (py2texi-argument-pattern (1- count))
	      "}" filler "\\)*" filler))))
(defconst py2texi-tex-argument
  (concat
   "{\\("
   (py2texi-argument-pattern 10)	;really at least 10!
   "\\)}[ \t%@c\n]*")
  "Regexp describing LaTeX command argument including argument separators.")


(defun py2texi-regexp (n)
  "Make regexp matching N LaTeX command arguments."
  (if (= n 0)
      ""
    (let ((regexp "^[^{]*"))
      (while (> n 0)
	(setq regexp (concat regexp py2texi-tex-argument))
	(setq n (1- n)))
      regexp)))


(defun py2texi-tex-arguments (n)
  "Remove N LaTeX command arguments and return them as a string."
  (let ((point (point))
	(i 0)
	result
	match)
    (if (= n 0)
	(progn
	  (when (re-search-forward "\\=\\({}\\| *\\)" nil t)
	    (replace-match ""))
	  "")
      (while (> n 0)
	(unless (re-search-forward
		 "\\(\\=\\|[^\\\\]\\)\\(\\\\\\\\\\)*\\([{}]\\)" nil t)
	  (debug))
	(if (string= (match-string 3) "{")
	    (setq i (1+ i))
	  (setq i (1- i))
	  (when (<= i 0)
	    (setq n (1- n)))))
      (setq result (buffer-substring-no-properties point (point)))
      (while (string-match "\n[ \t]*" result)
	(setq result (replace-match " " t nil result)))
      (delete-region point (point))
      result)))


(defun py2texi-process-simple-commands ()
  "Replace single character LaTeX commands."
  (let (char)
    (py2texi-search-safe "\\\\\\([^a-z]\\)"
      (setq char (match-string 1))
      (replace-match (format "%s%s"
			     (if (or (string= char "{")
				     (string= char "}")
				     (string= char " "))
				 "@"
			       "")
			     (if (string= char "\\")
				 "\\\\"
			       char))))))


(defun py2texi-fix-indentation ()
  "Remove white space at the beginning of lines."
  (py2texi-search-safe "^[ \t]+"
    (replace-match "")))


(defun py2texi-fix-nodes ()
  "Remove unwanted characters from nodes and make nodes unique."
  (let ((nodes (make-hash-table :test 'equal))
	id
	counter
	string
	label)
    (py2texi-search "^@node +\\(.*\\)$"
      (setq string (match-string 1))
      (if py2texi-xemacs
	  (replace-match "@node " t)
	(replace-match "" t nil nil 1))
      (when (string-match "@label{[^}]*}" string)
	(setq label (match-string 0 string))
	(setq string (replace-match "" t nil string)))
      (while (string-match "@[a-zA-Z]+\\|[{}():]\\|``\\|''" string)
	(setq string (replace-match "" t nil string)))
      (while (string-match " -- " string)
	(setq string (replace-match " - " t nil string)))
      (when (string-match " +$" string)
	(setq string (replace-match "" t nil string)))
      (when (string-match "^\\(Built-in\\|Standard\\) Module \\|The " string)
	(setq string (replace-match "" t nil string)))
      (string-match "^[^,]+" string)
      (setq id (match-string 0 string))
      (setq counter (gethash id nodes))
      (if counter
	  (progn
	    (setq counter (1+ counter))
	    (setq string (replace-match (format "\\& %d" counter)
					t nil string)))
	(setq counter 1))
      (setf (gethash id nodes) counter)
      (insert string)
      (when label
	(beginning-of-line 3)
	(insert label "\n")))))


(defun py2texi-fix-references ()
  "Process labels and make references to point to appropriate nodes."
  (let ((labels ())
	node)
    (py2texi-search-safe "@label{\\([^}]*\\)}"
      (setq node (save-excursion
		   (save-match-data
		     (and (re-search-backward "@node +\\([^,\n]+\\)" nil t)
			  (match-string 1)))))
      (when node
	(setq labels (cons (cons (match-string 1) node) labels)))
      (replace-match ""))
    (py2texi-search-safe "@ref{\\([^}]*\\)}"
      (setq node (assoc (match-string 1) labels))
      (replace-match "")
      (when node
	(insert (format "@ref{%s}" (cdr node)))))))


(defun py2texi-fix-indices ()
  "Remove unwanted characters from @*index commands and create final indices."
  (py2texi-search-safe "@..?index\\>[^\n]*\\(\\)\n"
    (replace-match "" t nil nil 1))
  (py2texi-search-safe "@..?index\\>[^\n]*\\(\\)"
    (replace-match "\n" t nil nil 1))
  (py2texi-search-safe "@..?index\\({\\)\\([^}]+\\)\\(}+\\)"
    (replace-match " " t nil nil 1)
    (replace-match "" t nil nil 3)
    (let ((string (match-string 2)))
      (save-match-data
	(while (string-match "@[a-z]+{" string)
	  (setq string (replace-match "" nil nil string)))
	(while (string-match "{" string)
	  (setq string (replace-match "" nil nil string))))
      (replace-match string t t nil 2)))
  (py2texi-search-safe "@..?index\\>.*\\([{}]\\|@[a-z]*\\)"
    (replace-match "" t nil nil 1)
    (goto-char (match-beginning 0)))
  (py2texi-search-safe "[^\n]\\(\\)@..?index\\>"
    (replace-match "\n" t nil nil 1))
  (goto-char (point-max))
  (re-search-backward "@indices")
  (replace-match "")
  (insert (if moindex
	      (concat "@node Module Index\n"
		      "@unnumbered Module Index\n"
		      "@printindex mo\n")
	    "")
	  (if obindex
	      (concat "@node Class-Exception-Object Index\n"
		      "@unnumbered Class, Exception, and Object Index\n"
		      "@printindex ob\n")
	    "")
	  (if findex
	      (concat "@node Function-Method-Variable Index\n"
		      "@unnumbered Function, Method, and Variable Index\n"
		      "@printindex fn\n")
	    "")
	  (if cindex
	      (concat "@node Miscellaneous Index\n"
		      "@unnumbered Miscellaneous Index\n"
		      "@printindex cp\n")
	    "")))


(defun py2texi-fix-backslashes ()
  "Make backslashes from auxiliary commands."
  (py2texi-search-safe "@backslash{}"
    (replace-match "\\\\")))


(defun py2texi-fix-fonts ()
  "Remove garbage after unstructured font commands."
  (let (string)
    (py2texi-search-safe "@destroy"
      (replace-match "")
      (when (eq (preceding-char) ?{)
	(forward-char -1)
	(setq string (py2texi-tex-arguments 1))
	(insert (substring string 1 (1- (length string))))))))


(defun py2texi-fix-braces ()
  "Escape braces for Texinfo."
  (let (string)
    (py2texi-search "{"
       (unless (or (py2texi-protected)
		   (save-excursion
		     (re-search-backward
		      "@\\([a-zA-Z]*\\|multitable.*\\){\\=" nil t)))
	 (forward-char -1)
	 (setq string (py2texi-tex-arguments 1))
	 (insert "@" (substring string 0 (1- (length string))) "@}")))))


(defun py2texi-fix-newlines ()
  "Remove extra newlines."
  (py2texi-search "\n\n\n+"
    (replace-match "\n\n"))
  (py2texi-search-safe "@item.*\n\n"
    (delete-backward-char 1))
  (py2texi-search "@end example"
    (unless (looking-at "\n\n")
      (insert "\n"))))


(defun py2texi-destroy-empties ()
  "Remove all comments.
This avoids some makeinfo errors."
  (py2texi-search "@c\\>"
    (unless (eq (py2texi-protected) t)
      (delete-region (- (point) 2) (save-excursion (end-of-line) (point)))
      (cond
       ((looking-at "\n\n")
	(delete-char 1))
       ((save-excursion (re-search-backward "^[ \t]*\\=" nil t))
	(delete-region (save-excursion (beginning-of-line) (point))
		       (1+ (point))))))))


(defun py2texi-adjust-level ()
  "Increase heading level to @chapter, if needed.
This is only needed for distutils, so it has a very simple form only."
  (goto-char (point-min))
  (unless (re-search-forward "@chapter\\>" nil t)
    (py2texi-search-safe "@section\\>"
      (replace-match "@chapter" t))
    (py2texi-search-safe "@\\(sub\\)\\(sub\\)?section\\>"
      (replace-match "" nil nil nil 1))))


(defun py2texi-texinfo-escape (beg end)
  "Escape Texinfo special characters in region."
  (save-excursion
    (goto-char beg)
    (while (re-search-forward "[@{}]" end t)
      (replace-match "@\\&"))))


(defun py2texi-protected ()
  "Return protection status of the point before current point."
  (get-text-property (1- (point)) 'py2texi-protected))


;;; Announce

(provide 'py2texi)


;;; py2texi.el ends here

Index: mkinfo
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/tools/mkinfo,v
retrieving revision 1.2
retrieving revision 1.2.30.1
diff -C2 -d -r1.2 -r1.2.30.1
*** mkinfo	8 Jan 1999 15:32:27 -0000	1.2
--- mkinfo	3 May 2002 04:54:20 -0000	1.2.30.1
***************
*** 18,22 ****
  DOCDIR=`dirname "$FILENAME"`
  DOCFILE=`basename "$FILENAME"`
! DOCNAME=`basename "$FILENAME" .html`
  
  # Now build the real directory names, and locate our support stuff:
--- 18,27 ----
  DOCDIR=`dirname "$FILENAME"`
  DOCFILE=`basename "$FILENAME"`
! DOCNAME=`basename "$FILENAME" .tex`
! if [ $# -gt 1 ]; then
!     INFONAME="$2"
! else
!     INFONAME="python-$DOCNAME.info"
! fi
  
  # Now build the real directory names, and locate our support stuff:
***************
*** 32,36 ****
      # show what we're doing, like make does:
      echo "$*"
!     $* || exit $?
  }
  
--- 37,41 ----
      # show what we're doing, like make does:
      echo "$*"
!     "$@" || exit $?
  }
  
***************
*** 38,44 ****
  # generate the Texinfo file:
  
! run $PERL -I$TOOLSDIR $TOOLSDIR/html2texi.pl $DOCDIR/$DOCFILE
! run $EMACS -batch -l $TOOLSDIR/fixinfo.el $DOCNAME.texi
! rm -f $DOCNAME.texi~
  
  
--- 43,51 ----
  # generate the Texinfo file:
  
! run $EMACS -batch -q --no-site-file -l $TOOLSDIR/py2texi.el \
!     --eval "(setq py2texi-dirs '(\"./\" \"../texinputs/\" \"$DOCDIR\"))" \
!     --eval "(py2texi \"$DOCDIR/$DOCFILE\")" \
!     -f kill-emacs
! echo Done
  
  
***************
*** 46,48 ****
  
  run $MAKEINFO --footnote-style end --fill-column 72 \
! 	      --paragraph-indent 0 $DOCNAME.texi
--- 53,55 ----
  
  run $MAKEINFO --footnote-style end --fill-column 72 \
! 	      --paragraph-indent 0 --output=$INFONAME $DOCNAME.texi