property-list 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -0
- data/README.md +73 -35
- data/Rakefile +3 -0
- data/gems.locked +16 -11
- data/gems.rb +6 -1
- data/lib/property-list/ascii_parser.rb +9 -6
- data/lib/property-list/binary_generator.rb +110 -144
- data/lib/property-list/binary_parser.rb +44 -34
- data/lib/property-list/version.rb +1 -1
- data/lib/property-list/xml_generator.rb +1 -5
- data/lib/property-list/xml_parser.rb +1 -2
- data/lib/property-list.rb +39 -8
- data/property-list.gemspec +15 -10
- metadata +8 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c5d4e0a084175a1ea51a31be189467896203dc9
|
4
|
+
data.tar.gz: 796a23fede74ba3c0bcf9bd767963c3d7630123e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dab686da733d62fc7dc66215b8f2df1d101fd12d73b82e385b4795ca83bdc1979e388cab4e94e2a6e88f7b73abe997cf0f7fdf0159c0e3522ace940a8f197b8
|
7
|
+
data.tar.gz: 1781700ecee2756062c5d45f6308e1bd7d90fc2f48172b9b03a52c6bf7b00b32b51e59fcb5442ce457cafb1859d624b836a83610a58fdcbd8d9f20d860858614
|
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.0.0-p648 # macOS
|
5
|
+
- 2.1.10
|
6
|
+
- 2.2.7
|
7
|
+
- 2.3.4
|
8
|
+
- 2.4.1
|
9
|
+
- ruby-head
|
10
|
+
- jruby-head
|
11
|
+
- rbx-3.72
|
12
|
+
before_install: gem install bundler -v '~> 1.15' --conservative
|
13
|
+
install: bundle install
|
14
|
+
script: CI_COVERAGE=1 bundle exec rake
|
data/README.md
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
[![Build Status](https://travis-ci.org/luikore/property-list.svg?branch=master)](https://travis-ci.org/luikore/property-list)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/property-list.svg)](https://badge.fury.io/rb/property-list)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/github/luikore/property-list/badge.svg?branch=master)](https://coveralls.io/github/luikore/property-list?branch=master)
|
4
|
+
|
5
|
+
Fully featured plist library.
|
6
|
+
Can load and dump XML/ASCII/Binary/SMIME propertylist and offer fine-grained formatting options.
|
3
7
|
Cross platform, clean code, performance tuned, no dependency.
|
4
8
|
|
5
9
|
## Install
|
6
10
|
|
7
|
-
Requires Ruby
|
11
|
+
Requires Ruby 2.0+
|
8
12
|
|
9
13
|
gem ins property-list
|
10
14
|
|
@@ -23,9 +27,9 @@ Generate a plist file data
|
|
23
27
|
PropertyList.dump_binary obj
|
24
28
|
PropertyList.dump_ascii obj
|
25
29
|
|
26
|
-
XML formatting options for `PropertyList.dump_xml object, options`
|
30
|
+
**XML formatting** options for `PropertyList.dump_xml object, options`
|
27
31
|
|
28
|
-
- `segment:` whether output an XML segment (not wrapped with `<?xml
|
32
|
+
- `segment:` whether output an XML segment (not wrapped with `<?xml>, <DOCTYPE>, <plist>` tags), default is `false`.
|
29
33
|
- `xml_version:` you can also specify `"1.1"` for https://www.w3.org/TR/xml11/, default is `"1.0"`, no effect if `:segment` is set to `true`.
|
30
34
|
- `gnu_dtd:` use GNUStep DTD instead (which is a bit different in string escaping), default is `false`.
|
31
35
|
- `indent_unit:` the indent unit, default value is `"\t"`, set to or `''` if you don't need indent.
|
@@ -33,44 +37,78 @@ XML formatting options for `PropertyList.dump_xml object, options`
|
|
33
37
|
- `base64_width:` the width of characters per line when serializing data with Base64, default value is `68`, must be multiple of `4`.
|
34
38
|
- `base64_indent:` whether indent the Base64 encoded data, you can use `false` for compatibility to generate same output for other frameworks, default value is `true`.
|
35
39
|
|
36
|
-
ASCII formatting options for `PropertyList.dump_ascii object, options`
|
40
|
+
**ASCII formatting** options for `PropertyList.dump_ascii object, options`
|
37
41
|
|
38
42
|
- `indent_unit:` the indent unit, default value is `"\t"`, set to `''` if you don't need indent.
|
39
43
|
- `initial_indent:` initial indent space, default is `''`, the indentation per line equals to `initial_indent + indent * current_indent_level`.
|
40
44
|
- `wrap:` wrap the top level output with `{...}` when obj is a Hash, default is `true`.
|
41
45
|
- `encoding_comment:` add encoding comment `// !$*UTF8*$!` on top of file, default is `false`.
|
42
46
|
- `sort_keys:` sort dict keys, default is `true`.
|
43
|
-
- `gnu_extension
|
47
|
+
- `gnu_extension:` whether allow GNUStep extensions for ASCII plist to support serializing more types, default is `true`.
|
44
48
|
|
45
49
|
## Data type mapping
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
PropertyList::
|
71
|
-
|
72
|
-
|
73
|
-
|
51
|
+
When loading, plist data types will be mapped to native Ruby types:
|
52
|
+
|
53
|
+
Plist type Ruby type
|
54
|
+
------------------------------
|
55
|
+
real Float
|
56
|
+
string String
|
57
|
+
unicode_string String
|
58
|
+
integer Integer
|
59
|
+
data StringIO
|
60
|
+
date DateTime
|
61
|
+
true TrueClass
|
62
|
+
false FalseClass
|
63
|
+
uid PropertyList::Uid [*1]
|
64
|
+
array Array
|
65
|
+
dict Hash
|
66
|
+
|
67
|
+
# binary plist v1x elements:
|
68
|
+
|
69
|
+
null NilClass
|
70
|
+
set Set
|
71
|
+
ordset PropertyList::OrdSet
|
72
|
+
uuid PropertyList::Uuid
|
73
|
+
url_base PropertyList::Url [*2]
|
74
|
+
url_no_base PropertyList::Url
|
75
|
+
|
76
|
+
Notes:
|
77
|
+
|
78
|
+
- \[\*1] **uid** is only available in binary plist, `PropertyList::Uid#uid` is the integer index.
|
79
|
+
- \[\*2] **url_base** means URL with base.
|
80
|
+
|
81
|
+
When dumping, native Ruby types will be mapped to plist data types:
|
82
|
+
|
83
|
+
Ruby type Plist type
|
84
|
+
-----------------------------------
|
85
|
+
Float real
|
86
|
+
String string, unicode_string
|
87
|
+
Symbol string, unicode_string
|
88
|
+
Integer integer
|
89
|
+
StringIO data
|
90
|
+
IO data
|
91
|
+
Time date
|
92
|
+
DateTime date
|
93
|
+
Date date
|
94
|
+
true true
|
95
|
+
false false
|
96
|
+
PropertyList::Uid uid
|
97
|
+
Dict dict
|
98
|
+
Array array
|
99
|
+
Set set
|
100
|
+
|
101
|
+
# binary plist v1x elements:
|
102
|
+
|
103
|
+
NilClass null
|
104
|
+
Set set
|
105
|
+
PropertyList::OrdSet ordset
|
106
|
+
PropertyList::Uuid uuid
|
107
|
+
PropertyList::Url url_base, url_no_base
|
108
|
+
|
109
|
+
Notes:
|
110
|
+
|
111
|
+
- `PropertyList::Uid` and `Set` can only be serialized in binary plist.
|
74
112
|
|
75
113
|
Type mappings in ASCII plist depends on the DTD.
|
76
114
|
|
@@ -81,4 +119,4 @@ The binary generating code is modified from https://github.com/jarib/plist with
|
|
81
119
|
## Alternative plist libraries for Ruby
|
82
120
|
|
83
121
|
- [plist](https://github.com/patsplat/plist): only deals with XML plist, and generates wrong plist when there is handle line endings in strings.
|
84
|
-
- [CFPropertyList](https://github.com/ckruse/CFPropertyList): also deals with XML/Binary/ASCII plist files, more
|
122
|
+
- [CFPropertyList](https://github.com/ckruse/CFPropertyList): also deals with XML/Binary/ASCII plist files, but not v1x binary plist, more hard-to-use API and more thourough tests.
|
data/Rakefile
CHANGED
data/gems.locked
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
property-list (1.0)
|
5
|
-
|
6
1
|
GEM
|
7
2
|
remote: https://rubygems.org/
|
8
3
|
specs:
|
9
4
|
coderay (1.1.1)
|
5
|
+
coveralls (0.8.21)
|
6
|
+
json (>= 1.8, < 3)
|
7
|
+
simplecov (~> 0.14.1)
|
8
|
+
term-ansicolor (~> 1.3)
|
9
|
+
thor (~> 0.19.4)
|
10
|
+
tins (~> 1.6)
|
10
11
|
docile (1.1.5)
|
11
12
|
hoe (3.16.1)
|
12
13
|
rake (>= 0.8, < 13.0)
|
@@ -23,19 +24,23 @@ GEM
|
|
23
24
|
simplecov-html (~> 0.10.0)
|
24
25
|
simplecov-html (0.10.2)
|
25
26
|
slop (3.6.0)
|
27
|
+
term-ansicolor (1.6.0)
|
28
|
+
tins (~> 1.0)
|
26
29
|
test-unit (1.2.3)
|
27
30
|
hoe (>= 1.5.1)
|
31
|
+
thor (0.19.4)
|
32
|
+
tins (1.15.0)
|
28
33
|
|
29
34
|
PLATFORMS
|
30
35
|
ruby
|
31
36
|
|
32
37
|
DEPENDENCIES
|
33
|
-
bundler (~> 1.15
|
34
|
-
|
35
|
-
pry (~> 0.10
|
36
|
-
rake (~> 12.0
|
37
|
-
simplecov (~> 0.14
|
38
|
-
test-unit (~> 1.2
|
38
|
+
bundler (~> 1.15)
|
39
|
+
coveralls
|
40
|
+
pry (~> 0.10)
|
41
|
+
rake (~> 12.0)
|
42
|
+
simplecov (~> 0.14)
|
43
|
+
test-unit (~> 1.2)
|
39
44
|
|
40
45
|
BUNDLED WITH
|
41
46
|
1.15.4
|
data/gems.rb
CHANGED
@@ -130,18 +130,21 @@ module PropertyList
|
|
130
130
|
chars << "\r"
|
131
131
|
when 't'
|
132
132
|
chars << "\t"
|
133
|
-
when 'U'
|
133
|
+
when 'U', 'u'
|
134
134
|
if (hex = @lexer.scan /[0-9a-h]{4}/i)
|
135
|
-
chars << [hex].pack('U')
|
135
|
+
chars << [hex.to_i(16)].pack('U')
|
136
136
|
else
|
137
137
|
syntax_error "Expect 4 digit hex code"
|
138
138
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
139
|
+
when /(\d)/
|
140
|
+
oct_init = $1
|
141
|
+
if (oct = @lexer.scan /[0-7]{2}/)
|
142
|
+
chars << [(oct_init + oct).to_i(8)].pack('U')
|
142
143
|
else
|
143
144
|
syntax_error "Expect 3 digit oct code"
|
144
145
|
end
|
146
|
+
else
|
147
|
+
syntax_error "Bad escape"
|
145
148
|
end
|
146
149
|
else
|
147
150
|
chars << ch
|
@@ -211,7 +214,7 @@ module PropertyList
|
|
211
214
|
def syntax_error msg
|
212
215
|
pre = @lexer.string[0...@lexer.pos]
|
213
216
|
line = pre.count("\n") + 1
|
214
|
-
col = pre.size - pre.rindex("\n")
|
217
|
+
col = pre.size - (pre.rindex("\n") || -1)
|
215
218
|
raise SyntaxError, msg + " at line: #{line} col: #{col} #{@lexer.inspect}", caller
|
216
219
|
end
|
217
220
|
end
|
@@ -5,6 +5,7 @@ module PropertyList
|
|
5
5
|
def self.dump_binary obj, options=nil
|
6
6
|
generator = BinaryGenerator.new options
|
7
7
|
generator.generate obj
|
8
|
+
binding.pry if $test
|
8
9
|
generator.output.join
|
9
10
|
end
|
10
11
|
|
@@ -15,109 +16,95 @@ module PropertyList
|
|
15
16
|
class BinaryGenerator #:nodoc:
|
16
17
|
include BinaryMarkers
|
17
18
|
|
19
|
+
Collection = Struct.new :marker, :size, :refs
|
20
|
+
|
21
|
+
V0_MAGIC = "bplist00".force_encoding('binary').freeze
|
22
|
+
V1_MAGIC = "bplist10".force_encoding('binary').freeze
|
23
|
+
TAIL_PADDING = "\0\0\0\0\0\0".force_encoding('binary').freeze
|
24
|
+
|
18
25
|
def initialize opts
|
19
26
|
@output = []
|
20
27
|
@offset = 0
|
28
|
+
@objs = []
|
29
|
+
@ref_size = 0
|
21
30
|
end
|
22
31
|
attr_reader :output
|
23
32
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
flatten_objects = flatten_collection object
|
28
|
-
ref_byte_size = min_byte_size flatten_objects.size - 1
|
33
|
+
def generate obj
|
34
|
+
flatten obj
|
35
|
+
ref_byte_size = min_byte_size @ref_size - 1
|
29
36
|
|
30
|
-
|
31
|
-
# TODO use bplist10 when there are version 1x elements
|
32
|
-
add_output "bplist00"
|
37
|
+
add_output V0_MAGIC
|
33
38
|
offset_table = []
|
34
|
-
|
39
|
+
@objs.each do |o|
|
35
40
|
offset_table << @offset
|
36
41
|
binary_object o, ref_byte_size
|
37
42
|
end
|
38
43
|
|
39
|
-
|
44
|
+
if @v1
|
45
|
+
@output[0] = V1_MAGIC
|
46
|
+
end
|
47
|
+
|
40
48
|
offset_table_addr = @offset
|
41
49
|
offset_byte_size = min_byte_size @offset
|
42
50
|
offset_table.each do |offset|
|
43
51
|
binary_integer offset, offset_byte_size
|
44
52
|
end
|
45
53
|
|
46
|
-
|
47
|
-
|
48
|
-
"\0\0\0\0\0\0", # padding
|
54
|
+
add_output pack([
|
55
|
+
TAIL_PADDING,
|
49
56
|
offset_byte_size, ref_byte_size,
|
50
|
-
|
57
|
+
@ref_size,
|
51
58
|
0, # index of root object
|
52
59
|
offset_table_addr
|
53
|
-
]
|
60
|
+
], "a*C2Q>3")
|
54
61
|
end
|
55
62
|
|
56
|
-
|
63
|
+
def flatten obj
|
64
|
+
@ref_size += 1
|
57
65
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#
|
66
|
-
# ary = [:a, :b, :c]
|
67
|
-
# flatten_collection(ary) # => [[1, 2, 3], :a, :b, :c]
|
68
|
-
#
|
69
|
-
# In the case of a Hash, keys and values are both appended to the one-
|
70
|
-
# dimensional array and then replaced with indexes.
|
71
|
-
#
|
72
|
-
# hsh = {:a => "blue", :b => "purple", :c => "green"}
|
73
|
-
# flatten_collection(hsh)
|
74
|
-
# # => [{1 => 2, 3 => 4, 5 => 6}, :a, "blue", :b, "purple", :c, "green"]
|
75
|
-
#
|
76
|
-
# An object will never be added to the one-dimensional array twice. If a
|
77
|
-
# collection refers to an object more than once, the object will be added
|
78
|
-
# to the one-dimensional array only once.
|
79
|
-
#
|
80
|
-
# ary = [:a, :a, :a]
|
81
|
-
# flatten_collection(ary) # => [[1, 1, 1], :a]
|
82
|
-
#
|
83
|
-
# The +obj_list+ and +id_refs+ parameters are private; they're used for
|
84
|
-
# descending into sub-collections recursively.
|
85
|
-
def flatten_collection collection, obj_list=[], id_refs={}
|
86
|
-
case collection
|
87
|
-
when Array, Set
|
88
|
-
if id_refs[collection.object_id]
|
89
|
-
return obj_list[id_refs[collection.object_id]]
|
66
|
+
case obj
|
67
|
+
when Array
|
68
|
+
refs = []
|
69
|
+
@objs << Collection[MARKER_ARRAY, obj.size, refs]
|
70
|
+
obj.each do |e|
|
71
|
+
refs << @ref_size
|
72
|
+
flatten e
|
90
73
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
74
|
+
|
75
|
+
when Set
|
76
|
+
@v1 = true
|
77
|
+
refs = []
|
78
|
+
@objs << Collection[MARKER_SET, obj.size, refs]
|
79
|
+
obj.each do |e|
|
80
|
+
refs << @ref_size
|
81
|
+
flatten e
|
97
82
|
end
|
98
|
-
return obj_list
|
99
83
|
|
100
84
|
when Hash
|
101
|
-
|
102
|
-
|
85
|
+
refs = []
|
86
|
+
@objs << Collection[MARKER_DICT, obj.size, refs]
|
87
|
+
obj.each do |e, _|
|
88
|
+
refs << @ref_size
|
89
|
+
flatten e
|
103
90
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
collection.keys.sort.each do |key|
|
108
|
-
value = collection[key]
|
109
|
-
key = key.to_s if key.is_a?(Symbol)
|
110
|
-
flatten_collection(key, obj_list, id_refs)
|
111
|
-
flatten_collection(value, obj_list, id_refs)
|
112
|
-
obj_refs[id_refs[key.object_id]] = id_refs[value.object_id]
|
91
|
+
obj.each do |_, e|
|
92
|
+
refs << @ref_size
|
93
|
+
flatten e
|
113
94
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
95
|
+
|
96
|
+
when OrdSet
|
97
|
+
@v1 = true
|
98
|
+
refs = []
|
99
|
+
elements = obj.elements
|
100
|
+
@objs << Collection[MARKER_ORD_SET, elements.size, refs]
|
101
|
+
elements.each do |e|
|
102
|
+
refs << @ref_size
|
103
|
+
flatten e
|
119
104
|
end
|
120
|
-
|
105
|
+
|
106
|
+
else
|
107
|
+
@objs << obj
|
121
108
|
end
|
122
109
|
end
|
123
110
|
|
@@ -126,92 +113,72 @@ module PropertyList
|
|
126
113
|
@offset += data.bytesize
|
127
114
|
end
|
128
115
|
|
129
|
-
# Returns a binary property list fragment that represents +obj+. The
|
130
|
-
# returned string is not a complete property list, just a fragment that
|
131
|
-
# describes +obj+, and is not useful without a header, offset table, and
|
132
|
-
# trailer.
|
133
|
-
#
|
134
|
-
# The following classes are recognized: String, Float, Integer, the Boolean
|
135
|
-
# classes, Time, IO, StringIO, Array, Set, and Hash. IO and StringIO
|
136
|
-
# objects are rewound, read, and the contents stored as data (i.e., Cocoa
|
137
|
-
# applications will decode them as NSData). All other classes are dumped
|
138
|
-
# with Marshal and stored as data.
|
139
|
-
#
|
140
|
-
# Note that subclasses of the supported classes will be encoded as though
|
141
|
-
# they were the supported superclass. Thus, a subclass of (for example)
|
142
|
-
# String will be encoded and decoded as a String, not as the subclass:
|
143
|
-
#
|
144
|
-
# class ExampleString < String
|
145
|
-
# ...
|
146
|
-
# end
|
147
|
-
#
|
148
|
-
# s = ExampleString.new("disquieting plantlike mystery")
|
149
|
-
# encoded_s = binary_object(s)
|
150
|
-
# decoded_s = decode_binary_object(encoded_s)
|
151
|
-
# puts decoded_s.class # => String
|
152
|
-
#
|
153
|
-
# +ref_byte_size+ is the number of bytes to use for storing references to
|
154
|
-
# other objects.
|
155
116
|
def binary_object obj, ref_byte_size = 4
|
156
117
|
case obj
|
157
118
|
when Symbol
|
158
119
|
binary_string obj.to_s
|
159
120
|
when String
|
160
121
|
binary_string obj
|
161
|
-
when URL
|
162
|
-
binary_url obj.url
|
163
122
|
when Float
|
164
|
-
add_output [(MARKER_REAL | 3), obj]
|
123
|
+
add_output pack([(MARKER_REAL | 3), obj], "CG")
|
165
124
|
when Integer
|
166
125
|
nbytes = min_byte_size obj
|
167
126
|
size_bits = { 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4 }[nbytes]
|
168
|
-
add_output (MARKER_INT | size_bits)
|
127
|
+
add_output chr(MARKER_INT | size_bits)
|
169
128
|
binary_integer obj, nbytes
|
170
129
|
when TrueClass
|
171
|
-
add_output MARKER_TRUE
|
130
|
+
add_output chr(MARKER_TRUE)
|
172
131
|
when FalseClass
|
173
|
-
add_output MARKER_FALSE
|
132
|
+
add_output chr(MARKER_FALSE)
|
174
133
|
when Time
|
175
|
-
add_output [MARKER_DATE, obj.to_f - TIME_INTERVAL_SINCE_1970]
|
134
|
+
add_output pack([MARKER_DATE, obj.to_f - TIME_INTERVAL_SINCE_1970], "CG")
|
176
135
|
when Date # also covers DateTime
|
177
|
-
add_output [MARKER_DATE, obj.to_time.to_f - TIME_INTERVAL_SINCE_1970]
|
178
|
-
when
|
136
|
+
add_output pack([MARKER_DATE, obj.to_time.to_f - TIME_INTERVAL_SINCE_1970], "CG")
|
137
|
+
when StringIO
|
138
|
+
data = obj.string.force_encoding('binary') # NOTE StringIO.binmode doesn't work in <2.0.0 & rbx
|
139
|
+
binary_marker MARKER_DATA, data.bytesize
|
140
|
+
add_output data
|
141
|
+
when IO
|
179
142
|
obj.rewind
|
180
143
|
obj.binmode
|
181
144
|
data = obj.read
|
182
145
|
binary_marker MARKER_DATA, data.bytesize
|
183
146
|
add_output data
|
184
|
-
when
|
185
|
-
|
186
|
-
|
187
|
-
obj.each do |i|
|
188
|
-
binary_integer i, ref_byte_size
|
189
|
-
end
|
190
|
-
when Set
|
191
|
-
# Must be a set of object references as returned by flatten_collection.
|
192
|
-
binary_marker MARKER_SET, obj.size
|
193
|
-
obj.each do |i|
|
147
|
+
when Collection
|
148
|
+
binary_marker obj.marker, obj.size
|
149
|
+
obj.refs.each do |i|
|
194
150
|
binary_integer i, ref_byte_size
|
195
151
|
end
|
196
|
-
when
|
197
|
-
#
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
152
|
+
when Uid
|
153
|
+
# Encoding is as integers, except values are unsigned.
|
154
|
+
nbytes = min_byte_size obj.uid
|
155
|
+
size_bits = { 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4 }[nbytes]
|
156
|
+
add_output chr(MARKER_UID | size_bits)
|
157
|
+
binary_integer obj.uid, nbytes
|
158
|
+
|
159
|
+
## the following are v1x data types ##
|
160
|
+
|
161
|
+
when Uuid
|
162
|
+
@v1 = true
|
163
|
+
data = pack([obj.uuid], 'H*')
|
164
|
+
add_output chr(MARKER_UUID)
|
165
|
+
add_output data
|
166
|
+
when Url
|
167
|
+
@v1 = true
|
168
|
+
binary_url obj
|
169
|
+
when NilClass
|
170
|
+
@v1 = true
|
171
|
+
add_output chr(MARKER_NULL)
|
205
172
|
else
|
206
|
-
raise
|
173
|
+
raise UnsupportedTypeError, obj.class.to_s
|
207
174
|
end
|
208
175
|
end
|
209
176
|
|
210
177
|
def binary_marker marker, size
|
211
178
|
if size < 15
|
212
|
-
add_output (marker | size)
|
179
|
+
add_output chr(marker | size)
|
213
180
|
else
|
214
|
-
add_output (marker | 0xf)
|
181
|
+
add_output chr(marker | 0xf)
|
215
182
|
binary_object size
|
216
183
|
end
|
217
184
|
end
|
@@ -233,22 +200,21 @@ module PropertyList
|
|
233
200
|
end
|
234
201
|
|
235
202
|
def binary_url obj
|
236
|
-
|
237
|
-
|
238
|
-
add_output MARKER_WITH_BASE_URL.chr
|
203
|
+
if obj.url =~ /\A\w+:\/\//
|
204
|
+
add_output chr(MARKER_WITH_BASE_URL)
|
239
205
|
else
|
240
|
-
add_output MARKER_NO_BASE_URL
|
206
|
+
add_output chr(MARKER_NO_BASE_URL)
|
241
207
|
end
|
242
|
-
binary_marker MARKER_ASCII_STRING, obj.bytesize
|
243
|
-
add_output obj
|
208
|
+
binary_marker MARKER_ASCII_STRING, obj.url.bytesize
|
209
|
+
add_output obj.url
|
244
210
|
end
|
245
211
|
|
246
|
-
def
|
247
|
-
|
212
|
+
def chr c
|
213
|
+
c.chr.force_encoding 'binary'
|
248
214
|
end
|
249
215
|
|
250
|
-
def
|
251
|
-
|
216
|
+
def pack objs, format
|
217
|
+
objs.pack(format).force_encoding 'binary'
|
252
218
|
end
|
253
219
|
|
254
220
|
# Packs an integer +i+ into its binary representation in the specified
|
@@ -260,19 +226,19 @@ module PropertyList
|
|
260
226
|
end
|
261
227
|
case num_bytes
|
262
228
|
when 1
|
263
|
-
add_output [i]
|
229
|
+
add_output pack([i], "C")
|
264
230
|
when 2
|
265
|
-
add_output [i]
|
231
|
+
add_output pack([i], "n")
|
266
232
|
when 4
|
267
|
-
add_output [i]
|
233
|
+
add_output pack([i], "N")
|
268
234
|
when 8
|
269
|
-
add_output [i]
|
235
|
+
add_output pack([i], "q>")
|
270
236
|
when 16
|
271
237
|
# TODO verify 128 bit integer encoding
|
272
238
|
if i < 0
|
273
239
|
i = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff ^ i.abs + 1
|
274
240
|
end
|
275
|
-
add_output [i >> 64, i & 0xffff_ffff_ffff_ffff]
|
241
|
+
add_output pack([i >> 64, i & 0xffff_ffff_ffff_ffff], "q>2")
|
276
242
|
else
|
277
243
|
raise ArgumentError, "num_bytes must be 1, 2, 4, 8, or 16"
|
278
244
|
end
|
@@ -24,36 +24,26 @@ module PropertyList
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def decode_object offset
|
27
|
-
first_byte, = @src.unpack "@#{offset}C"
|
27
|
+
first_byte, _ = @src.unpack "@#{offset}C"
|
28
28
|
marker = first_byte & 0xF0
|
29
29
|
if marker == 0 or first_byte == MARKER_DATE
|
30
30
|
marker = first_byte
|
31
31
|
end
|
32
32
|
|
33
33
|
case marker
|
34
|
-
when MARKER_NULL
|
35
|
-
nil
|
36
34
|
when MARKER_FALSE
|
37
35
|
false
|
38
36
|
when MARKER_TRUE
|
39
37
|
true
|
40
|
-
when MARKER_NO_BASE_URL
|
41
|
-
raise 'todo'
|
42
|
-
when MARKER_WITH_BASE_URL
|
43
|
-
raise 'todo'
|
44
|
-
when MARKER_UUID
|
45
|
-
raise 'todo'
|
46
|
-
when MARKER_FILL
|
47
|
-
decode_object offest + 1
|
48
38
|
when MARKER_INT
|
49
39
|
size_bits = first_byte & 0x0F
|
50
40
|
num_bytes = 2 ** size_bits
|
51
41
|
decode_integer offset + 1, num_bytes
|
52
42
|
when MARKER_REAL
|
53
|
-
r, = @src.unpack "@#{offset + 1}G"
|
43
|
+
r, _ = @src.unpack "@#{offset + 1}G"
|
54
44
|
r
|
55
45
|
when MARKER_DATE
|
56
|
-
seconds_since_2001, = @src.unpack "@#{offset + 1}G"
|
46
|
+
seconds_since_2001, _ = @src.unpack "@#{offset + 1}G"
|
57
47
|
Time.at(TIME_INTERVAL_SINCE_1970 + seconds_since_2001).to_datetime
|
58
48
|
when MARKER_DATA
|
59
49
|
data = @src.byteslice *(decode_vl_info offset)
|
@@ -64,13 +54,7 @@ module PropertyList
|
|
64
54
|
str_offset, str_size = decode_vl_info offset
|
65
55
|
s = @src.byteslice str_offset, str_size * 2
|
66
56
|
s.force_encoding('utf-16be').encode 'utf-8'
|
67
|
-
when MARKER_UTF8_STRING
|
68
|
-
s = @src.byteslice *(decode_vl_info offset)
|
69
|
-
s.force_encoding 'utf-8'
|
70
57
|
when MARKER_UID
|
71
|
-
# Encoding is as integers, except values are unsigned.
|
72
|
-
# These are used extensively in files written using NSKeyedArchiver, a serializer for Objective-C objects.
|
73
|
-
# The value is the index in parse_result["$objects"]
|
74
58
|
size = (first_byte & 0xF) + 1
|
75
59
|
bytes = @src.byteslice offset + 1, size
|
76
60
|
res = 0
|
@@ -78,21 +62,13 @@ module PropertyList
|
|
78
62
|
res *= 256
|
79
63
|
res += byte
|
80
64
|
end
|
81
|
-
|
65
|
+
Uid.new res
|
82
66
|
when MARKER_ARRAY
|
83
67
|
offset, size = decode_vl_info offset
|
84
68
|
size.times.map do |i|
|
85
69
|
id = decode_ref_id offset + i * @ref_byte_size
|
86
70
|
decode_id id
|
87
71
|
end
|
88
|
-
when MARKER_ORD_SET, MARKER_SET
|
89
|
-
r = Set.new
|
90
|
-
offset, size = decode_vl_info offset
|
91
|
-
size.times do |i|
|
92
|
-
id = decode_ref_id offset + i * @ref_byte_size
|
93
|
-
r << (decode_id id)
|
94
|
-
end
|
95
|
-
r
|
96
72
|
when MARKER_DICT
|
97
73
|
offset, size = decode_vl_info offset
|
98
74
|
keys_byte_size = @ref_byte_size * size
|
@@ -107,18 +83,52 @@ module PropertyList
|
|
107
83
|
end
|
108
84
|
entries.sort_by! &:first
|
109
85
|
Hash[entries]
|
86
|
+
|
87
|
+
## the following are v1x data types ##
|
88
|
+
|
89
|
+
when MARKER_UTF8_STRING
|
90
|
+
s = @src.byteslice *(decode_vl_info offset)
|
91
|
+
s.force_encoding 'utf-8'
|
92
|
+
when MARKER_ORD_SET
|
93
|
+
r = []
|
94
|
+
offset, size = decode_vl_info offset
|
95
|
+
size.times do |i|
|
96
|
+
id = decode_ref_id offset + i * @ref_byte_size
|
97
|
+
r << (decode_id id)
|
98
|
+
end
|
99
|
+
OrdSet.new r
|
100
|
+
when MARKER_SET
|
101
|
+
r = Set.new
|
102
|
+
offset, size = decode_vl_info offset
|
103
|
+
size.times do |i|
|
104
|
+
id = decode_ref_id offset + i * @ref_byte_size
|
105
|
+
r << (decode_id id)
|
106
|
+
end
|
107
|
+
r
|
108
|
+
when MARKER_NULL
|
109
|
+
nil
|
110
|
+
when MARKER_NO_BASE_URL, MARKER_WITH_BASE_URL
|
111
|
+
# TODO check with real v1x plist
|
112
|
+
url = decode_object offset + 1
|
113
|
+
Url.new url
|
114
|
+
when MARKER_UUID
|
115
|
+
bytes = @src.byteslice offset + 1, 16
|
116
|
+
hex, _ = bytes.unpack 'H*'
|
117
|
+
Uuid.new hex.upcase
|
118
|
+
when MARKER_FILL
|
119
|
+
decode_object offest + 1
|
110
120
|
else
|
111
121
|
raise "unused marker: 0b#{marker.to_s(2).rjust 8, '0'}"
|
112
122
|
end
|
113
123
|
end
|
114
124
|
|
115
125
|
def decode_vl_info offset
|
116
|
-
marker, = @src.unpack "@#{offset}C"
|
126
|
+
marker, _ = @src.unpack "@#{offset}C"
|
117
127
|
vl_size_bits = marker & 0x0F
|
118
128
|
|
119
129
|
if vl_size_bits == 0x0F
|
120
130
|
# size is followed by marker int
|
121
|
-
int_marker, = @src.unpack "@#{offset + 1}C"
|
131
|
+
int_marker, _ = @src.unpack "@#{offset + 1}C"
|
122
132
|
num_bytes = 2 ** (int_marker & 0x0F)
|
123
133
|
size = decode_integer offset + 2, num_bytes
|
124
134
|
[offset + 2 + num_bytes, size]
|
@@ -151,13 +161,13 @@ module PropertyList
|
|
151
161
|
# NOTE: only num_bytes = 8 or 16 it can be negative
|
152
162
|
case num_bytes
|
153
163
|
when 1
|
154
|
-
i, = @src.unpack "@#{offset}C"
|
164
|
+
i, _ = @src.unpack "@#{offset}C"
|
155
165
|
when 2
|
156
|
-
i, = @src.unpack "@#{offset}n"
|
166
|
+
i, _ = @src.unpack "@#{offset}n"
|
157
167
|
when 4
|
158
|
-
i, = @src.unpack "@#{offset}N"
|
168
|
+
i, _ = @src.unpack "@#{offset}N"
|
159
169
|
when 8
|
160
|
-
i, = @src.unpack "@#{offset}q>"
|
170
|
+
i, _ = @src.unpack "@#{offset}q>"
|
161
171
|
when 16
|
162
172
|
hi, lo = @src.unpack "@#{offset}q>2"
|
163
173
|
i = (hi << 64) | lo
|
@@ -4,7 +4,7 @@ module PropertyList
|
|
4
4
|
#
|
5
5
|
# Options:
|
6
6
|
#
|
7
|
-
# - `segment:` whether output an XML segment (not wrapped with `<?xml
|
7
|
+
# - `segment:` whether output an XML segment (not wrapped with `<?xml>, <DOCTYPE>, <plist>` tags), default is `false`.
|
8
8
|
# - `xml_version:` you can also specify `"1.1"` for https://www.w3.org/TR/xml11/, default is `"1.0"`, no effect if `:segment` is set to `true`.
|
9
9
|
# - `gnu_dtd:` use GNUStep DTD instead (which is a bit different in string escaping), default is `false`.
|
10
10
|
# - `indent_unit:` the indent unit, default value is `"\t"`, set to or `''` if you don't need indent.
|
@@ -124,10 +124,6 @@ module PropertyList
|
|
124
124
|
@output << "#@indent<data>\n#{base64}#@indent</data>\n"
|
125
125
|
end
|
126
126
|
|
127
|
-
def comment_tag content
|
128
|
-
@output << "#@indent<!-- #{content} -->\n"
|
129
|
-
end
|
130
|
-
|
131
127
|
def tag name, contents=nil
|
132
128
|
if block_given?
|
133
129
|
@output << "#@indent<#{name}>\n"
|
@@ -22,7 +22,6 @@ module PropertyList
|
|
22
22
|
# TODO xml_encoding = xml_declaration.match(/(?:\A|\s)encoding=(?:"(.*?)"|'(.*?)')(?:\s|\Z)/)
|
23
23
|
|
24
24
|
skip_space_and_comments
|
25
|
-
# TODO doctype should co-exist with xml
|
26
25
|
@lexer.skip(/\s*<!DOCTYPE\s+.*?>/m)
|
27
26
|
skip_space_and_comments
|
28
27
|
|
@@ -115,7 +114,7 @@ module PropertyList
|
|
115
114
|
def syntax_error msg
|
116
115
|
pre = @lexer.string[0...@lexer.pos]
|
117
116
|
line = pre.count("\n") + 1
|
118
|
-
col = pre.size - pre.rindex("\n")
|
117
|
+
col = pre.size - (pre.rindex("\n") || -1)
|
119
118
|
raise SyntaxError, msg + " at line: #{line} col: #{col} #{@lexer.inspect}", caller
|
120
119
|
end
|
121
120
|
end
|
data/lib/property-list.rb
CHANGED
@@ -44,38 +44,69 @@ module PropertyList
|
|
44
44
|
|
45
45
|
# binary plist v0x elements:
|
46
46
|
|
47
|
-
|
47
|
+
# These are used extensively in files written using NSKeyedArchiver, a serializer for Objective-C objects.
|
48
|
+
# The value is the index in parse_result["$objects"]
|
49
|
+
#
|
50
|
+
# call-seq:
|
51
|
+
#
|
52
|
+
# PropertyList::Uid.new 34
|
53
|
+
class Uid
|
48
54
|
def initialize uid
|
49
55
|
@uid = uid
|
50
56
|
end
|
57
|
+
|
51
58
|
attr_reader :uid
|
59
|
+
|
60
|
+
def == other
|
61
|
+
other.is_a?(Uid) and @uid == other.uid
|
62
|
+
end
|
52
63
|
end
|
53
64
|
|
54
65
|
# binary plist v1x elements:
|
55
66
|
|
56
|
-
|
67
|
+
# call-seq:
|
68
|
+
#
|
69
|
+
# PropertyList::Url.new 'http://foo.com' # with base
|
70
|
+
# PropertyList::Url.new '/foo.com' # no base
|
71
|
+
class Url
|
57
72
|
def initialize url
|
58
73
|
@url = url
|
59
74
|
end
|
75
|
+
|
60
76
|
attr_reader :url
|
77
|
+
|
78
|
+
def == other
|
79
|
+
other.is_a?(Url) and @url == other.url
|
80
|
+
end
|
61
81
|
end
|
62
82
|
|
63
|
-
|
83
|
+
# call-seq:
|
84
|
+
#
|
85
|
+
# PropertyList::Uuid.new 'F' * 32
|
86
|
+
class Uuid
|
64
87
|
def initialize uuid
|
65
88
|
@uuid = uuid
|
66
89
|
end
|
90
|
+
|
67
91
|
attr_reader :uuid
|
92
|
+
|
93
|
+
def == other
|
94
|
+
other.is_a?(Uuid) and @uuid == other.uuid
|
95
|
+
end
|
68
96
|
end
|
69
97
|
|
70
|
-
|
98
|
+
# call-seq:
|
99
|
+
#
|
100
|
+
# PropertyList::OrdSet.new ['foo', 'bar', 3, 4]
|
101
|
+
class OrdSet
|
71
102
|
def initialize elements
|
72
103
|
@elements = elements.uniq
|
73
104
|
end
|
74
105
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
106
|
+
attr_reader :elements
|
107
|
+
|
108
|
+
def == other
|
109
|
+
other.is_a?(OrdSet) and @elements == other.elements
|
79
110
|
end
|
80
111
|
end
|
81
112
|
end
|
data/property-list.gemspec
CHANGED
@@ -1,24 +1,29 @@
|
|
1
1
|
require_relative 'lib/property-list/version'
|
2
2
|
|
3
|
+
class Array
|
4
|
+
if ![].respond_to?(:grep_v)
|
5
|
+
def grep_v reg
|
6
|
+
self - grep(reg)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
puts 'wa?'
|
3
12
|
Gem::Specification.new do |spec|
|
4
13
|
spec.name = "property-list"
|
5
14
|
spec.version = PropertyList::VERSION
|
6
|
-
|
15
|
+
puts 'gsd'
|
16
|
+
spec.author = "Luikore"
|
17
|
+
puts 'hmm'
|
7
18
|
spec.email = 'no@email'
|
8
19
|
|
9
|
-
spec.summary = "Property
|
10
|
-
spec.description = "Fully featured
|
11
|
-
Can load and dump XML/ASCII/Binary
|
20
|
+
spec.summary = "Property list (plist) library with all formats support"
|
21
|
+
spec.description = "Fully featured property_plist library.
|
22
|
+
Can load and dump XML/ASCII/Binary/SMIME propertyplist and offer fine-grained formatting options.
|
12
23
|
Cross platform, clean code, performance tuned, no dependency."
|
13
24
|
spec.homepage = "https://github.com/luikore/property-list"
|
14
25
|
spec.license = "BSD-3-Clause"
|
15
26
|
|
16
27
|
spec.files = `git ls-files -z`.split("\x0").grep_v %r{^(test|spec|features)/}
|
17
28
|
spec.require_paths = ["lib"]
|
18
|
-
|
19
|
-
spec.add_development_dependency "bundler", "~> 1.15"
|
20
|
-
spec.add_development_dependency "rake", "~> 12.0"
|
21
|
-
spec.add_development_dependency "test-unit", "~> 1.2"
|
22
|
-
spec.add_development_dependency "pry", "~> 0.10"
|
23
|
-
spec.add_development_dependency "simplecov", "~> 0.14"
|
24
29
|
end
|
metadata
CHANGED
@@ -1,88 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: property-list
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luikore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.15'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.15'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '12.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '12.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: test-unit
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '1.2'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '1.2'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0.10'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0.10'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: simplecov
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0.14'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0.14'
|
11
|
+
date: 2017-09-15 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
83
13
|
description: |-
|
84
|
-
Fully featured
|
85
|
-
Can load and dump XML/ASCII/Binary
|
14
|
+
Fully featured property_plist library.
|
15
|
+
Can load and dump XML/ASCII/Binary/SMIME propertyplist and offer fine-grained formatting options.
|
86
16
|
Cross platform, clean code, performance tuned, no dependency.
|
87
17
|
email: no@email
|
88
18
|
executables: []
|
@@ -91,6 +21,7 @@ extra_rdoc_files: []
|
|
91
21
|
files:
|
92
22
|
- ".gitignore"
|
93
23
|
- ".rdoc_options"
|
24
|
+
- ".travis.yml"
|
94
25
|
- LICENSE
|
95
26
|
- README.md
|
96
27
|
- Rakefile
|
@@ -126,8 +57,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
57
|
version: '0'
|
127
58
|
requirements: []
|
128
59
|
rubyforge_project:
|
129
|
-
rubygems_version: 2.6.
|
60
|
+
rubygems_version: 2.6.13
|
130
61
|
signing_key:
|
131
62
|
specification_version: 4
|
132
|
-
summary: Property
|
63
|
+
summary: Property list (plist) library with all formats support
|
133
64
|
test_files: []
|