#!/usr/bin/env python from optparse import OptionParser import sys import os import os.path import pty import re import select import subprocess import fnmatch import dircache import termios import signal class TexallOptionParser(OptionParser): def __init__(self): OptionParser.__init__(self, usage="%prog [options] DIR/FILE", version="%prog $Id$", description="Call LaTeX and friends to typeset all tex files in DIR/FILE. By default up-to-date files are skipped. Care is taken to run all required commands until the result is stable. By default, pdflatex is used, but use of pstricks forces dvi->ps->pdf. The utility program rmligs ist used to improve output for german language texts. Use \"texall -Sqfi batchmode file.tex\" to simulate the behaviour of (pdf)latex in non-interactive mode (useful for emacs).") self.add_option("-n", "--dry-run", action="store_false", dest="act", default=True, help="do not run any program") self.add_option("-f", "--force", action="store_true", dest="force", default=False, help="regenerate up-to-date files") self.add_option("-N", "--non-recursive", action="store_false", dest="recurse", default=True, help="disable recursion into subdirectories") self.add_option("-s", "--summary", dest="summary", help="summarise failures and processed files at the end of the run (one of failures (default), files, both, no)", choices=("no","files","failures","both"), default="failures") self.add_option("-v", "--verbose", action="count", dest="verbosity", default=1, help="explain what is going on") self.add_option("-q", "--quiet", action="store_const", dest="verbosity", const=0, help="suppress progress reports") self.add_option("-o", "--show-output", action="store_true", dest="showoutput", default=False, help="show the output of the called programs") self.add_option("-i", "--interaction", dest="interactionmode", choices=("batchmode", "nonstopmode", "scrollmode", "errorstopmode"), help="control the behaviour of latex (on of batchmode (default), nonstopmode (default for -o), scrollmode, errorstopmode)") self.add_option("-I", "--ignore", action="store", dest="ignorepattern", default=".*vorlage|\.\#|.*-rmligs\.tex$", help="regular expression of filenames to ignore") self.add_option("-P", "--required-pattern", action="store", dest="requiredpattern", default='^\\\\documentclass|^%%% TeX-master: t', help="regular expression that must match the file content") self.add_option("-T", "--preserve-tempfiles", action="store_false", dest="deletetempfiles", default=True, help="preserve temporary files (default: delete)") self.add_option("-K", "--no-kpathsea", action="store_true", dest="nokpse", default=False, help="do not call kpsewhich (might be faster)") self.add_option("-S", "--single-run", action="store_true", dest="singlerun", default=False, help="stop after the first LaTeX run, no matter if finished") def parse_args(self, args=None, values=None): options, arguments = OptionParser.parse_args(self, args, values) # provide default for interactionmode: if options.interactionmode == None: if options.showoutput: options.interactionmode = "nonstopmode" else: options.interactionmode = "batchmode" return options, arguments class AnalyserData: def __init__(self, name, filetype): self.name = name self.filetype = filetype class Analyser: """Abstract class for analysing of program output and file content""" def __init__(self, d): """Instance creation. d -- dictionary to store data in""" self.d = d def parse(self, text): """parse text and change own state accordingly. text -- the filecontent or line to analyse""" raise NotImplementedError def merge(self, d): """merge information gathered from an included file. d -- dictionary with information gathered from the included file""" raise NotImplementedError def finish(self, dirname): """final steps after all dependecy file information hat been merged. Only executed if this data object belongs to a main .tex file. dirname -- directory of current job""" pass class AnalyseTexRequired(Analyser): def __init__(self, d): d.required = False Analyser.__init__(self,d) def parse(self, text): if re_required.search(text): self.d.required = True def merge(self, d): self.d.required |= d.required class AnalyseTexDependencies(Analyser): def __init__(self, d): d.deps = [] d.grfdeps = [] Analyser.__init__(self, d) def parse(self, text): for m in re_inputinclude.finditer(text): self.d.deps.append((m.group(2), (".tex", ""))) m = re_documentclass.search(text) if m: cls = m.group(2) self.d.deps.append((cls, (".cls", ""))) for m in re_usepackage.finditer(text): stys = m.group(2) for sty in re_commawhitespace.split(stys): self.d.deps.append((sty, (".sty", ""))) for m in re_usetheme.finditer(text): themes = m.group(3) ttype = m.group(1) for theme in re_commawhitespace.split(themes): self.d.deps.append(("beamer%stheme%s"%(ttype, theme), (".sty",))) for m in re_graphics.finditer(text): self.d.grfdeps.append(m.group(2)) def merge(self, d): self.d.deps.extend(d.deps) self.d.grfdeps.extend(d.grfdeps) def finish(self, dirname): if hasattr(self.d, "needsPS"): if self.d.needsPS: for f in self.d.grfdeps: self.d.deps.append((f, (".eps", ".jpg", ".png", ""))) else: for f in self.d.grfdeps: self.d.deps.append((f, (".pdf", ".jpg", ".png", ""))) expandeddeps = [] for dep, extlist in self.d.deps: if extlist == None: expandeddeps.append((dep, extlist)) else: try: expandeddeps.append((texpath(dirname, dep, extlist=extlist), None)) except ValueError, e: if opts.verbosity > 2: error(self.d.name, "ignoring dependency: %s"%e, warning=True) self.d.deps = expandeddeps class AnalyseTexBibfiles(Analyser): def __init__(self, d): d.bibfiles = [] Analyser.__init__(self, d) def parse(self, text): for m in re_bibliography.finditer(text): bibs = m.group(1) for bib in re_commawhitespace.split(bibs): if bib[-8:] != "-blx.bib" and bib[-4:] != "-blx": self.d.bibfiles.append(bib) def merge(self, d): self.d.bibfiles.extend(d.bibfiles) def finish(self, dirname): bibpaths = [] for bib in self.d.bibfiles: try: bibpath = texpath(dirname, bib, pathtype="bib",progname="bibtex") bibpath = strippath(bibpath, dirname) self.d.deps.append((bibpath, None)) bibpaths.append(bibpath) except ValueError, e: error(self.d.name, str(e), warning=True) bibpaths.append(bib) pass self.d.bibfiles = bibpaths class AnalyseTexNeedsPs(Analyser): def __init__(self, d): d.needsPS = False Analyser.__init__(self, d) def parse(self, text): if re_needsps.search(text): self.d.needsPS = True def merge(self, d): self.d.needsPS |= d.needsPS class AnalyseTexRmligs(Analyser): def __init__(self, d): d.rmligs = False Analyser.__init__(self, d) def parse(self, text): if re_rmligs.search(text): self.d.rmligs = True def merge(self, d): self.d.rmligs |= d.rmligs texanalysers = (AnalyseTexRequired, AnalyseTexDependencies, AnalyseTexBibfiles, AnalyseTexNeedsPs, AnalyseTexRmligs) class AnalyseLogErrors(Analyser): def __init__(self, d): d.errors = False Analyser.__init__(self, d) def parse(self, text): if re_error.search(text): self.d.errors = True class AnalyseLogRequests(Analyser): def __init__(self, d): d.requests = {} d.requestreason = {} Analyser.__init__(self, d) self.inrequest = False def addrequest(self, name, priority, reason=None): if self.d.requests.has_key(name): if self.d.requests[name] < priority: self.d.requests[name] = priority if reason: self.d.reqeustreason[name] = reason elif not self.d.requestreason[name]: self.d.requestreason[name] = reason else: self.d.requests[name] = priority self.d.requestreason[name] = reason def parse(self, text): for m in re_logmatcher.finditer(text): if m.group() == 'LaTeX Warning: There were undefined references.': self.addrequest("latex", 0, " (because of undefined references)") elif m.group() == 'LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.': self.addrequest("latex", 0, " (because of changed labels)") elif m.group(1)!=None: self.addrequest(m.group(2), int(m.group(1))) elif m.group(3)!=None: pri = int(m.group(3)) binary = m.group(4) binary = None options = [] infile = None for l in re_longreq.finditer(m.group(5)): if l.group(1) == "infile": infile = l.group(2) elif l.group(1) == "option": options.extend(l.group(2).split(" ")) elif l.group(1) == "binary": binary = l.group(2) if binary not in ("bibtex", "bibtex8"): error(self.d.name, "ignoring request to run %s for security reasons"%binary, warning=True) continue options[:0] = [binary] if binary == "bibtex8": options[1:1] = [ "--wolfgang" ] if infile != None: options.append(infile) self.addrequest(tuple(options), pri) def finish(self, _): if self.d.errors: self.addrequest("latex", 0, " (because of errors in .log file)") loganalysers = (AnalyseLogErrors, AnalyseLogRequests) analysecachedir = None analysecache = {} def analyseFile(dirname, filename, analyserclasses, parsetype, fulltext=False, cache=False, recurse=None, recursesystemfiles=False): global analysecachedir, analysecache if dirname != analysecachedir: analysecachedir = dirname analysecache = {} pathname = os.path.join(dirname, filename) if cache and (parsetype, pathname) in analysecache: return analysecache[(parsetype, pathname)] data = AnalyserData(pathname, parsetype) analysers = [] for analyser in analyserclasses: analysers.append(analyser(data)) try: f = open(pathname, "r") if fulltext: text = f.read() if parsetype == "tex": text = re_texcomment.sub("", text) for a in analysers: a.parse(text) else: for line in f: for a in analysers: a.parse(line) except IOError, e: error(pathname, "could not be analysed: %s"%e) else: f.close() if hasattr(data, "deps") and (recurse or (recurse==None and hasattr(data, "required") and data.required)): # recurse: for dep, extlist in data.deps: try: deppath = texpath(dirname, dep, extlist=extlist) except ValueError, e: continue if (not recursesystemfiles) and re_systemfile.match(deppath): # skip system files continue depname = strippath(deppath, dirname) depdata = analyseFile(dirname, depname, analyserclasses, parsetype, fulltext, cache, True, recursesystemfiles) for a in analysers: a.merge(depdata) if (hasattr(data, "required") and data.required) or parsetype == "log": for a in analysers: a.finish(dirname) if cache: analysecache[(parsetype, pathname)] = data return data def analyseTex(dirname, filename): return analyseFile(dirname, filename, texanalysers, "tex", fulltext=True, cache=True) def analyseLog(dirname, filename): return analyseFile(dirname, filename, loganalysers, "log", fulltext=True, cache=False) kpsecache = {} kpseproc = {} def texpath(dirname,filename,pathtype="tex",progname="latex",extlist=("",)): if extlist == None: return filename filename = os.path.expanduser(filename) # first try files in current directory if pathtype=="bib" and extlist==("",): extlist=(".bib", "") for ext in extlist: if os.path.exists(os.path.join(dirname,filename+ext)): return filename+ext if opts.nokpse: raise ValueError("file not found, not using kpsewhich: %s (extensions: %r)") # call kpsewhich to locate the file if not kpsecache.has_key((pathtype,progname)): kpsecache[(pathtype,progname)] = {} cache = kpsecache[(pathtype,progname)] for ext in extlist: lookupname = filename + ext if cache.has_key(lookupname): if cache[lookupname]: return cache[lookupname] else: continue else: if iswindows: kpse=subprocess.Popen(["kpsewhich", "-format", pathtype, "-progname", progname, lookupname], stdout=subprocess.PIPE, cwd=dirname) (out,err)=kpse.communicate() if kpse.wait() == 0: pathname = out[:-1] cache[lookupname] = pathname return pathname else: cache[lookupname] = None else: if kpseproc.has_key((pathtype, progname)): _, master = kpseproc[(pathtype, progname)] os.write(master, lookupname + "\n") else: master, slave = pty.openpty() #turn off echo attr = termios.tcgetattr(slave) attr[3] = attr[3] & ~termios.ECHO termios.tcsetattr(slave, termios.TCSANOW, attr) proc = subprocess.Popen(["kpsewhich", "-interactive", "-format", pathtype, "-progname", progname, lookupname], stdout=slave, stdin=slave, cwd="/") kpseproc[(pathtype, progname)] = (proc, master) if select.select([master], [], [], .05)[0]: pathname = os.read(master, 1024) else: pathname = None if pathname: while pathname[-1] in ("\n", "\r"): pathname = pathname[:-2] cache[lookupname] = pathname return pathname else: cache[lookupname] = None raise ValueError("file not found by kpsewhich: %s (extensions: %r)"%(filename,extlist)) errors = [] procfiles = [] errstr = {False: "ERROR", True: "WARNING"} def error(file, desc, warning=False): msg = "%s: %s: %s"%(errstr[warning], os.path.normpath(file), desc) if opts.verbosity: print msg errors.append(msg) null = file(os.path.devnull, "wb") # platform checking: iswindows = (sys.platform == "win32") # provide a (modified) copy of os.path.relpath from python 2.6 def relpath(pathname, start=os.path.curdir): """Return a relative version of a path""" if not iswindows: pathname = os.path.expanduser(pathname) start_list = os.path.abspath(start).split(os.path.sep) path_list = os.path.abspath(pathname).split(os.path.sep) # Work out how much of the filepath is shared by start and path. i = len(os.path.commonprefix([start_list, path_list])) rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] return os.path.join(*rel_list) else: start_list = os.path.abspath(start).split(os.path.sep) path_list = os.path.abspath(pathname).split(os.path.sep) if start_list[0].lower() != path_list[0].lower(): unc_path, rest = os.path.splitunc(pathname) unc_start, rest = os.path.splitunc(start) if bool(unc_path) ^ bool(unc_start): raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" % (pathname, start)) else: raise ValueError("path is on drive %s, start on drive %s" % (path_list[0], start_list[0])) # Work out how much of the filepath is shared by start and path. for i in range(min(len(start_list), len(path_list))): if start_list[i].lower() != path_list[i].lower(): break else: i += 1 rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] return os.path.join(*rel_list) def strippath(pathname, start): pathname = os.path.expanduser(pathname) pathname = os.path.abspath(os.path.join(start, pathname)) start = os.path.abspath(start) if pathname[:len(start)] == start: return pathname[len(start)+1:] else: return pathname binpath = os.path.expandvars("$PATH") if binpath == "$PATH": binpath = os.path.defpath binpath = binpath.split(os.path.pathsep) def isinpath(name): for p in binpath: if os.path.exists(os.path.join(p, name)): return True return False if isinpath("rmligs"): rmligs_name = "rmligs" elif isinpath("rmligs-german"): rmligs_name = "rmligs-german" else: rmligs_name = "" error("rmligs", "Command not found. Please install rmligs (or rmligs-german) for optimal results", warning=True) def alltexfiles(arglist): """iterate over all .tex files that are not ignored by -I""" for a in arglist: if os.path.isdir(a): for directory, subdirs, files in os.walk(a): if not opts.recurse: subdirs[:] = [] else: for skipdir in (".svn", ".bzr"): if skipdir in subdirs: subdirs.remove(skipdir) for name in files: if re_texfile.match(name): if re_ignore.match(name): if opts.verbosity > 2: print "%s: skipped because of ignored name"%os.path.normpath(os.path.join(directory,name)) else: yield (directory, name) elif os.path.exists(a): if re_texfile.match(a): (dirname, filename) = os.path.split(a) if dirname == "": dirname = "." yield (dirname, filename) else: error(a, "is no .tex file; skipped") elif os.path.exists(a+".tex"): (dirname, filename) = os.path.split(a+".tex") if dirname == "": dirname = "." yield(dirname, filename) else: error(a, "file not found") def needsupdate(dirname, texname, data=None): jobname = texname[:-4] if data == None: data = analyseTex(dirname, texname) # skip non-main files if not data.required: if opts.verbosity > 2: print "%s: skipped because no main latex file"%os.path.normpath(os.path.join(dirname,texname)) return False # silent force if opts.force and not opts.verbosity: return True # check for nonexistant output file outname = jobname + ".pdf" outpath = os.path.join(dirname, outname) if not os.path.exists(outpath): return " (because .pdf does not exist)" # check for updated source and dependency files outdate = os.path.getmtime(outpath) if outdate < os.path.getmtime(os.path.join(dirname, texname)): return " (because .tex is newer than .pdf)" for deppath, extlist in data.deps: assert extlist == None if outdate < os.path.getmtime(os.path.join(dirname, deppath)): return " (because %s is newer than .pdf)"%deppath # check for errors and requests logname = jobname + ".log" if os.path.exists(os.path.join(dirname, logname)): logdata = analyseLog(dirname, jobname + ".log") if logdata.errors: return " (because of error(s) in .log file)" if logdata.requests: return " (because of request(s) in .log file)" # verbose force if opts.force: return " (because of --force)" # up to date if opts.verbosity > 2: print "%s: skipped because up-to-date"%os.path.normpath(os.path.join(dirname, texname)) return False MAXRUNS=5 def processTex(dirname, texname, data=None, reason=""): #strip .tex extension jobname=texname[:-4] jobpath = os.path.join(dirname,jobname) texpath = jobpath + ".tex" procfiles.append(os.path.normpath(texpath)) if data == None: data = analyseTex(dirname, texname) if opts.verbosity: print "processing %s%s..."%(os.path.normpath(texpath),reason) # list of temporary files to be removed later tmplist = [] # support for pstricks/plain latex: if data.needsPS: tex = "latex" else: tex = "pdflatex" # detect usage of german/ngerman and generate temporary files with rmlig realname = texname if data.rmligs: realname = preparermligs(texname, dirname, texpath, tmplist) # remove all outdated .bbl files (will be recreated by requests) bbls = bblfiles(dirname, jobname) for bib in data.bibfiles: bibpath = os.path.join(dirname, bib) bibtime = os.path.getmtime(bibpath) nextbbls = bbls[:] for bbl in bbls: bblpath = os.path.normpath(os.path.join(dirname, bbl)) bbltime = os.path.getmtime(bblpath) if (opts.force and not opts.singlerun) or bbltime < bibtime: reason = "" if opts.verbosity: if bbltime < bibtime: reason = " (because %s is newer than %s)"%(bib,bbl) else: reason = " (because of --force)" rmtempfile(bblpath, force=True, reason=reason) nextbbls.remove(bbl) bbls = nextbbls runtex(tex, texname, realname, dirname) if not opts.singlerun: # check for undefined references and run requests numrun=0 reqs=opts.act # ignore requests if programs are not run logname = jobname + ".log" while reqs: logdata = analyseLog(dirname, logname) reqs = logdata.requests if not reqs: break pri = reqs.values() pri.sort(reverse=True) lastp = pri[0] + 1 for p in pri: if p == lastp: continue lastp = p for req in reqs.keys(): rp = reqs[req] if rp == p: reason = "" if opts.verbosity: reason = logdata.requestreason[req] if not reason: reason = " (because of request in .log file, priority %d)"%rp if req == "latex": runtex(tex, texname, realname, dirname, reason=reason) elif isinstance(req, tuple): run(req, dirname, reason) else: error(jobpath+".log", "unsupported request: %s"%req) numrun+=1 if numrun==MAXRUNS: error(texpath, "does not stabilise after %i runs"%MAXRUNS) break # update index if it exists - no way of knowing if it was updated if os.path.exists(jobpath + ".idx"): run(["makeindex", jobname], dirname, reason=" (because .idx file might have changed)") runtex(tex, texname, realname, dirname, reason=" (because .ind file might have changed)") if data.needsPS: run(["dvips", jobname+".dvi"], dirname, stderr=subprocess.STDOUT) run(["ps2pdf", jobname+".ps"], dirname) rmtempfile(tmplist) def bblfiles(dirname, jobname): files = dircache.listdir(dirname) return fnmatch.filter(files, "%s*.bbl"%jobname) def rmtempfile(filenames, force=False, reason=""): if (opts.deletetempfiles or force) and filenames: if not isinstance(filenames, list): filenames = [filenames] if opts.verbosity > 1: print " removing %s%s"%(", ".join(filenames), reason) if opts.act: for f in filenames: if os.path.exists(f): os.remove(f) else: error(f, "could not be removed (does not exist)", warning=True) def run(arglist, dirname, reason="", inf=None, outf=None, stderr=None): """Run a command with optional io redirections. arglist -- list of progam name and arguments dirname -- the directory the command will be executed in reason -- reason for running this command, used in progress output inf -- file to be used as stdin, or alternativly a filter function for user input outf -- file to be used as stdout, or alternativly a filter function for program output stderr -- file to be used as stderr """ if opts.verbosity: redir ="" if hasattr(inf, "name") and inf.name != "" and inf.name[0] != "<": name = relpath(inf.name, dirname) redir = " < %s"%name print " running %s%s%s..."%(" ".join(arglist), redir, reason) if opts.act: master, slave = None, None if isinstance(inf, file): stdin = inf inf = None elif opts.showoutput and not iswindows: master, slave = pty.openpty() master = os.fdopen(master, "rw") stdin=slave elif inf or opts.showoutput: stdin=subprocess.PIPE else: stdin=null if isinstance(outf, file): stdout = outf outf = None elif opts.showoutput and not iswindows: if slave == None: master, slave = pty.openpty() master = os.fdopen(master, "rw") stdout=slave elif outf or opts.showoutput: stdout=subprocess.PIPE else: stdout=null try: proc = subprocess.Popen(arglist, stdin=stdin, stdout=stdout, stderr=stderr, cwd=dirname) if stdin == slave and stdin != None: childin = master else: childin = proc.stdin if stdout == slave and stdout != None: childout = master else: childout = proc.stdout selectlist = [] outlist = [] if childin != None: selectlist.append(sys.stdin) if childout != None: selectlist.append(childout) outlist.append(childout) while proc.poll() == None or select.select(outlist, [], [], 0)[0]: ready = select.select(selectlist, [], [], 0.1)[0] for f in ready: if f == childout: data = os.read(childout.fileno(), 1024) if data == "": # EOF outlist.remove(childout) selectlist.remove(childout) continue if outf: data = outf(data) if data != None: sys.stdout.write(data) sys.stdout.flush() if f == sys.stdin: if proc.poll() != None: # process terminated, does not take input anymore selectlist.remove(sys.stdin) continue data = os.read(sys.stdin.fileno(), 1024) if data == "": # EOF selectlist.remove(sys.stdin) continue if inf: data = inf(data) if data != None: childin.write(data) childin.flush() ret = proc.wait() if ret: error(dirname, "failed command: %s returned %i"%(" ".join(arglist), ret)) except (OSError, IOError), e: error(dirname, "failed command: %s: %s"%(" ".join(arglist),str(e))) def runtex(tex, texname, realname, dirname, reason=""): if not opts.showoutput: outf = None else: def outf(d): return re_rmligsfile.sub("\\1.tex", d) if texname != realname: run([tex, "-interaction", opts.interactionmode, "-jobname", texname[:-4], realname], dirname, outf=outf, reason=reason) else: run([tex, "-interaction", opts.interactionmode, texname], dirname, outf=outf, reason=reason) def preparermligs(texname, dirname, texpath, tmplist): try: texfile = open(texpath) except IOError, (errno, strerror): error(texpath, "could not be read: %s. Not using rmligs."%strerror) return texname else: try: (subdir, texbasename) = os.path.split(texname) rmligsbasename = "%s-rmligs.tex"%texbasename[:-4] rmligsname = os.path.join(subdir, rmligsbasename) rmligspath = os.path.join(dirname, rmligsname) if opts.act: rmligsfile = open(rmligspath, "w") else: rmligsfile = null except IOError, (errno, strerror): error(rmligspath, "could not be written: %s. Not using rmligs."%strerror) return texname else: tmplist.append(rmligspath) def rewriteinputrmligs(m): name = m.group(2) if not os.path.exists(os.path.join(dirname,name)): name+=".tex" path = os.path.join(dirname, name) if os.path.exists(path): name = preparermligs(name, dirname, path, tmplist) else: error(texpath, "%s: File could not be located. Not using rmligs."%name, warning=True) return "\\%s{%s}"%(m.group(1), name[:-4]) def filteroutput(d): d = re_commentinclude.sub("", d) rmligsfile.write(re_inputinclude.sub(rewriteinputrmligs, d)) return None run([rmligs_name, "-f"], dirname, inf=texfile, outf=filteroutput) texfile.close() if rmligsfile is not null: rmligsfile.close() return rmligsname # main program: def main(arguments): if not arguments: arguments = (".",) for dirname, texname in alltexfiles(arguments): data = analyseTex(dirname, texname) reason = needsupdate(dirname, texname, data) if reason: processTex(dirname, texname, data, reason) if opts.summary in [ "files", "both" ] : if procfiles: print "Processed the following files:" for f in procfiles: print " %s"%f else: print "Processed no files." if opts.summary in [ "failures", "both" ] : if errors: print "The following problems occured:" for f in errors: print " %s"%f if errors: sys.exit(1) try: if __name__ == "__main__": # setup global variables opts, programargs = TexallOptionParser().parse_args() else: opts, programargs = TexallOptionParser().parse_args([]) re_ignore = None if opts.ignorepattern != "": re_ignore = re.compile(opts.ignorepattern) re_texfile = re.compile(".*\.tex$", re.I) if iswindows: re_systemfile = re.compile("^?:\\\\Program", re.I) re_nonlocal = re.compile("^?:\\\\") else: re_systemfile = re.compile("^/usr/") re_nonlocal = re.compile("^/") re_texcomment = re.compile("(?