2015-04-09 20:01:23 +00:00
|
|
|
# encoding: utf-8
|
|
|
|
# copyright: 2015, Dominik Richter
|
2015-10-06 16:55:44 +00:00
|
|
|
# author: Dominik Richter
|
|
|
|
# author: Christoph Hartmann
|
2015-04-09 20:01:23 +00:00
|
|
|
|
2015-10-06 11:50:25 +00:00
|
|
|
require 'utils/parser'
|
|
|
|
|
2015-04-09 20:01:23 +00:00
|
|
|
class SimpleConfig
|
2015-12-31 00:01:11 +00:00
|
|
|
include CommentParser
|
2015-10-06 11:50:25 +00:00
|
|
|
|
2015-09-30 10:18:09 +00:00
|
|
|
attr_reader :params, :groups
|
2015-09-03 18:43:58 +00:00
|
|
|
def initialize(raw_data, opts = {})
|
2015-04-09 20:01:23 +00:00
|
|
|
parse(raw_data, opts)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Parse some data
|
|
|
|
# quotes: quoting characters, which are parsed, so everything inside
|
|
|
|
# it will be part of a string
|
|
|
|
# multiline: allow quoted text to span multiple lines
|
|
|
|
# comment_char: char which identifies comments
|
|
|
|
# standalone_comments: comments must appear alone in a line; if set to true,
|
|
|
|
# no comments can be added to the end of an assignment/statement line
|
2015-10-26 15:16:42 +00:00
|
|
|
def parse(raw_data, opts = nil)
|
2015-04-09 20:01:23 +00:00
|
|
|
@params = {}
|
2015-09-30 10:18:09 +00:00
|
|
|
@groups = []
|
2015-09-30 09:32:24 +00:00
|
|
|
@vals = @params
|
|
|
|
options = default_options.merge(opts || {})
|
2015-10-26 15:16:42 +00:00
|
|
|
return if raw_data.nil?
|
2015-10-03 12:24:13 +00:00
|
|
|
|
|
|
|
# prepare raw data if required
|
|
|
|
if !options[:line_separator].nil?
|
|
|
|
raw_data = raw_data.tr(options[:line_separator], "\n")
|
|
|
|
end
|
2015-04-09 20:01:23 +00:00
|
|
|
rest = raw_data
|
2017-02-08 22:49:16 +00:00
|
|
|
rest = parse_rest(rest, options) until rest.empty?
|
2015-04-09 20:01:23 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2015-09-03 18:43:58 +00:00
|
|
|
def parse_values(match, values)
|
2015-07-26 10:30:46 +00:00
|
|
|
start_idx = 2
|
|
|
|
i = 0
|
|
|
|
count = values - 1
|
2015-11-13 00:03:15 +00:00
|
|
|
return match[start_idx] if values == 1
|
2015-09-10 01:39:43 +00:00
|
|
|
|
|
|
|
# iterate over expected parameters
|
|
|
|
values = []
|
|
|
|
loop do
|
|
|
|
values.push(match[start_idx + i])
|
|
|
|
i += 1
|
|
|
|
break if i > count
|
2015-07-26 10:30:46 +00:00
|
|
|
end
|
2015-09-10 01:39:43 +00:00
|
|
|
values
|
2015-07-26 10:30:46 +00:00
|
|
|
end
|
|
|
|
|
2015-09-30 09:32:24 +00:00
|
|
|
def parse_params_line(line, opts)
|
2017-04-26 21:18:14 +00:00
|
|
|
# Deprecation handling
|
|
|
|
if opts.key?(:assignment_re)
|
|
|
|
warn '[DEPRECATION] `:assignment_re` is deprecated in favor of `:assignment_regex` '\
|
|
|
|
'and will be removed in the next major version. See: https://github.com/chef/inspec/issues/1709'
|
|
|
|
opts[:assignment_regex] = opts[:assignment_re]
|
|
|
|
end
|
|
|
|
if opts.key?(:key_vals)
|
|
|
|
warn '[DEPRECATION] `:key_vals` is deprecated in favor of `:key_values` '\
|
|
|
|
'and will be removed in the next major version. See: https://github.com/chef/inspec/issues/1709'
|
|
|
|
opts[:key_values] = opts[:key_vals]
|
|
|
|
end
|
|
|
|
|
2015-04-09 20:01:23 +00:00
|
|
|
# now line contains what we are interested in parsing
|
|
|
|
# check if it is an assignment
|
2017-04-26 21:18:14 +00:00
|
|
|
m = opts[:assignment_regex].match(line)
|
2015-09-30 09:32:24 +00:00
|
|
|
return nil if m.nil?
|
|
|
|
|
|
|
|
if opts[:multiple_values]
|
|
|
|
@vals[m[1]] ||= []
|
2017-04-26 21:18:14 +00:00
|
|
|
@vals[m[1]].push(parse_values(m, opts[:key_values]))
|
2015-09-30 09:32:24 +00:00
|
|
|
else
|
2017-04-26 21:18:14 +00:00
|
|
|
@vals[m[1]] = parse_values(m, opts[:key_values])
|
2015-09-30 09:32:24 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_group_line(line, opts)
|
|
|
|
return nil if opts[:group_re].nil?
|
|
|
|
m = opts[:group_re].match(line)
|
|
|
|
return nil if m.nil?
|
2015-09-30 10:18:09 +00:00
|
|
|
@groups.push(m[1])
|
2017-03-06 22:46:35 +00:00
|
|
|
|
|
|
|
# We use a Hashie::Mash to provide method syntax for retrieving
|
|
|
|
# values. For configs that have keys whose values may be hashes
|
|
|
|
# (such as a mysql config that has [sections]), providing
|
|
|
|
# a hash whose values are accessible via methods provide
|
|
|
|
# a better user experience with `its` blocks.
|
|
|
|
@vals = @params[m[1]] = Hashie::Mash.new
|
2015-09-30 09:32:24 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def parse_implicit_assignment_line(line, opts)
|
|
|
|
return nil if is_empty_line(line)
|
|
|
|
if opts[:multiple_values]
|
|
|
|
@vals[line.strip] ||= []
|
|
|
|
else
|
|
|
|
@vals[line.strip] = ''
|
2015-04-09 20:01:23 +00:00
|
|
|
end
|
2015-09-10 01:39:43 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def parse_rest(rest, opts)
|
|
|
|
line, idx_nl = parse_comment_line(rest, opts)
|
2015-09-30 09:32:24 +00:00
|
|
|
parse_params_line(line, opts) or
|
|
|
|
parse_group_line(line, opts) or
|
|
|
|
parse_implicit_assignment_line(line, opts)
|
2015-04-09 20:01:23 +00:00
|
|
|
|
|
|
|
# return whatever is left
|
2015-09-05 14:07:54 +00:00
|
|
|
rest[(idx_nl + 1)..-1] || ''
|
2015-04-09 20:01:23 +00:00
|
|
|
end
|
|
|
|
|
2015-09-05 14:07:54 +00:00
|
|
|
def is_empty_line(l)
|
2015-04-09 20:01:23 +00:00
|
|
|
l =~ /^\s*$/
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_options
|
|
|
|
{
|
|
|
|
quotes: '',
|
|
|
|
multiline: false,
|
|
|
|
comment_char: '#',
|
2015-10-03 12:24:13 +00:00
|
|
|
line_separator: nil, # uses this char to seperate lines before parsing
|
2017-04-26 21:18:14 +00:00
|
|
|
assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
|
2015-09-30 09:32:24 +00:00
|
|
|
group_re: /\[([^\]]+)\]\s*$/,
|
2017-04-26 21:18:14 +00:00
|
|
|
key_values: 1, # default for key=value, may require for 'key val1 val2 val3'
|
2015-04-17 13:36:18 +00:00
|
|
|
standalone_comments: false,
|
2015-09-10 01:39:43 +00:00
|
|
|
multiple_values: false,
|
2015-04-09 20:01:23 +00:00
|
|
|
}
|
|
|
|
end
|
2015-09-03 18:43:58 +00:00
|
|
|
end
|