Added the pbook.py file used to generate the documentation
authorMarco Baringer <mb@bese.it>
Sat, 13 Aug 2005 13:37:32 +0000 (13:37 +0000)
committerMarco Baringer <mb@bese.it>
Sat, 13 Aug 2005 13:37:32 +0000 (13:37 +0000)
pbook.py [new file with mode: 0644]

diff --git a/pbook.py b/pbook.py
new file mode 100644 (file)
index 0000000..f000cf1
--- /dev/null
+++ b/pbook.py
@@ -0,0 +1,392 @@
+#! /usr/bin/env python\r
+\r
+import re\r
+import tempfile\r
+import os, getopt, sys, shutil, random\r
+from os.path import splitext, basename\r
+\r
+class TxtFile:\r
+    def __init__(self, title, author = None, style = None, includeToc = None):\r
+        self.title = title\r
+        self.author = author\r
+        self.buffer = ""\r
+\r
+    def reset(self):\r
+        self.buffer = ""\r
+\r
+    def __add__(self, aStr):\r
+        self.buffer += aStr\r
+        return self\r
+\r
+    def tempFile(self):\r
+        return tempfile.NamedTemporaryFile('w+', -1, '.txt', 'pbook')\r
+    \r
+    def writeFile(self, fileName = None):\r
+        if not fileName:\r
+            file = self.tempFile()\r
+        elif fileName == "-":\r
+            file = sys.stdout\r
+        else:\r
+            name,ext = splitext(fileName)\r
+            if ext == "":\r
+                ext = ".txt"\r
+            elif ext != ".txt":\r
+                raise "Can not write format " + ext\r
+            file = open(name + ext, "w+")\r
+        file.write(self.buffer)\r
+        file.flush()\r
+        return file\r
+\r
+    def addEscape(self, escape, fileName, line):\r
+        return\r
+    def addHeading(self, level, heading, fileName, line):\r
+        heading = re.sub("\s+", " ", heading)\r
+        self += "++ " + heading + "\n"\r
+    def addComment(self, comment, fileName, startLine, endLine):\r
+        self += comment\r
+    def addFigure(self, figureFile, figureScale, figureName, fileName, endLine):\r
+        self += "            " + figureName + " (" + figureFile + ")" + "\n"\r
+    def addCode(self, code, fileName, startLine, endLine):\r
+        code = code.rstrip()\r
+        code = re.sub("\t", "   ", code)\r
+        self += "\n== %s (%s:%s) ================\n" % (basename(fileName), startLine, endLine)\r
+        self += code\r
+        self += "\n=========================================================\n\n"\r
+\r
+class TexFile(TxtFile):\r
+    def __init__(self, title, author = None, style = "article", includeToc = True):\r
+        TxtFile.__init__(self, title, author, style, includeToc)\r
+        self.style = style\r
+        self.includeToc = includeToc\r
+        self.bookSectioningCommands = ("chapter", "section", \\r
+                                       "subsection", "subsubsection")\r
+        self.articleSectioningCommands = ("section", "subsection", \\r
+                                          "subsubsection")\r
+\r
+    def beginning(self):\r
+        return '\n\\documentclass[notitlepage,a4paper,makeidx]{' + self.style + '}\n' + \\r
+               '\\usepackage{fancyvrb,color,palatino,makeidx}\n' + \\r
+               "\\newif\\ifpdf\n\\ifx\\pdfoutput\\undefined\n\\pdffalse\n" + \\r
+               "\\else\n\\pdfoutput=1\n\\pdftrue\n\\fi\n" + \\r
+               "\\ifpdf\n\\usepackage[pdftex]{graphicx}\n" + \\r
+               "\\else\n\\usepackage{graphicx}\n\\fi\n" + \\r
+               '\\definecolor{gray}{gray}{0.6}\n' + \\r
+               '\\title{' + TexFile.escapeString(self.title) + '}\n' +  \\r
+               (self.author and ('\\author{' + self.author + '}\n') or '') + \\r
+               '\\makeindex' + \\r
+               '\\begin{document}\n\\maketitle\n' + \\r
+               (self.includeToc and '\\tableofcontents\n' or '')\r
+    def ending(self):\r
+        return '\\printindex\n\\end{document}\n'\r
+    def sectioningCommand(self, level):\r
+        if self.style == "article":\r
+            return self.articleSectioningCommands[min(level, len(self.articleSectioningCommands))]\r
+        elif self.style == "book":\r
+            return self.bookSectioningCommands[min(level, len(self.bookSectioningCommands))]\r
+    \r
+    def escapeString(aStr):\r
+        aStr = re.sub("\\\\", "$\\\\backslash$", aStr)\r
+        def escapeRepl(match):\r
+            if match.group(1) != '$' or \\r
+               not (aStr[match.start():].startswith("$\\backslash$") or \\r
+                    aStr[:match.start()].endswith("$\\backslash")):\r
+                return '\\' + match.group(1)\r
+            else:\r
+                return match.group(0)\r
+        return re.sub("([#%&~$_^{}])", escapeRepl, aStr)\r
+    escapeString = staticmethod(escapeString)\r
+    \r
+    def tempFile(self):\r
+        return tempfile.NamedTemporaryFile('w+', -1, '.tex', 'pbook')\r
+    def writeFile(self, fileName = None):\r
+        if not fileName:\r
+            file = self.tempFile()\r
+        elif fileName == "-":\r
+            return self.writeTex(sys.stdout)\r
+        else:\r
+            name,ext = splitext(fileName)\r
+            if ext == "":\r
+                ext = ".pdf"\r
+            file = open(name + ext, "w+")\r
+        name,ext = splitext(file.name)\r
+        if ext == ".tex":\r
+            return self.writeTex(file)\r
+        elif ext == ".pdf":\r
+            return self.writePdf(file)\r
+        else:\r
+            raise "Can not write format " + ext\r
+\r
+    def writeTex(self, output):\r
+        output.write(self.beginning())\r
+        output.write(self.buffer)\r
+        output.write(self.ending())\r
+        output.flush()\r
+        return output\r
+\r
+    def writePdf(self, output):\r
+        tmpfile = self.tempFile()\r
+        self.writeTex(tmpfile)\r
+        (dir, name) = os.path.split(tmpfile.name)\r
+        print "cd " + dir + "; latex " + name + " && pdflatex " + name\r
+        os.system("cd " + dir + "; latex " + name + " && pdflatex " + name)\r
+        tmpfile.close()\r
+        pdfname = splitext(tmpfile.name)[0] + ".pdf"\r
+        shutil.copyfile(pdfname, output.name)\r
+        os.remove(pdfname)\r
+        return output\r
+\r
+    def addEscape(self, escape, fileName, line):\r
+        self += escape + "\n"\r
+    def addFigure(self, figureFile, figureScale, figureName, fileName, endLine):\r
+        self += "\\begin{figure}[htbp]\n  \\centering\n"\r
+        self += "  \\fbox{\\includegraphics[scale=" + figureScale + "]{" + figureFile + "}}\n"\r
+        self += "  \\caption{" + figureName + "}\n"\r
+        self += "\\end{figure}\n"\r
+    def addHeading(self, level, heading, fileName, line):\r
+        heading = re.sub("\s+", " ", heading)\r
+        self += "\n\n\\" + self.sectioningCommand(level) + "{" + \\r
+                TexFile.escapeString(heading) + "}\n"\r
+    def addComment(self, comment, fileName, startLine, endLine):\r
+        comment = TexFile.escapeString(comment)\r
+        comment = re.sub("`([^`']*)'", "{\\\\tt \\1}", comment)\r
+        self += re.sub("\"([^\"]*)\"", "``\\1''", comment)\r
+    def addCode(self, code, fileName, startLine, endLine):\r
+        code = code.rstrip()\r
+        code = re.sub("\\\\end{Verbatim}", "\\\\_end{Verbatim}", code)\r
+        code = re.sub("\t", "   ", code)\r
+        self += "\n\\begin{Verbatim}[fontsize=\\small,frame=leftline,framerule=0.9mm," + \\r
+                "rulecolor=\\color{gray},framesep=5.1mm,xleftmargin=5mm,fontfamily=cmtt]\n"\r
+        self += code\r
+        self +=  "\n\\end{Verbatim}\n"\r
+\r
+class IdqTexFile(TexFile):\r
+    def __init__(self, title, author = "id Quantique", style = "article", includeToc = True):\r
+        TexFile.__init__(self, title, author, style, includeToc)\r
+\r
+class BknrTexFile(TexFile):\r
+    def __init__(self, title, author, style, includeToc):\r
+        TexFile.__init__(self, title, author, style, includeToc)\r
+        self.firstSection = True\r
+    def beginning(self):\r
+        return '\\chapter{' + TexFile.escapeString(self.title) + '}\n'\r
+    def ending(self):\r
+        return ''\r
+    def addComment(self, comment, fileName, startLine, endLine):\r
+        comment = TexFile.escapeString(comment)\r
+        self += re.sub("\"([^\"]*)\"", "``\\1''", comment)\r
+    def sectioningCommand(self, level):\r
+        string = ""\r
+        if level == 0:\r
+            if self.firstSection == False:\r
+                string = ""\r
+#                string = "vbox{\n\\vspace{1cm}\n\\centering\n" + \\r
+#                         "\\includegraphics[scale=0.6]{peecol" + \\r
+#                         str(random.randint(1, 10) ) +  "}}\n\n\\"\r
+            else:\r
+                self.firstSection = False\r
+        if self.style == "article":\r
+            return string + self.articleSectioningCommands[min(level, len(self.articleSectioningCommands))]\r
+        elif self.style == "book":\r
+            return string + self.bookSectioningCommands[min(level, len(self.bookSectioningCommands))]\r
+\r
+class Pbook:\r
+    def __init__(self, files, outFile):\r
+        self.files = files\r
+        self.commentRe = None\r
+        self.headingRe = None\r
+        self.removeRe = None\r
+        self.outFile = outFile\r
+        self.lineCounter = 0\r
+        if not self.outFile.title: self.outFile.title = basename(file)\r
+\r
+    def formatBuffer(self):\r
+        self.outFile.reset()\r
+        self.lineCounter = 0\r
+        for file in self.files:\r
+            data = open(file, "r").read()\r
+            if self.removeRe:\r
+                data = self.removeRe.sub("", data)\r
+            # search the first heading\r
+            startMatch = self.headingRe.search(data)\r
+            if not startMatch:\r
+                raise "File must have at least one heading"\r
+            self.lineCounter += len(data[:startMatch.start()].split('\n'))\r
+            data = data[startMatch.start():]\r
+            self.fileName = file\r
+\r
+            lines = data.split('\n')\r
+            while len(lines) > 0:\r
+                line = lines[0]\r
+                if re.match("^\s*$", line):\r
+                    lines.pop(0)\r
+                    self.lineCounter += 1\r
+                    continue\r
+               elif self.figureRe.match(line):\r
+                    line = lines.pop(0)\r
+                    self.doFigure(line)\r
+                   self.lineCounter += 1\r
+               elif self.escapeRe.match(line):\r
+                    line = lines.pop(0)\r
+                    self.doEscape(line)\r
+                   self.lineCounter += 1\r
+                elif self.headingRe.match(line):\r
+                    line = lines.pop(0)\r
+                    self.doHeading(line)\r
+                    self.lineCounter += 1\r
+                elif self.commentRe.match(line):\r
+                    self.doComment(lines)\r
+                else:\r
+                    self.doCode(lines)\r
+\r
+    def doHeading(self, line):\r
+        match = self.headingRe.match(line)\r
+        assert(match != None)\r
+        level = len(match.group(1)) - 1\r
+        headingName = line[match.end():]\r
+        self.outFile.addHeading(level, headingName, self.fileName, self.lineCounter)\r
+\r
+    def doFigure(self, line):\r
+        match = self.figureRe.match(line)\r
+        assert(match != None)\r
+        figureFile = match.group(1)\r
+        figureName = match.group(3)\r
+        figureScale = match.group(2)\r
+        self.outFile.addFigure(figureFile, figureScale, figureName, self.fileName, self.lineCounter)\r
+\r
+    def doEscape(self, line):\r
+        match = self.escapeRe.match(line)\r
+        assert(match != None)\r
+        escape = match.group(1)\r
+        self.outFile.addEscape(escape, self.fileName, self.lineCounter)\r
+\r
+    def doComment(self, lines):\r
+        comment = ""\r
+        lineCount = 0\r
+        while len(lines) > 0:\r
+            line = lines[0]\r
+            match = self.commentRe.match(line)\r
+            if not match: break\r
+            line = lines.pop(0)\r
+            lineCount += 1\r
+            comment += line[:match.start()] + line[match.end():] + "\n"\r
+        self.outFile.addComment(comment, self.fileName, self.lineCounter, self.lineCounter + lineCount)\r
+        self.lineCounter += lineCount\r
+\r
+    def doCode(self, lines):\r
+        lineCount = 0\r
+        code = ""\r
+        while len(lines) > 0:\r
+            line = lines[0]\r
+            if (self.headingRe.match(line) or self.escapeRe.match(line) \\r
+                                           or self.figureRe.match(line) \\r
+                                           or self.commentRe.match(line)):\r
+                break\r
+            line = lines.pop(0)\r
+            lineCount += 1\r
+            code += line + "\n"\r
+        self.outFile.addCode(code, self.fileName, self.lineCounter, self.lineCounter + lineCount)\r
+        self.lineCounter += lineCount\r
+\r
+    def makeFile(self, fileName):\r
+        self.outFile.reset()\r
+        self.formatBuffer()\r
+        return self.outFile.writeFile(fileName)\r
+    \r
+class LispPbook(Pbook):\r
+    def __init__(self, files, outFile):\r
+        Pbook.__init__(self, files, outFile)\r
+        self.commentRe = re.compile('^;;;($|[^#f])', re.M)\r
+        self.headingRe = re.compile('^;;;(#+)', re.M)\r
+        self.figureRe = re.compile('^;;;f\s+\"(.+)\"\s+([^\s]+)\s+(.*)', re.M)\r
+        self.escapeRe = re.compile('^;;;t\s+(.+)', re.M)\r
+\r
+class CPbook(Pbook):\r
+    def __init__(self, files, outFile):\r
+        Pbook.__init__(self, files, outFile)\r
+        self.commentRe = re.compile('^(\s|/)*\*\*($|[^f#/])', re.M);\r
+        self.headingRe = re.compile("^/\*\*(#+)", re.M)\r
+        self.removeRe = re.compile('\*\*+/', re.M)\r
+        self.figureRe = re.compile('^/\*\*f \"(.+)\"\s+([^\s]+)\s(.*)', re.M)\r
+\r
+\r
+def usage():\r
+    print "Usage: ", sys.argv[0], " [-h] [-c TexFile|BknrTexFile|IdqTexFile|TxtFile] ", \\r
+          "[-T C|Lisp] [-t title] [-a author] [-O] [-o output] [-s style] file ..."\r
+\r
+def extToType(ext):\r
+    fileExtToType = ( ((".c", ".cpp", ".C", ".h"), CPbook),\r
+                      ((".lisp", ".el", ".l", ".cl"), LispPbook) )\r
+    for types, typeClass in fileExtToType:\r
+        if (ext in types):\r
+            return typeClass\r
+    return None\r
+\r
+def main():\r
+    texClass = TexFile\r
+    type = None\r
+    output = None\r
+    (author, title, toc, style) = (None, None, True, "article")\r
+    try:\r
+        opts, args = getopt.getopt(sys.argv[1:], "hc:T:t:a:Oo:s:")\r
+        if not args:\r
+            raise getopt.error, "At least one file argument required"\r
+        for optname, optvalue in opts:\r
+            if optname == "-h":\r
+                usage()\r
+                return\r
+            elif optname == "-c":\r
+                if optvalue == "TexFile":\r
+                    texClass = TexFile\r
+                elif optvalue == "IdqTexFile":\r
+                    texClass = IdqTexFile\r
+                elif optvalue == "BknrTexFile":\r
+                    texClass = BknrTexFile\r
+                elif optvalue == "TxtFile":\r
+                    texClass = TxtFile\r
+                else:\r
+                    raise getopt.error, "Unknown TexFile class ", optvalue\r
+            elif optname == "-t":\r
+                title = optvalue\r
+            elif optname == "-a":\r
+                author = optvalue\r
+            elif optname == "-O":\r
+                toc = False\r
+            elif optname == "-s":\r
+                style = optvalue\r
+            elif optname == "-T":\r
+                if optvalue == "C":\r
+                    type = CPbook\r
+                elif optvalue == "Lisp":\r
+                    type = LispPbook\r
+                else:\r
+                    raise getopt.error, "Unknown pbook file type ", optvalue\r
+            elif optname == "-o":\r
+                output = optvalue\r
+    except getopt.error, msg:\r
+        print msg\r
+        usage()\r
+        return 1\r
+    file = args[0]\r
+    name,ext = splitext(file)\r
+    if not title:\r
+        title = basename(name)\r
+    if not type:\r
+        type = extToType(ext)\r
+        if not type:\r
+            print "Could not get type for ", ext\r
+            return 2\r
+    pbook = type(args, texClass(title, author, style, toc))\r
+    if not output:\r
+        output = basename(name)\r
+    try:\r
+        file = pbook.makeFile(output)\r
+        if output != "-":\r
+            print "Wrote output to ", file.name\r
+    except IOError, (errno, strerror):\r
+        print "Caught an error while generating \"%s\": %s" % (output, strerror)\r
+    except:\r
+        print "Caught an error while generating \"%s\"" % (output)\r
+        return 1\r
+\r
+if __name__ == "__main__":\r
+    sys.exit(main())\r