blob: 38b6e49e8cf9d24abbe3c31a52551abfe50ff025 [file] [log] [blame]
Fred Drake30a68c71998-11-23 16:59:39 +00001#! /usr/bin/env python
2
Fred Drake0eb7b2a1999-05-19 17:37:37 +00003"""Generate ESIS events based on a LaTeX source document and
4configuration data.
5
6The conversion is not strong enough to work with arbitrary LaTeX
7documents; it has only been designed to work with the highly stylized
8markup used in the standard Python documentation. A lot of
9information about specific markup is encoded in the control table
10passed to the convert() function; changing this table can allow this
11tool to support additional LaTeX markups.
12
13The format of the table is largely undocumented; see the commented
14headers where the table is specified in main(). There is no provision
15to load an alternate table from an external file.
Fred Drake30a68c71998-11-23 16:59:39 +000016"""
Fred Drake30a68c71998-11-23 16:59:39 +000017
18import errno
Fred Drake96e4a061999-07-29 22:22:13 +000019import getopt
20import os
Fred Drake30a68c71998-11-23 16:59:39 +000021import re
22import string
Fred Drake30a68c71998-11-23 16:59:39 +000023import sys
Fred Drake96e4a061999-07-29 22:22:13 +000024import UserList
Fred Drake381832e2001-11-30 19:30:03 +000025import xml.sax
Fred Drake691a5a72000-11-22 17:56:43 +000026import xml.sax.saxutils
Fred Drake30a68c71998-11-23 16:59:39 +000027
Fred Drake54fb7fb1999-05-10 19:36:03 +000028from types import ListType, StringType, TupleType
Fred Drakeaeea9811998-12-01 19:04:12 +000029
Fred Drake2262a802001-03-23 16:53:34 +000030from esistools import encode
31
32
Fred Draked7acf021999-01-14 17:38:12 +000033DEBUG = 0
34
35
Fred Drake96e4a061999-07-29 22:22:13 +000036class LaTeXFormatError(Exception):
Fred Drake30a68c71998-11-23 16:59:39 +000037 pass
38
39
Fred Drake96e4a061999-07-29 22:22:13 +000040class LaTeXStackError(LaTeXFormatError):
41 def __init__(self, found, stack):
42 msg = "environment close for %s doesn't match;\n stack = %s" \
43 % (found, stack)
44 self.found = found
45 self.stack = stack[:]
46 LaTeXFormatError.__init__(self, msg)
47
48
Fred Drake30a68c71998-11-23 16:59:39 +000049_begin_env_rx = re.compile(r"[\\]begin{([^}]*)}")
50_end_env_rx = re.compile(r"[\\]end{([^}]*)}")
Fred Drake0eb7b2a1999-05-19 17:37:37 +000051_begin_macro_rx = re.compile(r"[\\]([a-zA-Z]+[*]?) ?({|\s*\n?)")
Fred Drake96c00b01999-05-07 19:59:02 +000052_comment_rx = re.compile("%+ ?(.*)\n[ \t]*")
Fred Drake691a5a72000-11-22 17:56:43 +000053_text_rx = re.compile(r"[^]~%\\{}]+")
Fred Drakeb5fc0ab2001-07-06 21:01:19 +000054_optional_rx = re.compile(r"\s*[[]([^]]*)[]]", re.MULTILINE)
Fred Drakeaeea9811998-12-01 19:04:12 +000055# _parameter_rx is this complicated to allow {...} inside a parameter;
56# this is useful to match tabular layout specifications like {c|p{24pt}}
57_parameter_rx = re.compile("[ \n]*{(([^{}}]|{[^}]*})*)}")
Fred Drake30a68c71998-11-23 16:59:39 +000058_token_rx = re.compile(r"[a-zA-Z][a-zA-Z0-9.-]*$")
59_start_group_rx = re.compile("[ \n]*{")
60_start_optional_rx = re.compile("[ \n]*[[]")
61
62
Fred Drake42f52981998-11-30 14:45:24 +000063ESCAPED_CHARS = "$%#^ {}&~"
Fred Drake30a68c71998-11-23 16:59:39 +000064
65
Fred Drakef79acbd1999-05-07 21:12:21 +000066def dbgmsg(msg):
Fred Draked7acf021999-01-14 17:38:12 +000067 if DEBUG:
Fred Drakef79acbd1999-05-07 21:12:21 +000068 sys.stderr.write(msg + "\n")
69
70def pushing(name, point, depth):
Fred Drake96e4a061999-07-29 22:22:13 +000071 dbgmsg("pushing <%s> at %s" % (name, point))
Fred Draked7acf021999-01-14 17:38:12 +000072
73def popping(name, point, depth):
Fred Drake96e4a061999-07-29 22:22:13 +000074 dbgmsg("popping </%s> at %s" % (name, point))
Fred Draked7acf021999-01-14 17:38:12 +000075
76
Fred Drake96e4a061999-07-29 22:22:13 +000077class _Stack(UserList.UserList):
Fred Drake96e4a061999-07-29 22:22:13 +000078 def append(self, entry):
Fred Drake4fbdf971999-08-02 14:35:25 +000079 if type(entry) is not StringType:
Fred Drake96e4a061999-07-29 22:22:13 +000080 raise LaTeXFormatError("cannot push non-string on stack: "
81 + `entry`)
Fred Drake2262a802001-03-23 16:53:34 +000082 #dbgmsg("%s<%s>" % (" "*len(self.data), entry))
Fred Drake96e4a061999-07-29 22:22:13 +000083 self.data.append(entry)
84
85 def pop(self, index=-1):
86 entry = self.data[index]
87 del self.data[index]
Fred Drake2262a802001-03-23 16:53:34 +000088 #dbgmsg("%s</%s>" % (" "*len(self.data), entry))
Fred Drake96e4a061999-07-29 22:22:13 +000089
90 def __delitem__(self, index):
91 entry = self.data[index]
92 del self.data[index]
Fred Drake2262a802001-03-23 16:53:34 +000093 #dbgmsg("%s</%s>" % (" "*len(self.data), entry))
Fred Drake96e4a061999-07-29 22:22:13 +000094
95
96def new_stack():
97 if DEBUG:
98 return _Stack()
99 return []
100
101
Fred Drake4fbdf971999-08-02 14:35:25 +0000102class Conversion:
103 def __init__(self, ifp, ofp, table):
104 self.write = ofp.write
105 self.ofp = ofp
Fred Drake96c00b01999-05-07 19:59:02 +0000106 self.table = table
Fred Drake00c96ae2001-11-19 05:27:40 +0000107 L = [s.rstrip() for s in ifp.readlines()]
108 L.append("")
109 self.line = string.join(L, "\n")
Fred Drake96c00b01999-05-07 19:59:02 +0000110 self.preamble = 1
Fred Drake96c00b01999-05-07 19:59:02 +0000111
Fred Drake96e4a061999-07-29 22:22:13 +0000112 def convert(self):
113 self.subconvert()
114
Fred Drake96e4a061999-07-29 22:22:13 +0000115 def subconvert(self, endchar=None, depth=0):
116 #
117 # Parses content, including sub-structures, until the character
118 # 'endchar' is found (with no open structures), or until the end
119 # of the input data is endchar is None.
120 #
121 stack = new_stack()
122 line = self.line
123 while line:
124 if line[0] == endchar and not stack:
125 self.line = line
126 return line
127 m = _comment_rx.match(line)
128 if m:
129 text = m.group(1)
130 if text:
131 self.write("(COMMENT\n- %s \n)COMMENT\n-\\n\n"
132 % encode(text))
133 line = line[m.end():]
134 continue
135 m = _begin_env_rx.match(line)
136 if m:
137 name = m.group(1)
138 entry = self.get_env_entry(name)
139 # re-write to use the macro handler
140 line = r"\%s %s" % (name, line[m.end():])
141 continue
142 m = _end_env_rx.match(line)
143 if m:
144 # end of environment
145 envname = m.group(1)
146 entry = self.get_entry(envname)
147 while stack and envname != stack[-1] \
148 and stack[-1] in entry.endcloses:
149 self.write(")%s\n" % stack.pop())
150 if stack and envname == stack[-1]:
151 self.write(")%s\n" % entry.outputname)
152 del stack[-1]
153 else:
154 raise LaTeXStackError(envname, stack)
155 line = line[m.end():]
156 continue
157 m = _begin_macro_rx.match(line)
158 if m:
159 # start of macro
160 macroname = m.group(1)
Fred Drake691a5a72000-11-22 17:56:43 +0000161 if macroname == "c":
162 # Ugh! This is a combining character...
163 endpos = m.end()
164 self.combining_char("c", line[endpos])
165 line = line[endpos + 1:]
166 continue
Fred Drake96e4a061999-07-29 22:22:13 +0000167 entry = self.get_entry(macroname)
168 if entry.verbatim:
169 # magic case!
Fred Drake0f9bfd32001-09-28 16:26:13 +0000170 pos = line.find("\\end{%s}" % macroname)
Fred Drake96e4a061999-07-29 22:22:13 +0000171 text = line[m.end(1):pos]
172 stack.append(entry.name)
173 self.write("(%s\n" % entry.outputname)
174 self.write("-%s\n" % encode(text))
175 self.write(")%s\n" % entry.outputname)
176 stack.pop()
177 line = line[pos + len("\\end{%s}" % macroname):]
178 continue
179 while stack and stack[-1] in entry.closes:
180 top = stack.pop()
181 topentry = self.get_entry(top)
182 if topentry.outputname:
183 self.write(")%s\n-\\n\n" % topentry.outputname)
184 #
Fred Drake9eda3ae2001-09-25 20:57:36 +0000185 if entry.outputname and entry.empty:
186 self.write("e\n")
Fred Drake96e4a061999-07-29 22:22:13 +0000187 #
Fred Drake9eda3ae2001-09-25 20:57:36 +0000188 params, optional, empty = self.start_macro(macroname)
Fred Drake96e4a061999-07-29 22:22:13 +0000189 # rip off the macroname
190 if params:
191 line = line[m.end(1):]
192 elif empty:
193 line = line[m.end(1):]
194 else:
195 line = line[m.end():]
196 opened = 0
197 implied_content = 0
198
199 # handle attribute mappings here:
200 for pentry in params:
201 if pentry.type == "attribute":
202 if pentry.optional:
203 m = _optional_rx.match(line)
Fred Drake4fbdf971999-08-02 14:35:25 +0000204 if m and entry.outputname:
Fred Drake96e4a061999-07-29 22:22:13 +0000205 line = line[m.end():]
206 self.dump_attr(pentry, m.group(1))
Fred Drake4fbdf971999-08-02 14:35:25 +0000207 elif pentry.text and entry.outputname:
Fred Drake96e4a061999-07-29 22:22:13 +0000208 # value supplied by conversion spec:
209 self.dump_attr(pentry, pentry.text)
210 else:
211 m = _parameter_rx.match(line)
212 if not m:
213 raise LaTeXFormatError(
214 "could not extract parameter %s for %s: %s"
215 % (pentry.name, macroname, `line[:100]`))
Fred Drake4fbdf971999-08-02 14:35:25 +0000216 if entry.outputname:
217 self.dump_attr(pentry, m.group(1))
Fred Drake96e4a061999-07-29 22:22:13 +0000218 line = line[m.end():]
219 elif pentry.type == "child":
220 if pentry.optional:
221 m = _optional_rx.match(line)
222 if m:
223 line = line[m.end():]
224 if entry.outputname and not opened:
225 opened = 1
226 self.write("(%s\n" % entry.outputname)
227 stack.append(macroname)
228 stack.append(pentry.name)
229 self.write("(%s\n" % pentry.name)
230 self.write("-%s\n" % encode(m.group(1)))
231 self.write(")%s\n" % pentry.name)
232 stack.pop()
233 else:
234 if entry.outputname and not opened:
235 opened = 1
236 self.write("(%s\n" % entry.outputname)
237 stack.append(entry.name)
238 self.write("(%s\n" % pentry.name)
239 stack.append(pentry.name)
240 self.line = skip_white(line)[1:]
241 line = self.subconvert(
242 "}", len(stack) + depth + 1)[1:]
243 self.write(")%s\n" % stack.pop())
244 elif pentry.type == "content":
245 if pentry.implied:
246 implied_content = 1
247 else:
248 if entry.outputname and not opened:
249 opened = 1
250 self.write("(%s\n" % entry.outputname)
251 stack.append(entry.name)
252 line = skip_white(line)
253 if line[0] != "{":
254 raise LaTeXFormatError(
255 "missing content for " + macroname)
256 self.line = line[1:]
257 line = self.subconvert("}", len(stack) + depth + 1)
258 if line and line[0] == "}":
259 line = line[1:]
Fred Drake4fbdf971999-08-02 14:35:25 +0000260 elif pentry.type == "text" and pentry.text:
261 if entry.outputname and not opened:
262 opened = 1
263 stack.append(entry.name)
264 self.write("(%s\n" % entry.outputname)
Fred Drake2262a802001-03-23 16:53:34 +0000265 #dbgmsg("--- text: %s" % `pentry.text`)
Fred Drake4fbdf971999-08-02 14:35:25 +0000266 self.write("-%s\n" % encode(pentry.text))
Fred Drakef6199ed1999-08-26 17:54:16 +0000267 elif pentry.type == "entityref":
268 self.write("&%s\n" % pentry.name)
Fred Drake96e4a061999-07-29 22:22:13 +0000269 if entry.outputname:
270 if not opened:
271 self.write("(%s\n" % entry.outputname)
272 stack.append(entry.name)
273 if not implied_content:
274 self.write(")%s\n" % entry.outputname)
275 stack.pop()
Fred Drake96e4a061999-07-29 22:22:13 +0000276 continue
277 if line[0] == endchar and not stack:
278 self.line = line[1:]
279 return self.line
280 if line[0] == "}":
281 # end of macro or group
282 macroname = stack[-1]
283 if macroname:
Fred Drake2262a802001-03-23 16:53:34 +0000284 conversion = self.table[macroname]
Fred Drake96e4a061999-07-29 22:22:13 +0000285 if conversion.outputname:
286 # otherwise, it was just a bare group
287 self.write(")%s\n" % conversion.outputname)
288 del stack[-1]
289 line = line[1:]
290 continue
Fred Drake691a5a72000-11-22 17:56:43 +0000291 if line[0] == "~":
292 # don't worry about the "tie" aspect of this command
293 line = line[1:]
294 self.write("- \n")
295 continue
Fred Drake96e4a061999-07-29 22:22:13 +0000296 if line[0] == "{":
297 stack.append("")
298 line = line[1:]
299 continue
300 if line[0] == "\\" and line[1] in ESCAPED_CHARS:
301 self.write("-%s\n" % encode(line[1]))
302 line = line[2:]
303 continue
304 if line[:2] == r"\\":
305 self.write("(BREAK\n)BREAK\n")
306 line = line[2:]
307 continue
Fred Drake691a5a72000-11-22 17:56:43 +0000308 if line[:2] == r"\_":
309 line = "_" + line[2:]
310 continue
311 if line[:2] in (r"\'", r'\"'):
312 # combining characters...
313 self.combining_char(line[1], line[2])
314 line = line[3:]
315 continue
Fred Drake96e4a061999-07-29 22:22:13 +0000316 m = _text_rx.match(line)
317 if m:
318 text = encode(m.group())
319 self.write("-%s\n" % text)
320 line = line[m.end():]
321 continue
322 # special case because of \item[]
323 # XXX can we axe this???
324 if line[0] == "]":
325 self.write("-]\n")
326 line = line[1:]
327 continue
328 # avoid infinite loops
329 extra = ""
330 if len(line) > 100:
331 extra = "..."
332 raise LaTeXFormatError("could not identify markup: %s%s"
333 % (`line[:100]`, extra))
334 while stack:
335 entry = self.get_entry(stack[-1])
336 if entry.closes:
337 self.write(")%s\n-%s\n" % (entry.outputname, encode("\n")))
338 del stack[-1]
339 else:
340 break
341 if stack:
342 raise LaTeXFormatError("elements remain on stack: "
343 + string.join(stack, ", "))
344 # otherwise we just ran out of input here...
345
Fred Drake691a5a72000-11-22 17:56:43 +0000346 # This is a really limited table of combinations, but it will have
347 # to do for now.
348 _combinations = {
349 ("c", "c"): 0x00E7,
350 ("'", "e"): 0x00E9,
351 ('"', "o"): 0x00F6,
352 }
353
354 def combining_char(self, prefix, char):
355 ordinal = self._combinations[(prefix, char)]
356 self.write("-\\%%%d;\n" % ordinal)
357
Fred Drake96e4a061999-07-29 22:22:13 +0000358 def start_macro(self, name):
359 conversion = self.get_entry(name)
360 parameters = conversion.parameters
361 optional = parameters and parameters[0].optional
Fred Drake9eda3ae2001-09-25 20:57:36 +0000362 return parameters, optional, conversion.empty
Fred Drake96e4a061999-07-29 22:22:13 +0000363
364 def get_entry(self, name):
365 entry = self.table.get(name)
366 if entry is None:
Fred Drake2262a802001-03-23 16:53:34 +0000367 dbgmsg("get_entry(%s) failing; building default entry!" % `name`)
Fred Drake96e4a061999-07-29 22:22:13 +0000368 # not defined; build a default entry:
369 entry = TableEntry(name)
370 entry.has_content = 1
371 entry.parameters.append(Parameter("content"))
372 self.table[name] = entry
373 return entry
374
375 def get_env_entry(self, name):
376 entry = self.table.get(name)
377 if entry is None:
378 # not defined; build a default entry:
379 entry = TableEntry(name, 1)
380 entry.has_content = 1
381 entry.parameters.append(Parameter("content"))
382 entry.parameters[-1].implied = 1
383 self.table[name] = entry
384 elif not entry.environment:
385 raise LaTeXFormatError(
386 name + " is defined as a macro; expected environment")
387 return entry
388
389 def dump_attr(self, pentry, value):
390 if not (pentry.name and value):
391 return
392 if _token_rx.match(value):
393 dtype = "TOKEN"
394 else:
395 dtype = "CDATA"
396 self.write("A%s %s %s\n" % (pentry.name, dtype, encode(value)))
397
398
Fred Drakeeac8abe1999-07-29 22:42:27 +0000399def convert(ifp, ofp, table):
400 c = Conversion(ifp, ofp, table)
Fred Drake96e4a061999-07-29 22:22:13 +0000401 try:
402 c.convert()
403 except IOError, (err, msg):
404 if err != errno.EPIPE:
405 raise
406
407
Fred Draked7acf021999-01-14 17:38:12 +0000408def skip_white(line):
Fred Drake96e4a061999-07-29 22:22:13 +0000409 while line and line[0] in " %\n\t\r":
Fred Drake0f9bfd32001-09-28 16:26:13 +0000410 line = line[1:].lstrip()
Fred Draked7acf021999-01-14 17:38:12 +0000411 return line
412
413
Fred Drake96e4a061999-07-29 22:22:13 +0000414
415class TableEntry:
416 def __init__(self, name, environment=0):
417 self.name = name
418 self.outputname = name
419 self.environment = environment
420 self.empty = not environment
421 self.has_content = 0
422 self.verbatim = 0
423 self.auto_close = 0
424 self.parameters = []
425 self.closes = []
426 self.endcloses = []
427
428class Parameter:
429 def __init__(self, type, name=None, optional=0):
430 self.type = type
431 self.name = name
432 self.optional = optional
433 self.text = ''
434 self.implied = 0
435
436
Fred Drake381832e2001-11-30 19:30:03 +0000437class TableHandler(xml.sax.handler.ContentHandler):
438 def __init__(self):
439 self.__table = {}
Fred Drake96e4a061999-07-29 22:22:13 +0000440 self.__buffer = ''
Fred Drake381832e2001-11-30 19:30:03 +0000441 self.__methods = {}
Fred Drake96e4a061999-07-29 22:22:13 +0000442
443 def get_table(self):
444 for entry in self.__table.values():
445 if entry.environment and not entry.has_content:
446 p = Parameter("content")
447 p.implied = 1
448 entry.parameters.append(p)
449 entry.has_content = 1
450 return self.__table
451
Fred Drake381832e2001-11-30 19:30:03 +0000452 def startElement(self, tag, attrs):
453 try:
454 start, end = self.__methods[tag]
455 except KeyError:
456 start = getattr(self, "start_" + tag, None)
457 end = getattr(self, "end_" + tag, None)
458 self.__methods[tag] = (start, end)
459 if start:
460 start(attrs)
461
462 def endElement(self, tag):
463 start, end = self.__methods[tag]
464 if end:
465 end()
466
467 def endDocument(self):
468 self.__methods.clear()
469
470 def characters(self, data):
471 self.__buffer += data
472
Fred Drake96e4a061999-07-29 22:22:13 +0000473 def start_environment(self, attrs):
474 name = attrs["name"]
475 self.__current = TableEntry(name, environment=1)
476 self.__current.verbatim = attrs.get("verbatim") == "yes"
477 if attrs.has_key("outputname"):
478 self.__current.outputname = attrs.get("outputname")
Fred Drake0f9bfd32001-09-28 16:26:13 +0000479 self.__current.endcloses = attrs.get("endcloses", "").split()
Fred Drake96e4a061999-07-29 22:22:13 +0000480 def end_environment(self):
481 self.end_macro()
482
483 def start_macro(self, attrs):
484 name = attrs["name"]
485 self.__current = TableEntry(name)
Fred Drake0f9bfd32001-09-28 16:26:13 +0000486 self.__current.closes = attrs.get("closes", "").split()
Fred Drake96e4a061999-07-29 22:22:13 +0000487 if attrs.has_key("outputname"):
488 self.__current.outputname = attrs.get("outputname")
489 def end_macro(self):
Fred Drake96e4a061999-07-29 22:22:13 +0000490 self.__table[self.__current.name] = self.__current
491 self.__current = None
492
493 def start_attribute(self, attrs):
494 name = attrs.get("name")
495 optional = attrs.get("optional") == "yes"
496 if name:
497 p = Parameter("attribute", name, optional=optional)
498 else:
499 p = Parameter("attribute", optional=optional)
500 self.__current.parameters.append(p)
501 self.__buffer = ''
502 def end_attribute(self):
503 self.__current.parameters[-1].text = self.__buffer
504
Fred Drakef6199ed1999-08-26 17:54:16 +0000505 def start_entityref(self, attrs):
506 name = attrs["name"]
507 p = Parameter("entityref", name)
508 self.__current.parameters.append(p)
509
Fred Drake96e4a061999-07-29 22:22:13 +0000510 def start_child(self, attrs):
511 name = attrs["name"]
512 p = Parameter("child", name, attrs.get("optional") == "yes")
513 self.__current.parameters.append(p)
514 self.__current.empty = 0
515
516 def start_content(self, attrs):
517 p = Parameter("content")
518 p.implied = attrs.get("implied") == "yes"
519 if self.__current.environment:
520 p.implied = 1
521 self.__current.parameters.append(p)
522 self.__current.has_content = 1
523 self.__current.empty = 0
524
525 def start_text(self, attrs):
Fred Drake4fbdf971999-08-02 14:35:25 +0000526 self.__current.empty = 0
Fred Drake96e4a061999-07-29 22:22:13 +0000527 self.__buffer = ''
528 def end_text(self):
529 p = Parameter("text")
530 p.text = self.__buffer
531 self.__current.parameters.append(p)
532
Fred Drake96e4a061999-07-29 22:22:13 +0000533
Fred Drake381832e2001-11-30 19:30:03 +0000534def load_table(fp):
535 ch = TableHandler()
536 xml.sax.parse(fp, ch)
537 return ch.get_table()
Fred Drake96e4a061999-07-29 22:22:13 +0000538
539
Fred Drake30a68c71998-11-23 16:59:39 +0000540def main():
Fred Drake96e4a061999-07-29 22:22:13 +0000541 global DEBUG
542 #
Fred Drakeeac8abe1999-07-29 22:42:27 +0000543 opts, args = getopt.getopt(sys.argv[1:], "D", ["debug"])
Fred Drake96e4a061999-07-29 22:22:13 +0000544 for opt, arg in opts:
Fred Drakeeac8abe1999-07-29 22:42:27 +0000545 if opt in ("-D", "--debug"):
Fred Drake96e4a061999-07-29 22:22:13 +0000546 DEBUG = DEBUG + 1
547 if len(args) == 0:
548 ifp = sys.stdin
Fred Drake30a68c71998-11-23 16:59:39 +0000549 ofp = sys.stdout
Fred Drake96e4a061999-07-29 22:22:13 +0000550 elif len(args) == 1:
Fred Draked15a0a02002-04-05 18:09:22 +0000551 ifp = open(args[0])
Fred Drake96e4a061999-07-29 22:22:13 +0000552 ofp = sys.stdout
553 elif len(args) == 2:
554 ifp = open(args[0])
555 ofp = open(args[1], "w")
Fred Drake30a68c71998-11-23 16:59:39 +0000556 else:
557 usage()
558 sys.exit(2)
Fred Drakeeac8abe1999-07-29 22:42:27 +0000559
560 table = load_table(open(os.path.join(sys.path[0], 'conversion.xml')))
561 convert(ifp, ofp, table)
Fred Drake30a68c71998-11-23 16:59:39 +0000562
563
564if __name__ == "__main__":
565 main()