texi2html.py 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075
  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(r'^\* ([^:]*):(:|[ \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()