--- /dev/null
+#! /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