;;; py2texi.el -- Conversion of Python LaTeX documentation to Texinfo ;; Copyright (C) 1998, 1999, 2001, 2002 Milan Zamazal ;; Author: Milan Zamazal ;; Version: $Id: py2texi.el,v 1.8.8.1 2003/09/29 17:22:33 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-texi-file-name nil "If non-nil, that string is used as the name of the Texinfo file. Otherwise a generated Texinfo file name is used.") (defvar py2texi-info-file-name nil "If non-nil, that string is used as the name of the Info file. Otherwise a generated Info file name is used.") (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\n") ("cmemberdesc" 3 "\n@table @code\n@item \\2 \\3\n" "@end table\n") ("classdesc" 2 (progn (setq obindex t) "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n") "@end table\n") ("classdesc*" 1 (progn (setq obindex t) "\n@table @code\n@item \\1\n@obindex \\1\n") "@end table\n") ("comment" 0 "\n@ignore\n" "\n@end ignore\n") ("csimplemacrodesc" 1 (progn (setq cindex t) "\n@table @code\n@item \\1\n@cindex \\1\n") "@end table\n") ("ctypedesc" 1 (progn (setq cindex t) "\n@table @code\n@item \\1\n@cindex \\1\n") "@end table\n") ("cvardesc" 2 (progn (setq findex t) "\n@table @code\n@item \\1 \\2\n@findex \\2\n") "@end table\n") ("datadesc" 1 (progn (setq findex t) "\n@table @code\n@item \\1\n@findex \\1\n") "@end table\n") ("datadescni" 1 "\n@table @code\n@item \\1\n" "@end table\n") ("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") ("envdesc" 2 (concat "\n@table @code" "\n@item @backslash{}begin@{\\1@}\\2") "@end table\n") ("excdesc" 1 (progn (setq obindex t) "\n@table @code\n@item \\1\n@obindex \\1\n") "@end table\n") ("excclassdesc" 2 (progn (setq obindex t) "\n@table @code\n@item \\1(\\2)\n@obindex \\1\n") "@end table\n") ("flushleft" 0 "" "") ("fulllineitems" 0 "\n@table @code\n" "@end table\n") ("funcdesc" 2 (progn (setq findex t) "\n@table @code\n@item \\1(\\2)\n@findex \\1\n") "@end table\n") ("funcdescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table\n") ("itemize" 0 "@itemize @bullet" "@end itemize\n") ("list" 2 "\n@table @code\n" "@end table\n") ("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") ("macrodesc" 2 (concat "\n@table @code" "\n@item \\1@{\\2@}") "@end table\n") ("memberdesc" 1 (progn (setq findex t) "\n@table @code\n@item \\1\n@findex \\1\n") "@end table\n") ("memberdescni" 1 "\n@table @code\n@item \\1\n" "@end table\n") ("methoddesc" 2 (progn (setq findex t) "\n@table @code\n@item \\1(\\2)\n@findex \\1\n") "@end table\n") ("methoddescni" 2 "\n@table @code\n@item \\1(\\2)\n" "@end table\n") ("notice" 0 "@emph{Notice:} " "") ("opcodedesc" 2 (progn (setq findex t) "\n@table @code\n@item \\1 \\2\n@findex \\1\n") "@end table\n") ("productionlist" 0 "\n@table @code\n" "@end table\n") ("quotation" 0 "@quotation" "@end quotation") ("seealso" 0 "See also:\n@table @emph\n" "@end table\n") ("seealso*" 0 "@table @emph\n" "@end table\n") ("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") ("alltt" 0 "@example" "@end example") ) "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 '(("AA" 0 "@AA{}") ("aa" 0 "@aa{}") ("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") ("cfuncline" 3 "@itemx \\1 \\2(\\3)\n@findex \\2") ("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}") ("cmemberline" 3 "@itemx \\2 \\3\n") ("code" 1 "@code{\\1}") ("command" 1 "@command{\\1}") ("constant" 1 "@code{\\1}") ("copyright" 1 "@copyright{}") ("Cpp" 0 "C++") ("csimplemacro" 1 "@code{\\1}") ("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"))) ("env" 1 "@code{\\1}") ("EOF" 0 "@code{EOF}") ("email" 1 "@email{\\1}") ("emph" 1 "@emph{\\1}") ("envvar" 1 "@env{\\1}") ("exception" 1 "@code{\\1}") ("exindex" 1 (progn (setq obindex t) "@obindex{\\1}")) ("fi" 0 (concat "@end " last-if)) ("file" 1 "@file{\\1}") ("filenq" 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}") ("macro" 1 "@code{@backslash{}\\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 "") ("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") ("productioncont" 1 "@item @w{} \\1") ("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 "") ("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") ("TeX" 0 "@TeX{}") ("textasciitilde" 0 "~") ("textasciicircum" 0 "^") ("textbackslash" 0 "@backslash{}") ("textgreater" 0 ">") ("textless" 0 "<") ("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* ((texi-file-name (or py2texi-texi-file-name (py2texi-texi-file-name file))) (info-file-name (or py2texi-info-file-name (py2texi-info-file-name texi-file-name)))) (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 " info-file-name)) (when (re-search-forward "@chapter" nil t) (texinfo-all-menus-update t)) (goto-char (point-min)) (write-file texi-file-name) (message (format "You can apply `makeinfo %s' now." texi-file-name)))) (defun py2texi-texi-file-name (filename) "Generate name of Texinfo file from original file name FILENAME." (concat filename (if (string-match "\\.tex$" filename) "i" ".texi"))) (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 (file-name-as-directory (car dirs)) filename)) (if (not (file-name-absolute-p includefile)) (setq includefile (concat (file-name-as-directory path) includefile))) (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 index) (py2texi-search "^@node +\\(.*\\)$" (setq string (match-string 1)) (if py2texi-xemacs (replace-match "@node " t) (replace-match "" t nil nil 1)) (while (string-match "@label{[^}]*}" string) (setq label (match-string 0 string)) (setq string (replace-match "" t nil string))) (while (string-match "@..?index{[^}]*}" string) (setq index (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))) (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) (beginning-of-line 3) (when label (insert label "\n")) (when index (insert index "\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