mkhowto 23.2 KB
Newer Older
1 2 3 4 5
#! /usr/bin/env python
#  -*- Python -*-
"""usage: %(program)s [options...] file ...

Options specifying formats to build:
6 7
    --html		HyperText Markup Language (default)
    --pdf		Portable Document Format
8 9 10 11 12 13 14 15
    --ps		PostScript
    --dvi		'DeVice Indepentent' format from TeX
    --text		ASCII text (requires lynx)

    More than one output format may be specified, or --all.

HTML options:
    --address, -a	Specify an address for page footers.
16
    --dir		Specify the directory for HTML output.
17 18
    --link		Specify the number of levels to include on each page.
    --split, -s		Specify a section level for page splitting, default: %(max_split_depth)s.
19
    --iconserver, -i	Specify location of icons (default: ./).
20
    --image-type	Specify the image type to use in HTML output;
21
                        values: gif, png (default).
22 23
    --numeric           Don't rename the HTML files; just keep node#.html for
                        the filenames.
24 25
    --style             Specify the CSS file to use for the output (filename,
                        not a URL).
26 27
    --up-link           URL to a parent document.
    --up-title          Title of a parent document.
28
    --favicon           Icon to display in the browsers location bar.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

Other options:
    --a4		Format for A4 paper.
    --letter		Format for US letter paper (the default).
    --help, -H		Show this text.
    --logging, -l	Log stdout and stderr to a file (*.how).
    --debugging, -D	Echo commands as they are executed.
    --keep, -k		Keep temporary files around.
    --quiet, -q		Do not print command output to stdout.
			(stderr is also lost,  sorry; see *.how for errors)
"""

import getopt
import glob
import os
44
import re
45 46 47 48
import shutil
import sys


49 50
MYDIR = os.path.abspath(sys.path[0])
TOPDIR = os.path.dirname(MYDIR)
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

ISTFILE = os.path.join(TOPDIR, "texinputs", "python.ist")
NODE2LABEL_SCRIPT = os.path.join(MYDIR, "node2label.pl")
L2H_INIT_FILE = os.path.join(TOPDIR, "perl", "l2hinit.perl")

BIBTEX_BINARY = "bibtex"
DVIPS_BINARY = "dvips"
LATEX_BINARY = "latex"
LATEX2HTML_BINARY = "latex2html"
LYNX_BINARY = "lynx"
MAKEINDEX_BINARY = "makeindex"
PDFLATEX_BINARY = "pdflatex"
PERL_BINARY = "perl"
PYTHON_BINARY = "python"


67 68
def usage(options, file):
    print >>file, __doc__ % options
69 70

def error(options, message, err=2):
71 72 73
    print >>sys.stderr, message
    print >>sys.stderr
    usage(options, sys.stderr)
74 75 76 77 78 79 80
    sys.exit(2)


class Options:
    program = os.path.basename(sys.argv[0])
    #
    address = ''
81
    builddir = None
82 83 84
    debugging = 0
    discard_temps = 1
    have_temps = 0
85
    icon_server = "."
86
    image_type = "png"
87 88 89 90 91
    logging = 0
    max_link_depth = 3
    max_split_depth = 6
    paper = "letter"
    quiet = 0
92
    runs = 0
93
    numeric = 0
94
    global_module_index = None
95
    style_file = os.path.join(TOPDIR, "html", "style.css")
96
    about_file = os.path.join(TOPDIR, "html", "about.dat")
97 98
    up_link = None
    up_title = None
99
    favicon = None
100
    #
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    # 'dvips_safe' is a weird option.  It is used mostly to make
    # LaTeX2HTML not try to be too smart about protecting the user
    # from a bad version of dvips -- some versions would core dump if
    # the path to the source DVI contained a dot, and it's appearantly
    # difficult to determine if the version available has that bug.
    # This option gets set when PostScript output is requested
    # (because we're going to run dvips regardless, and we'll either
    # know it succeeds before LaTeX2HTML is run, or we'll have
    # detected the failure and bailed), or the user asserts that it's
    # safe from the command line.
    #
    # So, why does LaTeX2HTML think it appropriate to protect the user
    # from a dvips that's only potentially going to core dump?  Only
    # because they want to avoid doing a lot of work just to have to
    # bail later with no useful intermediates.  Unfortunately, they
    # bail *before* they know whether dvips will be needed at all.
    # I've gone around the bush a few times with the LaTeX2HTML
    # developers over whether this is appropriate behavior, and they
    # don't seem interested in changing their position.
    #
    dvips_safe = 0
    #
123
    DEFAULT_FORMATS = ("html",)
124 125 126 127
    ALL_FORMATS = ("dvi", "html", "pdf", "ps", "text")

    def __init__(self):
        self.formats = []
128
        self.l2h_init_files = []
129 130 131 132 133 134 135 136 137

    def __getitem__(self, key):
        # This is used when formatting the usage message.
        try:
            return getattr(self, key)
        except AttributeError:
            raise KeyError, key

    def parse(self, args):
138
        opts, args = getopt.getopt(args, "Hi:a:s:lDkqr:",
139
                                   ["all", "postscript", "help", "iconserver=",
140
                                    "address=", "a4", "letter", "l2h-init=",
141
                                    "link=", "split=", "logging", "debugging",
142
                                    "keep", "quiet", "runs=", "image-type=",
143 144
                                    "about=", "numeric", "style=", "paper=",
                                    "up-link=", "up-title=", "dir=",
145 146
                                    "global-module-index=", "dvips-safe",
                                    "favicon="]
147
                                   + list(self.ALL_FORMATS))
148 149 150
        for opt, arg in opts:
            if opt == "--all":
                self.formats = list(self.ALL_FORMATS)
151
                self.dvips_safe = "ps" in self.formats
152
            elif opt in ("-H", "--help"):
153
                usage(self, sys.stdout)
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
                sys.exit()
            elif opt == "--iconserver":
                self.icon_server = arg
            elif opt in ("-a", "--address"):
                self.address = arg
            elif opt == "--a4":
                self.paper = "a4"
            elif opt == "--letter":
                self.paper = "letter"
            elif opt == "--link":
                self.max_link_depth = int(arg)
            elif opt in ("-s", "--split"):
                self.max_split_depth = int(arg)
            elif opt in ("-l", "--logging"):
                self.logging = self.logging + 1
            elif opt in ("-D", "--debugging"):
                self.debugging = self.debugging + 1
            elif opt in ("-k", "--keep"):
                self.discard_temps = 0
            elif opt in ("-q", "--quiet"):
                self.quiet = 1
175 176 177 178
            elif opt in ("-r", "--runs"):
                self.runs = int(arg)
            elif opt == "--image-type":
                self.image_type = arg
179 180 181
            elif opt == "--about":
                # always make this absolute:
                self.about_file = os.path.normpath(
182
                    os.path.abspath(arg))
183 184
            elif opt == "--numeric":
                self.numeric = 1
185 186
            elif opt == "--style":
                self.style_file = os.path.abspath(arg)
187 188
            elif opt == "--l2h-init":
                self.l2h_init_files.append(os.path.abspath(arg))
189 190
            elif opt == "--favicon":
                self.favicon = arg
191 192 193 194
            elif opt == "--up-link":
                self.up_link = arg
            elif opt == "--up-title":
                self.up_title = arg
195 196
            elif opt == "--global-module-index":
                self.global_module_index = arg
197
            elif opt == "--dir":
198
                if os.sep == "\\":
199
                    arg = re.sub("/", "\\\\", arg)
200
                self.builddir = os.path.expanduser(arg)
201 202
            elif opt == "--paper":
                self.paper = arg
203 204
            elif opt == "--dvips-safe":
                self.dvips_safe = 1
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
            #
            # Format specifiers:
            #
            elif opt[2:] in self.ALL_FORMATS:
                self.add_format(opt[2:])
            elif opt == "--postscript":
                # synonym for --ps
                self.add_format("ps")
        self.initialize()
        #
        # return the args to allow the caller access:
        #
        return args

    def add_format(self, format):
        """Add a format to the formats list if not present."""
        if not format in self.formats:
222 223 224
            if format == "ps":
                # assume this is safe since we're going to run it anyway
                self.dvips_safe = 1
225 226 227 228 229 230 231 232
            self.formats.append(format)

    def initialize(self):
        """Complete initialization.  This is needed if parse() isn't used."""
        # add the default format if no formats were specified:
        if not self.formats:
            self.formats = self.DEFAULT_FORMATS
        # determine the base set of texinputs directories:
Fred Drake's avatar
Fred Drake committed
233
        texinputs = os.environ.get("TEXINPUTS", "").split(os.pathsep)
234 235
        if not texinputs:
            texinputs = ['']
Fred Drake's avatar
Fred Drake committed
236 237 238 239 240 241 242 243 244
        mydirs = [os.path.join(TOPDIR, "paper-" + self.paper),
                  os.path.join(TOPDIR, "texinputs"),
                  ]
        if '' in texinputs:
            i = texinputs.index('')
            texinputs[i:i] = mydirs
        else:
            texinputs += mydirs
        self.base_texinputs = texinputs
245 246
        if self.builddir:
            self.builddir = os.path.abspath(self.builddir)
247 248 249


class Job:
250 251
    latex_runs = 0

252 253
    def __init__(self, options, path):
        self.options = options
254
        self.doctype = get_doctype(path)
255
        self.filedir, self.doc = split_pathname(path)
256
        self.builddir = os.path.abspath(options.builddir or self.doc)
257 258 259 260 261 262
        if ("html" in options.formats or "text" in options.formats):
            if not os.path.exists(self.builddir):
                os.mkdir(self.builddir)
            self.log_filename = os.path.join(self.builddir, self.doc + ".how")
        else:
            self.log_filename = os.path.abspath(self.doc + ".how")
263 264
        if os.path.exists(self.log_filename):
            os.unlink(self.log_filename)
265 266 267 268 269 270
        l2hconf = self.doc + ".l2h"
        if os.path.exists(l2hconf):
            if os.path.exists(l2hconf + "~"):
                os.unlink(l2hconf + "~")
            os.rename(l2hconf, l2hconf + "~")
        self.l2h_aux_init_file = self.doc + ".l2h"
271 272 273 274 275 276 277 278 279 280 281 282 283
        self.write_l2h_aux_init_file()

    def build(self):
        self.setup_texinputs()
        formats = self.options.formats
        if "dvi" in formats or "ps" in formats:
            self.build_dvi()
        if "pdf" in formats:
            self.build_pdf()
        if "ps" in formats:
            self.build_ps()
        if "html" in formats:
            self.require_temps()
284
            self.build_html(self.builddir)
285
            if self.options.icon_server == ".":
286 287 288 289 290 291 292 293
                pattern = os.path.join(TOPDIR, "html", "icons",
                                       "*." + self.options.image_type)
                imgs = glob.glob(pattern)
                if not imgs:
                    self.warning(
                        "Could not locate support images of type %s."
                        % `self.options.image_type`)
                for fn in imgs:
294
                    new_fn = os.path.join(self.builddir, os.path.basename(fn))
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
                    shutil.copyfile(fn, new_fn)
        if "text" in formats:
            self.require_temps()
            tempdir = self.doc
            need_html = "html" not in formats
            if self.options.max_split_depth != 1:
                fp = open(self.l2h_aux_init_file, "a")
                fp.write("# re-hack this file for --text:\n")
                l2hoption(fp, "MAX_SPLIT_DEPTH", "1")
                fp.write("1;\n")
                fp.close()
                tempdir = self.doc + "-temp-html"
                need_html = 1
            if need_html:
                self.build_html(tempdir, max_split_depth=1)
            self.build_text(tempdir)
        if self.options.discard_temps:
            self.cleanup()

    def setup_texinputs(self):
Fred Drake's avatar
Fred Drake committed
315 316
        texinputs = [self.filedir] + self.options.base_texinputs
        os.environ["TEXINPUTS"] = os.pathsep.join(texinputs)
317
        self.message("TEXINPUTS=" + os.environ["TEXINPUTS"])
318 319 320 321 322 323 324 325

    def build_aux(self, binary=None):
        if binary is None:
            binary = LATEX_BINARY
        new_index(   "%s.ind" % self.doc, "genindex")
        new_index("mod%s.ind" % self.doc, "modindex")
        self.run("%s %s" % (binary, self.doc))
        self.use_bibtex = check_for_bibtex(self.doc + ".aux")
326
        self.latex_runs = 1
327 328 329 330 331 332 333 334 335

    def build_dvi(self):
        self.use_latex(LATEX_BINARY)

    def build_pdf(self):
        self.use_latex(PDFLATEX_BINARY)

    def use_latex(self, binary):
        self.require_temps(binary=binary)
