]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - conf/format-options.py
x509: Encode challenge passwords as PrintableString if possible
[thirdparty/strongswan.git] / conf / format-options.py
index 3073943997f58603bc4d21027f03a0d5e13bef3f..b36e28671cb178f0d272c3420be9bd527a0b34f2 100755 (executable)
@@ -1,7 +1,8 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
-# Copyright (C) 2014-2015 Tobias Brunner
-# Hochschule fuer Technik Rapperswil
+# Copyright (C) 2014-2019 Tobias Brunner
+#
+# Copyright (C) secunet Security Networks AG
 #
 # This program is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by the
@@ -49,6 +50,12 @@ full.section.name {[#]}
 If a # is added between the curly braces the section header will be commented
 out in the configuration file snippet, which is useful for example sections.
 
+To add include statements to generated config files (ignored when generating
+man pages) the following format can be used:
+
+full.section.name.include files/to/include
+       Description of this include statement
+
 Dots in section/option names may be escaped with a backslash.  For instance,
 with the following section description
 
@@ -61,21 +68,26 @@ charon.filelog./var/log/daemon\.log {}
 import sys
 import re
 from textwrap import TextWrapper
-from optparse import OptionParser
-from operator import attrgetter
+from argparse import ArgumentParser
+from functools import cmp_to_key, total_ordering
 
+@total_ordering
 class ConfigOption:
        """Representing a configuration option or described section in strongswan.conf"""
-       def __init__(self, path, default = None, section = False, commented = False):
+       def __init__(self, path, default = None, section = False, commented = False, include = False):
                self.path = path
                self.name = path[-1]
                self.fullname = '.'.join(path)
                self.default = default
                self.section = section
                self.commented = commented
+               self.include = include
                self.desc = []
                self.options = []
 
+       def __eq__(self, other):
+               return self.name == other.name
+
        def __lt__(self, other):
                return self.name < other.name
 
@@ -99,6 +111,13 @@ class ConfigOption:
                self.commented = other.commented
                self.desc = other.desc
 
+       @staticmethod
+       def cmp(a, b):
+               # order options before sections and includes last
+               if a.include or b.include:
+                       return a.include - b.include
+               return a.section - b.section
+
 class Parser:
        """Parses one or more files of configuration options"""
        def __init__(self, sort = True):
@@ -135,6 +154,14 @@ class Parser:
                        self.__current = ConfigOption(path, section = True,
                                                                                  commented = m.group('comment'))
                        return
+               # include definition
+               m = re.match(r'^(?P<name>\S+\.include|include)\s+(?P<pattern>\S+)\s*$', line)
+               if m:
+                       if self.__current:
+                               self.__add_option(self.__current)
+                       path = self.__split_name(m.group('name'))
+                       self.__current = ConfigOption(path, m.group('pattern'), include = True)
+                       return
                # paragraph separator
                m = re.match(r'^\s*$', line)
                if m and self.__current:
@@ -195,7 +222,7 @@ class TagReplacer:
                return re.compile(r'''
                        (^|\s|(?P<brack>[(\[])) # prefix with optional opening bracket
                        (?P<tag>''' + tag + r''') # start tag
-                       (?P<text>\w|\S.*?\S) # text
+                       (?P<text>\S|\S.*?\S) # text
                        ''' + tag + r''' # end tag
                        (?P<punct>([.,!:)\]]|\(\d+\))*) # punctuation
                        (?=$|\s) # suffix (don't consume it so that subsequent tags can match)
@@ -248,7 +275,9 @@ class ConfFormatter:
                """Print a single option with description and default value"""
                comment = "# " if commented or opt.commented else ""
                self.__print_description(opt, indent)
-               if opt.default:
+               if opt.include:
+                       print('{0}{1} {2}'.format(self.__indent * indent, opt.name, opt.default))
+               elif opt.default:
                        print('{0}{1}{2} = {3}'.format(self.__indent * indent, comment, opt.name, opt.default))
                else:
                        print('{0}{1}{2} ='.format(self.__indent * indent, comment, opt.name))
@@ -261,7 +290,7 @@ class ConfFormatter:
                self.__print_description(section, indent)
                print('{0}{1}{2} {{'.format(self.__indent * indent, comment, section.name))
                print('')
-               for o in sorted(section.options, key=attrgetter('section')):
+               for o in sorted(section.options, key=cmp_to_key(ConfigOption.cmp)):
                        if o.section:
                                self.__print_section(o, indent + 1, commented)
                        else:
@@ -273,7 +302,7 @@ class ConfFormatter:
                """Print a list of options"""
                if not options:
                        return
-               for option in sorted(options, key=attrgetter('section')):
+               for option in sorted(options, key=cmp_to_key(ConfigOption.cmp)):
                        if option.section:
                                self.__print_section(option, 0, False)
                        else:
@@ -297,6 +326,8 @@ class ManFormatter:
                """Print a single option"""
                if option.section and not len(option.desc):
                        return
+               if option.include:
+                       return
                if option.section:
                        print('.TP\n.B {0}\n.br'.format(option.fullname))
                else:
@@ -318,21 +349,22 @@ class ManFormatter:
                        else:
                                self.__format_option(option)
 
-options = OptionParser(usage = "Usage: %prog [options] file1 file2\n\n"
-                                          "If no filenames are provided the input is read from stdin.")
-options.add_option("-f", "--format", dest="format", type="choice", choices=["conf", "man"],
-                                  help="output format: conf, man [default: %default]", default="conf")
-options.add_option("-r", "--root", dest="root", metavar="NAME",
-                                  help="root section of which options are printed, "
-                                  "if not found everything is printed")
-options.add_option("-n", "--nosort", action="store_false", dest="sort",
-                                  default=True, help="do not sort sections alphabetically")
+args = ArgumentParser()
+args.add_argument('file', nargs='*',
+                                 help="files to process, omit to read input from stdin")
+args.add_argument("-f", "--format", dest="format", choices=["conf", "man"],
+                                 help="output format (default: %(default)s)", default="conf")
+args.add_argument("-r", "--root", dest="root", metavar="NAME",
+                                 help="root section of which options are printed; everything"
+                                 "is printed if not found")
+args.add_argument("-n", "--nosort", action="store_false", dest="sort",
+                                 default=True, help="do not sort sections alphabetically")
 
-(opts, args) = options.parse_args()
+opts = args.parse_args()
 
 parser = Parser(opts.sort)
-if len(args):
-       for filename in args:
+if len(opts.file):
+       for filename in opts.file:
                try:
                        with open(filename, 'r') as file:
                                parser.parse(file)