8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/ini_file/parser.rb', line 8
def parse(contents)
raise ArgumentError.new('contents cannot be nil') if contents.nil?
raise ArgumentError.new('contents must be a string') unless contents.is_a? String
raise ArgumentError.new('contents cannot be blank') if contents.strip.empty?
root = {}
section_pattern = /^\s*\[([^\]]+)\]\s*$/
property_pattern = /^\s*([^;#:=\r\n]++)\s*[:=](.*)$/
= /^\s*([;#].*)$/
pattern = /#{section_pattern}|#{property_pattern}|#{comment_pattern}|^(.*)$/
current = root
contents.scan(pattern) do |section, key, value, , garbage|
if section
if section.scan(/[\.\\\/,]/).uniq.size > 1
raise IniFormatError.new("Section hierarchy names must use one delimiter: #{section}")
end
sections = section.strip.split(/\s*[\.\\\/,\s]{1}\s*/)
if sections.empty?
raise IniFormatError.new("Section names cannot be blank: #{section}")
end
current = root
sections.each do |section|
section = $1 || $2 if section =~ /^"([^"]+)"$|^'([^']+)'$/
section = section.strip.downcase.to_sym
if section.empty?
raise IniFormatError.new("Section names cannot be blank: #{section}")
elsif section =~ /[\W]+/
raise IniFormatError.new("Section names cannot contain punctuation: #{section}")
elsif current.has_key?(section) && !current[section].is_a?(Hash)
raise IniFormatError.new("Section name matches existing property name: #{section}")
else
current[section] = {} unless current.has_key?(section)
current = current[section]
end
end
elsif key && value
key = key.strip.downcase.gsub(/-/, '_').to_sym
value = value.strip
if key.empty?
raise IniFormatError.new("Property names cannot be blank: #{key}")
elsif current[key]
raise IniFormatError.new("Duplicate keys are not allowed: #{key}")
elsif key =~ /[\W\s]+/
raise IniFormatError.new("Property names cannot contain spaces or punctuation: #{key}")
end
value = value.gsub(/\s+/, ' ') unless value =~ /^"(.+)"$|^'(.+)'$/
if value =~ /^\d+$/
value = value.to_i
elsif value =~ /^\d*\.\d+$/
value = value.to_f
else
if value =~ /^"(.+)"$|^'(.+)'$/
value = $1 || $2
else
value = value.gsub(/\s*[^\\]{1}[#;].*$/, '')
value = value.gsub(/\\([#;])/, '\1')
end
value = value.gsub(/\\[0abtrn\\]/) {|s| eval('"%s"' % "#{s}") }
value = value.gsub(/\\[ux](\d{4})/) {|s| eval('"%s"' % "\\u#{$1}") }
end
current[key] = value
elsif
elsif garbage
raise IniFormatError.new("Unrecognized pattern: #{garbage}")
else
end
end
root.freeze
return root
end
|