def _parse_option_value(token, lineno): # Grabs the first token before the first non-quoted whitespace. match = re.search(r'[^=\s"\']+|"([^"]*)"|\'([^\']*)\'', token) if not match: raise ApacheConfigError( 'Syntax error in option-value pair %s on line ' '%d' % (token, lineno)) option = match.group(0) if len(token.strip()) == len(option): return token, None, None # If there's more, split it out into whitespace and value. _, middle, value = re.split(r'((?:\s|=|\\\s)+)', token[len(option):], maxsplit=1) if not option: raise ApacheConfigError( 'Syntax error in option-value pair %s on line ' '%d' % (token, lineno)) if value: stripped = value.strip() if stripped[0] == '"' and stripped[-1] == '"': value = DoubleQuotedString(stripped[1:-1]) if stripped[0] == "'" and stripped[-1] == "'": value = SingleQuotedString(stripped[1:-1]) return option, middle, value
def _parse_option_value(token): if not re.match(r'.*?[ \n\r\t=]+', token): raise ApacheConfigError('Syntax error in option-value pair %s' % token) option, value = re.split(r'[ \n\r\t=]+', token, maxsplit=1) if not option: raise ApacheConfigError('Syntax error in option-value pair %s' % token) if value: if value[0] == '"' and value[-1] == '"': value = DoubleQuotedString(value[1:-1]) if value[0] == "'" and value[-1] == "'": value = SingleQuotedString(value[1:-1]) if '#' in value: value = value.replace('\\#', '#') return option, value
def load(self, filepath, initialize=True): if initialize: self._stack = [] self._includes = set() try: pre_open = self._options['plug']['pre_open'] filename, basedir = os.path.basename(filepath), os.path.dirname( filepath) process, filename, basedir = pre_open(filename, basedir) filepath = os.path.join(basedir, filename) if not process: return {} except KeyError: pass if filepath in self._includes and not self._options.get( 'includeagain'): return {} self._includes.add(filepath) try: with open(filepath) as f: return self.loads(f.read(), source=filepath) except IOError as ex: raise ApacheConfigError('File %s can\'t be open: %s' % (filepath, ex))
def g_include(self, ast): filepath = ast[0] options = self._options if os.path.isabs(filepath): configpath = [os.path.dirname(filepath)] filename = os.path.basename(filepath) else: configpath = options.get('configpath', []) if 'configroot' in options and options.get('includerelative'): configpath.insert(0, options['configroot']) if 'programpath' in options: configpath.append(options['programpath']) else: configpath.append('.') if os.path.isdir(filepath): configpath.insert(0, filepath) filename = '.' else: filename = filepath for configdir in configpath: filepath = os.path.join(configdir, filename) if os.path.isdir(filepath): if options.get('includedirectories'): contents = {} for include_file in sorted(os.listdir(filepath)): items = self.load(os.path.join(filepath, include_file), initialize=False) self._merge_contents(contents, items) return contents elif options.get('includeglob'): contents = {} for include_file in sorted(glob.glob(filepath)): items = self.load(include_file, initialize=False) self._merge_contents(contents, items) return contents elif os.path.exists(filepath): return self.load(filepath, initialize=False) else: raise ApacheConfigError( 'Config file "%s" not found in search path %s' % (filename, ':'.join(configpath)))
def _walkast(self, ast): if not ast: return node_type = ast[0] try: handler = getattr(self, 'g_' + node_type) except AttributeError: raise ApacheConfigError('Unsupported AST node type %s' % node_type) return handler(ast[1:])
def lookup(match): option = match.groups()[0] if option in statements: return interpolate(statements[option]) for frame in self._stack: if option in frame: return interpolate(frame[option]) if self._options.get('interpolateenv', False): if option in os.environ: return interpolate(os.environ[option]) if self._options.get('strictvars', True): raise ApacheConfigError( 'Undefined variable "${%s}" referenced' % option) return interpolate(match.string)
def t_ccomment_error(self, t): raise ApacheConfigError("Illegal character '%s' in C-style comment" % t.value[0])
def t_error(self, t): raise ApacheConfigError("Illegal character '%s'" % t.value[0])
def t_heredoc_error(self, t): raise ApacheConfigError( "Illegal character '%s' in here-document text" % t.value[0])
def t_multiline_error(self, t): raise ApacheConfigError("Illegal character '%s' in multiline text" % t.value[0])
def p_error(self, p): raise ApacheConfigError("Parser error at '%s'" % p.value if p else 'Unexpected EOF')
def p_error(self, p): raise ApacheConfigError("Parser error at '%s'" % p.value if p else '?')
def g_statements(self, ast): statements = {} for subtree in ast: items = self._walkast(subtree) for item in items: if item in statements: if (self._options.get('allowmultioptions', True) and not self._options.get('mergeduplicateoptions', False)): if not isinstance(statements[item], list): statements[item] = [statements[item]] statements[item].append(items[item]) elif self._options.get('mergeduplicateoptions', False): statements[item] = items[item] else: raise ApacheConfigError( 'Duplicate option "%s" prohibited' % item) else: statements[item] = items[item] if (self._options.get('interpolateenv', False) or self._options.get('allowsinglequoteinterpolation', False)): self._options['interpolatevars'] = True if self._options.get('interpolatevars', False): def lookup(match): option = match.groups()[0] if option in statements: return interpolate(statements[option]) for frame in self._stack: if option in frame: return interpolate(frame[option]) if self._options.get('interpolateenv', False): if option in os.environ: return interpolate(os.environ[option]) if self._options.get('strictvars', True): raise ApacheConfigError( 'Undefined variable "${%s}" referenced' % option) return interpolate(match.string) def interpolate(value): expanded = re.sub(r'(?<!\\)\${([^\n\r]+?)}', lookup, value) if expanded != value: return expanded return re.sub(r'(?<!\\)\$([^\n\r $]+?)', lookup, value) for option, value in tuple(statements.items()): if (not getattr(value, 'is_single_quoted', False) or self._options.get('allowsinglequoteinterpolation', False)): if isinstance(value, list): statements[option] = [interpolate(x) for x in value] else: statements[option] = interpolate(value) self._stack.insert(0, statements) return statements
def t_error(self, t): raise ApacheConfigError("Illegal character '%s' on line %d" % (t.value[0], t.lineno))
def t_heredoc_error(self, t): raise ApacheConfigError( "Illegal character '%s' in here-document text on line " "%d" % (t.value[0], t.lineno))
def t_multiline_error(self, t): raise ApacheConfigError( "Illegal character '%s' in multi-line text on line " "%d" % (t.value[0], t.lineno))