CFPropertyList 2.0.14 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +79 -0
- data/{README → README.rdoc} +8 -1
- data/THANKS +7 -0
- data/lib/{rbBinaryCFPropertyList.rb → cfpropertylist/rbBinaryCFPropertyList.rb} +223 -228
- data/lib/{rbCFPlistError.rb → cfpropertylist/rbCFPlistError.rb} +1 -1
- data/lib/{rbCFPropertyList.rb → cfpropertylist/rbCFPropertyList.rb} +157 -59
- data/lib/{rbCFTypes.rb → cfpropertylist/rbCFTypes.rb} +158 -50
- data/lib/{rbXMLCFPropertyList.rb → cfpropertylist/rbLibXMLParser.rb} +36 -12
- data/lib/cfpropertylist/rbNokogiriParser.rb +151 -0
- data/lib/cfpropertylist/rbPlainCFPropertyList.rb +199 -0
- data/lib/cfpropertylist/rbREXMLParser.rb +148 -0
- data/lib/cfpropertylist.rb +2 -2
- metadata +52 -59
@@ -0,0 +1,199 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
|
5
|
+
module CFPropertyList
|
6
|
+
# XML parser
|
7
|
+
class PlainParser < XMLParserInterface
|
8
|
+
# read a XML file
|
9
|
+
# opts::
|
10
|
+
# * :file - The filename of the file to load
|
11
|
+
# * :data - The data to parse
|
12
|
+
def load(opts)
|
13
|
+
@doc = nil
|
14
|
+
|
15
|
+
if(opts.has_key?(:file)) then
|
16
|
+
File.open(opts[:file], :external_encoding => "ASCII") do |fd|
|
17
|
+
@doc = StringScanner.new(fd.read)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@doc = StringScanner.new(opts[:data])
|
21
|
+
end
|
22
|
+
|
23
|
+
if @doc
|
24
|
+
root = import_plain
|
25
|
+
raise CFFormatError.new('content after root object') unless @doc.eos?
|
26
|
+
|
27
|
+
return root
|
28
|
+
end
|
29
|
+
|
30
|
+
raise CFFormatError.new('invalid plist string or file not found')
|
31
|
+
end
|
32
|
+
|
33
|
+
SPACES_AND_COMMENTS = %r{((?:/\*.*?\*/)|(?://.*?$\n?)|(?:\s*))+}x
|
34
|
+
|
35
|
+
# serialize CFPropertyList object to XML
|
36
|
+
# opts = {}:: Specify options: :formatted - Use indention and line breaks
|
37
|
+
def to_str(opts={})
|
38
|
+
opts[:root].to_plain(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def skip_whitespaces
|
43
|
+
@doc.skip SPACES_AND_COMMENTS
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_dict
|
47
|
+
skip_whitespaces
|
48
|
+
hsh = {}
|
49
|
+
|
50
|
+
while not @doc.scan(/\}/)
|
51
|
+
key = import_plain
|
52
|
+
raise CFFormatError.new("invalid dictionary format") if !key
|
53
|
+
|
54
|
+
if key.is_a?(CFString)
|
55
|
+
key = key.value
|
56
|
+
elsif key.is_a?(CFInteger) or key.is_a?(CFReal)
|
57
|
+
key = key.value.to_s
|
58
|
+
else
|
59
|
+
raise CFFormatError.new("invalid key format")
|
60
|
+
end
|
61
|
+
|
62
|
+
skip_whitespaces
|
63
|
+
|
64
|
+
raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/=/)
|
65
|
+
|
66
|
+
skip_whitespaces
|
67
|
+
val = import_plain
|
68
|
+
|
69
|
+
skip_whitespaces
|
70
|
+
raise CFFormatError.new("invalid dictionary format") unless @doc.scan(/;/)
|
71
|
+
skip_whitespaces
|
72
|
+
|
73
|
+
hsh[key] = val
|
74
|
+
raise CFFormatError.new("invalid dictionary format") if @doc.eos?
|
75
|
+
end
|
76
|
+
|
77
|
+
CFDictionary.new(hsh)
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_array
|
81
|
+
skip_whitespaces
|
82
|
+
ary = []
|
83
|
+
|
84
|
+
while not @doc.scan(/\)/)
|
85
|
+
val = import_plain
|
86
|
+
|
87
|
+
return nil if not val or not val.value
|
88
|
+
skip_whitespaces
|
89
|
+
|
90
|
+
if not @doc.skip(/,\s*/)
|
91
|
+
if @doc.scan(/\)/)
|
92
|
+
ary << val
|
93
|
+
return CFArray.new(ary)
|
94
|
+
end
|
95
|
+
|
96
|
+
raise CFFormatError.new("invalid array format")
|
97
|
+
end
|
98
|
+
|
99
|
+
ary << val
|
100
|
+
raise CFFormatError.new("invalid array format") if @doc.eos?
|
101
|
+
end
|
102
|
+
|
103
|
+
CFArray.new(ary)
|
104
|
+
end
|
105
|
+
|
106
|
+
def escape_char
|
107
|
+
case @doc.matched
|
108
|
+
when '"'
|
109
|
+
'"'
|
110
|
+
when '\\'
|
111
|
+
'\\'
|
112
|
+
when 'a'
|
113
|
+
"\a"
|
114
|
+
when 'b'
|
115
|
+
"\b"
|
116
|
+
when 'f'
|
117
|
+
"\f"
|
118
|
+
when 'n'
|
119
|
+
"\n"
|
120
|
+
when 'v'
|
121
|
+
"\v"
|
122
|
+
when 'r'
|
123
|
+
"\r"
|
124
|
+
when 't'
|
125
|
+
"\t"
|
126
|
+
when 'U'
|
127
|
+
@doc.scan(/.{4}/).hex.chr('utf-8')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def read_quoted
|
132
|
+
str = ''
|
133
|
+
|
134
|
+
while not @doc.scan(/"/)
|
135
|
+
if @doc.scan(/\\/)
|
136
|
+
@doc.scan(/./)
|
137
|
+
str << escape_char
|
138
|
+
|
139
|
+
elsif @doc.eos?
|
140
|
+
raise CFFormatError.new("unterminated string")
|
141
|
+
|
142
|
+
else @doc.scan(/./)
|
143
|
+
str << @doc.matched
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
CFString.new(str)
|
148
|
+
end
|
149
|
+
|
150
|
+
def read_unquoted
|
151
|
+
raise CFFormatError.new("unexpected end of file") if @doc.eos?
|
152
|
+
|
153
|
+
if @doc.scan(/(\d\d\d\d)-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)(?:\s+(\+|-)(\d\d)(\d\d))?/)
|
154
|
+
year,month,day,hour,min,sec,pl_min,tz_hour, tz_min = @doc[1], @doc[2], @doc[3], @doc[4], @doc[5], @doc[6], @doc[7], @doc[8], @doc[9]
|
155
|
+
CFDate.new(Time.new(year, month, day, hour, min, sec, pl_min ? sprintf("%s%s:%s", pl_min, tz_hour, tz_min) : nil))
|
156
|
+
|
157
|
+
elsif @doc.scan(/-?\d+?\.\d+\b/)
|
158
|
+
CFReal.new(@doc.matched.to_f)
|
159
|
+
|
160
|
+
elsif @doc.scan(/-?\d+\b/)
|
161
|
+
CFInteger.new(@doc.matched.to_i)
|
162
|
+
|
163
|
+
elsif @doc.scan(/\b(true|false)\b/)
|
164
|
+
CFBoolean.new(@doc.matched == 'true')
|
165
|
+
else
|
166
|
+
CFString.new(@doc.scan(/\w+/))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def read_binary
|
171
|
+
@doc.scan(/(.*?)>/)
|
172
|
+
|
173
|
+
hex_str = @doc[1].gsub(/ /, '')
|
174
|
+
CFData.new([hex_str].pack("H*"), CFData::DATA_RAW)
|
175
|
+
end
|
176
|
+
|
177
|
+
# import the XML values
|
178
|
+
def import_plain
|
179
|
+
skip_whitespaces
|
180
|
+
ret = nil
|
181
|
+
|
182
|
+
if @doc.scan(/\{/) # dict
|
183
|
+
ret = read_dict
|
184
|
+
elsif @doc.scan(/\(/) # array
|
185
|
+
ret = read_array
|
186
|
+
elsif @doc.scan(/"/) # string
|
187
|
+
ret = read_quoted
|
188
|
+
elsif @doc.scan(/</) # binary
|
189
|
+
ret = read_binary
|
190
|
+
else # string w/o quotes
|
191
|
+
ret = read_unquoted
|
192
|
+
end
|
193
|
+
|
194
|
+
return ret
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# eof
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module CFPropertyList
|
6
|
+
# XML parser
|
7
|
+
class ReXMLParser < ParserInterface
|
8
|
+
# read a XML file
|
9
|
+
# opts::
|
10
|
+
# * :file - The filename of the file to load
|
11
|
+
# * :data - The data to parse
|
12
|
+
def load(opts)
|
13
|
+
|
14
|
+
doc = nil
|
15
|
+
if(opts.has_key?(:file)) then
|
16
|
+
File.open(opts[:file], "rb") { |fd| doc = REXML::Document.new(fd) }
|
17
|
+
else
|
18
|
+
doc = REXML::Document.new(opts[:data])
|
19
|
+
end
|
20
|
+
|
21
|
+
if doc
|
22
|
+
root = doc.root.elements[1]
|
23
|
+
return import_xml(root)
|
24
|
+
end
|
25
|
+
rescue REXML::ParseException => e
|
26
|
+
raise CFFormatError.new('invalid XML: ' + e.message)
|
27
|
+
end
|
28
|
+
|
29
|
+
# serialize CFPropertyList object to XML
|
30
|
+
# opts = {}:: Specify options: :formatted - Use indention and line breaks
|
31
|
+
def to_str(opts={})
|
32
|
+
doc = REXML::Document.new
|
33
|
+
@doc = doc
|
34
|
+
|
35
|
+
doc.context[:attribute_quote] = :quote
|
36
|
+
|
37
|
+
doc.add_element 'plist', {'version' => '1.0'}
|
38
|
+
doc.root << opts[:root].to_xml(self)
|
39
|
+
|
40
|
+
formatter = if opts[:formatted] then
|
41
|
+
f = REXML::Formatters::Pretty.new(2)
|
42
|
+
f.compact = true
|
43
|
+
f.width = Float::INFINITY
|
44
|
+
f
|
45
|
+
else
|
46
|
+
REXML::Formatters::Default.new
|
47
|
+
end
|
48
|
+
|
49
|
+
str = formatter.write(doc.root, "")
|
50
|
+
str1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + str + "\n"
|
51
|
+
str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
|
52
|
+
|
53
|
+
return str1
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_node(name)
|
57
|
+
REXML::Element.new(name)
|
58
|
+
end
|
59
|
+
|
60
|
+
def new_text(val)
|
61
|
+
val
|
62
|
+
end
|
63
|
+
|
64
|
+
def append_node(parent, child)
|
65
|
+
if child.is_a?(String) then
|
66
|
+
parent.add_text child
|
67
|
+
else
|
68
|
+
parent.elements << child
|
69
|
+
end
|
70
|
+
parent
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
# get the value of a DOM node
|
76
|
+
def get_value(n)
|
77
|
+
content = n.text
|
78
|
+
|
79
|
+
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
|
80
|
+
content
|
81
|
+
end
|
82
|
+
|
83
|
+
# import the XML values
|
84
|
+
def import_xml(node)
|
85
|
+
ret = nil
|
86
|
+
|
87
|
+
case node.name
|
88
|
+
when 'dict'
|
89
|
+
hsh = Hash.new
|
90
|
+
key = nil
|
91
|
+
|
92
|
+
if node.has_elements? then
|
93
|
+
node.elements.each do |n|
|
94
|
+
next if n.name == '#text' # avoid a bug of libxml
|
95
|
+
next if n.name == '#comment'
|
96
|
+
|
97
|
+
if n.name == "key" then
|
98
|
+
key = get_value(n)
|
99
|
+
key = '' if key.nil? # REXML returns nil if key is empty
|
100
|
+
else
|
101
|
+
raise CFFormatError.new("Format error!") if key.nil?
|
102
|
+
hsh[key] = import_xml(n)
|
103
|
+
key = nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if hsh['CF$UID'] and hsh.keys.length == 1
|
109
|
+
ret = CFUid.new(hsh['CF$UID'].value)
|
110
|
+
else
|
111
|
+
ret = CFDictionary.new(hsh)
|
112
|
+
end
|
113
|
+
|
114
|
+
when 'array'
|
115
|
+
ary = Array.new
|
116
|
+
|
117
|
+
if node.has_elements? then
|
118
|
+
node.elements.each do |n|
|
119
|
+
next if n.name == '#text' # avoid a bug of libxml
|
120
|
+
ary.push import_xml(n)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
ret = CFArray.new(ary)
|
125
|
+
|
126
|
+
when 'true'
|
127
|
+
ret = CFBoolean.new(true)
|
128
|
+
when 'false'
|
129
|
+
ret = CFBoolean.new(false)
|
130
|
+
when 'real'
|
131
|
+
ret = CFReal.new(get_value(node).to_f)
|
132
|
+
when 'integer'
|
133
|
+
ret = CFInteger.new(get_value(node).to_i)
|
134
|
+
when 'string'
|
135
|
+
ret = CFString.new(get_value(node))
|
136
|
+
ret.value = '' if ret.value.nil? # REXML returns nil for empty elements' .text attribute
|
137
|
+
when 'data'
|
138
|
+
ret = CFData.new(get_value(node))
|
139
|
+
when 'date'
|
140
|
+
ret = CFDate.new(CFDate.parse_date(get_value(node)))
|
141
|
+
end
|
142
|
+
|
143
|
+
return ret
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# eof
|
data/lib/cfpropertylist.rb
CHANGED
metadata
CHANGED
@@ -1,80 +1,73 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: CFPropertyList
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
|
-
authors:
|
6
|
+
authors:
|
7
7
|
- Christian Kruse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: libxml-ruby
|
17
|
-
type: :runtime
|
18
|
-
version_requirement:
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 1.1.0
|
24
|
-
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2018-01-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
26
14
|
name: rake
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
31
24
|
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
25
|
+
- !ruby/object:Gem::Version
|
33
26
|
version: 0.7.0
|
34
|
-
|
35
|
-
|
36
|
-
email: cjk@
|
27
|
+
description: This is a module to read, write and manipulate both binary and XML property
|
28
|
+
lists as defined by apple.
|
29
|
+
email: cjk@defunct.ch
|
37
30
|
executables: []
|
38
|
-
|
39
31
|
extensions: []
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
extra_rdoc_files:
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- README.rdoc
|
38
|
+
- THANKS
|
44
39
|
- lib/cfpropertylist.rb
|
45
|
-
- lib/rbBinaryCFPropertyList.rb
|
46
|
-
- lib/rbCFPlistError.rb
|
47
|
-
- lib/rbCFPropertyList.rb
|
48
|
-
- lib/rbCFTypes.rb
|
49
|
-
- lib/
|
50
|
-
-
|
51
|
-
|
40
|
+
- lib/cfpropertylist/rbBinaryCFPropertyList.rb
|
41
|
+
- lib/cfpropertylist/rbCFPlistError.rb
|
42
|
+
- lib/cfpropertylist/rbCFPropertyList.rb
|
43
|
+
- lib/cfpropertylist/rbCFTypes.rb
|
44
|
+
- lib/cfpropertylist/rbLibXMLParser.rb
|
45
|
+
- lib/cfpropertylist/rbNokogiriParser.rb
|
46
|
+
- lib/cfpropertylist/rbPlainCFPropertyList.rb
|
47
|
+
- lib/cfpropertylist/rbREXMLParser.rb
|
52
48
|
homepage: http://github.com/ckruse/CFPropertyList
|
53
|
-
licenses:
|
54
|
-
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
metadata: {}
|
55
52
|
post_install_message:
|
56
53
|
rdoc_options: []
|
57
|
-
|
58
|
-
require_paths:
|
54
|
+
require_paths:
|
59
55
|
- lib
|
60
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
-
requirements:
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
62
58
|
- - ">="
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version:
|
65
|
-
|
66
|
-
|
67
|
-
requirements:
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
68
63
|
- - ">="
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version:
|
71
|
-
version:
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
72
66
|
requirements: []
|
73
|
-
|
74
|
-
|
75
|
-
rubygems_version: 1.3.5
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 2.7.3
|
76
69
|
signing_key:
|
77
|
-
specification_version:
|
78
|
-
summary: Read, write and manipulate both binary and XML property lists as defined
|
70
|
+
specification_version: 4
|
71
|
+
summary: Read, write and manipulate both binary and XML property lists as defined
|
72
|
+
by apple
|
79
73
|
test_files: []
|
80
|
-
|