diff --git a/lib/puppet/settings/config_file.rb b/lib/puppet/settings/config_file.rb index 77db99977..d584f31e6 100644 --- a/lib/puppet/settings/config_file.rb +++ b/lib/puppet/settings/config_file.rb @@ -1,99 +1,96 @@ require 'puppet/settings/ini_file' ## # @api private # # Parses puppet configuration files # class Puppet::Settings::ConfigFile ## # @param value_converter [Proc] a function that will convert strings into ruby types # def initialize(value_converter) @value_converter = value_converter end def parse_file(file, text) result = {} - # Default to 'main' for the section. - section_name = :main - result[section_name] = empty_section - line_number = 1 - Puppet::Settings::IniFile.parse(StringIO.new(text)).each do |line| - case line - when Puppet::Settings::IniFile::SectionLine - section_name = line.name.intern - fail_when_illegal_section_name(section_name, file, line_number) - if result[section_name].nil? - result[section_name] = empty_section - end - when Puppet::Settings::IniFile::SettingLine - var = line.name.intern + ini = Puppet::Settings::IniFile.parse(StringIO.new(text)) + ini.sections.each do |section| + section_name = section.name.intern + fail_when_illegal_section_name(section_name, file, section.line_number) + result[section_name] = empty_section - # We don't want to munge modes, because they're specified in octal, so we'll - # just leave them as a String, since Puppet handles that case correctly. - if var == :mode - value = line.value - else - value = @value_converter[line.value] - end - - # Check to see if this is a file argument and it has extra options - begin - if value.is_a?(String) and options = extract_fileinfo(value) - value = options[:value] - options.delete(:value) - result[section_name][:_meta][var] = options - end - result[section_name][var] = value - rescue Puppet::Error => detail - raise Puppet::Settings::ParseError.new(detail.message, file, line_number, detail) - end - else - if line.text !~ /^\s*#|^\s*$/ - raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line_number) + ini.lines_in(section.name).each do |line| + if line.is_a?(Puppet::Settings::IniFile::SettingLine) + parse_setting(line, result[section_name]) + elsif line.text !~ /^\s*#|^\s*$/ + raise Puppet::Settings::ParseError.new("Could not match line #{line.text}", file, line.line_number) end end - line_number += 1 end result end private + def parse_setting(setting, result) + var = setting.name.intern + + # We don't want to munge modes, because they're specified in octal, so we'll + # just leave them as a String, since Puppet handles that case correctly. + if var == :mode + value = setting.value + else + value = @value_converter[setting.value] + end + + # Check to see if this is a file argument and it has extra options + begin + if value.is_a?(String) and options = extract_fileinfo(value) + value = options[:value] + options.delete(:value) + result[:_meta][var] = options + end + result[var] = value + rescue Puppet::Error => detail + raise Puppet::Settings::ParseError.new(detail.message, file, setting.line_number, detail) + end + end + def empty_section { :_meta => {} } end def fail_when_illegal_section_name(section, file, line) if section == :application_defaults or section == :global_defaults raise Puppet::Error, "Illegal section '#{section}' in config file #{file} at line #{line}" end end def extract_fileinfo(string) result = {} value = string.sub(/\{\s*([^}]+)\s*\}/) do params = $1 params.split(/\s*,\s*/).each do |str| if str =~ /^\s*(\w+)\s*=\s*([\w\d]+)\s*$/ param, value = $1.intern, $2 result[param] = value raise ArgumentError, "Invalid file option '#{param}'" unless [:owner, :mode, :group].include?(param) if param == :mode and value !~ /^\d+$/ raise ArgumentError, "File modes must be numbers" end else raise ArgumentError, "Could not parse '#{string}'" end end '' end result[:value] = value.sub(/\s*$/, '') result end end diff --git a/lib/puppet/settings/ini_file.rb b/lib/puppet/settings/ini_file.rb index e99dce4ba..11b727217 100644 --- a/lib/puppet/settings/ini_file.rb +++ b/lib/puppet/settings/ini_file.rb @@ -1,110 +1,145 @@ # @api private class Puppet::Settings::IniFile DEFAULT_SECTION_NAME = "main" def self.update(config_fh, &block) config = parse(config_fh) manipulator = Manipulator.new(config) yield manipulator config.write(config_fh) end def self.parse(config_fh) - config = new + lines = [DefaultSection.new] config_fh.each_line do |line| case line when /^(\s*)\[(\w+)\](\s*)$/ - config << SectionLine.new($1, $2, $3) + lines << SectionLine.new(lines[-1], $1, $2, $3) when /^(\s*)(\w+)(\s*=\s*)(.*?)(\s*)$/ - config << SettingLine.new($1, $2, $3, $4, $5) + lines << SettingLine.new(lines[-1], $1, $2, $3, $4, $5) else - config << Line.new(line) + lines << Line.new(lines[-1], line) end end - config + new(lines) end - def initialize - @lines = [] + def initialize(lines = []) + @lines = lines end - def <<(line) - @lines << line + def add_section(name) + @lines << SectionLine.new(@lines[-1], "", name, "") + end + + def add_setting(name, value) + @lines << SettingLine.new(@lines[-1], "", name, "=", value, "") end def each(&block) @lines.each(&block) end + def sections + sections = @lines.select { |line| line.is_a?(SectionLine) } + end + def setting(section, name) - settings_in(section).find do |line| + lines_in(section).find do |line| line.is_a?(SettingLine) && line.name == name end end - def settings_in(section) + def lines_in(section) section_lines = [] current_section = DEFAULT_SECTION_NAME @lines.each do |line| if line.is_a?(SectionLine) current_section = line.name elsif current_section == section section_lines << line end end section_lines end def write(fh) fh.truncate(0) fh.rewind @lines.each do |line| line.write(fh) end fh.flush end class Manipulator def initialize(config) @config = config end def set(section, name, value) setting = @config.setting(section, name) if setting setting.value = value else - @config << SectionLine.new("", section, "") - @config << SettingLine.new("", name, "=", value, "") + @config.add_section(section) + @config.add_setting(name, value) end end end - Line = Struct.new(:text) do + module LineNumber + def line_number + line = 0 + previous_line = previous + while previous_line + line += 1 + previous_line = previous_line.previous + end + line + end + end + + Line = Struct.new(:previous, :text) do + include LineNumber + def write(fh) fh.puts(text) end end - SettingLine = Struct.new(:prefix, :name, :infix, :value, :suffix) do + SettingLine = Struct.new(:previous, :prefix, :name, :infix, :value, :suffix) do + include LineNumber + def write(fh) fh.write(prefix) fh.write(name) fh.write(infix) fh.write(value) fh.puts(suffix) end end - SectionLine = Struct.new(:prefix, :name, :suffix) do + SectionLine = Struct.new(:previous, :prefix, :name, :suffix) do + include LineNumber + def write(fh) fh.write(prefix) fh.write("[") fh.write(name) fh.write("]") fh.puts(suffix) end end + + class DefaultSection < SectionLine + def initialize + super(nil, "", DEFAULT_SECTION_NAME, "") + end + + def write(fh) + end + end end