2 # python script to generate configoptions.cpp and config.doc from config.xml
4 # Copyright (C) 1997-2014 by Dimitri van Heesch.
6 # Permission to use, copy, modify, and distribute this software and its
7 # documentation under the terms of the GNU General Public License is hereby
8 # granted. No representations are made about the suitability of this software
9 # for any purpose. It is provided "as is" without express or implied warranty.
10 # See the GNU General Public License for more details.
12 # Documents produced by Doxygen are derivative works derived from the
13 # input used in their production; they are not affected by this license.
15 import xml.dom.minidom
19 from xml.dom import minidom, Node
22 def transformDocs(doc):
23 # join lines, unless it is an empty line
24 # remove doxygen layout constructs
26 doc = doc.replace("\n", " ")
27 doc = doc.replace("\r", " ")
28 doc = doc.replace("\t", " ")
29 doc = doc.replace("\\&", "&")
30 doc = doc.replace("\\c ", " ")
31 doc = doc.replace("\\b ", " ")
32 doc = doc.replace("\\e ", " ")
33 doc = doc.replace("\\$", "$")
34 doc = doc.replace("\\#include ", "#include ")
35 doc = doc.replace("\\#undef ", "#undef ")
36 doc = doc.replace("-# ", "\n - ")
37 doc = doc.replace(" - ", "\n - ")
38 doc = doc.replace("\\sa", "\nSee also: ")
39 doc = doc.replace("\\par", "\n")
40 doc = doc.replace("@note", "\nNote:")
41 doc = doc.replace("\\note", "\nNote:")
42 doc = doc.replace("\\verbatim", "\n")
43 doc = doc.replace("\\endverbatim", "\n")
44 doc = doc.replace("<code>", "")
45 doc = doc.replace("</code>", "")
46 doc = doc.replace("`", "")
47 doc = doc.replace("\\<", "<")
48 doc = doc.replace("\\>", ">")
49 doc = doc.replace("\\@", "@")
50 doc = doc.replace("\\\\", "\\")
51 # \ref name "description" -> description
52 doc = re.sub('\\\\ref +[^ ]* +"([^"]*)"', '\\1', doc)
54 # \ref <key> -> description
55 doc = re.sub('\\\\ref +doxygen_usage', '"Doxygen usage"', doc)
56 doc = re.sub('\\\\ref +extsearch', '"External Indexing and Searching"',
58 doc = re.sub('\\\\ref +external', '"Linking to external documentation"',
60 # fallback for not handled
61 doc = re.sub('\\\\ref', '', doc)
62 #<a href="address">description</a> -> description (see: address)
63 doc = re.sub('<a +href="([^"]*)" *>([^<]*)</a>', '\\2 (see: \\1)', doc)
64 # LaTeX name as formula -> LaTeX
65 doc = doc.replace("\\f$\\mbox{\\LaTeX}\\f$", "LaTeX")
66 # Other forula's (now just 2) so explicitely mentioned.
67 doc = doc.replace("\\f$2^{(16+\\mbox{LOOKUP\\_CACHE\\_SIZE})}\\f$",
68 "2^(16+LOOKUP_CACHE_SIZE)")
69 doc = doc.replace("\\f$2^{16} = 65536\\f$", "2^16=65536")
70 # remove consecutive spaces
71 doc = re.sub(" +", " ", doc)
72 # a dirty trick to get an extra empty line in Doxyfile documentation.
73 # <br> will be removed later on again, we need it here otherwise splitlines
74 # will filter the extra line.
75 doc = doc.replace("<br>", "\n<br>\n")
76 # a dirty trick to go to the next line in Doxyfile documentation.
77 # <br/> will be removed later on again, we need it here otherwise splitlines
78 # will filter the line break.
79 doc = doc.replace("<br/>", "\n<br/>\n")
81 doc = doc.splitlines()
84 split_doc += textwrap.wrap(line, 78)
85 # replace \ by \\, replace " by \", and ' ' by a newline with end string
86 # and start string at next line
88 for line in split_doc:
89 if (line.strip() != "<br/>"):
90 docC.append(line.strip().replace('\\', '\\\\').
91 replace('"', '\\"').replace("<br>", ""))
95 def collectValues(node):
97 for n in node.childNodes:
98 if (n.nodeName == "value"):
99 if n.nodeType == Node.ELEMENT_NODE:
100 if n.getAttribute('name') != "":
101 if n.getAttribute('show_docu') != "NO":
102 name = "<code>" + n.getAttribute('name') + "</code>"
103 desc = n.getAttribute('desc')
110 def addValues(var, node):
111 for n in node.childNodes:
112 if (n.nodeName == "value"):
113 if n.nodeType == Node.ELEMENT_NODE:
114 name = n.getAttribute('name')
115 print " %s->addValue(\"%s\");" % (var, name)
118 def parseHeader(node,objName):
120 for n in node.childNodes:
121 if n.nodeType == Node.ELEMENT_NODE:
122 if (n.nodeName == "docs"):
123 if (n.getAttribute('doxyfile') != "0"):
125 docC = transformDocs(doc)
126 print " %s->setHeader(" % (objName)
130 if i != rng - 1: # since we go from 0 to rng-1
131 print " \"%s\\n\"" % (line)
133 print " \"%s\"" % (line)
138 type = node.getAttribute('type')
139 format = node.getAttribute('format')
140 defval = node.getAttribute('defval')
141 adefval = node.getAttribute('altdefval')
143 if (type != 'obsolete'):
144 for n in node.childNodes:
145 if (n.nodeName == "docs"):
146 if (n.getAttribute('doxyfile') != "0"):
147 if n.nodeType == Node.ELEMENT_NODE:
150 values = collectValues(node)
151 doc += "<br/>Possible values are: "
156 doc += "%s and " % (val)
160 doc += "%s, " % (val)
162 doc += "<br/>The default value is: <code>%s</code>." % (defval)
163 elif (type == 'int'):
164 minval = node.getAttribute('minval')
165 maxval = node.getAttribute('maxval')
166 doc += "<br/>%s: %s, %s: %s, %s: %s." % (" Minimum value", minval,
167 "maximum value", maxval,
168 "default value", defval)
169 elif (type == 'bool'):
170 if (node.hasAttribute('altdefval')):
171 doc += "<br/>%s: %s." % ("The default value is", "system dependent")
173 doc += "<br/>%s: %s." % ("The default value is", "YES" if (defval == "1") else "NO")
174 elif (type == 'list'):
175 if format == 'string':
176 values = collectValues(node)
181 doc += "%s and " % (val)
185 doc += "%s, " % (val)
186 elif (type == 'string'):
189 doc += "<br/>The default directory is: <code>%s</code>." % (
191 elif format == 'file':
192 abspath = node.getAttribute('abspath')
195 doc += "<br/>The default file is: <code>%s</code>." % (
198 doc += "<br/>%s: %s%s%s." % (
199 "The default file (with absolute path) is",
200 "<code>",defval,"</code>")
203 doc += "<br/>The file has to be specified with full path."
205 abspath = node.getAttribute('abspath')
208 doc += "<br/>The default image is: <code>%s</code>." % (
211 doc += "<br/>%s: %s%s%s." % (
212 "The default image (with absolute path) is",
213 "<code>",defval,"</code>")
216 doc += "<br/>The image has to be specified with full path."
217 else: # format == 'string':
219 doc += "<br/>The default value is: <code>%s</code>." % (
222 if (node.hasAttribute('depends')):
223 depends = node.getAttribute('depends')
224 doc += "<br/>%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
225 "This tag requires that the tag", depends.lower(), depends.upper())
227 docC = transformDocs(doc)
230 def parseOption(node):
231 # Handling part for Doxyfile
232 name = node.getAttribute('id')
233 type = node.getAttribute('type')
234 format = node.getAttribute('format')
235 defval = node.getAttribute('defval')
236 adefval = node.getAttribute('altdefval')
237 depends = node.getAttribute('depends')
238 setting = node.getAttribute('setting')
239 docC = prepCDocs(node);
241 print "#if %s" % (setting)
250 print " cb = cfg->addBool("
251 print " \"%s\"," % (name)
255 if i != rng - 1: # since we go from 0 to rng-1
256 print " \"%s\\n\"" % (line)
258 print " \"%s\"," % (line)
259 print " %s" % (enabled)
262 print " cb->addDependency(\"%s\");" % (depends)
263 elif type == 'string':
264 print " cs = cfg->addString("
265 print " \"%s\"," % (name)
269 if i != rng - 1: # since we go from 0 to rng-1
270 print " \"%s\\n\"" % (line)
272 print " \"%s\"" % (line)
275 print " cs->setDefaultValue(\"%s\");" % (defval)
277 print " cs->setWidgetType(ConfigString::File);"
278 elif format == 'image':
279 print " cs->setWidgetType(ConfigString::Image);"
280 elif format == 'dir':
281 print " cs->setWidgetType(ConfigString::Dir);"
283 print " cs->addDependency(\"%s\");" % (depends)
285 print " ce = cfg->addEnum("
286 print " \"%s\"," % (name)
290 if i != rng - 1: # since we go from 0 to rng-1
291 print " \"%s\\n\"" % (line)
293 print " \"%s\"," % (line)
294 print " \"%s\"" % (defval)
296 addValues("ce", node)
298 print " ce->addDependency(\"%s\");" % (depends)
300 minval = node.getAttribute('minval')
301 maxval = node.getAttribute('maxval')
302 print " ci = cfg->addInt("
303 print " \"%s\"," % (name)
307 if i != rng - 1: # since we go from 0 to rng-1
308 print " \"%s\\n\"" % (line)
310 print " \"%s\"," % (line)
311 print " %s,%s,%s" % (minval, maxval, defval)
314 print " ci->addDependency(\"%s\");" % (depends)
316 print " cl = cfg->addList("
317 print " \"%s\"," % (name)
321 if i != rng - 1: # since we go from 0 to rng-1
322 print " \"%s\\n\"" % (line)
324 print " \"%s\"" % (line)
326 addValues("cl", node)
328 print " cl->addDependency(\"%s\");" % (depends)
330 print " cl->setWidgetType(ConfigList::File);"
331 elif format == 'dir':
332 print " cl->setWidgetType(ConfigList::Dir);"
333 elif format == 'filedir':
334 print " cl->setWidgetType(ConfigList::FileAndDir);"
335 elif type == 'obsolete':
336 print " cfg->addObsolete(\"%s\");" % (name)
339 print " cfg->addDisabled(\"%s\");" % (name)
343 def parseGroups(node):
344 name = node.getAttribute('name')
345 doc = node.getAttribute('docs')
346 print "%s%s" % (" //-----------------------------------------",
347 "----------------------------------")
348 print " cfg->addInfo(\"%s\",\"%s\");" % (name, doc)
349 print "%s%s" % (" //-----------------------------------------",
350 "----------------------------------")
352 for n in node.childNodes:
353 if n.nodeType == Node.ELEMENT_NODE:
356 def parseGroupCDocs(node):
357 for n in node.childNodes:
358 if n.nodeType == Node.ELEMENT_NODE:
359 type = n.getAttribute('type')
360 name = n.getAttribute('id')
362 if type != 'obsolete':
364 print " \"%s\"," % (name)
368 if i != rng - 1: # since we go from 0 to rng-1
369 print " \"%s\\n\"" % (line)
371 print " \"%s\"" % (line)
374 def parseOptionDoc(node, first):
375 # Handling part for documentation
376 name = node.getAttribute('id')
377 type = node.getAttribute('type')
378 format = node.getAttribute('format')
379 defval = node.getAttribute('defval')
380 adefval = node.getAttribute('altdefval')
381 depends = node.getAttribute('depends')
382 setting = node.getAttribute('setting')
384 if (type != 'obsolete'):
385 for n in node.childNodes:
386 if (n.nodeName == "docs"):
387 if (n.getAttribute('documentation') != "0"):
388 if n.nodeType == Node.ELEMENT_NODE:
391 print " \\anchor cfg_%s" % (name.lower())
394 print "<dt>\\c %s <dd>" % (name)
396 print " \\anchor cfg_%s" % (name.lower())
397 print "<dt>\\c %s <dd>" % (name)
398 print " \\addindex %s" % (name)
401 values = collectValues(node)
403 print "Possible values are: "
408 print "%s and " % (val)
416 print "The default value is: <code>%s</code>." % (defval)
418 elif (type == 'int'):
419 minval = node.getAttribute('minval')
420 maxval = node.getAttribute('maxval')
423 print "%s: %s%s%s, %s: %s%s%s, %s: %s%s%s." % (
424 " Minimum value", "<code>", minval, "</code>",
425 "maximum value", "<code>", maxval, "</code>",
426 "default value", "<code>", defval, "</code>")
428 elif (type == 'bool'):
431 if (node.hasAttribute('altdefval')):
432 print "The default value is: system dependent."
434 print "The default value is: <code>%s</code>." % (
435 "YES" if (defval == "1") else "NO")
437 elif (type == 'list'):
438 if format == 'string':
439 values = collectValues(node)
444 print "%s and " % (val)
450 elif (type == 'string'):
454 print "The default directory is: <code>%s</code>." % (
456 elif format == 'file':
457 abspath = node.getAttribute('abspath')
461 print "The default file is: <code>%s</code>." % (
464 print "%s: %s%s%s." % (
465 "The default file (with absolute path) is",
466 "<code>",defval,"</code>")
470 print "The file has to be specified with full path."
472 abspath = node.getAttribute('abspath')
476 print "The default image is: <code>%s</code>." % (
479 print "%s: %s%s%s." % (
480 "The default image (with absolute path) is",
481 "<code>",defval,"</code>")
485 print "The image has to be specified with full path."
486 else: # format == 'string':
489 print "The default value is: <code>%s</code>." % (
493 if (node.hasAttribute('depends')):
494 depends = node.getAttribute('depends')
496 print "%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
497 "This tag requires that the tag", depends.lower(), depends.upper())
501 def parseGroupsDoc(node):
502 name = node.getAttribute('name')
503 doc = node.getAttribute('docs')
504 print "\section config_%s %s" % (name.lower(), doc)
505 # Start of list has been moved to the first option for better
510 for n in node.childNodes:
511 if n.nodeType == Node.ELEMENT_NODE:
512 first = parseOptionDoc(n, first)
517 def parseGroupsList(node, commandsList):
519 for n in node.childNodes:
520 if n.nodeType == Node.ELEMENT_NODE:
521 type = n.getAttribute('type')
522 if type != 'obsolete':
523 commandsList = commandsList + (n.getAttribute('id'),)
529 for n in node.childNodes:
530 if n.nodeType == Node.TEXT_NODE:
531 doc += n.nodeValue.strip()
532 if n.nodeType == Node.CDATA_SECTION_NODE:
533 doc += n.nodeValue.rstrip("\r\n ").lstrip("\r\n")
538 def parseHeaderDoc(node):
540 for n in node.childNodes:
541 if n.nodeType == Node.ELEMENT_NODE:
542 if (n.nodeName == "docs"):
543 if (n.getAttribute('documentation') != "0"):
548 def parseFooterDoc(node):
550 for n in node.childNodes:
551 if n.nodeType == Node.ELEMENT_NODE:
552 if (n.nodeName == "docs"):
553 if (n.getAttribute('documentation') != "0"):
559 if len(sys.argv)<3 or (not sys.argv[1] in ['-doc','-cpp','-wiz']):
560 sys.exit('Usage: %s -doc|-cpp|-wiz config.xml' % sys.argv[0])
562 doc = xml.dom.minidom.parse(sys.argv[2])
563 except Exception as inst:
565 print >> sys.stderr, inst
568 elem = doc.documentElement
569 if (sys.argv[1] == "-doc"):
570 print "/* WARNING: This file is generated!"
571 print " * Do not edit this file, but edit config.xml instead and run"
572 print " * python configgen.py -doc config.xml to regenerate this file!"
575 for n in elem.childNodes:
576 if n.nodeType == Node.ELEMENT_NODE:
577 if (n.nodeName == "header"):
579 # generate list with all commands
581 for n in elem.childNodes:
582 if n.nodeType == Node.ELEMENT_NODE:
583 if (n.nodeName == "group"):
584 commandsList = parseGroupsList(n, commandsList)
586 for x in sorted(commandsList):
587 print "\\refitem cfg_%s %s" % (x.lower(), x)
588 print "\\endsecreflist"
589 # process groups and options
590 for n in elem.childNodes:
591 if n.nodeType == Node.ELEMENT_NODE:
592 if (n.nodeName == "group"):
595 for n in elem.childNodes:
596 if n.nodeType == Node.ELEMENT_NODE:
597 if (n.nodeName == "footer"):
599 elif (sys.argv[1] == "-cpp"):
600 print "/* WARNING: This file is generated!"
601 print " * Do not edit this file, but edit config.xml instead and run"
602 print " * python configgen.py -cpp config.xml to regenerate this file!"
605 print "#include \"configoptions.h\""
606 print "#include \"config.h\""
607 print "#include \"portable.h\""
608 print "#include \"settings.h\""
610 print "void addConfigOptions(Config *cfg)"
612 print " ConfigString *cs;"
613 print " ConfigEnum *ce;"
614 print " ConfigList *cl;"
615 print " ConfigInt *ci;"
616 print " ConfigBool *cb;"
619 for n in elem.childNodes:
620 if n.nodeType == Node.ELEMENT_NODE:
621 if (n.nodeName == "header"):
623 for n in elem.childNodes:
624 if n.nodeType == Node.ELEMENT_NODE:
625 if (n.nodeName == "group"):
628 elif (sys.argv[1] == "-wiz"):
629 print "/* WARNING: This file is generated!"
630 print " * Do not edit this file, but edit config.xml instead and run"
631 print " * python configgen.py -wiz config.xml to regenerate this file!"
633 print "#include \"configdoc.h\""
634 print "#include \"docintf.h\""
636 print "void addConfigDocs(DocIntf *doc)"
638 for n in elem.childNodes:
639 if n.nodeType == Node.ELEMENT_NODE:
640 if (n.nodeName == "header"):
642 for n in elem.childNodes:
643 if n.nodeType == Node.ELEMENT_NODE:
644 if (n.nodeName == "group"):
648 if __name__ == '__main__':