property-list 1.0.2 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a77c93353f4ae1b3e24f38e0976a133a68251ec
4
- data.tar.gz: 2c68650d8755fc85dd1752951e930f153624a372
3
+ metadata.gz: 3c5d4e0a084175a1ea51a31be189467896203dc9
4
+ data.tar.gz: 796a23fede74ba3c0bcf9bd767963c3d7630123e
5
5
  SHA512:
6
- metadata.gz: 786bb0eaba6ebf83dee461f951ce9966b51c649e945fd1a06ebb3d6a7176ebd886abb6c773172ae85455a310a23a9b93ed00b567b18d58b5bc74cc4696985e4d
7
- data.tar.gz: 4c64f96962192fa6cea63ceb476baaa9129996ec33f322465134b169b19b270ff136836419ca18ddd96fe5a804a35948fa89bdb6c0f8dc89dc4cc8fee4f10ed4
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
- Fully featured propertylist library.
2
- Can load and dump XML/ASCII/Binary plists and offer fine-grained formatting options.
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 1.9+
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>`, `<DOCTYPE>`, `<plist>` tags), default is `false`.
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` whether allow GNUStep extensions for ASCII plist to support serializing more types, default is `true`.
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
- Data type mapping in `PropertyList.load`:
48
-
49
- real: Float
50
- string: String
51
- integer: Integer
52
- data: StringIO
53
- date: DateTime
54
- true: true
55
- false: false
56
- uid: PropertyList::UID # only in binary plist, obj.uid is the integer index
57
- array: Array
58
- dict: Hash
59
- set: Set # only in binary plist
60
-
61
- Reverse mapping in `PropertyList.dump_*`:
62
-
63
- Float: real
64
- String, Symbol: string
65
- Integer: integer
66
- StringIO, IO: data
67
- Time, DateTime, Date: date
68
- true: true
69
- false: false
70
- PropertyList::Uid: uid # only in binary plist
71
- Dict: dict
72
- Array: array
73
- Set: set # only in binary plist
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 complex API and more thourough tests.
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
@@ -1,3 +1,6 @@
1
+ desc "default task"
2
+ task default: :test
3
+
1
4
  desc "run tests"
2
5
  task :test do
3
6
  Dir.glob './test/test_*.rb' do |f|
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.4)
34
- property-list!
35
- pry (~> 0.10.4)
36
- rake (~> 12.0.0)
37
- simplecov (~> 0.14.1)
38
- test-unit (~> 1.2.3)
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
@@ -1,3 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec
3
+ gem "bundler", "~> 1.15"
4
+ gem "rake", "~> 12.0"
5
+ gem "test-unit", "~> 1.2"
6
+ gem "pry", "~> 0.10"
7
+ gem "simplecov", "~> 0.14", require: false
8
+ gem 'coveralls', require: false
@@ -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
- else
140
- if (oct = @lexer.scan /[0-7]{3}/)
141
- chars << [oct.to_i(8)].pack('U')
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
- # Encodes +obj+ as a binary property list. If +obj+ is an Array, Hash, or
25
- # Set, the property list includes its contents.
26
- def generate object
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
- # Write header and encoded objects.
31
- # TODO use bplist10 when there are version 1x elements
32
- add_output "bplist00"
37
+ add_output V0_MAGIC
33
38
  offset_table = []
34
- flatten_objects.each do |o|
39
+ @objs.each do |o|
35
40
  offset_table << @offset
36
41
  binary_object o, ref_byte_size
37
42
  end
38
43
 
39
- # Write offset table.
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
- # Write trailer. (6 + 2 + 24 = 32 bytes)
47
- add_output [
48
- "\0\0\0\0\0\0", # padding
54
+ add_output pack([
55
+ TAIL_PADDING,
49
56
  offset_byte_size, ref_byte_size,
50
- flatten_objects.size,
57
+ @ref_size,
51
58
  0, # index of root object
52
59
  offset_table_addr
53
- ].pack("a*C2Q>3")
60
+ ], "a*C2Q>3")
54
61
  end
55
62
 
56
- private
63
+ def flatten obj
64
+ @ref_size += 1
57
65
 
