You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2075 lines
68 KiB

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