3 """usage: %(program)s [options...] file ...
5 Options specifying formats to build:
6 --html HyperText Markup Language
7 --pdf Portable Document Format (default)
9 --dvi 'DeVice Indepentent' format from TeX
10 --text ASCII text (requires lynx)
12 More than one output format may be specified, or --all.
15 --address, -a Specify an address for page footers.
16 --link Specify the number of levels to include on each page.
17 --split, -s Specify a section level for page splitting, default: %(max_split_depth)s.
18 --iconserver, -i Specify location of icons (default: ../).
19 --image-type Specify the image type to use in HTML output;
20 values: gif (default), png.
21 --numeric Don't rename the HTML files; just keep node#.html for
23 --style Specify the CSS file to use for the output (filename,
25 --up-link URL to a parent document.
26 --up-title Title of a parent document.
29 --a4 Format for A4 paper.
30 --letter Format for US letter paper (the default).
31 --help, -H Show this text.
32 --logging, -l Log stdout and stderr to a file (*.how).
33 --debugging, -D Echo commands as they are executed.
34 --keep, -k Keep temporary files around.
35 --quiet, -q Do not print command output to stdout.
36 (stderr is also lost, sorry; see *.how for errors)
49 MYDIR
= os
.path
.abspath(sys
.path
[0])
50 TOPDIR
= os
.path
.dirname(MYDIR
)
52 ISTFILE
= os
.path
.join(TOPDIR
, "texinputs", "python.ist")
53 NODE2LABEL_SCRIPT
= os
.path
.join(MYDIR
, "node2label.pl")
54 L2H_INIT_FILE
= os
.path
.join(TOPDIR
, "perl", "l2hinit.perl")
56 BIBTEX_BINARY
= "bibtex"
57 DVIPS_BINARY
= "dvips"
58 LATEX_BINARY
= "latex"
59 LATEX2HTML_BINARY
= "latex2html"
61 MAKEINDEX_BINARY
= "makeindex"
62 PDFLATEX_BINARY
= "pdflatex"
64 PYTHON_BINARY
= "python"
68 print __doc__
% options
70 def error(options
, message
, err
=2):
71 sys
.stdout
= sys
.stderr
79 program
= os
.path
.basename(sys
.argv
[0])
94 style_file
= os
.path
.join(TOPDIR
, "html", "style.css")
95 about_file
= os
.path
.join(TOPDIR
, "html", "about.dat")
99 DEFAULT_FORMATS
= ("pdf",)
100 ALL_FORMATS
= ("dvi", "html", "pdf", "ps", "text")
104 self
.l2h_init_files
= []
106 def __getitem__(self
, key
):
107 # This is used when formatting the usage message.
109 return getattr(self
, key
)
110 except AttributeError:
113 def parse(self
, args
):
114 opts
, args
= getopt
.getopt(args
, "Hi:a:s:lDkqr:",
115 ["all", "postscript", "help", "iconserver=",
116 "address=", "a4", "letter", "l2h-init=",
117 "link=", "split=", "logging", "debugging",
118 "keep", "quiet", "runs=", "image-type=",
119 "about=", "numeric", "style=",
120 "up-link=", "up-title="]
121 + list(self
.ALL_FORMATS
))
122 for opt
, arg
in opts
:
124 self
.formats
= list(self
.ALL_FORMATS
)
125 elif opt
in ("-H", "--help"):
128 elif opt
== "--iconserver":
129 self
.icon_server
= arg
130 elif opt
in ("-a", "--address"):
134 elif opt
== "--letter":
135 self
.paper
= "letter"
136 elif opt
== "--link":
137 self
.max_link_depth
= int(arg
)
138 elif opt
in ("-s", "--split"):
139 self
.max_split_depth
= int(arg
)
140 elif opt
in ("-l", "--logging"):
141 self
.logging
= self
.logging
+ 1
142 elif opt
in ("-D", "--debugging"):
143 self
.debugging
= self
.debugging
+ 1
144 elif opt
in ("-k", "--keep"):
145 self
.discard_temps
= 0
146 elif opt
in ("-q", "--quiet"):
148 elif opt
in ("-r", "--runs"):
150 elif opt
== "--image-type":
151 self
.image_type
= arg
152 elif opt
== "--about":
153 # always make this absolute:
154 self
.about_file
= os
.path
.normpath(
155 os
.path
.abspath(arg
))
156 elif opt
== "--numeric":
158 elif opt
== "--style":
159 self
.style_file
= os
.path
.abspath(arg
)
160 elif opt
== "--l2h-init":
161 self
.l2h_init_files
.append(os
.path
.abspath(arg
))
162 elif opt
== "--up-link":
164 elif opt
== "--up-title":
169 elif opt
[2:] in self
.ALL_FORMATS
:
170 self
.add_format(opt
[2:])
171 elif opt
== "--postscript":
173 self
.add_format("ps")
176 # return the args to allow the caller access:
180 def add_format(self
, format
):
181 """Add a format to the formats list if not present."""
182 if not format
in self
.formats
:
183 self
.formats
.append(format
)
185 def initialize(self
):
186 """Complete initialization. This is needed if parse() isn't used."""
187 # add the default format if no formats were specified:
189 self
.formats
= self
.DEFAULT_FORMATS
190 # determine the base set of texinputs directories:
191 texinputs
= string
.split(os
.environ
.get("TEXINPUTS", ""), os
.pathsep
)
194 self
.base_texinputs
= [
195 os
.path
.join(TOPDIR
, "paper-" + self
.paper
),
196 os
.path
.join(TOPDIR
, "texinputs"),
203 def __init__(self
, options
, path
):
204 self
.options
= options
205 self
.doctype
= get_doctype(path
)
206 self
.filedir
, self
.doc
= split_pathname(path
)
207 self
.log_filename
= self
.doc
+ ".how"
208 if os
.path
.exists(self
.log_filename
):
209 os
.unlink(self
.log_filename
)
210 if os
.path
.exists(self
.doc
+ ".l2h"):
211 self
.l2h_aux_init_file
= tempfile
.mktemp()
213 self
.l2h_aux_init_file
= self
.doc
+ ".l2h"
214 self
.write_l2h_aux_init_file()
217 self
.setup_texinputs()
218 formats
= self
.options
.formats
219 if "dvi" in formats
or "ps" in formats
:
225 if "html" in formats
:
227 self
.build_html(self
.doc
)
228 if self
.options
.icon_server
== ".":
229 pattern
= os
.path
.join(TOPDIR
, "html", "icons",
230 "*." + self
.options
.image_type
)
231 imgs
= glob
.glob(pattern
)
234 "Could not locate support images of type %s."
235 % `self
.options
.image_type`
)
237 new_fn
= os
.path
.join(self
.doc
, os
.path
.basename(fn
))
238 shutil
.copyfile(fn
, new_fn
)
239 if "text" in formats
:
242 need_html
= "html" not in formats
243 if self
.options
.max_split_depth
!= 1:
244 fp
= open(self
.l2h_aux_init_file
, "a")
245 fp
.write("# re-hack this file for --text:\n")
246 l2hoption(fp
, "MAX_SPLIT_DEPTH", "1")
249 tempdir
= self
.doc
+ "-temp-html"
252 self
.build_html(tempdir
, max_split_depth
=1)
253 self
.build_text(tempdir
)
254 if self
.options
.discard_temps
:
257 def setup_texinputs(self
):
258 texinputs
= [self
.filedir
] + list(self
.options
.base_texinputs
)
259 os
.environ
["TEXINPUTS"] = string
.join(texinputs
, os
.pathsep
)
260 self
.message("TEXINPUTS=" + os
.environ
["TEXINPUTS"])
262 def build_aux(self
, binary
=None):
264 binary
= LATEX_BINARY
265 new_index( "%s.ind" % self
.doc
, "genindex")
266 new_index("mod%s.ind" % self
.doc
, "modindex")
267 self
.run("%s %s" % (binary
, self
.doc
))
268 self
.use_bibtex
= check_for_bibtex(self
.doc
+ ".aux")
272 self
.use_latex(LATEX_BINARY
)
275 self
.use_latex(PDFLATEX_BINARY
)
277 def use_latex(self
, binary
):
278 self
.require_temps(binary
=binary
)
279 if self
.latex_runs
< 2:
280 if os
.path
.isfile("mod%s.idx" % self
.doc
):
281 self
.run("%s mod%s.idx" % (MAKEINDEX_BINARY
, self
.doc
))
282 if os
.path
.isfile(self
.doc
+ ".idx"):
283 # call to Doc/tools/fix_hack omitted; doesn't appear necessary
284 self
.run("%s %s.idx" % (MAKEINDEX_BINARY
, self
.doc
))
286 indfix
.process(self
.doc
+ ".ind")
288 self
.run("%s %s" % (BIBTEX_BINARY
, self
.doc
))
289 self
.process_synopsis_files()
291 # let the doctype-specific handler do some intermediate work:
293 self
.run("%s %s" % (binary
, self
.doc
))
295 if os
.path
.isfile("mod%s.idx" % self
.doc
):
296 self
.run("%s -s %s mod%s.idx"
297 % (MAKEINDEX_BINARY
, ISTFILE
, self
.doc
))
298 if os
.path
.isfile(self
.doc
+ ".idx"):
299 self
.run("%s -s %s %s.idx"
300 % (MAKEINDEX_BINARY
, ISTFILE
, self
.doc
))
301 self
.process_synopsis_files()
303 # and now finish it off:
305 if os
.path
.isfile(self
.doc
+ ".toc") and binary
== PDFLATEX_BINARY
:
307 if self
.doctype
== "manual":
311 toc2bkm
.process(self
.doc
+ ".toc", self
.doc
+ ".bkm", bigpart
)
313 self
.run("%s %s" % (BIBTEX_BINARY
, self
.doc
))
314 self
.run("%s %s" % (binary
, self
.doc
))
317 def process_synopsis_files(self
):
318 synopsis_files
= glob
.glob(self
.doc
+ "*.syn")
319 for path
in synopsis_files
:
320 uniqify_module_table(path
)
323 self
.run("%s -N0 -o %s.ps %s" % (DVIPS_BINARY
, self
.doc
, self
.doc
))
325 def build_html(self
, builddir
=None, max_split_depth
=None):
328 if max_split_depth
is None:
329 max_split_depth
= self
.options
.max_split_depth
331 for p
in string
.split(os
.environ
["TEXINPUTS"], os
.pathsep
):
332 fn
= os
.path
.join(p
, self
.doc
+ ".tex")
333 if os
.path
.isfile(fn
):
337 self
.warning("Could not locate %s.tex; aborting." % self
.doc
)
339 # remove leading ./ (or equiv.); might avoid problems w/ dvips
340 if texfile
[:2] == os
.curdir
+ os
.sep
:
341 texfile
= texfile
[2:]
342 # build the command line and run LaTeX2HTML:
343 if not os
.path
.isdir(builddir
):
346 for fname
in glob
.glob(os
.path
.join(builddir
, "*.html")):
348 args
= [LATEX2HTML_BINARY
,
349 "-init_file", self
.l2h_aux_init_file
,
353 self
.run(string
.join(args
)) # XXX need quoting!
355 shutil
.copyfile(self
.options
.style_file
,
356 os
.path
.join(builddir
, self
.doc
+ ".css"))
357 shutil
.copyfile(os
.path
.join(builddir
, self
.doc
+ ".html"),
358 os
.path
.join(builddir
, "index.html"))
359 if max_split_depth
!= 1 and not self
.options
.numeric
:
363 self
.run("%s %s *.html" % (PERL_BINARY
, NODE2LABEL_SCRIPT
))
367 def build_text(self
, tempdir
=None):
370 indexfile
= os
.path
.join(tempdir
, "index.html")
371 self
.run("%s -nolist -dump %s >%s.txt"
372 % (LYNX_BINARY
, indexfile
, self
.doc
))
374 def require_temps(self
, binary
=None):
375 if not self
.latex_runs
:
376 self
.build_aux(binary
=binary
)
378 def write_l2h_aux_init_file(self
):
379 options
= self
.options
380 fp
= open(self
.l2h_aux_init_file
, "w")
381 d
= string_to_perl(os
.path
.dirname(L2H_INIT_FILE
))
382 fp
.write("package main;\n"
383 "push (@INC, '%s');\n"
386 fp
.write(open(L2H_INIT_FILE
).read())
387 for filename
in options
.l2h_init_files
:
388 fp
.write("\n# initialization code incorporated from:\n# ")
391 fp
.write(open(filename
).read())
393 "# auxillary init file for latex2html\n"
394 "# generated by mkhowto\n"
395 "$NO_AUTO_LINK = 1;\n"
397 l2hoption(fp
, "ABOUT_FILE", options
.about_file
)
398 l2hoption(fp
, "ICONSERVER", options
.icon_server
)
399 l2hoption(fp
, "IMAGE_TYPE", options
.image_type
)
400 l2hoption(fp
, "ADDRESS", options
.address
)
401 l2hoption(fp
, "MAX_LINK_DEPTH", options
.max_link_depth
)
402 l2hoption(fp
, "MAX_SPLIT_DEPTH", options
.max_split_depth
)
403 l2hoption(fp
, "EXTERNAL_UP_LINK", options
.up_link
)
404 l2hoption(fp
, "EXTERNAL_UP_TITLE", options
.up_title
)
409 self
.__have
_temps
= 0
410 for pattern
in ("%s.aux", "%s.log", "%s.out", "%s.toc", "%s.bkm",
411 "%s.idx", "%s.ilg", "%s.ind", "%s.pla",
413 "mod%s.idx", "mod%s.ind", "mod%s.ilg",
415 safe_unlink(pattern
% self
.doc
)
416 map(safe_unlink
, glob
.glob(self
.doc
+ "*.syn"))
417 for spec
in ("IMG*", "*.pl", "WARNINGS", "index.dat", "modindex.dat"):
418 pattern
= os
.path
.join(self
.doc
, spec
)
419 map(safe_unlink
, glob
.glob(pattern
))
420 if "dvi" not in self
.options
.formats
:
421 safe_unlink(self
.doc
+ ".dvi")
422 if os
.path
.isdir(self
.doc
+ "-temp-html"):
423 shutil
.rmtree(self
.doc
+ "-temp-html", ignore_errors
=1)
424 if not self
.options
.logging
:
425 os
.unlink(self
.log_filename
)
426 if not self
.options
.debugging
:
427 os
.unlink(self
.l2h_aux_init_file
)
429 def run(self
, command
):
430 self
.message(command
)
431 rc
= os
.system("(%s) </dev/null >>%s 2>&1"
432 % (command
, self
.log_filename
))
435 "Session transcript and error messages are in %s."
439 def message(self
, msg
):
441 if not self
.options
.quiet
:
445 def warning(self
, msg
):
446 msg
= "*** %s\n" % msg
447 sys
.stderr
.write(msg
)
451 fp
= open(self
.log_filename
, "a")
456 def safe_unlink(path
):
463 def split_pathname(path
):
464 path
= os
.path
.normpath(os
.path
.join(os
.getcwd(), path
))
465 dirname
, basename
= os
.path
.split(path
)
466 if basename
[-4:] == ".tex":
467 basename
= basename
[:-4]
468 return dirname
, basename
471 _doctype_rx
= re
.compile(r
"\\documentclass(?:\[[^]]*\])?{([a-zA-Z]*)}")
472 def get_doctype(path
):
479 m
= _doctype_rx
.match(line
)
490 args
= options
.parse(sys
.argv
[1:])
491 except getopt
.error
, msg
:
494 # attempt to locate single .tex file in current directory:
495 args
= glob
.glob("*.tex")
497 error(options
, "No file to process.")
499 error(options
, "Could not deduce which files should be processed.")
501 # parameters are processed, let's go!
504 Job(options
, path
).build()
507 def l2hoption(fp
, option
, value
):
509 fp
.write('$%s = "%s";\n' % (option
, string_to_perl(str(value
))))
513 for c
in map(chr, range(1, 256)):
515 _to_perl
["@"] = "\\@"
516 _to_perl
["$"] = "\\$"
517 _to_perl
['"'] = '\\"'
519 def string_to_perl(s
):
520 return string
.join(map(_to_perl
.get
, s
), '')
523 def check_for_bibtex(filename
):
525 pos
= string
.find(fp
.read(), r
"\bibdata{")
529 def uniqify_module_table(filename
):
530 lines
= open(filename
).readlines()
532 if lines
[-1] == lines
[-2]:
534 open(filename
, "w").writelines(lines
)
537 def new_index(filename
, label
="genindex"):
538 fp
= open(filename
, "w")
547 if __name__
== "__main__":