58
- # Takes an object (nominally a collection, like an Array, Set, or Hash, but
59
- # any object is acceptable) and flattens it into a one-dimensional array.
60
- # Non-collection objects appear in the array as-is, but the contents of
61
- # Arrays, Sets, and Hashes are modified like so: (1) The contents of the
62
- # collection are added, one-by-one, to the one-dimensional array. (2) The
63
- # collection itself is modified so that it contains indexes pointing to the
64
- # objects in the one-dimensional array. Here's an example with an Array:
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
- obj_refs = collection.class.new
92
- id_refs[collection.object_id] = obj_list.length
93
- obj_list << obj_refs
94
- collection.each do |obj|
95
- flatten_collection(obj, obj_list, id_refs)
96
- obj_refs << id_refs[obj.object_id]
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
- if id_refs[collection.object_id]
102
- return obj_list[id_refs[collection.object_id]]
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
- obj_refs = {}
105
- id_refs[collection.object_id] = obj_list.length
106
- obj_list << obj_refs
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
- return obj_list
115
- else
116
- unless id_refs[collection.object_id]
117
- id_refs[collection.object_id] = obj_list.length
118
- obj_list << collection
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
- return obj_list
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].pack("CG")
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).chr
127
+ add_output chr(MARKER_INT | size_bits)
169
128
  binary_integer obj, nbytes
170
129
  when TrueClass
171
- add_output MARKER_TRUE.chr
130
+ add_output chr(MARKER_TRUE)
172
131
  when FalseClass
173
- add_output MARKER_FALSE.chr
132
+ add_output chr(MARKER_FALSE)
174
133
  when Time
175
- add_output [MARKER_DATE, obj.to_f - TIME_INTERVAL_SINCE_1970].pack("CG")
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].pack("CG")
178
- when IO, StringIO
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 Array
185
- # Must be an array of object references as returned by flatten_collection.
186
- binary_marker MARKER_ARRAY, obj.size
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 Hash
197
- # Must be a table of object references as returned by flatten_collection.
198
- binary_marker MARKER_DICT, obj.size
199
- obj.keys.each do |k|
200
- binary_integer k, ref_byte_size
201
- end
202
- obj.values.each do |v|
203
- binary_integer v, ref_byte_size
204
- end
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 "Unsupported class: #{obj.class}"
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).chr
179
+ add_output chr(marker | size)
213
180
  else
214
- add_output (marker | 0xf).chr
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
- @v1 = true
237
- if obj =~ /\A\w+:/
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.chr
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 binary_uuid obj
247
- # TODO
212
+ def chr c
213
+ c.chr.force_encoding 'binary'
248
214
  end
249
215
 
250
- def binary_ordered_set obj
251
- # TODO
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].pack("C")
229
+ add_output pack([i], "C")
264
230
  when 2
265
- add_output [i].pack("n")
231
+ add_output pack([i], "n")
266
232
  when 4
267
- add_output [i].pack("N")
233
+ add_output pack([i], "N")
268
234
  when 8
269
- add_output [i].pack("q>")
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].pack("q>2")
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
- UID[res]
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
@@ -1,3 +1,3 @@
1
1
  module PropertyList
2
- VERSION = '1.0.2'.freeze
2
+ VERSION = '1.0.3'.freeze
3
3
  end
@@ -4,7 +4,7 @@ module PropertyList
4
4
  #
5
5
  # Options:
6
6
  #
7
- # - `segment:` whether output an XML segment (not wrapped with `<?xml>`, `<DOCTYPE>`, `<plist>` tags), default is `false`.
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
- class UID
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
- class URL
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
- class UUID
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
- class OrderedSet
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
- def each
76
- @elements.each do |e|
77
- yield e
78
- end
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
@@ -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
- spec.authors = ["Luikore"]
15
+ puts 'gsd'
16
+ spec.author = "Luikore"
17
+ puts 'hmm'
7
18
  spec.email = 'no@email'
8
19
 
9
- spec.summary = "Property List (plist, propertylist) library with all formats support"
10
- spec.description = "Fully featured propertylist library.
11
- Can load and dump XML/ASCII/Binary plists and offer fine-grained formatting options.
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.2
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-13 00:00:00.000000000 Z
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 propertylist library.
85
- Can load and dump XML/ASCII/Binary plists and offer fine-grained formatting options.
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.11
60
+ rubygems_version: 2.6.13
130
61
  signing_key:
131
62
  specification_version: 4
132
- summary: Property List (plist, propertylist) library with all formats support
63
+ summary: Property list (plist) library with all formats support
133
64
  test_files: []