conf: Add support to generate include statements in .conf files

This commit is contained in:
Tobias Brunner
2017-07-27 13:19:38 +02:00
parent 67402ec77b
commit fb8c9b3d08
+33 -7
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Copyright (C) 2014-2015 Tobias Brunner # Copyright (C) 2014-2017 Tobias Brunner
# Hochschule fuer Technik Rapperswil # HSR Hochschule fuer Technik Rapperswil
# #
# This program is free software; you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by the
@@ -49,6 +49,12 @@ full.section.name {[#]}
If a # is added between the curly braces the section header will be commented 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. 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, Dots in section/option names may be escaped with a backslash. For instance,
with the following section description with the following section description
@@ -62,17 +68,18 @@ import sys
import re import re
from textwrap import TextWrapper from textwrap import TextWrapper
from optparse import OptionParser from optparse import OptionParser
from operator import attrgetter from functools import cmp_to_key
class ConfigOption: class ConfigOption:
"""Representing a configuration option or described section in strongswan.conf""" """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.path = path
self.name = path[-1] self.name = path[-1]
self.fullname = '.'.join(path) self.fullname = '.'.join(path)
self.default = default self.default = default
self.section = section self.section = section
self.commented = commented self.commented = commented
self.include = include
self.desc = [] self.desc = []
self.options = [] self.options = []
@@ -99,6 +106,13 @@ class ConfigOption:
self.commented = other.commented self.commented = other.commented
self.desc = other.desc 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: class Parser:
"""Parses one or more files of configuration options""" """Parses one or more files of configuration options"""
def __init__(self, sort = True): def __init__(self, sort = True):
@@ -135,6 +149,14 @@ class Parser:
self.__current = ConfigOption(path, section = True, self.__current = ConfigOption(path, section = True,
commented = m.group('comment')) commented = m.group('comment'))
return 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 # paragraph separator
m = re.match(r'^\s*$', line) m = re.match(r'^\s*$', line)
if m and self.__current: if m and self.__current:
@@ -248,7 +270,9 @@ class ConfFormatter:
"""Print a single option with description and default value""" """Print a single option with description and default value"""
comment = "# " if commented or opt.commented else "" comment = "# " if commented or opt.commented else ""
self.__print_description(opt, indent) 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)) print('{0}{1}{2} = {3}'.format(self.__indent * indent, comment, opt.name, opt.default))
else: else:
print('{0}{1}{2} ='.format(self.__indent * indent, comment, opt.name)) print('{0}{1}{2} ='.format(self.__indent * indent, comment, opt.name))
@@ -261,7 +285,7 @@ class ConfFormatter:
self.__print_description(section, indent) self.__print_description(section, indent)
print('{0}{1}{2} {{'.format(self.__indent * indent, comment, section.name)) print('{0}{1}{2} {{'.format(self.__indent * indent, comment, section.name))
print('') 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: if o.section:
self.__print_section(o, indent + 1, commented) self.__print_section(o, indent + 1, commented)
else: else:
@@ -273,7 +297,7 @@ class ConfFormatter:
"""Print a list of options""" """Print a list of options"""
if not options: if not options:
return return
for option in sorted(options, key=attrgetter('section')): for option in sorted(options, key=cmp_to_key(ConfigOption.cmp)):
if option.section: if option.section:
self.__print_section(option, 0, False) self.__print_section(option, 0, False)
else: else:
@@ -297,6 +321,8 @@ class ManFormatter:
"""Print a single option""" """Print a single option"""
if option.section and not len(option.desc): if option.section and not len(option.desc):
return return
if option.include:
return
if option.section: if option.section:
print('.TP\n.B {0}\n.br'.format(option.fullname)) print('.TP\n.B {0}\n.br'.format(option.fullname))
else: else: