symbian-qemu-0.9.1-12/python-2.6.1/Tools/scripts/texi2html.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 #! /usr/bin/env python
       
     2 
       
     3 # Convert GNU texinfo files into HTML, one file per node.
       
     4 # Based on Texinfo 2.14.
       
     5 # Usage: texi2html [-d] [-d] [-c] inputfile outputdirectory
       
     6 # The input file must be a complete texinfo file, e.g. emacs.texi.
       
     7 # This creates many files (one per info node) in the output directory,
       
     8 # overwriting existing files of the same name.  All files created have
       
     9 # ".html" as their extension.
       
    10 
       
    11 
       
    12 # XXX To do:
       
    13 # - handle @comment*** correctly
       
    14 # - handle @xref {some words} correctly
       
    15 # - handle @ftable correctly (items aren't indexed?)
       
    16 # - handle @itemx properly
       
    17 # - handle @exdent properly
       
    18 # - add links directly to the proper line from indices
       
    19 # - check against the definitive list of @-cmds; we still miss (among others):
       
    20 # - @defindex (hard)
       
    21 # - @c(omment) in the middle of a line (rarely used)
       
    22 # - @this* (not really needed, only used in headers anyway)
       
    23 # - @today{} (ever used outside title page?)
       
    24 
       
    25 # More consistent handling of chapters/sections/etc.
       
    26 # Lots of documentation
       
    27 # Many more options:
       
    28 #       -top    designate top node
       
    29 #       -links  customize which types of links are included
       
    30 #       -split  split at chapters or sections instead of nodes
       
    31 #       -name   Allow different types of filename handling. Non unix systems
       
    32 #               will have problems with long node names
       
    33 #       ...
       
    34 # Support the most recent texinfo version and take a good look at HTML 3.0
       
    35 # More debugging output (customizable) and more flexible error handling
       
    36 # How about icons ?
       
    37 
       
    38 # rpyron 2002-05-07
       
    39 # Robert Pyron <rpyron@alum.mit.edu>
       
    40 # 1. BUGFIX: In function makefile(), strip blanks from the nodename.
       
    41 #    This is necesary to match the behavior of parser.makeref() and
       
    42 #    parser.do_node().
       
    43 # 2. BUGFIX fixed KeyError in end_ifset (well, I may have just made
       
    44 #    it go away, rather than fix it)
       
    45 # 3. BUGFIX allow @menu and menu items inside @ifset or @ifclear
       
    46 # 4. Support added for:
       
    47 #       @uref        URL reference
       
    48 #       @image       image file reference (see note below)
       
    49 #       @multitable  output an HTML table
       
    50 #       @vtable
       
    51 # 5. Partial support for accents, to match MAKEINFO output
       
    52 # 6. I added a new command-line option, '-H basename', to specify
       
    53 #    HTML Help output. This will cause three files to be created
       
    54 #    in the current directory:
       
    55 #       `basename`.hhp  HTML Help Workshop project file
       
    56 #       `basename`.hhc  Contents file for the project
       
    57 #       `basename`.hhk  Index file for the project
       
    58 #    When fed into HTML Help Workshop, the resulting file will be
       
    59 #    named `basename`.chm.
       
    60 # 7. A new class, HTMLHelp, to accomplish item 6.
       
    61 # 8. Various calls to HTMLHelp functions.
       
    62 # A NOTE ON IMAGES: Just as 'outputdirectory' must exist before
       
    63 # running this program, all referenced images must already exist
       
    64 # in outputdirectory.
       
    65 
       
    66 import os
       
    67 import sys
       
    68 import string
       
    69 import re
       
    70 
       
    71 MAGIC = '\\input texinfo'
       
    72 
       
    73 cmprog = re.compile('^@([a-z]+)([ \t]|$)')        # Command (line-oriented)
       
    74 blprog = re.compile('^[ \t]*$')                   # Blank line
       
    75 kwprog = re.compile('@[a-z]+')                    # Keyword (embedded, usually
       
    76                                                   # with {} args)
       
    77 spprog = re.compile('[\n@{}&<>]')                 # Special characters in
       
    78                                                   # running text
       
    79                                                   #
       
    80                                                   # menu item (Yuck!)
       
    81 miprog = re.compile('^\* ([^:]*):(:|[ \t]*([^\t,\n.]+)([^ \t\n]*))[ \t\n]*')
       
    82 #                   0    1     1 2        3          34         42        0
       
    83 #                         -----            ----------  ---------
       
    84 #                                 -|-----------------------------
       
    85 #                    -----------------------------------------------------
       
    86 
       
    87 
       
    88 
       
    89 
       
    90 class HTMLNode:
       
    91     """Some of the parser's functionality is separated into this class.
       
    92 
       
    93     A Node accumulates its contents, takes care of links to other Nodes
       
    94     and saves itself when it is finished and all links are resolved.
       
    95     """
       
    96 
       
    97     DOCTYPE = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">'
       
    98 
       
    99     type = 0
       
   100     cont = ''
       
   101     epilogue = '</BODY></HTML>\n'
       
   102 
       
   103     def __init__(self, dir, name, topname, title, next, prev, up):
       
   104         self.dirname = dir
       
   105         self.name = name
       
   106         if topname:
       
   107             self.topname = topname
       
   108         else:
       
   109             self.topname = name
       
   110         self.title = title
       
   111         self.next = next
       
   112         self.prev = prev
       
   113         self.up = up
       
   114         self.lines = []
       
   115 
       
   116     def write(self, *lines):
       
   117         map(self.lines.append, lines)
       
   118 
       
   119     def flush(self):
       
   120         fp = open(self.dirname + '/' + makefile(self.name), 'w')
       
   121         fp.write(self.prologue)
       
   122         fp.write(self.text)
       
   123         fp.write(self.epilogue)
       
   124         fp.close()
       
   125 
       
   126     def link(self, label, nodename, rel=None, rev=None):
       
   127         if nodename:
       
   128             if nodename.lower() == '(dir)':
       
   129                 addr = '../dir.html'
       
   130                 title = ''
       
   131             else:
       
   132                 addr = makefile(nodename)
       
   133                 title = ' TITLE="%s"' % nodename
       
   134             self.write(label, ': <A HREF="', addr, '"', \
       
   135                        rel and (' REL=' + rel) or "", \
       
   136                        rev and (' REV=' + rev) or "", \
       
   137                        title, '>', nodename, '</A>  \n')
       
   138 
       
   139     def finalize(self):
       
   140         length = len(self.lines)
       
   141         self.text = ''.join(self.lines)
       
   142         self.lines = []
       
   143         self.open_links()
       
   144         self.output_links()
       
   145         self.close_links()
       
   146         links = ''.join(self.lines)
       
   147         self.lines = []
       
   148         self.prologue = (
       
   149             self.DOCTYPE +
       
   150             '\n<HTML><HEAD>\n'
       
   151             '  <!-- Converted with texi2html and Python -->\n'
       
   152             '  <TITLE>' + self.title + '</TITLE>\n'
       
   153             '  <LINK REL=Next HREF="'
       
   154                 + makefile(self.next) + '" TITLE="' + self.next + '">\n'
       
   155             '  <LINK REL=Previous HREF="'
       
   156                 + makefile(self.prev) + '" TITLE="' + self.prev  + '">\n'
       
   157             '  <LINK REL=Up HREF="'
       
   158                 + makefile(self.up) + '" TITLE="' + self.up  + '">\n'
       
   159             '</HEAD><BODY>\n' +
       
   160             links)
       
   161         if length > 20:
       
   162             self.epilogue = '<P>\n%s</BODY></HTML>\n' % links
       
   163 
       
   164     def open_links(self):
       
   165         self.write('<HR>\n')
       
   166 
       
   167     def close_links(self):
       
   168         self.write('<HR>\n')
       
   169 
       
   170     def output_links(self):
       
   171         if self.cont != self.next:
       
   172             self.link('  Cont', self.cont)
       
   173         self.link('  Next', self.next, rel='Next')
       
   174         self.link('  Prev', self.prev, rel='Previous')
       
   175         self.link('  Up', self.up, rel='Up')
       
   176         if self.name <> self.topname:
       
   177             self.link('  Top', self.topname)
       
   178 
       
   179 
       
   180 class HTML3Node(HTMLNode):
       
   181 
       
   182     DOCTYPE = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML Level 3//EN//3.0">'
       
   183 
       
   184     def open_links(self):
       
   185         self.write('<DIV CLASS=Navigation>\n <HR>\n')
       
   186 
       
   187     def close_links(self):
       
   188         self.write(' <HR>\n</DIV>\n')
       
   189 
       
   190 
       
   191 class TexinfoParser:
       
   192 
       
   193     COPYRIGHT_SYMBOL = "&copy;"
       
   194     FN_ID_PATTERN = "(%(id)s)"
       
   195     FN_SOURCE_PATTERN = '<A NAME=footnoteref%(id)s' \
       
   196                         ' HREF="#footnotetext%(id)s">' \
       
   197                         + FN_ID_PATTERN + '</A>'
       
   198     FN_TARGET_PATTERN = '<A NAME=footnotetext%(id)s' \
       
   199                         ' HREF="#footnoteref%(id)s">' \
       
   200                         + FN_ID_PATTERN + '</A>\n%(text)s<P>\n'
       
   201     FN_HEADER = '\n<P>\n<HR NOSHADE SIZE=1 WIDTH=200>\n' \
       
   202                 '<STRONG><EM>Footnotes</EM></STRONG>\n<P>'
       
   203 
       
   204 
       
   205     Node = HTMLNode
       
   206 
       
   207     # Initialize an instance
       
   208     def __init__(self):
       
   209         self.unknown = {}       # statistics about unknown @-commands
       
   210         self.filenames = {}     # Check for identical filenames
       
   211         self.debugging = 0      # larger values produce more output
       
   212         self.print_headers = 0  # always print headers?
       
   213         self.nodefp = None      # open file we're writing to
       
   214         self.nodelineno = 0     # Linenumber relative to node
       
   215         self.links = None       # Links from current node
       
   216         self.savetext = None    # If not None, save text head instead
       
   217         self.savestack = []     # If not None, save text head instead
       
   218         self.htmlhelp = None    # html help data
       
   219         self.dirname = 'tmp'    # directory where files are created
       
   220         self.includedir = '.'   # directory to search @include files
       
   221         self.nodename = ''      # name of current node
       
   222         self.topname = ''       # name of top node (first node seen)
       
   223         self.title = ''         # title of this whole Texinfo tree
       
   224         self.resetindex()       # Reset all indices
       
   225         self.contents = []      # Reset table of contents
       
   226         self.numbering = []     # Reset section numbering counters
       
   227         self.nofill = 0         # Normal operation: fill paragraphs
       
   228         self.values={'html': 1} # Names that should be parsed in ifset
       
   229         self.stackinfo={}       # Keep track of state in the stack
       
   230         # XXX The following should be reset per node?!
       
   231         self.footnotes = []     # Reset list of footnotes
       
   232         self.itemarg = None     # Reset command used by @item
       
   233         self.itemnumber = None  # Reset number for @item in @enumerate
       
   234         self.itemindex = None   # Reset item index name
       
   235         self.node = None
       
   236         self.nodestack = []
       
   237         self.cont = 0
       
   238         self.includedepth = 0
       
   239 
       
   240     # Set htmlhelp helper class
       
   241     def sethtmlhelp(self, htmlhelp):
       
   242         self.htmlhelp = htmlhelp
       
   243 
       
   244     # Set (output) directory name
       
   245     def setdirname(self, dirname):
       
   246         self.dirname = dirname
       
   247 
       
   248     # Set include directory name
       
   249     def setincludedir(self, includedir):
       
   250         self.includedir = includedir
       
   251 
       
   252     # Parse the contents of an entire file
       
   253     def parse(self, fp):
       
   254         line = fp.readline()
       
   255         lineno = 1
       
   256         while line and (line[0] == '%' or blprog.match(line)):
       
   257             line = fp.readline()
       
   258             lineno = lineno + 1
       
   259         if line[:len(MAGIC)] <> MAGIC:
       
   260             raise SyntaxError, 'file does not begin with %r' % (MAGIC,)
       
   261         self.parserest(fp, lineno)
       
   262 
       
   263     # Parse the contents of a file, not expecting a MAGIC header
       
   264     def parserest(self, fp, initial_lineno):
       
   265         lineno = initial_lineno
       
   266         self.done = 0
       
   267         self.skip = 0
       
   268         self.stack = []
       
   269         accu = []
       
   270         while not self.done:
       
   271             line = fp.readline()
       
   272             self.nodelineno = self.nodelineno + 1
       
   273             if not line:
       
   274                 if accu:
       
   275                     if not self.skip: self.process(accu)
       
   276                     accu = []
       
   277                 if initial_lineno > 0:
       
   278                     print '*** EOF before @bye'
       
   279                 break
       
   280             lineno = lineno + 1
       
   281             mo = cmprog.match(line)
       
   282             if mo:
       
   283                 a, b = mo.span(1)
       
   284                 cmd = line[a:b]
       
   285                 if cmd in ('noindent', 'refill'):
       
   286                     accu.append(line)
       
   287                 else:
       
   288                     if accu:
       
   289                         if not self.skip:
       
   290                             self.process(accu)
       
   291                         accu = []
       
   292                     self.command(line, mo)
       
   293             elif blprog.match(line) and \
       
   294                  'format' not in self.stack and \
       
   295                  'example' not in self.stack:
       
   296                 if accu:
       
   297                     if not self.skip:
       
   298                         self.process(accu)
       
   299                         if self.nofill:
       
   300                             self.write('\n')
       
   301                         else:
       
   302                             self.write('<P>\n')
       
   303                         accu = []
       
   304             else:
       
   305                 # Append the line including trailing \n!
       
   306                 accu.append(line)
       
   307         #
       
   308         if self.skip:
       
   309             print '*** Still skipping at the end'
       
   310         if self.stack:
       
   311             print '*** Stack not empty at the end'
       
   312             print '***', self.stack
       
   313         if self.includedepth == 0:
       
   314             while self.nodestack:
       
   315                 self.nodestack[-1].finalize()
       
   316                 self.nodestack[-1].flush()
       
   317                 del self.nodestack[-1]
       
   318 
       
   319     # Start saving text in a buffer instead of writing it to a file
       
   320     def startsaving(self):
       
   321         if self.savetext <> None:
       
   322             self.savestack.append(self.savetext)
       
   323             # print '*** Recursively saving text, expect trouble'
       
   324         self.savetext = ''
       
   325 
       
   326     # Return the text saved so far and start writing to file again
       
   327     def collectsavings(self):
       
   328         savetext = self.savetext
       
   329         if len(self.savestack) > 0:
       
   330             self.savetext = self.savestack[-1]
       
   331             del self.savestack[-1]
       
   332         else:
       
   333             self.savetext = None
       
   334         return savetext or ''
       
   335 
       
   336     # Write text to file, or save it in a buffer, or ignore it
       
   337     def write(self, *args):
       
   338         try:
       
   339             text = ''.join(args)
       
   340         except:
       
   341             print args
       
   342             raise TypeError
       
   343         if self.savetext <> None:
       
   344             self.savetext = self.savetext + text
       
   345         elif self.nodefp:
       
   346             self.nodefp.write(text)
       
   347         elif self.node:
       
   348             self.node.write(text)
       
   349 
       
   350     # Complete the current node -- write footnotes and close file
       
   351     def endnode(self):
       
   352         if self.savetext <> None:
       
   353             print '*** Still saving text at end of node'
       
   354             dummy = self.collectsavings()
       
   355         if self.footnotes:
       
   356             self.writefootnotes()
       
   357         if self.nodefp:
       
   358             if self.nodelineno > 20:
       
   359                 self.write('<HR>\n')
       
   360                 [name, next, prev, up] = self.nodelinks[:4]
       
   361                 self.link('Next', next)
       
   362                 self.link('Prev', prev)
       
   363                 self.link('Up', up)
       
   364                 if self.nodename <> self.topname:
       
   365                     self.link('Top', self.topname)
       
   366                 self.write('<HR>\n')
       
   367             self.write('</BODY>\n')
       
   368             self.nodefp.close()
       
   369             self.nodefp = None
       
   370         elif self.node:
       
   371             if not self.cont and \
       
   372                (not self.node.type or \
       
   373                 (self.node.next and self.node.prev and self.node.up)):
       
   374                 self.node.finalize()
       
   375                 self.node.flush()
       
   376             else:
       
   377                 self.nodestack.append(self.node)
       
   378             self.node = None
       
   379         self.nodename = ''
       
   380 
       
   381     # Process a list of lines, expanding embedded @-commands
       
   382     # This mostly distinguishes between menus and normal text
       
   383     def process(self, accu):
       
   384         if self.debugging > 1:
       
   385             print '!'*self.debugging, 'process:', self.skip, self.stack,
       
   386             if accu: print accu[0][:30],
       
   387             if accu[0][30:] or accu[1:]: print '...',
       
   388             print
       
   389         if self.inmenu():
       
   390             # XXX should be done differently
       
   391             for line in accu:
       
   392                 mo = miprog.match(line)
       
   393                 if not mo:
       
   394                     line = line.strip() + '\n'
       
   395                     self.expand(line)
       
   396                     continue
       
   397                 bgn, end = mo.span(0)
       
   398                 a, b = mo.span(1)
       
   399                 c, d = mo.span(2)
       
   400                 e, f = mo.span(3)
       
   401                 g, h = mo.span(4)
       
   402                 label = line[a:b]
       
   403                 nodename = line[c:d]
       
   404                 if nodename[0] == ':': nodename = label
       
   405                 else: nodename = line[e:f]
       
   406                 punct = line[g:h]
       
   407                 self.write('  <LI><A HREF="',
       
   408                            makefile(nodename),
       
   409                            '">', nodename,
       
   410                            '</A>', punct, '\n')
       
   411                 self.htmlhelp.menuitem(nodename)
       
   412                 self.expand(line[end:])
       
   413         else:
       
   414             text = ''.join(accu)
       
   415             self.expand(text)
       
   416 
       
   417     # find 'menu' (we might be inside 'ifset' or 'ifclear')
       
   418     def inmenu(self):
       
   419         #if 'menu' in self.stack:
       
   420         #    print 'inmenu   :', self.skip, self.stack, self.stackinfo
       
   421         stack = self.stack
       
   422         while stack and stack[-1] in ('ifset','ifclear'):
       
   423             try:
       
   424                 if self.stackinfo[len(stack)]:
       
   425                     return 0
       
   426             except KeyError:
       
   427                 pass
       
   428             stack = stack[:-1]
       
   429         return (stack and stack[-1] == 'menu')
       
   430 
       
   431     # Write a string, expanding embedded @-commands
       
   432     def expand(self, text):
       
   433         stack = []
       
   434         i = 0
       
   435         n = len(text)
       
   436         while i < n:
       
   437             start = i
       
   438             mo = spprog.search(text, i)
       
   439             if mo:
       
   440                 i = mo.start()
       
   441             else:
       
   442                 self.write(text[start:])
       
   443                 break
       
   444             self.write(text[start:i])
       
   445             c = text[i]
       
   446             i = i+1
       
   447             if c == '\n':
       
   448                 self.write('\n')
       
   449                 continue
       
   450             if c == '<':
       
   451                 self.write('&lt;')
       
   452                 continue
       
   453             if c == '>':
       
   454                 self.write('&gt;')
       
   455                 continue
       
   456             if c == '&':
       
   457                 self.write('&amp;')
       
   458                 continue
       
   459             if c == '{':
       
   460                 stack.append('')
       
   461                 continue
       
   462             if c == '}':
       
   463                 if not stack:
       
   464                     print '*** Unmatched }'
       
   465                     self.write('}')
       
   466                     continue
       
   467                 cmd = stack[-1]
       
   468                 del stack[-1]
       
   469                 try:
       
   470                     method = getattr(self, 'close_' + cmd)
       
   471                 except AttributeError:
       
   472                     self.unknown_close(cmd)
       
   473                     continue
       
   474                 method()
       
   475                 continue
       
   476             if c <> '@':
       
   477                 # Cannot happen unless spprog is changed
       
   478                 raise RuntimeError, 'unexpected funny %r' % c
       
   479             start = i
       
   480             while i < n and text[i] in string.ascii_letters: i = i+1
       
   481             if i == start:
       
   482                 # @ plus non-letter: literal next character
       
   483                 i = i+1
       
   484                 c = text[start:i]
       
   485                 if c == ':':
       
   486                     # `@:' means no extra space after
       
   487                     # preceding `.', `?', `!' or `:'
       
   488                     pass
       
   489                 else:
       
   490                     # `@.' means a sentence-ending period;
       
   491                     # `@@', `@{', `@}' quote `@', `{', `}'
       
   492                     self.write(c)
       
   493                 continue
       
   494             cmd = text[start:i]
       
   495             if i < n and text[i] == '{':
       
   496                 i = i+1
       
   497                 stack.append(cmd)
       
   498                 try:
       
   499                     method = getattr(self, 'open_' + cmd)
       
   500                 except AttributeError:
       
   501                     self.unknown_open(cmd)
       
   502                     continue
       
   503                 method()
       
   504                 continue
       
   505             try:
       
   506                 method = getattr(self, 'handle_' + cmd)
       
   507             except AttributeError:
       
   508                 self.unknown_handle(cmd)
       
   509                 continue
       
   510             method()
       
   511         if stack:
       
   512             print '*** Stack not empty at para:', stack
       
   513 
       
   514     # --- Handle unknown embedded @-commands ---
       
   515 
       
   516     def unknown_open(self, cmd):
       
   517         print '*** No open func for @' + cmd + '{...}'
       
   518         cmd = cmd + '{'
       
   519         self.write('@', cmd)
       
   520         if not self.unknown.has_key(cmd):
       
   521             self.unknown[cmd] = 1
       
   522         else:
       
   523             self.unknown[cmd] = self.unknown[cmd] + 1
       
   524 
       
   525     def unknown_close(self, cmd):
       
   526         print '*** No close func for @' + cmd + '{...}'
       
   527         cmd = '}' + cmd
       
   528         self.write('}')
       
   529         if not self.unknown.has_key(cmd):
       
   530             self.unknown[cmd] = 1
       
   531         else:
       
   532             self.unknown[cmd] = self.unknown[cmd] + 1
       
   533 
       
   534     def unknown_handle(self, cmd):
       
   535         print '*** No handler for @' + cmd
       
   536         self.write('@', cmd)
       
   537         if not self.unknown.has_key(cmd):
       
   538             self.unknown[cmd] = 1
       
   539         else:
       
   540             self.unknown[cmd] = self.unknown[cmd] + 1
       
   541 
       
   542     # XXX The following sections should be ordered as the texinfo docs
       
   543 
       
   544     # --- Embedded @-commands without {} argument list --
       
   545 
       
   546     def handle_noindent(self): pass
       
   547 
       
   548     def handle_refill(self): pass
       
   549 
       
   550     # --- Include file handling ---
       
   551 
       
   552     def do_include(self, args):
       
   553         file = args
       
   554         file = os.path.join(self.includedir, file)
       
   555         try:
       
   556             fp = open(file, 'r')
       
   557         except IOError, msg:
       
   558             print '*** Can\'t open include file', repr(file)
       
   559             return
       
   560         print '!'*self.debugging, '--> file', repr(file)
       
   561         save_done = self.done
       
   562         save_skip = self.skip
       
   563         save_stack = self.stack
       
   564         self.includedepth = self.includedepth + 1
       
   565         self.parserest(fp, 0)
       
   566         self.includedepth = self.includedepth - 1
       
   567         fp.close()
       
   568         self.done = save_done
       
   569         self.skip = save_skip
       
   570         self.stack = save_stack
       
   571         print '!'*self.debugging, '<-- file', repr(file)
       
   572 
       
   573     # --- Special Insertions ---
       
   574 
       
   575     def open_dmn(self): pass
       
   576     def close_dmn(self): pass
       
   577 
       
   578     def open_dots(self): self.write('...')
       
   579     def close_dots(self): pass
       
   580 
       
   581     def open_bullet(self): pass
       
   582     def close_bullet(self): pass
       
   583 
       
   584     def open_TeX(self): self.write('TeX')
       
   585     def close_TeX(self): pass
       
   586 
       
   587     def handle_copyright(self): self.write(self.COPYRIGHT_SYMBOL)
       
   588     def open_copyright(self): self.write(self.COPYRIGHT_SYMBOL)
       
   589     def close_copyright(self): pass
       
   590 
       
   591     def open_minus(self): self.write('-')
       
   592     def close_minus(self): pass
       
   593 
       
   594     # --- Accents ---
       
   595 
       
   596     # rpyron 2002-05-07
       
   597     # I would like to do at least as well as makeinfo when
       
   598     # it is producing HTML output:
       
   599     #
       
   600     #   input               output
       
   601     #     @"o                 @"o                umlaut accent
       
   602     #     @'o                 'o                 acute accent
       
   603     #     @,{c}               @,{c}              cedilla accent
       
   604     #     @=o                 @=o                macron/overbar accent
       
   605     #     @^o                 @^o                circumflex accent
       
   606     #     @`o                 `o                 grave accent
       
   607     #     @~o                 @~o                tilde accent
       
   608     #     @dotaccent{o}       @dotaccent{o}      overdot accent
       
   609     #     @H{o}               @H{o}              long Hungarian umlaut
       
   610     #     @ringaccent{o}      @ringaccent{o}     ring accent
       
   611     #     @tieaccent{oo}      @tieaccent{oo}     tie-after accent
       
   612     #     @u{o}               @u{o}              breve accent
       
   613     #     @ubaraccent{o}      @ubaraccent{o}     underbar accent
       
   614     #     @udotaccent{o}      @udotaccent{o}     underdot accent
       
   615     #     @v{o}               @v{o}              hacek or check accent
       
   616     #     @exclamdown{}       &#161;             upside-down !
       
   617     #     @questiondown{}     &#191;             upside-down ?
       
   618     #     @aa{},@AA{}         &#229;,&#197;      a,A with circle
       
   619     #     @ae{},@AE{}         &#230;,&#198;      ae,AE ligatures
       
   620     #     @dotless{i}         @dotless{i}        dotless i
       
   621     #     @dotless{j}         @dotless{j}        dotless j
       
   622     #     @l{},@L{}           l/,L/              suppressed-L,l
       
   623     #     @o{},@O{}           &#248;,&#216;      O,o with slash
       
   624     #     @oe{},@OE{}         oe,OE              oe,OE ligatures
       
   625     #     @ss{}               &#223;             es-zet or sharp S
       
   626     #
       
   627     # The following character codes and approximations have been
       
   628     # copied from makeinfo's HTML output.
       
   629 
       
   630     def open_exclamdown(self): self.write('&#161;')   # upside-down !
       
   631     def close_exclamdown(self): pass
       
   632     def open_questiondown(self): self.write('&#191;') # upside-down ?
       
   633     def close_questiondown(self): pass
       
   634     def open_aa(self): self.write('&#229;') # a with circle
       
   635     def close_aa(self): pass
       
   636     def open_AA(self): self.write('&#197;') # A with circle
       
   637     def close_AA(self): pass
       
   638     def open_ae(self): self.write('&#230;') # ae ligatures
       
   639     def close_ae(self): pass
       
   640     def open_AE(self): self.write('&#198;') # AE ligatures
       
   641     def close_AE(self): pass
       
   642     def open_o(self): self.write('&#248;')  # o with slash
       
   643     def close_o(self): pass
       
   644     def open_O(self): self.write('&#216;')  # O with slash
       
   645     def close_O(self): pass
       
   646     def open_ss(self): self.write('&#223;') # es-zet or sharp S
       
   647     def close_ss(self): pass
       
   648     def open_oe(self): self.write('oe')     # oe ligatures
       
   649     def close_oe(self): pass
       
   650     def open_OE(self): self.write('OE')     # OE ligatures
       
   651     def close_OE(self): pass
       
   652     def open_l(self): self.write('l/')      # suppressed-l
       
   653     def close_l(self): pass
       
   654     def open_L(self): self.write('L/')      # suppressed-L
       
   655     def close_L(self): pass
       
   656 
       
   657     # --- Special Glyphs for Examples ---
       
   658 
       
   659     def open_result(self): self.write('=&gt;')
       
   660     def close_result(self): pass
       
   661 
       
   662     def open_expansion(self): self.write('==&gt;')
       
   663     def close_expansion(self): pass
       
   664 
       
   665     def open_print(self): self.write('-|')
       
   666     def close_print(self): pass
       
   667 
       
   668     def open_error(self): self.write('error--&gt;')
       
   669     def close_error(self): pass
       
   670 
       
   671     def open_equiv(self): self.write('==')
       
   672     def close_equiv(self): pass
       
   673 
       
   674     def open_point(self): self.write('-!-')
       
   675     def close_point(self): pass
       
   676 
       
   677     # --- Cross References ---
       
   678 
       
   679     def open_pxref(self):
       
   680         self.write('see ')
       
   681         self.startsaving()
       
   682     def close_pxref(self):
       
   683         self.makeref()
       
   684 
       
   685     def open_xref(self):
       
   686         self.write('See ')
       
   687         self.startsaving()
       
   688     def close_xref(self):
       
   689         self.makeref()
       
   690 
       
   691     def open_ref(self):
       
   692         self.startsaving()
       
   693     def close_ref(self):
       
   694         self.makeref()
       
   695 
       
   696     def open_inforef(self):
       
   697         self.write('See info file ')
       
   698         self.startsaving()
       
   699     def close_inforef(self):
       
   700         text = self.collectsavings()
       
   701         args = [s.strip() for s in text.split(',')]
       
   702         while len(args) < 3: args.append('')
       
   703         node = args[0]
       
   704         file = args[2]
       
   705         self.write('`', file, '\', node `', node, '\'')
       
   706 
       
   707     def makeref(self):
       
   708         text = self.collectsavings()
       
   709         args = [s.strip() for s in text.split(',')]
       
   710         while len(args) < 5: args.append('')
       
   711         nodename = label = args[0]
       
   712         if args[2]: label = args[2]
       
   713         file = args[3]
       
   714         title = args[4]
       
   715         href = makefile(nodename)
       
   716         if file:
       
   717             href = '../' + file + '/' + href
       
   718         self.write('<A HREF="', href, '">', label, '</A>')
       
   719 
       
   720     # rpyron 2002-05-07  uref support
       
   721     def open_uref(self):
       
   722         self.startsaving()
       
   723     def close_uref(self):
       
   724         text = self.collectsavings()
       
   725         args = [s.strip() for s in text.split(',')]
       
   726         while len(args) < 2: args.append('')
       
   727         href = args[0]
       
   728         label = args[1]
       
   729         if not label: label = href
       
   730         self.write('<A HREF="', href, '">', label, '</A>')
       
   731 
       
   732     # rpyron 2002-05-07  image support
       
   733     # GNU makeinfo producing HTML output tries `filename.png'; if
       
   734     # that does not exist, it tries `filename.jpg'. If that does
       
   735     # not exist either, it complains. GNU makeinfo does not handle
       
   736     # GIF files; however, I include GIF support here because
       
   737     # MySQL documentation uses GIF files.
       
   738 
       
   739     def open_image(self):
       
   740         self.startsaving()
       
   741     def close_image(self):
       
   742         self.makeimage()
       
   743     def makeimage(self):
       
   744         text = self.collectsavings()
       
   745         args = [s.strip() for s in text.split(',')]
       
   746         while len(args) < 5: args.append('')
       
   747         filename = args[0]
       
   748         width    = args[1]
       
   749         height   = args[2]
       
   750         alt      = args[3]
       
   751         ext      = args[4]
       
   752 
       
   753         # The HTML output will have a reference to the image
       
   754         # that is relative to the HTML output directory,
       
   755         # which is what 'filename' gives us. However, we need
       
   756         # to find it relative to our own current directory,
       
   757         # so we construct 'imagename'.
       
   758         imagelocation = self.dirname + '/' + filename
       
   759 
       
   760         if   os.path.exists(imagelocation+'.png'):
       
   761             filename += '.png'
       
   762         elif os.path.exists(imagelocation+'.jpg'):
       
   763             filename += '.jpg'
       
   764         elif os.path.exists(imagelocation+'.gif'):   # MySQL uses GIF files
       
   765             filename += '.gif'
       
   766         else:
       
   767             print "*** Cannot find image " + imagelocation
       
   768         #TODO: what is 'ext'?
       
   769         self.write('<IMG SRC="', filename, '"',                     \
       
   770                     width  and (' WIDTH="'  + width  + '"') or "",  \
       
   771                     height and (' HEIGHT="' + height + '"') or "",  \
       
   772                     alt    and (' ALT="'    + alt    + '"') or "",  \
       
   773                     '/>' )
       
   774         self.htmlhelp.addimage(imagelocation)
       
   775 
       
   776 
       
   777     # --- Marking Words and Phrases ---
       
   778 
       
   779     # --- Other @xxx{...} commands ---
       
   780 
       
   781     def open_(self): pass # Used by {text enclosed in braces}
       
   782     def close_(self): pass
       
   783 
       
   784     open_asis = open_
       
   785     close_asis = close_
       
   786 
       
   787     def open_cite(self): self.write('<CITE>')
       
   788     def close_cite(self): self.write('</CITE>')
       
   789 
       
   790     def open_code(self): self.write('<CODE>')
       
   791     def close_code(self): self.write('</CODE>')
       
   792 
       
   793     def open_t(self): self.write('<TT>')
       
   794     def close_t(self): self.write('</TT>')
       
   795 
       
   796     def open_dfn(self): self.write('<DFN>')
       
   797     def close_dfn(self): self.write('</DFN>')
       
   798 
       
   799     def open_emph(self): self.write('<EM>')
       
   800     def close_emph(self): self.write('</EM>')
       
   801 
       
   802     def open_i(self): self.write('<I>')
       
   803     def close_i(self): self.write('</I>')
       
   804 
       
   805     def open_footnote(self):
       
   806         # if self.savetext <> None:
       
   807         #       print '*** Recursive footnote -- expect weirdness'
       
   808         id = len(self.footnotes) + 1
       
   809         self.write(self.FN_SOURCE_PATTERN % {'id': repr(id)})
       
   810         self.startsaving()
       
   811 
       
   812     def close_footnote(self):
       
   813         id = len(self.footnotes) + 1
       
   814         self.footnotes.append((id, self.collectsavings()))
       
   815 
       
   816     def writefootnotes(self):
       
   817         self.write(self.FN_HEADER)
       
   818         for id, text in self.footnotes:
       
   819             self.write(self.FN_TARGET_PATTERN
       
   820                        % {'id': repr(id), 'text': text})
       
   821         self.footnotes = []
       
   822 
       
   823     def open_file(self): self.write('<CODE>')
       
   824     def close_file(self): self.write('</CODE>')
       
   825 
       
   826     def open_kbd(self): self.write('<KBD>')
       
   827     def close_kbd(self): self.write('</KBD>')
       
   828 
       
   829     def open_key(self): self.write('<KEY>')
       
   830     def close_key(self): self.write('</KEY>')
       
   831 
       
   832     def open_r(self): self.write('<R>')
       
   833     def close_r(self): self.write('</R>')
       
   834 
       
   835     def open_samp(self): self.write('`<SAMP>')
       
   836     def close_samp(self): self.write('</SAMP>\'')
       
   837 
       
   838     def open_sc(self): self.write('<SMALLCAPS>')
       
   839     def close_sc(self): self.write('</SMALLCAPS>')
       
   840 
       
   841     def open_strong(self): self.write('<STRONG>')
       
   842     def close_strong(self): self.write('</STRONG>')
       
   843 
       
   844     def open_b(self): self.write('<B>')
       
   845     def close_b(self): self.write('</B>')
       
   846 
       
   847     def open_var(self): self.write('<VAR>')
       
   848     def close_var(self): self.write('</VAR>')
       
   849 
       
   850     def open_w(self): self.write('<NOBREAK>')
       
   851     def close_w(self): self.write('</NOBREAK>')
       
   852 
       
   853     def open_url(self): self.startsaving()
       
   854     def close_url(self):
       
   855         text = self.collectsavings()
       
   856         self.write('<A HREF="', text, '">', text, '</A>')
       
   857 
       
   858     def open_email(self): self.startsaving()
       
   859     def close_email(self):
       
   860         text = self.collectsavings()
       
   861         self.write('<A HREF="mailto:', text, '">', text, '</A>')
       
   862 
       
   863     open_titlefont = open_
       
   864     close_titlefont = close_
       
   865 
       
   866     def open_small(self): pass
       
   867     def close_small(self): pass
       
   868 
       
   869     def command(self, line, mo):
       
   870         a, b = mo.span(1)
       
   871         cmd = line[a:b]
       
   872         args = line[b:].strip()
       
   873         if self.debugging > 1:
       
   874             print '!'*self.debugging, 'command:', self.skip, self.stack, \
       
   875                   '@' + cmd, args
       
   876         try:
       
   877             func = getattr(self, 'do_' + cmd)
       
   878         except AttributeError:
       
   879             try:
       
   880                 func = getattr(self, 'bgn_' + cmd)
       
   881             except AttributeError:
       
   882                 # don't complain if we are skipping anyway
       
   883                 if not self.skip:
       
   884                     self.unknown_cmd(cmd, args)
       
   885                 return
       
   886             self.stack.append(cmd)
       
   887             func(args)
       
   888             return
       
   889         if not self.skip or cmd == 'end':
       
   890             func(args)
       
   891 
       
   892     def unknown_cmd(self, cmd, args):
       
   893         print '*** unknown', '@' + cmd, args
       
   894         if not self.unknown.has_key(cmd):
       
   895             self.unknown[cmd] = 1
       
   896         else:
       
   897             self.unknown[cmd] = self.unknown[cmd] + 1
       
   898 
       
   899     def do_end(self, args):
       
   900         words = args.split()
       
   901         if not words:
       
   902             print '*** @end w/o args'
       
   903         else:
       
   904             cmd = words[0]
       
   905             if not self.stack or self.stack[-1] <> cmd:
       
   906                 print '*** @end', cmd, 'unexpected'
       
   907             else:
       
   908                 del self.stack[-1]
       
   909             try:
       
   910                 func = getattr(self, 'end_' + cmd)
       
   911             except AttributeError:
       
   912                 self.unknown_end(cmd)
       
   913                 return
       
   914             func()
       
   915 
       
   916     def unknown_end(self, cmd):
       
   917         cmd = 'end ' + cmd
       
   918         print '*** unknown', '@' + cmd
       
   919         if not self.unknown.has_key(cmd):
       
   920             self.unknown[cmd] = 1
       
   921         else:
       
   922             self.unknown[cmd] = self.unknown[cmd] + 1
       
   923 
       
   924     # --- Comments ---
       
   925 
       
   926     def do_comment(self, args): pass
       
   927     do_c = do_comment
       
   928 
       
   929     # --- Conditional processing ---
       
   930 
       
   931     def bgn_ifinfo(self, args): pass
       
   932     def end_ifinfo(self): pass
       
   933 
       
   934     def bgn_iftex(self, args): self.skip = self.skip + 1
       
   935     def end_iftex(self): self.skip = self.skip - 1
       
   936 
       
   937     def bgn_ignore(self, args): self.skip = self.skip + 1
       
   938     def end_ignore(self): self.skip = self.skip - 1
       
   939 
       
   940     def bgn_tex(self, args): self.skip = self.skip + 1
       
   941     def end_tex(self): self.skip = self.skip - 1
       
   942 
       
   943     def do_set(self, args):
       
   944         fields = args.split(' ')
       
   945         key = fields[0]
       
   946         if len(fields) == 1:
       
   947             value = 1
       
   948         else:
       
   949             value = ' '.join(fields[1:])
       
   950         self.values[key] = value
       
   951 
       
   952     def do_clear(self, args):
       
   953         self.values[args] = None
       
   954 
       
   955     def bgn_ifset(self, args):
       
   956         if args not in self.values.keys() \
       
   957            or self.values[args] is None:
       
   958             self.skip = self.skip + 1
       
   959             self.stackinfo[len(self.stack)] = 1
       
   960         else:
       
   961             self.stackinfo[len(self.stack)] = 0
       
   962     def end_ifset(self):
       
   963         try:
       
   964             if self.stackinfo[len(self.stack) + 1]:
       
   965                 self.skip = self.skip - 1
       
   966             del self.stackinfo[len(self.stack) + 1]
       
   967         except KeyError:
       
   968             print '*** end_ifset: KeyError :', len(self.stack) + 1
       
   969 
       
   970     def bgn_ifclear(self, args):
       
   971         if args in self.values.keys() \
       
   972            and self.values[args] is not None:
       
   973             self.skip = self.skip + 1
       
   974             self.stackinfo[len(self.stack)] = 1
       
   975         else:
       
   976             self.stackinfo[len(self.stack)] = 0
       
   977     def end_ifclear(self):
       
   978         try:
       
   979             if self.stackinfo[len(self.stack) + 1]:
       
   980                 self.skip = self.skip - 1
       
   981             del self.stackinfo[len(self.stack) + 1]
       
   982         except KeyError:
       
   983             print '*** end_ifclear: KeyError :', len(self.stack) + 1
       
   984 
       
   985     def open_value(self):
       
   986         self.startsaving()
       
   987 
       
   988     def close_value(self):
       
   989         key = self.collectsavings()
       
   990         if key in self.values.keys():
       
   991             self.write(self.values[key])
       
   992         else:
       
   993             print '*** Undefined value: ', key
       
   994 
       
   995     # --- Beginning a file ---
       
   996 
       
   997     do_finalout = do_comment
       
   998     do_setchapternewpage = do_comment
       
   999     do_setfilename = do_comment
       
  1000 
       
  1001     def do_settitle(self, args):
       
  1002         self.startsaving()
       
  1003         self.expand(args)
       
  1004         self.title = self.collectsavings()
       
  1005     def do_parskip(self, args): pass
       
  1006 
       
  1007     # --- Ending a file ---
       
  1008 
       
  1009     def do_bye(self, args):
       
  1010         self.endnode()
       
  1011         self.done = 1
       
  1012 
       
  1013     # --- Title page ---
       
  1014 
       
  1015     def bgn_titlepage(self, args): self.skip = self.skip + 1
       
  1016     def end_titlepage(self): self.skip = self.skip - 1
       
  1017     def do_shorttitlepage(self, args): pass
       
  1018 
       
  1019     def do_center(self, args):
       
  1020         # Actually not used outside title page...
       
  1021         self.write('<H1>')
       
  1022         self.expand(args)
       
  1023         self.write('</H1>\n')
       
  1024     do_title = do_center
       
  1025     do_subtitle = do_center
       
  1026     do_author = do_center
       
  1027 
       
  1028     do_vskip = do_comment
       
  1029     do_vfill = do_comment
       
  1030     do_smallbook = do_comment
       
  1031 
       
  1032     do_paragraphindent = do_comment
       
  1033     do_setchapternewpage = do_comment
       
  1034     do_headings = do_comment
       
  1035     do_footnotestyle = do_comment
       
  1036 
       
  1037     do_evenheading = do_comment
       
  1038     do_evenfooting = do_comment
       
  1039     do_oddheading = do_comment
       
  1040     do_oddfooting = do_comment
       
  1041     do_everyheading = do_comment
       
  1042     do_everyfooting = do_comment
       
  1043 
       
  1044     # --- Nodes ---
       
  1045 
       
  1046     def do_node(self, args):
       
  1047         self.endnode()
       
  1048         self.nodelineno = 0
       
  1049         parts = [s.strip() for s in args.split(',')]
       
  1050         while len(parts) < 4: parts.append('')
       
  1051         self.nodelinks = parts
       
  1052         [name, next, prev, up] = parts[:4]
       
  1053         file = self.dirname + '/' + makefile(name)
       
  1054         if self.filenames.has_key(file):
       
  1055             print '*** Filename already in use: ', file
       
  1056         else:
       
  1057             if self.debugging: print '!'*self.debugging, '--- writing', file
       
  1058         self.filenames[file] = 1
       
  1059         # self.nodefp = open(file, 'w')
       
  1060         self.nodename = name
       
  1061         if self.cont and self.nodestack:
       
  1062             self.nodestack[-1].cont = self.nodename
       
  1063         if not self.topname: self.topname = name
       
  1064         title = name
       
  1065         if self.title: title = title + ' -- ' + self.title
       
  1066         self.node = self.Node(self.dirname, self.nodename, self.topname,
       
  1067                               title, next, prev, up)
       
  1068         self.htmlhelp.addnode(self.nodename,next,prev,up,file)
       
  1069 
       
  1070     def link(self, label, nodename):
       
  1071         if nodename:
       
  1072             if nodename.lower() == '(dir)':
       
  1073                 addr = '../dir.html'
       
  1074             else:
       
  1075                 addr = makefile(nodename)
       
  1076             self.write(label, ': <A HREF="', addr, '" TYPE="',
       
  1077                        label, '">', nodename, '</A>  \n')
       
  1078 
       
  1079     # --- Sectioning commands ---
       
  1080 
       
  1081     def popstack(self, type):
       
  1082         if (self.node):
       
  1083             self.node.type = type
       
  1084             while self.nodestack:
       
  1085                 if self.nodestack[-1].type > type:
       
  1086                     self.nodestack[-1].finalize()
       
  1087                     self.nodestack[-1].flush()
       
  1088                     del self.nodestack[-1]
       
  1089                 elif self.nodestack[-1].type == type:
       
  1090                     if not self.nodestack[-1].next:
       
  1091                         self.nodestack[-1].next = self.node.name
       
  1092                     if not self.node.prev:
       
  1093                         self.node.prev = self.nodestack[-1].name
       
  1094                     self.nodestack[-1].finalize()
       
  1095                     self.nodestack[-1].flush()
       
  1096                     del self.nodestack[-1]
       
  1097                 else:
       
  1098                     if type > 1 and not self.node.up:
       
  1099                         self.node.up = self.nodestack[-1].name
       
  1100                     break
       
  1101 
       
  1102     def do_chapter(self, args):
       
  1103         self.heading('H1', args, 0)
       
  1104         self.popstack(1)
       
  1105 
       
  1106     def do_unnumbered(self, args):
       
  1107         self.heading('H1', args, -1)
       
  1108         self.popstack(1)
       
  1109     def do_appendix(self, args):
       
  1110         self.heading('H1', args, -1)
       
  1111         self.popstack(1)
       
  1112     def do_top(self, args):
       
  1113         self.heading('H1', args, -1)
       
  1114     def do_chapheading(self, args):
       
  1115         self.heading('H1', args, -1)
       
  1116     def do_majorheading(self, args):
       
  1117         self.heading('H1', args, -1)
       
  1118 
       
  1119     def do_section(self, args):
       
  1120         self.heading('H1', args, 1)
       
  1121         self.popstack(2)
       
  1122 
       
  1123     def do_unnumberedsec(self, args):
       
  1124         self.heading('H1', args, -1)
       
  1125         self.popstack(2)
       
  1126     def do_appendixsec(self, args):
       
  1127         self.heading('H1', args, -1)
       
  1128         self.popstack(2)
       
  1129     do_appendixsection = do_appendixsec
       
  1130     def do_heading(self, args):
       
  1131         self.heading('H1', args, -1)
       
  1132 
       
  1133     def do_subsection(self, args):
       
  1134         self.heading('H2', args, 2)
       
  1135         self.popstack(3)
       
  1136     def do_unnumberedsubsec(self, args):
       
  1137         self.heading('H2', args, -1)
       
  1138         self.popstack(3)
       
  1139     def do_appendixsubsec(self, args):
       
  1140         self.heading('H2', args, -1)
       
  1141         self.popstack(3)
       
  1142     def do_subheading(self, args):
       
  1143         self.heading('H2', args, -1)
       
  1144 
       
  1145     def do_subsubsection(self, args):
       
  1146         self.heading('H3', args, 3)
       
  1147         self.popstack(4)
       
  1148     def do_unnumberedsubsubsec(self, args):
       
  1149         self.heading('H3', args, -1)
       
  1150         self.popstack(4)
       
  1151     def do_appendixsubsubsec(self, args):
       
  1152         self.heading('H3', args, -1)
       
  1153         self.popstack(4)
       
  1154     def do_subsubheading(self, args):
       
  1155         self.heading('H3', args, -1)
       
  1156 
       
  1157     def heading(self, type, args, level):
       
  1158         if level >= 0:
       
  1159             while len(self.numbering) <= level:
       
  1160                 self.numbering.append(0)
       
  1161             del self.numbering[level+1:]
       
  1162             self.numbering[level] = self.numbering[level] + 1
       
  1163             x = ''
       
  1164             for i in self.numbering:
       
  1165                 x = x + repr(i) + '.'
       
  1166             args = x + ' ' + args
       
  1167             self.contents.append((level, args, self.nodename))
       
  1168         self.write('<', type, '>')
       
  1169         self.expand(args)
       
  1170         self.write('</', type, '>\n')
       
  1171         if self.debugging or self.print_headers:
       
  1172             print '---', args
       
  1173 
       
  1174     def do_contents(self, args):
       
  1175         # pass
       
  1176         self.listcontents('Table of Contents', 999)
       
  1177 
       
  1178     def do_shortcontents(self, args):
       
  1179         pass
       
  1180         # self.listcontents('Short Contents', 0)
       
  1181     do_summarycontents = do_shortcontents
       
  1182 
       
  1183     def listcontents(self, title, maxlevel):
       
  1184         self.write('<H1>', title, '</H1>\n<UL COMPACT PLAIN>\n')
       
  1185         prevlevels = [0]
       
  1186         for level, title, node in self.contents:
       
  1187             if level > maxlevel:
       
  1188                 continue
       
  1189             if level > prevlevels[-1]:
       
  1190                 # can only advance one level at a time
       
  1191                 self.write('  '*prevlevels[-1], '<UL PLAIN>\n')
       
  1192                 prevlevels.append(level)
       
  1193             elif level < prevlevels[-1]:
       
  1194                 # might drop back multiple levels
       
  1195                 while level < prevlevels[-1]:
       
  1196                     del prevlevels[-1]
       
  1197                     self.write('  '*prevlevels[-1],
       
  1198                                '</UL>\n')
       
  1199             self.write('  '*level, '<LI> <A HREF="',
       
  1200                        makefile(node), '">')
       
  1201             self.expand(title)
       
  1202             self.write('</A>\n')
       
  1203         self.write('</UL>\n' * len(prevlevels))
       
  1204 
       
  1205     # --- Page lay-out ---
       
  1206 
       
  1207     # These commands are only meaningful in printed text
       
  1208 
       
  1209     def do_page(self, args): pass
       
  1210 
       
  1211     def do_need(self, args): pass
       
  1212 
       
  1213     def bgn_group(self, args): pass
       
  1214     def end_group(self): pass
       
  1215 
       
  1216     # --- Line lay-out ---
       
  1217 
       
  1218     def do_sp(self, args):
       
  1219         if self.nofill:
       
  1220             self.write('\n')
       
  1221         else:
       
  1222             self.write('<P>\n')
       
  1223 
       
  1224     def do_hline(self, args):
       
  1225         self.write('<HR>')
       
  1226 
       
  1227     # --- Function and variable definitions ---
       
  1228 
       
  1229     def bgn_deffn(self, args):
       
  1230         self.write('<DL>')
       
  1231         self.do_deffnx(args)
       
  1232 
       
  1233     def end_deffn(self):
       
  1234         self.write('</DL>\n')
       
  1235 
       
  1236     def do_deffnx(self, args):
       
  1237         self.write('<DT>')
       
  1238         words = splitwords(args, 2)
       
  1239         [category, name], rest = words[:2], words[2:]
       
  1240         self.expand('@b{%s}' % name)
       
  1241         for word in rest: self.expand(' ' + makevar(word))
       
  1242         #self.expand(' -- ' + category)
       
  1243         self.write('\n<DD>')
       
  1244         self.index('fn', name)
       
  1245 
       
  1246     def bgn_defun(self, args): self.bgn_deffn('Function ' + args)
       
  1247     end_defun = end_deffn
       
  1248     def do_defunx(self, args): self.do_deffnx('Function ' + args)
       
  1249 
       
  1250     def bgn_defmac(self, args): self.bgn_deffn('Macro ' + args)
       
  1251     end_defmac = end_deffn
       
  1252     def do_defmacx(self, args): self.do_deffnx('Macro ' + args)
       
  1253 
       
  1254     def bgn_defspec(self, args): self.bgn_deffn('{Special Form} ' + args)
       
  1255     end_defspec = end_deffn
       
  1256     def do_defspecx(self, args): self.do_deffnx('{Special Form} ' + args)
       
  1257 
       
  1258     def bgn_defvr(self, args):
       
  1259         self.write('<DL>')
       
  1260         self.do_defvrx(args)
       
  1261 
       
  1262     end_defvr = end_deffn
       
  1263 
       
  1264     def do_defvrx(self, args):
       
  1265         self.write('<DT>')
       
  1266         words = splitwords(args, 2)
       
  1267         [category, name], rest = words[:2], words[2:]
       
  1268         self.expand('@code{%s}' % name)
       
  1269         # If there are too many arguments, show them
       
  1270         for word in rest: self.expand(' ' + word)
       
  1271         #self.expand(' -- ' + category)
       
  1272         self.write('\n<DD>')
       
  1273         self.index('vr', name)
       
  1274 
       
  1275     def bgn_defvar(self, args): self.bgn_defvr('Variable ' + args)
       
  1276     end_defvar = end_defvr
       
  1277     def do_defvarx(self, args): self.do_defvrx('Variable ' + args)
       
  1278 
       
  1279     def bgn_defopt(self, args): self.bgn_defvr('{User Option} ' + args)
       
  1280     end_defopt = end_defvr
       
  1281     def do_defoptx(self, args): self.do_defvrx('{User Option} ' + args)
       
  1282 
       
  1283     # --- Ditto for typed languages ---
       
  1284 
       
  1285     def bgn_deftypefn(self, args):
       
  1286         self.write('<DL>')
       
  1287         self.do_deftypefnx(args)
       
  1288 
       
  1289     end_deftypefn = end_deffn
       
  1290 
       
  1291     def do_deftypefnx(self, args):
       
  1292         self.write('<DT>')
       
  1293         words = splitwords(args, 3)
       
  1294         [category, datatype, name], rest = words[:3], words[3:]
       
  1295         self.expand('@code{%s} @b{%s}' % (datatype, name))
       
  1296         for word in rest: self.expand(' ' + makevar(word))
       
  1297         #self.expand(' -- ' + category)
       
  1298         self.write('\n<DD>')
       
  1299         self.index('fn', name)
       
  1300 
       
  1301 
       
  1302     def bgn_deftypefun(self, args): self.bgn_deftypefn('Function ' + args)
       
  1303     end_deftypefun = end_deftypefn
       
  1304     def do_deftypefunx(self, args): self.do_deftypefnx('Function ' + args)
       
  1305 
       
  1306     def bgn_deftypevr(self, args):
       
  1307         self.write('<DL>')
       
  1308         self.do_deftypevrx(args)
       
  1309 
       
  1310     end_deftypevr = end_deftypefn
       
  1311 
       
  1312     def do_deftypevrx(self, args):
       
  1313         self.write('<DT>')
       
  1314         words = splitwords(args, 3)
       
  1315         [category, datatype, name], rest = words[:3], words[3:]
       
  1316         self.expand('@code{%s} @b{%s}' % (datatype, name))
       
  1317         # If there are too many arguments, show them
       
  1318         for word in rest: self.expand(' ' + word)
       
  1319         #self.expand(' -- ' + category)
       
  1320         self.write('\n<DD>')
       
  1321         self.index('fn', name)
       
  1322 
       
  1323     def bgn_deftypevar(self, args):
       
  1324         self.bgn_deftypevr('Variable ' + args)
       
  1325     end_deftypevar = end_deftypevr
       
  1326     def do_deftypevarx(self, args):
       
  1327         self.do_deftypevrx('Variable ' + args)
       
  1328 
       
  1329     # --- Ditto for object-oriented languages ---
       
  1330 
       
  1331     def bgn_defcv(self, args):
       
  1332         self.write('<DL>')
       
  1333         self.do_defcvx(args)
       
  1334 
       
  1335     end_defcv = end_deftypevr
       
  1336 
       
  1337     def do_defcvx(self, args):
       
  1338         self.write('<DT>')
       
  1339         words = splitwords(args, 3)
       
  1340         [category, classname, name], rest = words[:3], words[3:]
       
  1341         self.expand('@b{%s}' % name)
       
  1342         # If there are too many arguments, show them
       
  1343         for word in rest: self.expand(' ' + word)
       
  1344         #self.expand(' -- %s of @code{%s}' % (category, classname))
       
  1345         self.write('\n<DD>')
       
  1346         self.index('vr', '%s @r{on %s}' % (name, classname))
       
  1347 
       
  1348     def bgn_defivar(self, args):
       
  1349         self.bgn_defcv('{Instance Variable} ' + args)
       
  1350     end_defivar = end_defcv
       
  1351     def do_defivarx(self, args):
       
  1352         self.do_defcvx('{Instance Variable} ' + args)
       
  1353 
       
  1354     def bgn_defop(self, args):
       
  1355         self.write('<DL>')
       
  1356         self.do_defopx(args)
       
  1357 
       
  1358     end_defop = end_defcv
       
  1359 
       
  1360     def do_defopx(self, args):
       
  1361         self.write('<DT>')
       
  1362         words = splitwords(args, 3)
       
  1363         [category, classname, name], rest = words[:3], words[3:]
       
  1364         self.expand('@b{%s}' % name)
       
  1365         for word in rest: self.expand(' ' + makevar(word))
       
  1366         #self.expand(' -- %s of @code{%s}' % (category, classname))
       
  1367         self.write('\n<DD>')
       
  1368         self.index('fn', '%s @r{on %s}' % (name, classname))
       
  1369 
       
  1370     def bgn_defmethod(self, args):
       
  1371         self.bgn_defop('Method ' + args)
       
  1372     end_defmethod = end_defop
       
  1373     def do_defmethodx(self, args):
       
  1374         self.do_defopx('Method ' + args)
       
  1375 
       
  1376     # --- Ditto for data types ---
       
  1377 
       
  1378     def bgn_deftp(self, args):
       
  1379         self.write('<DL>')
       
  1380         self.do_deftpx(args)
       
  1381 
       
  1382     end_deftp = end_defcv
       
  1383 
       
  1384     def do_deftpx(self, args):
       
  1385         self.write('<DT>')
       
  1386         words = splitwords(args, 2)
       
  1387         [category, name], rest = words[:2], words[2:]
       
  1388         self.expand('@b{%s}' % name)
       
  1389         for word in rest: self.expand(' ' + word)
       
  1390         #self.expand(' -- ' + category)
       
  1391         self.write('\n<DD>')
       
  1392         self.index('tp', name)
       
  1393 
       
  1394     # --- Making Lists and Tables
       
  1395 
       
  1396     def bgn_enumerate(self, args):
       
  1397         if not args:
       
  1398             self.write('<OL>\n')
       
  1399             self.stackinfo[len(self.stack)] = '</OL>\n'
       
  1400         else:
       
  1401             self.itemnumber = args
       
  1402             self.write('<UL>\n')
       
  1403             self.stackinfo[len(self.stack)] = '</UL>\n'
       
  1404     def end_enumerate(self):
       
  1405         self.itemnumber = None
       
  1406         self.write(self.stackinfo[len(self.stack) + 1])
       
  1407         del self.stackinfo[len(self.stack) + 1]
       
  1408 
       
  1409     def bgn_itemize(self, args):
       
  1410         self.itemarg = args
       
  1411         self.write('<UL>\n')
       
  1412     def end_itemize(self):
       
  1413         self.itemarg = None
       
  1414         self.write('</UL>\n')
       
  1415 
       
  1416     def bgn_table(self, args):
       
  1417         self.itemarg = args
       
  1418         self.write('<DL>\n')
       
  1419     def end_table(self):
       
  1420         self.itemarg = None
       
  1421         self.write('</DL>\n')
       
  1422 
       
  1423     def bgn_ftable(self, args):
       
  1424         self.itemindex = 'fn'
       
  1425         self.bgn_table(args)
       
  1426     def end_ftable(self):
       
  1427         self.itemindex = None
       
  1428         self.end_table()
       
  1429 
       
  1430     def bgn_vtable(self, args):
       
  1431         self.itemindex = 'vr'
       
  1432         self.bgn_table(args)
       
  1433     def end_vtable(self):
       
  1434         self.itemindex = None
       
  1435         self.end_table()
       
  1436 
       
  1437     def do_item(self, args):
       
  1438         if self.itemindex: self.index(self.itemindex, args)
       
  1439         if self.itemarg:
       
  1440             if self.itemarg[0] == '@' and self.itemarg[1] and \
       
  1441                             self.itemarg[1] in string.ascii_letters:
       
  1442                 args = self.itemarg + '{' + args + '}'
       
  1443             else:
       
  1444                 # some other character, e.g. '-'
       
  1445                 args = self.itemarg + ' ' + args
       
  1446         if self.itemnumber <> None:
       
  1447             args = self.itemnumber + '. ' + args
       
  1448             self.itemnumber = increment(self.itemnumber)
       
  1449         if self.stack and self.stack[-1] == 'table':
       
  1450             self.write('<DT>')
       
  1451             self.expand(args)
       
  1452             self.write('\n<DD>')
       
  1453         elif self.stack and self.stack[-1] == 'multitable':
       
  1454             self.write('<TR><TD>')
       
  1455             self.expand(args)
       
  1456             self.write('</TD>\n</TR>\n')
       
  1457         else:
       
  1458             self.write('<LI>')
       
  1459             self.expand(args)
       
  1460             self.write('  ')
       
  1461     do_itemx = do_item # XXX Should suppress leading blank line
       
  1462 
       
  1463     # rpyron 2002-05-07  multitable support
       
  1464     def bgn_multitable(self, args):
       
  1465         self.itemarg = None     # should be handled by columnfractions
       
  1466         self.write('<TABLE BORDER="">\n')
       
  1467     def end_multitable(self):
       
  1468         self.itemarg = None
       
  1469         self.write('</TABLE>\n<BR>\n')
       
  1470     def handle_columnfractions(self):
       
  1471         # It would be better to handle this, but for now it's in the way...
       
  1472         self.itemarg = None
       
  1473     def handle_tab(self):
       
  1474         self.write('</TD>\n    <TD>')
       
  1475 
       
  1476     # --- Enumerations, displays, quotations ---
       
  1477     # XXX Most of these should increase the indentation somehow
       
  1478 
       
  1479     def bgn_quotation(self, args): self.write('<BLOCKQUOTE>')
       
  1480     def end_quotation(self): self.write('</BLOCKQUOTE>\n')
       
  1481 
       
  1482     def bgn_example(self, args):
       
  1483         self.nofill = self.nofill + 1
       
  1484         self.write('<PRE>')
       
  1485     def end_example(self):
       
  1486         self.write('</PRE>\n')
       
  1487         self.nofill = self.nofill - 1
       
  1488 
       
  1489     bgn_lisp = bgn_example # Synonym when contents are executable lisp code
       
  1490     end_lisp = end_example
       
  1491 
       
  1492     bgn_smallexample = bgn_example # XXX Should use smaller font
       
  1493     end_smallexample = end_example
       
  1494 
       
  1495     bgn_smalllisp = bgn_lisp # Ditto
       
  1496     end_smalllisp = end_lisp
       
  1497 
       
  1498     bgn_display = bgn_example
       
  1499     end_display = end_example
       
  1500 
       
  1501     bgn_format = bgn_display
       
  1502     end_format = end_display
       
  1503 
       
  1504     def do_exdent(self, args): self.expand(args + '\n')
       
  1505     # XXX Should really mess with indentation
       
  1506 
       
  1507     def bgn_flushleft(self, args):
       
  1508         self.nofill = self.nofill + 1
       
  1509         self.write('<PRE>\n')
       
  1510     def end_flushleft(self):
       
  1511         self.write('</PRE>\n')
       
  1512         self.nofill = self.nofill - 1
       
  1513 
       
  1514     def bgn_flushright(self, args):
       
  1515         self.nofill = self.nofill + 1
       
  1516         self.write('<ADDRESS COMPACT>\n')
       
  1517     def end_flushright(self):
       
  1518         self.write('</ADDRESS>\n')
       
  1519         self.nofill = self.nofill - 1
       
  1520 
       
  1521     def bgn_menu(self, args):
       
  1522         self.write('<DIR>\n')
       
  1523         self.write('  <STRONG><EM>Menu</EM></STRONG><P>\n')
       
  1524         self.htmlhelp.beginmenu()
       
  1525     def end_menu(self):
       
  1526         self.write('</DIR>\n')
       
  1527         self.htmlhelp.endmenu()
       
  1528 
       
  1529     def bgn_cartouche(self, args): pass
       
  1530     def end_cartouche(self): pass
       
  1531 
       
  1532     # --- Indices ---
       
  1533 
       
  1534     def resetindex(self):
       
  1535         self.noncodeindices = ['cp']
       
  1536         self.indextitle = {}
       
  1537         self.indextitle['cp'] = 'Concept'
       
  1538         self.indextitle['fn'] = 'Function'
       
  1539         self.indextitle['ky'] = 'Keyword'
       
  1540         self.indextitle['pg'] = 'Program'
       
  1541         self.indextitle['tp'] = 'Type'
       
  1542         self.indextitle['vr'] = 'Variable'
       
  1543         #
       
  1544         self.whichindex = {}
       
  1545         for name in self.indextitle.keys():
       
  1546             self.whichindex[name] = []
       
  1547 
       
  1548     def user_index(self, name, args):
       
  1549         if self.whichindex.has_key(name):
       
  1550             self.index(name, args)
       
  1551         else:
       
  1552             print '*** No index named', repr(name)
       
  1553 
       
  1554     def do_cindex(self, args): self.index('cp', args)
       
  1555     def do_findex(self, args): self.index('fn', args)
       
  1556     def do_kindex(self, args): self.index('ky', args)
       
  1557     def do_pindex(self, args): self.index('pg', args)
       
  1558     def do_tindex(self, args): self.index('tp', args)
       
  1559     def do_vindex(self, args): self.index('vr', args)
       
  1560 
       
  1561     def index(self, name, args):
       
  1562         self.whichindex[name].append((args, self.nodename))
       
  1563         self.htmlhelp.index(args, self.nodename)
       
  1564 
       
  1565     def do_synindex(self, args):
       
  1566         words = args.split()
       
  1567         if len(words) <> 2:
       
  1568             print '*** bad @synindex', args
       
  1569             return
       
  1570         [old, new] = words
       
  1571         if not self.whichindex.has_key(old) or \
       
  1572                   not self.whichindex.has_key(new):
       
  1573             print '*** bad key(s) in @synindex', args
       
  1574             return
       
  1575         if old <> new and \
       
  1576                   self.whichindex[old] is not self.whichindex[new]:
       
  1577             inew = self.whichindex[new]
       
  1578             inew[len(inew):] = self.whichindex[old]
       
  1579             self.whichindex[old] = inew
       
  1580     do_syncodeindex = do_synindex # XXX Should use code font
       
  1581 
       
  1582     def do_printindex(self, args):
       
  1583         words = args.split()
       
  1584         for name in words:
       
  1585             if self.whichindex.has_key(name):
       
  1586                 self.prindex(name)
       
  1587             else:
       
  1588                 print '*** No index named', repr(name)
       
  1589 
       
  1590     def prindex(self, name):
       
  1591         iscodeindex = (name not in self.noncodeindices)
       
  1592         index = self.whichindex[name]
       
  1593         if not index: return
       
  1594         if self.debugging:
       
  1595             print '!'*self.debugging, '--- Generating', \
       
  1596                   self.indextitle[name], 'index'
       
  1597         #  The node already provides a title
       
  1598         index1 = []
       
  1599         junkprog = re.compile('^(@[a-z]+)?{')
       
  1600         for key, node in index:
       
  1601             sortkey = key.lower()
       
  1602             # Remove leading `@cmd{' from sort key
       
  1603             # -- don't bother about the matching `}'
       
  1604             oldsortkey = sortkey
       
  1605             while 1:
       
  1606                 mo = junkprog.match(sortkey)
       
  1607                 if not mo:
       
  1608                     break
       
  1609                 i = mo.end()
       
  1610                 sortkey = sortkey[i:]
       
  1611             index1.append((sortkey, key, node))
       
  1612         del index[:]
       
  1613         index1.sort()
       
  1614         self.write('<DL COMPACT>\n')
       
  1615         prevkey = prevnode = None
       
  1616         for sortkey, key, node in index1:
       
  1617             if (key, node) == (prevkey, prevnode):
       
  1618                 continue
       
  1619             if self.debugging > 1: print '!'*self.debugging, key, ':', node
       
  1620             self.write('<DT>')
       
  1621             if iscodeindex: key = '@code{' + key + '}'
       
  1622             if key != prevkey:
       
  1623                 self.expand(key)
       
  1624             self.write('\n<DD><A HREF="%s">%s</A>\n' % (makefile(node), node))
       
  1625             prevkey, prevnode = key, node
       
  1626         self.write('</DL>\n')
       
  1627 
       
  1628     # --- Final error reports ---
       
  1629 
       
  1630     def report(self):
       
  1631         if self.unknown:
       
  1632             print '--- Unrecognized commands ---'
       
  1633             cmds = self.unknown.keys()
       
  1634             cmds.sort()
       
  1635             for cmd in cmds:
       
  1636                 print cmd.ljust(20), self.unknown[cmd]
       
  1637 
       
  1638 
       
  1639 class TexinfoParserHTML3(TexinfoParser):
       
  1640 
       
  1641     COPYRIGHT_SYMBOL = "&copy;"
       
  1642     FN_ID_PATTERN = "[%(id)s]"
       
  1643     FN_SOURCE_PATTERN = '<A ID=footnoteref%(id)s ' \
       
  1644                         'HREF="#footnotetext%(id)s">' + FN_ID_PATTERN + '</A>'
       
  1645     FN_TARGET_PATTERN = '<FN ID=footnotetext%(id)s>\n' \
       
  1646                         '<P><A HREF="#footnoteref%(id)s">' + FN_ID_PATTERN \
       
  1647                         + '</A>\n%(text)s</P></FN>\n'
       
  1648     FN_HEADER = '<DIV CLASS=footnotes>\n  <HR NOSHADE WIDTH=200>\n' \
       
  1649                 '  <STRONG><EM>Footnotes</EM></STRONG>\n  <P>\n'
       
  1650 
       
  1651     Node = HTML3Node
       
  1652 
       
  1653     def bgn_quotation(self, args): self.write('<BQ>')
       
  1654     def end_quotation(self): self.write('</BQ>\n')
       
  1655 
       
  1656     def bgn_example(self, args):
       
  1657         # this use of <CODE> would not be legal in HTML 2.0,
       
  1658         # but is in more recent DTDs.
       
  1659         self.nofill = self.nofill + 1
       
  1660         self.write('<PRE CLASS=example><CODE>')
       
  1661     def end_example(self):
       
  1662         self.write("</CODE></PRE>\n")
       
  1663         self.nofill = self.nofill - 1
       
  1664 
       
  1665     def bgn_flushleft(self, args):
       
  1666         self.nofill = self.nofill + 1
       
  1667         self.write('<PRE CLASS=flushleft>\n')
       
  1668 
       
  1669     def bgn_flushright(self, args):
       
  1670         self.nofill = self.nofill + 1
       
  1671         self.write('<DIV ALIGN=right CLASS=flushright><ADDRESS COMPACT>\n')
       
  1672     def end_flushright(self):
       
  1673         self.write('</ADDRESS></DIV>\n')
       
  1674         self.nofill = self.nofill - 1
       
  1675 
       
  1676     def bgn_menu(self, args):
       
  1677         self.write('<UL PLAIN CLASS=menu>\n')
       
  1678         self.write('  <LH>Menu</LH>\n')
       
  1679     def end_menu(self):
       
  1680         self.write('</UL>\n')
       
  1681 
       
  1682 
       
  1683 # rpyron 2002-05-07
       
  1684 class HTMLHelp:
       
  1685     """
       
  1686     This class encapsulates support for HTML Help. Node names,
       
  1687     file names, menu items, index items, and image file names are
       
  1688     accumulated until a call to finalize(). At that time, three
       
  1689     output files are created in the current directory:
       
  1690 
       
  1691         `helpbase`.hhp  is a HTML Help Workshop project file.
       
  1692                         It contains various information, some of
       
  1693                         which I do not understand; I just copied
       
  1694                         the default project info from a fresh
       
  1695                         installation.
       
  1696         `helpbase`.hhc  is the Contents file for the project.
       
  1697         `helpbase`.hhk  is the Index file for the project.
       
  1698 
       
  1699     When these files are used as input to HTML Help Workshop,
       
  1700     the resulting file will be named:
       
  1701 
       
  1702         `helpbase`.chm
       
  1703 
       
  1704     If none of the defaults in `helpbase`.hhp are changed,
       
  1705     the .CHM file will have Contents, Index, Search, and
       
  1706     Favorites tabs.
       
  1707     """
       
  1708 
       
  1709     codeprog = re.compile('@code{(.*?)}')
       
  1710 
       
  1711     def __init__(self,helpbase,dirname):
       
  1712         self.helpbase    = helpbase
       
  1713         self.dirname     = dirname
       
  1714         self.projectfile = None
       
  1715         self.contentfile = None
       
  1716         self.indexfile   = None
       
  1717         self.nodelist    = []
       
  1718         self.nodenames   = {}         # nodename : index
       
  1719         self.nodeindex   = {}
       
  1720         self.filenames   = {}         # filename : filename
       
  1721         self.indexlist   = []         # (args,nodename) == (key,location)
       
  1722         self.current     = ''
       
  1723         self.menudict    = {}
       
  1724         self.dumped      = {}
       
  1725 
       
  1726 
       
  1727     def addnode(self,name,next,prev,up,filename):
       
  1728         node = (name,next,prev,up,filename)
       
  1729         # add this file to dict
       
  1730         # retrieve list with self.filenames.values()
       
  1731         self.filenames[filename] = filename
       
  1732         # add this node to nodelist
       
  1733         self.nodeindex[name] = len(self.nodelist)
       
  1734         self.nodelist.append(node)
       
  1735         # set 'current' for menu items
       
  1736         self.current = name
       
  1737         self.menudict[self.current] = []
       
  1738 
       
  1739     def menuitem(self,nodename):
       
  1740         menu = self.menudict[self.current]
       
  1741         menu.append(nodename)
       
  1742 
       
  1743 
       
  1744     def addimage(self,imagename):
       
  1745         self.filenames[imagename] = imagename
       
  1746 
       
  1747     def index(self, args, nodename):
       
  1748         self.indexlist.append((args,nodename))
       
  1749 
       
  1750     def beginmenu(self):
       
  1751         pass
       
  1752 
       
  1753     def endmenu(self):
       
  1754         pass
       
  1755 
       
  1756     def finalize(self):
       
  1757         if not self.helpbase:
       
  1758             return
       
  1759 
       
  1760         # generate interesting filenames
       
  1761         resultfile   = self.helpbase + '.chm'
       
  1762         projectfile  = self.helpbase + '.hhp'
       
  1763         contentfile  = self.helpbase + '.hhc'
       
  1764         indexfile    = self.helpbase + '.hhk'
       
  1765 
       
  1766         # generate a reasonable title
       
  1767         title        = self.helpbase
       
  1768 
       
  1769         # get the default topic file
       
  1770         (topname,topnext,topprev,topup,topfile) = self.nodelist[0]
       
  1771         defaulttopic = topfile
       
  1772 
       
  1773         # PROJECT FILE
       
  1774         try:
       
  1775             fp = open(projectfile,'w')
       
  1776             print>>fp, '[OPTIONS]'
       
  1777             print>>fp, 'Auto Index=Yes'
       
  1778             print>>fp, 'Binary TOC=No'
       
  1779             print>>fp, 'Binary Index=Yes'
       
  1780             print>>fp, 'Compatibility=1.1'
       
  1781             print>>fp, 'Compiled file=' + resultfile + ''
       
  1782             print>>fp, 'Contents file=' + contentfile + ''
       
  1783             print>>fp, 'Default topic=' + defaulttopic + ''
       
  1784             print>>fp, 'Error log file=ErrorLog.log'
       
  1785             print>>fp, 'Index file=' + indexfile + ''
       
  1786             print>>fp, 'Title=' + title + ''
       
  1787             print>>fp, 'Display compile progress=Yes'
       
  1788             print>>fp, 'Full-text search=Yes'
       
  1789             print>>fp, 'Default window=main'
       
  1790             print>>fp, ''
       
  1791             print>>fp, '[WINDOWS]'
       
  1792             print>>fp, ('main=,"' + contentfile + '","' + indexfile
       
  1793                         + '","","",,,,,0x23520,222,0x1046,[10,10,780,560],'
       
  1794                         '0xB0000,,,,,,0')
       
  1795             print>>fp, ''
       
  1796             print>>fp, '[FILES]'
       
  1797             print>>fp, ''
       
  1798             self.dumpfiles(fp)
       
  1799             fp.close()
       
  1800         except IOError, msg:
       
  1801             print projectfile, ':', msg
       
  1802             sys.exit(1)
       
  1803 
       
  1804         # CONTENT FILE
       
  1805         try:
       
  1806             fp = open(contentfile,'w')
       
  1807             print>>fp, '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">'
       
  1808             print>>fp, '<!-- This file defines the table of contents -->'
       
  1809             print>>fp, '<HTML>'
       
  1810             print>>fp, '<HEAD>'
       
  1811             print>>fp, ('<meta name="GENERATOR"'
       
  1812                         'content="Microsoft&reg; HTML Help Workshop 4.1">')
       
  1813             print>>fp, '<!-- Sitemap 1.0 -->'
       
  1814             print>>fp, '</HEAD>'
       
  1815             print>>fp, '<BODY>'
       
  1816             print>>fp, '   <OBJECT type="text/site properties">'
       
  1817             print>>fp, '     <param name="Window Styles" value="0x800025">'
       
  1818             print>>fp, '     <param name="comment" value="title:">'
       
  1819             print>>fp, '     <param name="comment" value="base:">'
       
  1820             print>>fp, '   </OBJECT>'
       
  1821             self.dumpnodes(fp)
       
  1822             print>>fp, '</BODY>'
       
  1823             print>>fp, '</HTML>'
       
  1824             fp.close()
       
  1825         except IOError, msg:
       
  1826             print contentfile, ':', msg
       
  1827             sys.exit(1)
       
  1828 
       
  1829         # INDEX FILE
       
  1830         try:
       
  1831             fp = open(indexfile  ,'w')
       
  1832             print>>fp, '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">'
       
  1833             print>>fp, '<!-- This file defines the index -->'
       
  1834             print>>fp, '<HTML>'
       
  1835             print>>fp, '<HEAD>'
       
  1836             print>>fp, ('<meta name="GENERATOR"'
       
  1837                         'content="Microsoft&reg; HTML Help Workshop 4.1">')
       
  1838             print>>fp, '<!-- Sitemap 1.0 -->'
       
  1839             print>>fp, '</HEAD>'
       
  1840             print>>fp, '<BODY>'
       
  1841             print>>fp, '<OBJECT type="text/site properties">'
       
  1842             print>>fp, '</OBJECT>'
       
  1843             self.dumpindex(fp)
       
  1844             print>>fp, '</BODY>'
       
  1845             print>>fp, '</HTML>'
       
  1846             fp.close()
       
  1847         except IOError, msg:
       
  1848             print indexfile  , ':', msg
       
  1849             sys.exit(1)
       
  1850 
       
  1851     def dumpfiles(self, outfile=sys.stdout):
       
  1852         filelist = self.filenames.values()
       
  1853         filelist.sort()
       
  1854         for filename in filelist:
       
  1855             print>>outfile, filename
       
  1856 
       
  1857     def dumpnodes(self, outfile=sys.stdout):
       
  1858         self.dumped = {}
       
  1859         if self.nodelist:
       
  1860             nodename, dummy, dummy, dummy, dummy = self.nodelist[0]
       
  1861             self.topnode = nodename
       
  1862 
       
  1863         print>>outfile,  '<UL>'
       
  1864         for node in self.nodelist:
       
  1865             self.dumpnode(node,0,outfile)
       
  1866         print>>outfile,  '</UL>'
       
  1867 
       
  1868     def dumpnode(self, node, indent=0, outfile=sys.stdout):
       
  1869         if node:
       
  1870             # Retrieve info for this node
       
  1871             (nodename,next,prev,up,filename) = node
       
  1872             self.current = nodename
       
  1873 
       
  1874             # Have we been dumped already?
       
  1875             if self.dumped.has_key(nodename):
       
  1876                 return
       
  1877             self.dumped[nodename] = 1
       
  1878 
       
  1879             # Print info for this node
       
  1880             print>>outfile, ' '*indent,
       
  1881             print>>outfile, '<LI><OBJECT type="text/sitemap">',
       
  1882             print>>outfile, '<param name="Name" value="' + nodename +'">',
       
  1883             print>>outfile, '<param name="Local" value="'+ filename +'">',
       
  1884             print>>outfile, '</OBJECT>'
       
  1885 
       
  1886             # Does this node have menu items?
       
  1887             try:
       
  1888                 menu = self.menudict[nodename]
       
  1889                 self.dumpmenu(menu,indent+2,outfile)
       
  1890             except KeyError:
       
  1891                 pass
       
  1892 
       
  1893     def dumpmenu(self, menu, indent=0, outfile=sys.stdout):
       
  1894         if menu:
       
  1895             currentnode = self.current
       
  1896             if currentnode != self.topnode:    # XXX this is a hack
       
  1897                 print>>outfile, ' '*indent + '<UL>'
       
  1898                 indent += 2
       
  1899             for item in menu:
       
  1900                 menunode = self.getnode(item)
       
  1901                 self.dumpnode(menunode,indent,outfile)
       
  1902             if currentnode != self.topnode:    # XXX this is a hack
       
  1903                 print>>outfile, ' '*indent + '</UL>'
       
  1904                 indent -= 2
       
  1905 
       
  1906     def getnode(self, nodename):
       
  1907         try:
       
  1908             index = self.nodeindex[nodename]
       
  1909             return self.nodelist[index]
       
  1910         except KeyError:
       
  1911             return None
       
  1912         except IndexError:
       
  1913             return None
       
  1914 
       
  1915     # (args,nodename) == (key,location)
       
  1916     def dumpindex(self, outfile=sys.stdout):
       
  1917         print>>outfile,  '<UL>'
       
  1918         for (key,location) in self.indexlist:
       
  1919             key = self.codeexpand(key)
       
  1920             location = makefile(location)
       
  1921             location = self.dirname + '/' + location
       
  1922             print>>outfile, '<LI><OBJECT type="text/sitemap">',
       
  1923             print>>outfile, '<param name="Name" value="' + key + '">',
       
  1924             print>>outfile, '<param name="Local" value="' + location + '">',
       
  1925             print>>outfile, '</OBJECT>'
       
  1926         print>>outfile,  '</UL>'
       
  1927 
       
  1928     def codeexpand(self, line):
       
  1929         co = self.codeprog.match(line)
       
  1930         if not co:
       
  1931             return line
       
  1932         bgn, end = co.span(0)
       
  1933         a, b = co.span(1)
       
  1934         line = line[:bgn] + line[a:b] + line[end:]
       
  1935         return line
       
  1936 
       
  1937 
       
  1938 # Put @var{} around alphabetic substrings
       
  1939 def makevar(str):
       
  1940     return '@var{'+str+'}'
       
  1941 
       
  1942 
       
  1943 # Split a string in "words" according to findwordend
       
  1944 def splitwords(str, minlength):
       
  1945     words = []
       
  1946     i = 0
       
  1947     n = len(str)
       
  1948     while i < n:
       
  1949         while i < n and str[i] in ' \t\n': i = i+1
       
  1950         if i >= n: break
       
  1951         start = i
       
  1952         i = findwordend(str, i, n)
       
  1953         words.append(str[start:i])
       
  1954     while len(words) < minlength: words.append('')
       
  1955     return words
       
  1956 
       
  1957 
       
  1958 # Find the end of a "word", matching braces and interpreting @@ @{ @}
       
  1959 fwprog = re.compile('[@{} ]')
       
  1960 def findwordend(str, i, n):
       
  1961     level = 0
       
  1962     while i < n:
       
  1963         mo = fwprog.search(str, i)
       
  1964         if not mo:
       
  1965             break
       
  1966         i = mo.start()
       
  1967         c = str[i]; i = i+1
       
  1968         if c == '@': i = i+1 # Next character is not special
       
  1969         elif c == '{': level = level+1
       
  1970         elif c == '}': level = level-1
       
  1971         elif c == ' ' and level <= 0: return i-1
       
  1972     return n
       
  1973 
       
  1974 
       
  1975 # Convert a node name into a file name
       
  1976 def makefile(nodename):
       
  1977     nodename = nodename.strip()
       
  1978     return fixfunnychars(nodename) + '.html'
       
  1979 
       
  1980 
       
  1981 # Characters that are perfectly safe in filenames and hyperlinks
       
  1982 goodchars = string.ascii_letters + string.digits + '!@-=+.'
       
  1983 
       
  1984 # Replace characters that aren't perfectly safe by dashes
       
  1985 # Underscores are bad since Cern HTTPD treats them as delimiters for
       
  1986 # encoding times, so you get mismatches if you compress your files:
       
  1987 # a.html.gz will map to a_b.html.gz
       
  1988 def fixfunnychars(addr):
       
  1989     i = 0
       
  1990     while i < len(addr):
       
  1991         c = addr[i]
       
  1992         if c not in goodchars:
       
  1993             c = '-'
       
  1994             addr = addr[:i] + c + addr[i+1:]
       
  1995         i = i + len(c)
       
  1996     return addr
       
  1997 
       
  1998 
       
  1999 # Increment a string used as an enumeration
       
  2000 def increment(s):
       
  2001     if not s:
       
  2002         return '1'
       
  2003     for sequence in string.digits, string.lowercase, string.uppercase:
       
  2004         lastc = s[-1]
       
  2005         if lastc in sequence:
       
  2006             i = sequence.index(lastc) + 1
       
  2007             if i >= len(sequence):
       
  2008                 if len(s) == 1:
       
  2009                     s = sequence[0]*2
       
  2010                     if s == '00':
       
  2011                         s = '10'
       
  2012                 else:
       
  2013                     s = increment(s[:-1]) + sequence[0]
       
  2014             else:
       
  2015                 s = s[:-1] + sequence[i]
       
  2016             return s
       
  2017     return s # Don't increment
       
  2018 
       
  2019 
       
  2020 def test():
       
  2021     import sys
       
  2022     debugging = 0
       
  2023     print_headers = 0
       
  2024     cont = 0
       
  2025     html3 = 0
       
  2026     htmlhelp = ''
       
  2027 
       
  2028     while sys.argv[1] == ['-d']:
       
  2029         debugging = debugging + 1
       
  2030         del sys.argv[1]
       
  2031     if sys.argv[1] == '-p':
       
  2032         print_headers = 1
       
  2033         del sys.argv[1]
       
  2034     if sys.argv[1] == '-c':
       
  2035         cont = 1
       
  2036         del sys.argv[1]
       
  2037     if sys.argv[1] == '-3':
       
  2038         html3 = 1
       
  2039         del sys.argv[1]
       
  2040     if sys.argv[1] == '-H':
       
  2041         helpbase = sys.argv[2]
       
  2042         del sys.argv[1:3]
       
  2043     if len(sys.argv) <> 3:
       
  2044         print 'usage: texi2hh [-d [-d]] [-p] [-c] [-3] [-H htmlhelp]', \
       
  2045               'inputfile outputdirectory'
       
  2046         sys.exit(2)
       
  2047 
       
  2048     if html3:
       
  2049         parser = TexinfoParserHTML3()
       
  2050     else:
       
  2051         parser = TexinfoParser()
       
  2052     parser.cont = cont
       
  2053     parser.debugging = debugging
       
  2054     parser.print_headers = print_headers
       
  2055 
       
  2056     file = sys.argv[1]
       
  2057     dirname  = sys.argv[2]
       
  2058     parser.setdirname(dirname)
       
  2059     parser.setincludedir(os.path.dirname(file))
       
  2060 
       
  2061     htmlhelp = HTMLHelp(helpbase, dirname)
       
  2062     parser.sethtmlhelp(htmlhelp)
       
  2063 
       
  2064     try:
       
  2065         fp = open(file, 'r')
       
  2066     except IOError, msg:
       
  2067         print file, ':', msg
       
  2068         sys.exit(1)
       
  2069 
       
  2070     parser.parse(fp)
       
  2071     fp.close()
       
  2072     parser.report()
       
  2073 
       
  2074     htmlhelp.finalize()
       
  2075 
       
  2076 
       
  2077 if __name__ == "__main__":
       
  2078     test()