336 337 338
        if self.latex_runs < 2:
            if os.path.isfile("mod%s.idx" % self.doc):
                self.run("%s mod%s.idx" % (MAKEINDEX_BINARY, self.doc))
339
            use_indfix = 0
340
            if os.path.isfile(self.doc + ".idx"):
341
                use_indfix = 1
342 343 344 345 346 347 348 349
                # call to Doc/tools/fix_hack omitted; doesn't appear necessary
                self.run("%s %s.idx" % (MAKEINDEX_BINARY, self.doc))
                import indfix
                indfix.process(self.doc + ".ind")
            if self.use_bibtex:
                self.run("%s %s" % (BIBTEX_BINARY, self.doc))
            self.process_synopsis_files()
            self.run("%s %s" % (binary, self.doc))
350
            self.latex_runs = self.latex_runs + 1
351 352 353
            if os.path.isfile("mod%s.idx" % self.doc):
                self.run("%s -s %s mod%s.idx"
                         % (MAKEINDEX_BINARY, ISTFILE, self.doc))
354
            if use_indfix:
355 356
                self.run("%s -s %s %s.idx"
                         % (MAKEINDEX_BINARY, ISTFILE, self.doc))
357
                indfix.process(self.doc + ".ind")
358
            self.process_synopsis_files()
359 360 361
        #
        # and now finish it off:
        #
362 363
        if os.path.isfile(self.doc + ".toc") and binary == PDFLATEX_BINARY:
            import toc2bkm
364 365 366 367 368
            if self.doctype == "manual":
                bigpart = "chapter"
            else:
                bigpart = "section"
            toc2bkm.process(self.doc + ".toc", self.doc + ".bkm", bigpart)
369 370 371
        if self.use_bibtex:
            self.run("%s %s" % (BIBTEX_BINARY, self.doc))
        self.run("%s %s" % (binary, self.doc))
372
        self.latex_runs = self.latex_runs + 1
373 374 375 376 377 378

    def process_synopsis_files(self):
        synopsis_files = glob.glob(self.doc + "*.syn")
        for path in synopsis_files:
            uniqify_module_table(path)

379 380 381
    def build_ps(self):
        self.run("%s -N0 -o %s.ps %s" % (DVIPS_BINARY, self.doc, self.doc))

382
    def build_html(self, builddir, max_split_depth=None):
383 384 385
        if max_split_depth is None:
            max_split_depth = self.options.max_split_depth
        texfile = None
Fred Drake's avatar
Fred Drake committed
386
        for p in os.environ["TEXINPUTS"].split(os.pathsep):
387 388 389 390 391
            fn = os.path.join(p, self.doc + ".tex")
            if os.path.isfile(fn):
                texfile = fn
                break
        if not texfile:
392
            self.warning("Could not locate %s.tex; aborting." % self.doc)
393 394 395 396 397
            sys.exit(1)
        # remove leading ./ (or equiv.); might avoid problems w/ dvips
        if texfile[:2] == os.curdir + os.sep:
            texfile = texfile[2:]
        # build the command line and run LaTeX2HTML:
398 399
        if not os.path.isdir(builddir):
            os.mkdir(builddir)
400 401 402
        else:
            for fname in glob.glob(os.path.join(builddir, "*.html")):
                os.unlink(fname)
403 404 405 406 407
        args = [LATEX2HTML_BINARY,
                "-init_file", self.l2h_aux_init_file,
                "-dir", builddir,
                texfile
                ]
Fred Drake's avatar
Fred Drake committed
408
        self.run(" ".join(args))     # XXX need quoting!
409 410 411
        # ... postprocess
        shutil.copyfile(self.options.style_file,
                        os.path.join(builddir, self.doc + ".css"))
412 413
        shutil.copyfile(os.path.join(builddir, self.doc + ".html"),
                        os.path.join(builddir, "index.html"))
414
        if max_split_depth != 1:
415 416 417 418 419 420 421 422 423 424
            label_file = os.path.join(builddir, "labels.pl")
            fp = open(label_file)
            about_node = None
            target = " = q/about/;\n"
            x = len(target)
            while 1:
                line = fp.readline()
                if not line:
                    break
                if line[-x:] == target:
425
                    line = fp.readline()
426 427 428 429 430 431
                    m = re.search(r"\|(node\d+\.[a-z]+)\|", line)
                    about_node = m.group(1)
                    shutil.copyfile(os.path.join(builddir, about_node),
                                    os.path.join(builddir, "about.html"))
                    break
            if not self.options.numeric:
432 433 434 435 436 437
                pwd = os.getcwd()
                try:
                    os.chdir(builddir)
                    self.run("%s %s *.html" % (PERL_BINARY, NODE2LABEL_SCRIPT))
                finally:
                    os.chdir(pwd)
438 439 440 441 442
        # These files need to be cleaned up here since builddir there
        # can be more than one, so we clean each of them.
        if self.options.discard_temps:
            for fn in ("images.tex", "images.log", "images.aux"):
                safe_unlink(os.path.join(builddir, fn))
443 444 445 446 447 448 449 450 451

    def build_text(self, tempdir=None):
        if tempdir is None:
            tempdir = self.doc
        indexfile = os.path.join(tempdir, "index.html")
        self.run("%s -nolist -dump %s >%s.txt"
                 % (LYNX_BINARY, indexfile, self.doc))

    def require_temps(self, binary=None):
452
        if not self.latex_runs:
453 454 455
            self.build_aux(binary=binary)

    def write_l2h_aux_init_file(self):
456
        options = self.options
457
        fp = open(self.l2h_aux_init_file, "w")
458 459 460 461 462
        d = string_to_perl(os.path.dirname(L2H_INIT_FILE))
        fp.write("package main;\n"
                 "push (@INC, '%s');\n"
                 "$mydir = '%s';\n"
                 % (d, d))
463
        fp.write(open(L2H_INIT_FILE).read())
464 465 466 467 468
        for filename in options.l2h_init_files:
            fp.write("\n# initialization code incorporated from:\n# ")
            fp.write(filename)
            fp.write("\n")
            fp.write(open(filename).read())
469 470
        fp.write("\n"
                 "# auxillary init file for latex2html\n"
471
                 "# generated by mkhowto\n"
472
                 "$NO_AUTO_LINK = 1;\n"
473
                 )
474
        l2hoption(fp, "ABOUT_FILE", options.about_file)
475
        l2hoption(fp, "ICONSERVER", options.icon_server)
476
        l2hoption(fp, "IMAGE_TYPE", options.image_type)
477 478 479
        l2hoption(fp, "ADDRESS", options.address)
        l2hoption(fp, "MAX_LINK_DEPTH", options.max_link_depth)
        l2hoption(fp, "MAX_SPLIT_DEPTH", options.max_split_depth)
480 481
        l2hoption(fp, "EXTERNAL_UP_LINK", options.up_link)
        l2hoption(fp, "EXTERNAL_UP_TITLE", options.up_title)
482
        l2hoption(fp, "FAVORITES_ICON", options.favicon)
483
        l2hoption(fp, "GLOBAL_MODULE_INDEX", options.global_module_index)
484
        l2hoption(fp, "DVIPS_SAFE", options.dvips_safe)
485 486 487 488 489 490
        fp.write("1;\n")
        fp.close()

    def cleanup(self):
        self.__have_temps = 0
        for pattern in ("%s.aux", "%s.log", "%s.out", "%s.toc", "%s.bkm",
491
                        "%s.idx", "%s.ilg", "%s.ind", "%s.pla",
492 493 494 495
                        "%s.bbl", "%s.blg",
                        "mod%s.idx", "mod%s.ind", "mod%s.ilg",
                        ):
            safe_unlink(pattern % self.doc)
496
        map(safe_unlink, glob.glob(self.doc + "*.syn"))
497 498 499 500 501 502 503 504 505 506 507 508 509
        for spec in ("IMG*", "*.pl", "WARNINGS", "index.dat", "modindex.dat"):
            pattern = os.path.join(self.doc, spec)
            map(safe_unlink, glob.glob(pattern))
        if "dvi" not in self.options.formats:
            safe_unlink(self.doc + ".dvi")
        if os.path.isdir(self.doc + "-temp-html"):
            shutil.rmtree(self.doc + "-temp-html", ignore_errors=1)
        if not self.options.logging:
            os.unlink(self.log_filename)
        if not self.options.debugging:
            os.unlink(self.l2h_aux_init_file)

    def run(self, command):
