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
21 def transformDocs(doc):
22 # join lines, unless it is an empty line
23 # remove doxygen layout constructs
25 doc = doc.replace("\n", " ")
26 doc = doc.replace("\r", " ")
27 doc = doc.replace("\t", " ")
28 doc = doc.replace("\\&", "&")
29 doc = doc.replace("\\c ", " ")
30 doc = doc.replace("\\b ", " ")
31 doc = doc.replace("\\e ", " ")
32 doc = doc.replace("\\$", "$")
33 doc = doc.replace("\\#include ", "#include ")
34 doc = doc.replace("\\#undef ", "#undef ")
35 doc = doc.replace("-# ", "\n - ")
36 doc = doc.replace(" - ", "\n - ")
37 doc = doc.replace("\\sa", "\nSee also: ")
38 doc = doc.replace("\\par", "\n")
39 doc = doc.replace("@note", "\nNote:")
40 doc = doc.replace("\\note", "\nNote:")
41 doc = doc.replace("\\verbatim", "\n")
42 doc = doc.replace("\\endverbatim", "\n")
43 doc = doc.replace("<code>", "")
44 doc = doc.replace("</code>", "")
45 doc = doc.replace("`", "")
46 doc = doc.replace("\\<", "<")
47 doc = doc.replace("\\>", ">")
48 doc = doc.replace("\\@", "@")
49 doc = doc.replace("\\\\", "\\")
50 # \ref name "description" -> description
51 doc = re.sub('\\\\ref +[^ ]* +"([^"]*)"', '\\1', doc)
53 # \ref <key> -> description
54 doc = re.sub('\\\\ref +doxygen_usage', '"Doxygen usage"', doc)
55 doc = re.sub('\\\\ref +extsearch', '"External Indexing and Searching"',
57 doc = re.sub('\\\\ref +external', '"Linking to external documentation"',
59 # fallback for not handled
60 doc = re.sub('\\\\ref', '', doc)
61 #<a href="address">description</a> -> description (see: address)
62 doc = re.sub('<a +href="([^"]*)" *>([^<]*)</a>', '\\2 (see: \\1)', doc)
63 # LaTeX name as formula -> LaTeX
64 doc = doc.replace("\\f$\\mbox{\\LaTeX}\\f$", "LaTeX")
65 # Other forula's (now just 2) so explicitely mentioned.
66 doc = doc.replace("\\f$2^{(16+\\mbox{LOOKUP\\_CACHE\\_SIZE})}\\f$",
67 "2^(16+LOOKUP_CACHE_SIZE)")
68 doc = doc.replace("\\f$2^{16} = 65536\\f$", "2^16=65536")
69 # remove consecutive spaces
70 doc = re.sub(" +", " ", doc)
71 # a dirty trick to get an extra empty line in Doxyfile documentation.
72 # <br> will be removed later on again, we need it here otherwise splitlines
73 # will filter the extra line.
74 doc = doc.replace("<br>", "\n<br>\n")
75 # a dirty trick to go to the next line in Doxyfile documentation.
76 # <br/> will be removed later on again, we need it here otherwise splitlines
77 # will filter the line break.
78 doc = doc.replace("<br/>", "\n<br/>\n")
80 doc = doc.splitlines()
83 split_doc += textwrap.wrap(line, 78)
84 # replace \ by \\, replace " by \", and ' ' by a newline with end string
85 # and start string at next line
87 for line in split_doc:
88 if (line.strip() != "<br/>"):
89 docC.append(line.strip().replace('\\', '\\\\').
90 replace('"', '\\"').replace("<br>", ""))
94 def collectValues(node):
96 for n in node.childNodes:
97 if (n.nodeName == "value"):
98 if n.nodeType == Node.ELEMENT_NODE:
99 if n.getAttribute('name') != "":
100 if n.getAttribute('show_docu') != "NO":
101 name = "<code>" + n.getAttribute('name') + "</code>"
102 desc = n.getAttribute('desc')
109 def addValues(var, node):
110 for n in node.childNodes:
111 if (n.nodeName == "value"):
112 if n.nodeType == Node.ELEMENT_NODE:
113 name = n.getAttribute('name')
114 print(" %s->addValue(\"%s\");" % (var, name))
117 def parseHeader(node,objName):
119 for n in node.childNodes:
120 if n.nodeType == Node.ELEMENT_NODE:
121 if (n.nodeName == "docs"):
122 if (n.getAttribute('doxyfile') != "0"):
124 docC = transformDocs(doc)
125 print(" %s->setHeader(" % (objName))
129 if i != rng - 1: # since we go from 0 to rng-1
130 print(" \"%s\\n\"" % (line))
132 print(" \"%s\"" % (line))
137 type = node.getAttribute('type')
138 format = node.getAttribute('format')
139 defval = node.getAttribute('defval')
140 adefval = node.getAttribute('altdefval')
142 if (type != 'obsolete'):
143 for n in node.childNodes:
144 if (n.nodeName == "docs"):
145 if (n.getAttribute('doxyfile') != "0"):
146 if n.nodeType == Node.ELEMENT_NODE:
149 values = collectValues(node)
150 doc += "<br/>Possible values are: "
155 doc += "%s and " % (val)
159 doc += "%s, " % (val)
161 doc += "<br/>The default value is: <code>%s</code>." % (defval)
162 elif (type == 'int'):
163 minval = node.getAttribute('minval')
164 maxval = node.getAttribute('maxval')
165 doc += "<br/>%s: %s, %s: %s, %s: %s." % (" Minimum value", minval,
166 "maximum value", maxval,
167 "default value", defval)
168 elif (type == 'bool'):
169 if (node.hasAttribute('altdefval')):
170 doc += "<br/>%s: %s." % ("The default value is", "system dependent")
172 doc += "<br/>%s: %s." % ("The default value is", "YES" if (defval == "1") else "NO")
173 elif (type == 'list'):
174 if format == 'string':
175 values = collectValues(node)
180 doc += "%s and " % (val)
184 doc += "%s, " % (val)
185 elif (type == 'string'):
188 doc += "<br/>The default directory is: <code>%s</code>." % (
190 elif format == 'file':
191 abspath = node.getAttribute('abspath')
194 doc += "<br/>The default file is: <code>%s</code>." % (
197 doc += "<br/>%s: %s%s%s." % (
198 "The default file (with absolute path) is",
199 "<code>",defval,"</code>")
202 doc += "<br/>The file has to be specified with full path."
203 elif format =='image':
204 abspath = node.getAttribute('abspath')
207 doc += "<br/>The default image is: <code>%s</code>." % (
210 doc += "<br/>%s: %s%s%s." % (
211 "The default image (with absolute path) is",
212 "<code>",defval,"</code>")
215 doc += "<br/>The image has to be specified with full path."
216 else: # format == 'string':
218 doc += "<br/>The default value is: <code>%s</code>." % (
221 if (node.hasAttribute('depends')):
222 depends = node.getAttribute('depends')
223 doc += "<br/>%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
224 "This tag requires that the tag", depends.lower(), depends.upper())
226 docC = transformDocs(doc)
229 def parseOption(node):
230 # Handling part for Doxyfile
231 name = node.getAttribute('id')
232 type = node.getAttribute('type')
233 format = node.getAttribute('format')
234 defval = node.getAttribute('defval')
235 adefval = node.getAttribute('altdefval')
236 depends = node.getAttribute('depends')
237 setting = node.getAttribute('setting')
238 docC = prepCDocs(node);
240 print("#if %s" % (setting))
249 print(" cb = cfg->addBool(")
250 print(" \"%s\"," % (name))
254 if i != rng - 1: # since we go from 0 to rng-1
255 print(" \"%s\\n\"" % (line))
257 print(" \"%s\"," % (line))
258 print(" %s" % (enabled))
261 print(" cb->addDependency(\"%s\");" % (depends))
262 elif type == 'string':
263 print(" cs = cfg->addString(")
264 print(" \"%s\"," % (name))
268 if i != rng - 1: # since we go from 0 to rng-1
269 print(" \"%s\\n\"" % (line))
271 print(" \"%s\"" % (line))
274 print(" cs->setDefaultValue(\"%s\");" % (defval))
276 print(" cs->setWidgetType(ConfigString::File);")
277 elif format == 'image':
278 print(" cs->setWidgetType(ConfigString::Image);")
279 elif format == 'dir':
280 print(" cs->setWidgetType(ConfigString::Dir);")
282 print(" cs->addDependency(\"%s\");" % (depends))
284 print(" ce = cfg->addEnum(")
285 print(" \"%s\"," % (name))
289 if i != rng - 1: # since we go from 0 to rng-1
290 print(" \"%s\\n\"" % (line))
292 print(" \"%s\"," % (line))
293 print(" \"%s\"" % (defval))
295 addValues("ce", node)
297 print(" ce->addDependency(\"%s\");" % (depends))
299 minval = node.getAttribute('minval')
300 maxval = node.getAttribute('maxval')
301 print(" ci = cfg->addInt(")
302 print(" \"%s\"," % (name))
306 if i != rng - 1: # since we go from 0 to rng-1
307 print(" \"%s\\n\"" % (line))
309 print(" \"%s\"," % (line))
310 print(" %s,%s,%s" % (minval, maxval, defval))
313 print(" ci->addDependency(\"%s\");" % (depends))
315 print(" cl = cfg->addList(")
316 print(" \"%s\"," % (name))
320 if i != rng - 1: # since we go from 0 to rng-1
321 print(" \"%s\\n\"" % (line))
323 print(" \"%s\"" % (line))
325 addValues("cl", node)
327 print(" cl->addDependency(\"%s\");" % (depends))
329 print(" cl->setWidgetType(ConfigList::File);")
330 elif format == 'dir':
331 print(" cl->setWidgetType(ConfigList::Dir);")
332 elif format == 'filedir':
333 print(" cl->setWidgetType(ConfigList::FileAndDir);")
334 elif type == 'obsolete':
335 print(" cfg->addObsolete(\"%s\");" % (name))
338 print(" cfg->addDisabled(\"%s\");" % (name))
342 def parseGroups(node):
343 name = node.getAttribute('name')
344 doc = node.getAttribute('docs')
345 print("%s%s" % (" //-----------------------------------------",
346 "----------------------------------"))
347 print(" cfg->addInfo(\"%s\",\"%s\");" % (name, doc))
348 print("%s%s" % (" //-----------------------------------------",
349 "----------------------------------"))
351 for n in node.childNodes:
352 if n.nodeType == Node.ELEMENT_NODE:
355 def parseGroupCDocs(node):
356 for n in node.childNodes:
357 if n.nodeType == Node.ELEMENT_NODE:
358 type = n.getAttribute('type')
359 name = n.getAttribute('id')
361 if type != 'obsolete':
363 print(" \"%s\"," % (name))
367 if i != rng - 1: # since we go from 0 to rng-1
368 print(" \"%s\\n\"" % (line))
370 print(" \"%s\"" % (line))
373 def parseOptionDoc(node, first):
374 # Handling part for documentation
375 name = node.getAttribute('id')
376 type = node.getAttribute('type')
377 format = node.getAttribute('format')
378 defval = node.getAttribute('defval')
379 adefval = node.getAttribute('altdefval')
380 depends = node.getAttribute('depends')
381 setting = node.getAttribute('setting')
383 if (type != 'obsolete'):
384 for n in node.childNodes:
385 if (n.nodeName == "docs"):
386 if (n.getAttribute('documentation') != "0"):
387 if n.nodeType == Node.ELEMENT_NODE:
390 print(" \\anchor cfg_%s" % (name.lower()))
393 print("<dt>\\c %s <dd>" % (name))
395 print(" \\anchor cfg_%s" % (name.lower()))
396 print("<dt>\\c %s <dd>" % (name))
397 print(" \\addindex %s" % (name))
400 values = collectValues(node)
402 print("Possible values are: ")
407 print("%s and " % (val))
411 print("%s, " % (val))
415 print("The default value is: <code>%s</code>." % (defval))
417 elif (type == 'int'):
418 minval = node.getAttribute('minval')
419 maxval = node.getAttribute('maxval')
422 print("%s: %s%s%s, %s: %s%s%s, %s: %s%s%s." % (
423 " Minimum value", "<code>", minval, "</code>",
424 "maximum value", "<code>", maxval, "</code>",
425 "default value", "<code>", defval, "</code>"))
427 elif (type == 'bool'):
430 if (node.hasAttribute('altdefval')):
431 print("The default value is: system dependent.")
433 print("The default value is: <code>%s</code>." % (
434 "YES" if (defval == "1") else "NO"))
436 elif (type == 'list'):
437 if format == 'string':
438 values = collectValues(node)
443 print("%s and " % (val))
447 print("%s, " % (val))
449 elif (type == 'string'):
453 print("The default directory is: <code>%s</code>." % (
455 elif format == 'file':
456 abspath = node.getAttribute('abspath')
460 print("The default file is: <code>%s</code>." % (
463 print("%s: %s%s%s." % (
464 "The default file (with absolute path) is",
465 "<code>",defval,"</code>"))
469 print("The file has to be specified with full path.")
470 elif format =='image':
471 abspath = node.getAttribute('abspath')
475 print("The default image is: <code>%s</code>." % (
478 print("%s: %s%s%s." % (
479 "The default image (with absolute path) is",
480 "<code>",defval,"</code>"))
484 print("The image has to be specified with full path.")
485 else: # format == 'string':
488 print("The default value is: <code>%s</code>." % (
492 if (node.hasAttribute('depends')):
493 depends = node.getAttribute('depends')
495 print("%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
496 "This tag requires that the tag", depends.lower(), depends.upper()))
500 def parseGroupsDoc(node):
501 name = node.getAttribute('name')
502 doc = node.getAttribute('docs')
503 print("\section config_%s %s" % (name.lower(), doc))
504 # Start of list has been moved to the first option for better
509 for n in node.childNodes:
510 if n.nodeType == Node.ELEMENT_NODE:
511 first = parseOptionDoc(n, first)
516 def parseGroupsList(node, commandsList):
518 for n in node.childNodes:
519 if n.nodeType == Node.ELEMENT_NODE:
520 type = n.getAttribute('type')
521 if type != 'obsolete':
522 commandsList = commandsList + (n.getAttribute('id'),)
528 for n in node.childNodes:
529 if n.nodeType == Node.TEXT_NODE:
530 doc += n.nodeValue.strip()
531 if n.nodeType == Node.CDATA_SECTION_NODE:
532 doc += n.nodeValue.rstrip("\r\n ").lstrip("\r\n")
537 def parseHeaderDoc(node):
539 for n in node.childNodes:
540 if n.nodeType == Node.ELEMENT_NODE:
541 if (n.nodeName == "docs"):
542 if (n.getAttribute('documentation') != "0"):
547 def parseFooterDoc(node):
549 for n in node.childNodes:
550 if n.nodeType == Node.ELEMENT_NODE:
551 if (n.nodeName == "docs"):
552 if (n.getAttribute('documentation') != "0"):
558 if len(sys.argv)<3 or (not sys.argv[1] in ['-doc','-cpp','-wiz']):
559 sys.exit('Usage: %s -doc|-cpp|-wiz config.xml' % sys.argv[0])
561 doc = xml.dom.minidom.parse(sys.argv[2])
562 except Exception as inst:
563 sys.stdout = sys.stderr
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)
585 print("\\secreflist")
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__':