configgen.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 # python script to generate configoptions.cpp and config.doc from config.xml
3 #
4 # Copyright (C) 1997-2015 by Dimitri van Heesch.
5 #
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.
11 #
12 # Documents produced by Doxygen are derivative works derived from the
13 # input used in their production; they are not affected by this license.
14 #
15 import xml.dom.minidom
16 import sys
17 import re
18 import textwrap
19 from xml.dom import minidom, Node
20 
21 def transformDocs(doc):
22  # join lines, unless it is an empty line
23  # remove doxygen layout constructs
24  doc = doc.strip()
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("\\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)
53  # \ref specials
54  # \ref <key> -> description
55  doc = re.sub('\\\\ref +doxygen_usage', '"Doxygen usage"', doc)
56  doc = re.sub('\\\\ref +extsearch', '"External Indexing and Searching"',
57  doc)
58  doc = re.sub('\\\\ref +external', '"Linking to external documentation"',
59  doc)
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")
80  #
81  doc = doc.splitlines()
82  split_doc = []
83  for line in doc:
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
87  docC = []
88  for line in split_doc:
89  if (line.strip() != "<br/>"):
90  docC.append(line.strip().replace('\\', '\\\\').
91  replace('"', '\\"').replace("<br>", ""))
92  return docC
93 
94 
95 def collectValues(node):
96  values = []
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')
104  if (desc != ""):
105  name += " " + desc
106  values.append(name)
107  return values
108 
109 
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))
116 
117 
118 def parseHeader(node,objName):
119  doc = ""
120  for n in node.childNodes:
121  if n.nodeType == Node.ELEMENT_NODE:
122  if (n.nodeName == "docs"):
123  if (n.getAttribute('doxyfile') != "0"):
124  doc += parseDocs(n)
125  docC = transformDocs(doc)
126  print(" %s->setHeader(" % (objName))
127  rng = len(docC)
128  for i in range(rng):
129  line = docC[i]
130  if i != rng - 1: # since we go from 0 to rng-1
131  print(" \"%s\\n\"" % (line))
132  else:
133  print(" \"%s\"" % (line))
134  print(" );")
135 
136 
137 def prepCDocs(node):
138  type = node.getAttribute('type')
139  format = node.getAttribute('format')
140  defval = node.getAttribute('defval')
141  adefval = node.getAttribute('altdefval')
142  doc = "";
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:
148  doc += parseDocs(n)
149  if (type == 'enum'):
150  values = collectValues(node)
151  doc += "<br/>Possible values are: "
152  rng = len(values)
153  for i in range(rng):
154  val = values[i]
155  if i == rng - 2:
156  doc += "%s and " % (val)
157  elif i == rng - 1:
158  doc += "%s." % (val)
159  else:
160  doc += "%s, " % (val)
161  if (defval != ""):
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")
172  else:
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)
177  rng = len(values)
178  for i in range(rng):
179  val = values[i]
180  if i == rng - 2:
181  doc += "%s and " % (val)
182  elif i == rng - 1:
183  doc += "%s." % (val)
184  else:
185  doc += "%s, " % (val)
186  elif (type == 'string'):
187  if format == 'dir':
188  if defval != '':
189  doc += "<br/>The default directory is: <code>%s</code>." % (
190  defval)
191  elif format == 'file':
192  abspath = node.getAttribute('abspath')
193  if defval != '':
194  if abspath != '1':
195  doc += "<br/>The default file is: <code>%s</code>." % (
196  defval)
197  else:
198  doc += "<br/>%s: %s%s%s." % (
199  "The default file (with absolute path) is",
200  "<code>",defval,"</code>")
201  else:
202  if abspath == '1':
203  doc += "<br/>The file has to be specified with full path."
204  elif format =='image':
205  abspath = node.getAttribute('abspath')
206  if defval != '':
207  if abspath != '1':
208  doc += "<br/>The default image is: <code>%s</code>." % (
209  defval)
210  else:
211  doc += "<br/>%s: %s%s%s." % (
212  "The default image (with absolute path) is",
213  "<code>",defval,"</code>")
214  else:
215  if abspath == '1':
216  doc += "<br/>The image has to be specified with full path."
217  else: # format == 'string':
218  if defval != '':
219  doc += "<br/>The default value is: <code>%s</code>." % (
220  defval)
221  # depends handling
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())
226 
227  docC = transformDocs(doc)
228  return docC;
229 
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);
240  if len(setting) > 0:
241  print("#if %s" % (setting))
242  print(" //----")
243  if type == 'bool':
244  if len(adefval) > 0:
245  enabled = adefval
246  elif defval == '1':
247  enabled = "TRUE"
248  else:
249  enabled = "FALSE"
250  print(" cb = cfg->addBool(")
251  print(" \"%s\"," % (name))
252  rng = len(docC)
253  for i in range(rng):
254  line = docC[i]
255  if i != rng - 1: # since we go from 0 to rng-1
256  print(" \"%s\\n\"" % (line))
257  else:
258  print(" \"%s\"," % (line))
259  print(" %s" % (enabled))
260  print(" );")
261  if depends != '':
262  print(" cb->addDependency(\"%s\");" % (depends))
263  elif type == 'string':
264  print(" cs = cfg->addString(")
265  print(" \"%s\"," % (name))
266  rng = len(docC)
267  for i in range(rng):
268  line = docC[i]
269  if i != rng - 1: # since we go from 0 to rng-1
270  print(" \"%s\\n\"" % (line))
271  else:
272  print(" \"%s\"" % (line))
273  print(" );")
274  if defval != '':
275  print(" cs->setDefaultValue(\"%s\");" % (defval))
276  if format == 'file':
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);")
282  if depends != '':
283  print(" cs->addDependency(\"%s\");" % (depends))
284  elif type == 'enum':
285  print(" ce = cfg->addEnum(")
286  print(" \"%s\"," % (name))
287  rng = len(docC)
288  for i in range(rng):
289  line = docC[i]
290  if i != rng - 1: # since we go from 0 to rng-1
291  print(" \"%s\\n\"" % (line))
292  else:
293  print(" \"%s\"," % (line))
294  print(" \"%s\"" % (defval))
295  print(" );")
296  addValues("ce", node)
297  if depends != '':
298  print(" ce->addDependency(\"%s\");" % (depends))
299  elif type == 'int':
300  minval = node.getAttribute('minval')
301  maxval = node.getAttribute('maxval')
302  print(" ci = cfg->addInt(")
303  print(" \"%s\"," % (name))
304  rng = len(docC)
305  for i in range(rng):
306  line = docC[i]
307  if i != rng - 1: # since we go from 0 to rng-1
308  print(" \"%s\\n\"" % (line))
309  else:
310  print(" \"%s\"," % (line))
311  print(" %s,%s,%s" % (minval, maxval, defval))
312  print(" );")
313  if depends != '':
314  print(" ci->addDependency(\"%s\");" % (depends))
315  elif type == 'list':
316  print(" cl = cfg->addList(")
317  print(" \"%s\"," % (name))
318  rng = len(docC)
319  for i in range(rng):
320  line = docC[i]
321  if i != rng - 1: # since we go from 0 to rng-1
322  print(" \"%s\\n\"" % (line))
323  else:
324  print(" \"%s\"" % (line))
325  print(" );")
326  addValues("cl", node)
327  if depends != '':
328  print(" cl->addDependency(\"%s\");" % (depends))
329  if format == 'file':
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))
337  if len(setting) > 0:
338  print("#else")
339  print(" cfg->addDisabled(\"%s\");" % (name))
340  print("#endif")
341 
342 
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  "----------------------------------"))
351  print("")
352  for n in node.childNodes:
353  if n.nodeType == Node.ELEMENT_NODE:
354  parseOption(n)
355 
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')
361  docC = prepCDocs(n);
362  if type != 'obsolete':
363  print(" doc->add(")
364  print(" \"%s\"," % (name))
365  rng = len(docC)
366  for i in range(rng):
367  line = docC[i]
368  if i != rng - 1: # since we go from 0 to rng-1
369  print(" \"%s\\n\"" % (line))
370  else:
371  print(" \"%s\"" % (line))
372  print(" );")
373 
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')
383  doc = ""
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:
389  doc += parseDocs(n)
390  if (first):
391  print(" \\anchor cfg_%s" % (name.lower()))
392  print("<dl>")
393  print("")
394  print("<dt>\\c %s <dd>" % (name))
395  else:
396  print(" \\anchor cfg_%s" % (name.lower()))
397  print("<dt>\\c %s <dd>" % (name))
398  print(" \\addindex %s" % (name))
399  print(doc)
400  if (type == 'enum'):
401  values = collectValues(node)
402  print("")
403  print("Possible values are: ")
404  rng = len(values)
405  for i in range(rng):
406  val = values[i]
407  if i == rng - 2:
408  print("%s and " % (val))
409  elif i == rng - 1:
410  print("%s." % (val))
411  else:
412  print("%s, " % (val))
413  if (defval != ""):
414  print("")
415  print("")
416  print("The default value is: <code>%s</code>." % (defval))
417  print("")
418  elif (type == 'int'):
419  minval = node.getAttribute('minval')
420  maxval = node.getAttribute('maxval')
421  print("")
422  print("")
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>"))
427  print("")
428  elif (type == 'bool'):
429  print("")
430  print("")
431  if (node.hasAttribute('altdefval')):
432  print("The default value is: system dependent.")
433  else:
434  print("The default value is: <code>%s</code>." % (
435  "YES" if (defval == "1") else "NO"))
436  print("")
437  elif (type == 'list'):
438  if format == 'string':
439  values = collectValues(node)
440  rng = len(values)
441  for i in range(rng):
442  val = values[i]
443  if i == rng - 2:
444  print("%s and " % (val))
445  elif i == rng - 1:
446  print("%s." % (val))
447  else:
448  print("%s, " % (val))
449  print("")
450  elif (type == 'string'):
451  if format == 'dir':
452  if defval != '':
453  print("")
454  print("The default directory is: <code>%s</code>." % (
455  defval))
456  elif format == 'file':
457  abspath = node.getAttribute('abspath')
458  if defval != '':
459  print("")
460  if abspath != '1':
461  print("The default file is: <code>%s</code>." % (
462  defval))
463  else:
464  print("%s: %s%s%s." % (
465  "The default file (with absolute path) is",
466  "<code>",defval,"</code>"))
467  else:
468  if abspath == '1':
469  print("")
470  print("The file has to be specified with full path.")
471  elif format =='image':
472  abspath = node.getAttribute('abspath')
473  if defval != '':
474  print("")
475  if abspath != '1':
476  print("The default image is: <code>%s</code>." % (
477  defval))
478  else:
479  print("%s: %s%s%s." % (
480  "The default image (with absolute path) is",
481  "<code>",defval,"</code>"))
482  else:
483  if abspath == '1':
484  print("")
485  print("The image has to be specified with full path.")
486  else: # format == 'string':
487  if defval != '':
488  print("")
489  print("The default value is: <code>%s</code>." % (
490  defval))
491  print("")
492  # depends handling
493  if (node.hasAttribute('depends')):
494  depends = node.getAttribute('depends')
495  print("")
496  print("%s \\ref cfg_%s \"%s\" is set to \\c YES." % (
497  "This tag requires that the tag", depends.lower(), depends.upper()))
498  return False
499 
500 
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
506  # anchor placement
507  # print "<dl>"
508  # print ""
509  first = True
510  for n in node.childNodes:
511  if n.nodeType == Node.ELEMENT_NODE:
512  first = parseOptionDoc(n, first)
513  if (not first):
514  print("</dl>")
515 
516 
517 def parseGroupsList(node, commandsList):
518  list = ()
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'),)
524  return commandsList
525 
526 
527 def parseDocs(node):
528  doc = ""
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")
534  #doc += "<br>"
535  return doc
536 
537 
538 def parseHeaderDoc(node):
539  doc = ""
540  for n in node.childNodes:
541  if n.nodeType == Node.ELEMENT_NODE:
542  if (n.nodeName == "docs"):
543  if (n.getAttribute('documentation') != "0"):
544  doc += parseDocs(n)
545  print(doc)
546 
547 
548 def parseFooterDoc(node):
549  doc = ""
550  for n in node.childNodes:
551  if n.nodeType == Node.ELEMENT_NODE:
552  if (n.nodeName == "docs"):
553  if (n.getAttribute('documentation') != "0"):
554  doc += parseDocs(n)
555  print(doc)
556 
557 
558 def main():
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])
561  try:
562  doc = xml.dom.minidom.parse(sys.argv[2])
563  except Exception as inst:
564  sys.stdout = sys.stderr
565  print("")
566  print(inst)
567  print("")
568  sys.exit(1)
569  elem = doc.documentElement
570  if (sys.argv[1] == "-doc"):
571  print("/* WARNING: This file is generated!")
572  print(" * Do not edit this file, but edit config.xml instead and run")
573  print(" * python configgen.py -doc config.xml to regenerate this file!")
574  print(" */")
575  # process header
576  for n in elem.childNodes:
577  if n.nodeType == Node.ELEMENT_NODE:
578  if (n.nodeName == "header"):
579  parseHeaderDoc(n)
580  # generate list with all commands
581  commandsList = ()
582  for n in elem.childNodes:
583  if n.nodeType == Node.ELEMENT_NODE:
584  if (n.nodeName == "group"):
585  commandsList = parseGroupsList(n, commandsList)
586  print("\\secreflist")
587  for x in sorted(commandsList):
588  print("\\refitem cfg_%s %s" % (x.lower(), x))
589  print("\\endsecreflist")
590  # process groups and options
591  for n in elem.childNodes:
592  if n.nodeType == Node.ELEMENT_NODE:
593  if (n.nodeName == "group"):
594  parseGroupsDoc(n)
595  # process footers
596  for n in elem.childNodes:
597  if n.nodeType == Node.ELEMENT_NODE:
598  if (n.nodeName == "footer"):
599  parseFooterDoc(n)
600  elif (sys.argv[1] == "-cpp"):
601  print("/* WARNING: This file is generated!")
602  print(" * Do not edit this file, but edit config.xml instead and run")
603  print(" * python configgen.py -cpp config.xml to regenerate this file!")
604  print(" */")
605  print("")
606  print("#include \"configoptions.h\"")
607  print("#include \"config.h\"")
608  print("#include \"portable.h\"")
609  print("#include \"settings.h\"")
610  print("")
611  print("void addConfigOptions(Config *cfg)")
612  print("{")
613  print(" ConfigString *cs;")
614  print(" ConfigEnum *ce;")
615  print(" ConfigList *cl;")
616  print(" ConfigInt *ci;")
617  print(" ConfigBool *cb;")
618  print("")
619  # process header
620  for n in elem.childNodes:
621  if n.nodeType == Node.ELEMENT_NODE:
622  if (n.nodeName == "header"):
623  parseHeader(n,'cfg')
624  for n in elem.childNodes:
625  if n.nodeType == Node.ELEMENT_NODE:
626  if (n.nodeName == "group"):
627  parseGroups(n)
628  print("}")
629  elif (sys.argv[1] == "-wiz"):
630  print("/* WARNING: This file is generated!")
631  print(" * Do not edit this file, but edit config.xml instead and run")
632  print(" * python configgen.py -wiz config.xml to regenerate this file!")
633  print(" */")
634  print("#include \"configdoc.h\"")
635  print("#include \"docintf.h\"")
636  print("")
637  print("void addConfigDocs(DocIntf *doc)")
638  print("{")
639  for n in elem.childNodes:
640  if n.nodeType == Node.ELEMENT_NODE:
641  if (n.nodeName == "header"):
642  parseHeader(n,'doc')
643  for n in elem.childNodes:
644  if n.nodeType == Node.ELEMENT_NODE:
645  if (n.nodeName == "group"):
646  parseGroupCDocs(n)
647  print("}")
648 
649 if __name__ == '__main__':
650  main()
def parseFooterDoc(node)
Definition: configgen.py:548
def parseGroups(node)
Definition: configgen.py:343
def addValues(var, node)
Definition: configgen.py:110
def parseOptionDoc(node, first)
Definition: configgen.py:374
def main()
Definition: configgen.py:558
def transformDocs(doc)
Definition: configgen.py:21
def collectValues(node)
Definition: configgen.py:95
def parseGroupsList(node, commandsList)
Definition: configgen.py:517
def prepCDocs(node)
Definition: configgen.py:137
def parseHeaderDoc(node)
Definition: configgen.py:538
def parseHeader(node, objName)
Definition: configgen.py:118
def parseGroupsDoc(node)
Definition: configgen.py:501
def parseOption(node)
Definition: configgen.py:230
def parseDocs(node)
Definition: configgen.py:527
def parseGroupCDocs(node)
Definition: configgen.py:356