510
        self.message(command)
511 512 513 514 515
        if sys.platform.startswith("win"):
            rc = os.system(command)
        else:
            rc = os.system("(%s) </dev/null >>%s 2>&1"
                           % (command, self.log_filename))
516
        if rc:
517 518
            self.warning(
                "Session transcript and error messages are in %s."
519
                % self.log_filename)
520
            result = 1
521 522
            if hasattr(os, "WIFEXITED"):
                if os.WIFEXITED(rc):
523 524
                    result = os.WEXITSTATUS(rc)
                    self.warning("Exited with status %s." % result)
525 526 527 528
                else:
                    self.warning("Killed by signal %s." % os.WSTOPSIG(rc))
            else:
                self.warning("Return code: %s" % rc)
529 530 531
            sys.stderr.write("The relevant lines from the transcript are:\n")
            sys.stderr.write("-" * 72 + "\n")
            sys.stderr.writelines(get_run_transcript(self.log_filename))
532
            sys.exit(result)
533

534 535 536 537
    def message(self, msg):
        msg = "+++ " + msg
        if not self.options.quiet:
            print msg
538 539 540 541 542 543 544 545
        self.log(msg + "\n")

    def warning(self, msg):
        msg = "*** %s\n" % msg
        sys.stderr.write(msg)
        self.log(msg)

    def log(self, msg):
546
        fp = open(self.log_filename, "a")
547
        fp.write(msg)
548 549
        fp.close()

550

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
def get_run_transcript(filename):
    """Return lines from the transcript file for the most recent run() call."""
    fp = open(filename)
    lines = fp.readlines()
    fp.close()
    lines.reverse()
    L = []
    for line in lines:
        L.append(line)
        if line[:4] == "+++ ":
            break
    L.reverse()
    return L


566
def safe_unlink(path):
567
    """Unlink a file without raising an error if it doesn't exist."""
568 569 570 571 572 573
    try:
        os.unlink(path)
    except os.error:
        pass


574
def split_pathname(path):
575
    path = os.path.abspath(path)
576
    dirname, basename = os.path.split(path)
577 578 579 580 581
    if basename[-4:] == ".tex":
        basename = basename[:-4]
    return dirname, basename


582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
_doctype_rx = re.compile(r"\\documentclass(?:\[[^]]*\])?{([a-zA-Z]*)}")
def get_doctype(path):
    fp = open(path)
    doctype = None
    while 1:
        line = fp.readline()
        if not line:
            break
        m = _doctype_rx.match(line)
        if m:
            doctype = m.group(1)
            break
    fp.close()
    return doctype


598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
def main():
    options = Options()
    try:
        args = options.parse(sys.argv[1:])
    except getopt.error, msg:
        error(options, msg)
    if not args:
        # attempt to locate single .tex file in current directory:
        args = glob.glob("*.tex")
        if not args:
            error(options, "No file to process.")
        if len(args) > 1:
            error(options, "Could not deduce which files should be processed.")
    #
    # parameters are processed, let's go!
    #
    for path in args:
        Job(options, path).build()


def l2hoption(fp, option, value):
    if value:
        fp.write('$%s = "%s";\n' % (option, string_to_perl(str(value))))


_to_perl = {}
for c in map(chr, range(1, 256)):
    _to_perl[c] = c
_to_perl["@"] = "\\@"
_to_perl["$"] = "\\$"
_to_perl['"'] = '\\"'

def string_to_perl(s):
Fred Drake's avatar
Fred Drake committed
631
    return ''.join(map(_to_perl.get, s))
632 633 634 635


def check_for_bibtex(filename):
    fp = open(filename)
Fred Drake's avatar
Fred Drake committed
636
    pos = fp.read().find(r"\bibdata{")
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    fp.close()
    return pos >= 0

def uniqify_module_table(filename):
    lines = open(filename).readlines()
    if len(lines) > 1:
        if lines[-1] == lines[-2]:
            del lines[-1]
    open(filename, "w").writelines(lines)


def new_index(filename, label="genindex"):
    fp = open(filename, "w")
    fp.write(r"""\
\begin{theindex}
\label{%s}
\end{theindex}
""" % label)
    fp.close()


if __name__ == "__main__":
    main()