CFPropertyList 2.0.16 → 2.0.17
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.
- data/lib/rbBinaryCFPropertyList.rb +206 -218
- data/lib/rbCFPropertyList.rb +50 -25
- data/lib/rbCFTypes.rb +33 -31
- data/lib/rbXMLCFPropertyList.rb +4 -7
- metadata +5 -5
@@ -16,7 +16,7 @@ module CFPropertyList
|
|
16
16
|
@offsets = []
|
17
17
|
|
18
18
|
fd = nil
|
19
|
-
if(opts.has_key?(:file))
|
19
|
+
if(opts.has_key?(:file))
|
20
20
|
fd = File.open(opts[:file],"rb")
|
21
21
|
file = opts[:file]
|
22
22
|
else
|
@@ -40,7 +40,7 @@ module CFPropertyList
|
|
40
40
|
# decode offset table
|
41
41
|
formats = ["","C*","n*","(H6)*","N*"]
|
42
42
|
@offsets = coded_offset_table.unpack(formats[offset_size])
|
43
|
-
if(offset_size == 3)
|
43
|
+
if(offset_size == 3)
|
44
44
|
0.upto(@offsets.size-1) { |i| @offsets[i] = @offsets[i].to_i(16) }
|
45
45
|
end
|
46
46
|
|
@@ -48,7 +48,7 @@ module CFPropertyList
|
|
48
48
|
val = read_binary_object_at(file,fd,top_object)
|
49
49
|
|
50
50
|
fd.close
|
51
|
-
|
51
|
+
val
|
52
52
|
end
|
53
53
|
|
54
54
|
|
@@ -60,19 +60,21 @@ module CFPropertyList
|
|
60
60
|
|
61
61
|
@written_object_count = 0
|
62
62
|
@object_table = []
|
63
|
-
@object_ref_size = 0
|
64
63
|
|
65
64
|
@offsets = []
|
66
65
|
|
67
66
|
binary_str = "bplist00"
|
68
67
|
|
69
68
|
@object_refs = count_object_refs(opts[:root])
|
70
|
-
@object_ref_size = Binary.bytes_needed(@object_refs)
|
71
69
|
|
72
70
|
opts[:root].to_binary(self)
|
73
71
|
|
74
72
|
next_offset = 8
|
75
|
-
offsets = @object_table.
|
73
|
+
offsets = @object_table.map do |object|
|
74
|
+
offset = next_offset
|
75
|
+
next_offset += object.bytesize
|
76
|
+
offset
|
77
|
+
end
|
76
78
|
binary_str << @object_table.join
|
77
79
|
|
78
80
|
table_offset = next_offset
|
@@ -89,60 +91,59 @@ module CFPropertyList
|
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
92
|
-
binary_str << [offset_size, @
|
94
|
+
binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
|
93
95
|
binary_str << [@object_table.size].pack("x4N")
|
94
96
|
binary_str << [0].pack("x4N")
|
95
97
|
binary_str << [table_offset].pack("x4N")
|
96
98
|
|
97
|
-
|
99
|
+
binary_str
|
100
|
+
end
|
101
|
+
|
102
|
+
def object_ref_size object_refs
|
103
|
+
Binary.bytes_needed(object_refs)
|
98
104
|
end
|
99
105
|
|
100
106
|
# read a „null” type (i.e. null byte, marker byte, bool value)
|
101
107
|
def read_binary_null_type(length)
|
102
108
|
case length
|
103
|
-
when 0
|
104
|
-
when 8
|
105
|
-
when 9
|
106
|
-
when 15 then
|
109
|
+
when 0 then 0 # null byte
|
110
|
+
when 8 then CFBoolean.new(false)
|
111
|
+
when 9 then CFBoolean.new(true)
|
112
|
+
when 15 then 15 # fill type
|
113
|
+
else
|
114
|
+
raise CFFormatError.new("unknown null type: #{length}")
|
107
115
|
end
|
108
|
-
|
109
|
-
raise CFFormatError.new("unknown null type: #{length}")
|
110
116
|
end
|
111
117
|
protected :read_binary_null_type
|
112
118
|
|
113
119
|
# read a binary int value
|
114
120
|
def read_binary_int(fname,fd,length)
|
115
|
-
|
121
|
+
if length > 3
|
122
|
+
raise CFFormatError.new("Integer greater than 8 bytes: #{length}")
|
123
|
+
end
|
116
124
|
|
117
125
|
nbytes = 1 << length
|
118
126
|
|
119
|
-
val = nil
|
120
127
|
buff = fd.read(nbytes)
|
121
128
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
# doesn't consider the number to be negative, and won't sign extend.
|
139
|
-
val = -(2**63 - ((hiword & 0x7fffffff) << 32 | loword))
|
140
|
-
else
|
141
|
-
val = hiword << 32 | loword
|
129
|
+
CFInteger.new(
|
130
|
+
case length
|
131
|
+
when 0 then buff.unpack("C")[0]
|
132
|
+
when 1 then buff.unpack("n")[0]
|
133
|
+
when 2 then buff.unpack("N")[0]
|
134
|
+
when 3
|
135
|
+
hiword,loword = buff.unpack("NN")
|
136
|
+
if (hiword & 0x80000000) != 0
|
137
|
+
# 8 byte integers are always signed, and are negative when bit 63 is
|
138
|
+
# set. Decoding into either a Fixnum or Bignum is tricky, however,
|
139
|
+
# because the size of a Fixnum varies among systems, and Ruby
|
140
|
+
# doesn't consider the number to be negative, and won't sign extend.
|
141
|
+
-(2**63 - ((hiword & 0x7fffffff) << 32 | loword))
|
142
|
+
else
|
143
|
+
hiword << 32 | loword
|
144
|
+
end
|
142
145
|
end
|
143
|
-
|
144
|
-
|
145
|
-
return CFInteger.new(val);
|
146
|
+
)
|
146
147
|
end
|
147
148
|
protected :read_binary_int
|
148
149
|
|
@@ -151,23 +152,22 @@ module CFPropertyList
|
|
151
152
|
raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
|
152
153
|
|
153
154
|
nbytes = 1 << length
|
154
|
-
val = nil
|
155
155
|
buff = fd.read(nbytes)
|
156
156
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
157
|
+
CFReal.new(
|
158
|
+
case length
|
159
|
+
when 0 # 1 byte float? must be an error
|
160
|
+
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
|
161
|
+
when 1 # 2 byte float? must be an error
|
162
|
+
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
|
163
|
+
when 2 then
|
164
|
+
buff.reverse.unpack("f")[0]
|
165
|
+
when 3 then
|
166
|
+
buff.reverse.unpack("d")[0]
|
167
|
+
else
|
168
|
+
fail "unexpected length: #{length}"
|
169
|
+
end
|
170
|
+
)
|
171
171
|
end
|
172
172
|
protected :read_binary_real
|
173
173
|
|
@@ -176,48 +176,46 @@ module CFPropertyList
|
|
176
176
|
raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
|
177
177
|
|
178
178
|
nbytes = 1 << length
|
179
|
-
val = nil
|
180
179
|
buff = fd.read(nbytes)
|
181
180
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
return CFDate.new(val,CFDate::TIMESTAMP_APPLE)
|
181
|
+
CFDate.new(
|
182
|
+
case length
|
183
|
+
when 0 then # 1 byte CFDate is an error
|
184
|
+
raise CFFormatError.new("#{length+1} byte CFDate, error")
|
185
|
+
when 1 then # 2 byte CFDate is an error
|
186
|
+
raise CFFormatError.new("#{length+1} byte CFDate, error")
|
187
|
+
when 2 then
|
188
|
+
buff.reverse.unpack("f")[0]
|
189
|
+
when 3 then
|
190
|
+
buff.reverse.unpack("d")[0]
|
191
|
+
end,
|
192
|
+
CFDate::TIMESTAMP_APPLE
|
193
|
+
)
|
196
194
|
end
|
197
195
|
protected :read_binary_date
|
198
196
|
|
199
197
|
# Read a binary data value
|
200
198
|
def read_binary_data(fname,fd,length)
|
201
|
-
|
202
|
-
buff = fd.read(length) if length > 0
|
203
|
-
return CFData.new(buff,CFData::DATA_RAW)
|
199
|
+
CFData.new(read_fd(fd, length), CFData::DATA_RAW)
|
204
200
|
end
|
205
201
|
protected :read_binary_data
|
206
202
|
|
203
|
+
def read_fd fd, length
|
204
|
+
length > 0 ? fd.read(length) : ""
|
205
|
+
end
|
206
|
+
|
207
207
|
# Read a binary string value
|
208
208
|
def read_binary_string(fname,fd,length)
|
209
|
-
buff =
|
210
|
-
buff = fd.read(length) if length > 0
|
211
|
-
|
209
|
+
buff = read_fd fd, length
|
212
210
|
@unique_table[buff] = true unless @unique_table.has_key?(buff)
|
213
|
-
|
211
|
+
CFString.new(buff)
|
214
212
|
end
|
215
213
|
protected :read_binary_string
|
216
214
|
|
217
215
|
# Convert the given string from one charset to another
|
218
216
|
def Binary.charset_convert(str,from,to="UTF-8")
|
219
217
|
return str.clone.force_encoding(from).encode(to) if str.respond_to?("encode")
|
220
|
-
|
218
|
+
Iconv.conv(to,from,str)
|
221
219
|
end
|
222
220
|
|
223
221
|
# Count characters considering character set
|
@@ -228,7 +226,7 @@ module CFPropertyList
|
|
228
226
|
utf8_str = Iconv.conv("UTF-8",charset,str)
|
229
227
|
size = utf8_str.scan(/./mu).size
|
230
228
|
end
|
231
|
-
|
229
|
+
|
232
230
|
# UTF-16 code units in the range D800-DBFF are the beginning of
|
233
231
|
# a surrogate pair, and count as one additional character for
|
234
232
|
# length calculation.
|
@@ -239,8 +237,8 @@ module CFPropertyList
|
|
239
237
|
str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
|
240
238
|
end
|
241
239
|
end
|
242
|
-
|
243
|
-
|
240
|
+
|
241
|
+
size
|
244
242
|
end
|
245
243
|
|
246
244
|
# Read a unicode string value, coded as UTF-16BE
|
@@ -251,7 +249,7 @@ module CFPropertyList
|
|
251
249
|
buff = fd.read(2*length)
|
252
250
|
|
253
251
|
@unique_table[buff] = true unless @unique_table.has_key?(buff)
|
254
|
-
|
252
|
+
CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
|
255
253
|
end
|
256
254
|
protected :read_binary_unicode_string
|
257
255
|
|
@@ -260,7 +258,7 @@ module CFPropertyList
|
|
260
258
|
ary = []
|
261
259
|
|
262
260
|
# first: read object refs
|
263
|
-
if(length != 0)
|
261
|
+
if(length != 0)
|
264
262
|
buff = fd.read(length * @object_ref_size)
|
265
263
|
objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
|
266
264
|
|
@@ -271,7 +269,7 @@ module CFPropertyList
|
|
271
269
|
end
|
272
270
|
end
|
273
271
|
|
274
|
-
|
272
|
+
CFArray.new(ary)
|
275
273
|
end
|
276
274
|
protected :read_binary_array
|
277
275
|
|
@@ -296,7 +294,7 @@ module CFPropertyList
|
|
296
294
|
end
|
297
295
|
end
|
298
296
|
|
299
|
-
|
297
|
+
CFDictionary.new(dict)
|
300
298
|
end
|
301
299
|
protected :read_binary_dict
|
302
300
|
|
@@ -316,29 +314,26 @@ module CFPropertyList
|
|
316
314
|
object_length = object_length.value
|
317
315
|
end
|
318
316
|
|
319
|
-
retval = nil
|
320
317
|
case object_type
|
321
|
-
when '0'
|
322
|
-
|
323
|
-
when '1'
|
324
|
-
|
325
|
-
when '2'
|
326
|
-
|
327
|
-
when '3'
|
328
|
-
|
329
|
-
when '4'
|
330
|
-
|
331
|
-
when '5'
|
332
|
-
|
333
|
-
when '6'
|
334
|
-
|
335
|
-
when 'a'
|
336
|
-
|
337
|
-
when 'd'
|
338
|
-
|
318
|
+
when '0' # null, false, true, fillbyte
|
319
|
+
read_binary_null_type(object_length)
|
320
|
+
when '1' # integer
|
321
|
+
read_binary_int(fname,fd,object_length)
|
322
|
+
when '2' # real
|
323
|
+
read_binary_real(fname,fd,object_length)
|
324
|
+
when '3' # date
|
325
|
+
read_binary_date(fname,fd,object_length)
|
326
|
+
when '4' # data
|
327
|
+
read_binary_data(fname,fd,object_length)
|
328
|
+
when '5' # byte string, usually utf8 encoded
|
329
|
+
read_binary_string(fname,fd,object_length)
|
330
|
+
when '6' # unicode string (utf16be)
|
331
|
+
read_binary_unicode_string(fname,fd,object_length)
|
332
|
+
when 'a' # array
|
333
|
+
read_binary_array(fname,fd,object_length)
|
334
|
+
when 'd' # dictionary
|
335
|
+
read_binary_dict(fname,fd,object_length)
|
339
336
|
end
|
340
|
-
|
341
|
-
return retval
|
342
337
|
end
|
343
338
|
protected :read_binary_object
|
344
339
|
|
@@ -346,36 +341,30 @@ module CFPropertyList
|
|
346
341
|
def read_binary_object_at(fname,fd,pos)
|
347
342
|
position = @offsets[pos]
|
348
343
|
fd.seek(position,IO::SEEK_SET)
|
349
|
-
|
344
|
+
read_binary_object(fname,fd)
|
350
345
|
end
|
351
346
|
protected :read_binary_object_at
|
352
347
|
|
353
348
|
# pack an +int+ of +nbytes+ with size
|
354
349
|
def Binary.pack_it_with_size(nbytes,int)
|
355
350
|
case nbytes
|
356
|
-
when 1
|
357
|
-
|
358
|
-
when
|
359
|
-
return [int].pack('n')
|
360
|
-
when 4
|
361
|
-
return [int].pack('N')
|
351
|
+
when 1 then [int].pack('c')
|
352
|
+
when 2 then [int].pack('n')
|
353
|
+
when 4 then [int].pack('N')
|
362
354
|
when 8
|
363
|
-
|
355
|
+
[int >> 32, int & 0xFFFFFFFF].pack('NN')
|
364
356
|
else
|
365
357
|
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
366
358
|
end
|
367
359
|
end
|
368
|
-
|
360
|
+
|
369
361
|
def Binary.pack_int_array_with_size(nbytes, array)
|
370
362
|
case nbytes
|
371
|
-
when 1
|
372
|
-
|
373
|
-
when
|
374
|
-
array.pack('n*')
|
375
|
-
when 4
|
376
|
-
array.pack('N*')
|
363
|
+
when 1 then array.pack('C*')
|
364
|
+
when 2 then array.pack('n*')
|
365
|
+
when 4 then array.pack('N*')
|
377
366
|
when 8
|
378
|
-
array.
|
367
|
+
array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
|
379
368
|
else
|
380
369
|
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
381
370
|
end
|
@@ -383,41 +372,36 @@ module CFPropertyList
|
|
383
372
|
|
384
373
|
# calculate how many bytes are needed to save +count+
|
385
374
|
def Binary.bytes_needed(count)
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
nbytes = 4
|
392
|
-
elsif count < 2**64
|
393
|
-
nbytes = 8
|
375
|
+
case
|
376
|
+
when count < 2**8 then 1
|
377
|
+
when count < 2**16 then 2
|
378
|
+
when count < 2**32 then 4
|
379
|
+
when count < 2**64 then 8
|
394
380
|
else
|
395
381
|
raise CFFormatError.new("Data size too large: #{count}")
|
396
382
|
end
|
397
|
-
|
398
|
-
return nbytes
|
399
383
|
end
|
400
384
|
|
401
385
|
# Create a type byte for binary format as defined by apple
|
402
386
|
def Binary.type_bytes(type, length)
|
403
387
|
if length < 15
|
404
|
-
|
388
|
+
[(type << 4) | length].pack('C')
|
405
389
|
else
|
406
390
|
bytes = [(type << 4) | 0xF]
|
407
391
|
if length <= 0xFF
|
408
|
-
|
392
|
+
bytes.push(0x10, length).pack('CCC') # 1 byte length
|
409
393
|
elsif length <= 0xFFFF
|
410
|
-
|
394
|
+
bytes.push(0x11, length).pack('CCn') # 2 byte length
|
411
395
|
elsif length <= 0xFFFFFFFF
|
412
|
-
|
396
|
+
bytes.push(0x12, length).pack('CCN') # 4 byte length
|
413
397
|
elsif length <= 0x7FFFFFFFFFFFFFFF
|
414
|
-
|
398
|
+
bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
|
415
399
|
else
|
416
400
|
raise CFFormatError.new("Integer too large: #{int}")
|
417
401
|
end
|
418
402
|
end
|
419
403
|
end
|
420
|
-
|
404
|
+
|
421
405
|
def count_object_refs(object)
|
422
406
|
case object
|
423
407
|
when CFArray
|
@@ -443,140 +427,115 @@ module CFPropertyList
|
|
443
427
|
|
444
428
|
def Binary.ascii_string?(str)
|
445
429
|
if str.respond_to?(:ascii_only?)
|
446
|
-
|
430
|
+
str.ascii_only?
|
447
431
|
else
|
448
|
-
|
432
|
+
str !~ /[\x80-\xFF]/mn
|
449
433
|
end
|
450
434
|
end
|
451
|
-
|
435
|
+
|
452
436
|
# Uniques and transforms a string value to binary format and adds it to the object table
|
453
437
|
def string_to_binary(val)
|
454
|
-
|
455
|
-
|
456
|
-
unless(@unique_table.has_key?(val)) then
|
457
|
-
saved_object_count = @written_object_count
|
458
|
-
@written_object_count += 1
|
459
|
-
|
460
|
-
@unique_table[val] = saved_object_count
|
461
|
-
utf16 = !Binary.ascii_string?(val)
|
462
|
-
|
463
|
-
utf8_strlen = 0
|
464
|
-
if(utf16) then
|
438
|
+
@unique_table[val] ||= begin
|
439
|
+
if !Binary.ascii_string?(val)
|
465
440
|
utf8_strlen = Binary.charset_strlen(val, "UTF-8")
|
466
441
|
val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
|
467
442
|
bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
|
468
443
|
|
469
444
|
val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
|
470
|
-
@object_table[
|
445
|
+
@object_table[@written_object_count] = bdata << val
|
471
446
|
else
|
472
447
|
utf8_strlen = val.bytesize
|
473
448
|
bdata = Binary.type_bytes(0b0101,val.bytesize)
|
474
|
-
@object_table[
|
449
|
+
@object_table[@written_object_count] = bdata << val
|
475
450
|
end
|
476
|
-
|
477
|
-
|
451
|
+
@written_object_count += 1
|
452
|
+
@written_object_count - 1
|
478
453
|
end
|
479
|
-
|
480
|
-
return saved_object_count
|
481
454
|
end
|
482
455
|
|
483
456
|
# Codes an integer to binary format
|
484
457
|
def int_to_binary(value)
|
485
458
|
nbytes = 0
|
486
|
-
nbytes = 1
|
459
|
+
nbytes = 1 if value > 0xFF # 1 byte integer
|
487
460
|
nbytes += 1 if value > 0xFFFF # 4 byte integer
|
488
461
|
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
|
489
|
-
nbytes = 3
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
462
|
+
nbytes = 3 if value < 0 # 8 byte integer, since signed
|
463
|
+
|
464
|
+
Binary.type_bytes(0b0001, nbytes) <<
|
465
|
+
if nbytes < 3
|
466
|
+
[value].pack(
|
467
|
+
if nbytes == 0 then "C"
|
468
|
+
elsif nbytes == 1 then "n"
|
469
|
+
else "N"
|
470
|
+
end
|
471
|
+
)
|
472
|
+
else
|
473
|
+
# 64 bit signed integer; we need the higher and the lower 32 bit of the value
|
474
|
+
high_word = value >> 32
|
475
|
+
low_word = value & 0xFFFFFFFF
|
476
|
+
[high_word,low_word].pack("NN")
|
501
477
|
end
|
502
|
-
|
503
|
-
buff = [value].pack(fmt)
|
504
|
-
else
|
505
|
-
# 64 bit signed integer; we need the higher and the lower 32 bit of the value
|
506
|
-
high_word = value >> 32
|
507
|
-
low_word = value & 0xFFFFFFFF
|
508
|
-
buff = [high_word,low_word].pack("NN")
|
509
|
-
end
|
510
|
-
|
511
|
-
return bdata + buff
|
512
478
|
end
|
513
479
|
|
514
480
|
# Codes a real value to binary format
|
515
481
|
def real_to_binary(val)
|
516
|
-
|
517
|
-
buff = [val].pack("d")
|
518
|
-
return bdata + buff.reverse
|
482
|
+
Binary.type_bytes(0b0010,3) << [val].pack("d").reverse
|
519
483
|
end
|
520
484
|
|
521
485
|
# Converts a numeric value to binary and adds it to the object table
|
522
486
|
def num_to_binary(value)
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
else
|
530
|
-
val = real_to_binary(value.value)
|
531
|
-
end
|
487
|
+
@object_table[@written_object_count] =
|
488
|
+
if value.is_a?(CFInteger)
|
489
|
+
int_to_binary(value.value)
|
490
|
+
else
|
491
|
+
real_to_binary(value.value)
|
492
|
+
end
|
532
493
|
|
533
|
-
@
|
534
|
-
|
494
|
+
@written_object_count += 1
|
495
|
+
@written_object_count - 1
|
535
496
|
end
|
536
497
|
|
537
498
|
# Convert date value (apple format) to binary and adds it to the object table
|
538
499
|
def date_to_binary(val)
|
539
|
-
saved_object_count = @written_object_count
|
540
|
-
@written_object_count += 1
|
541
|
-
|
542
500
|
val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
|
543
501
|
|
544
|
-
|
545
|
-
|
502
|
+
@object_table[@written_object_count] =
|
503
|
+
(Binary.type_bytes(0b0011, 3) << [val].pack("d").reverse)
|
546
504
|
|
547
|
-
|
505
|
+
@written_object_count += 1
|
506
|
+
@written_object_count - 1
|
548
507
|
end
|
549
508
|
|
550
509
|
# Convert a bool value to binary and add it to the object table
|
551
510
|
def bool_to_binary(val)
|
552
|
-
saved_object_count = @written_object_count
|
553
|
-
@written_object_count += 1
|
554
511
|
|
555
|
-
@object_table[
|
556
|
-
|
512
|
+
@object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
|
513
|
+
@written_object_count += 1
|
514
|
+
@written_object_count - 1
|
557
515
|
end
|
558
516
|
|
559
517
|
# Convert data value to binary format and add it to the object table
|
560
518
|
def data_to_binary(val)
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
bdata = Binary.type_bytes(0b0100, val.bytesize)
|
565
|
-
@object_table[saved_object_count] = bdata + val
|
519
|
+
@object_table[@written_object_count] =
|
520
|
+
(Binary.type_bytes(0b0100, val.bytesize) << val)
|
566
521
|
|
567
|
-
|
522
|
+
@written_object_count += 1
|
523
|
+
@written_object_count - 1
|
568
524
|
end
|
569
525
|
|
570
526
|
# Convert array to binary format and add it to the object table
|
571
527
|
def array_to_binary(val)
|
572
528
|
saved_object_count = @written_object_count
|
573
529
|
@written_object_count += 1
|
530
|
+
#@object_refs += val.value.size
|
574
531
|
|
575
|
-
|
576
|
-
|
532
|
+
values = val.value.map { |v| v.to_binary(self) }
|
533
|
+
bdata = Binary.type_bytes(0b1010, val.value.size) <<
|
534
|
+
Binary.pack_int_array_with_size(object_ref_size(@object_refs),
|
535
|
+
values)
|
577
536
|
|
578
537
|
@object_table[saved_object_count] = bdata
|
579
|
-
|
538
|
+
saved_object_count
|
580
539
|
end
|
581
540
|
|
582
541
|
# Convert dictionary to binary format and add it to the object table
|
@@ -584,15 +543,44 @@ module CFPropertyList
|
|
584
543
|
saved_object_count = @written_object_count
|
585
544
|
@written_object_count += 1
|
586
545
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
546
|
+
#@object_refs += val.value.keys.size * 2
|
547
|
+
|
548
|
+
keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
|
549
|
+
keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
|
550
|
+
|
551
|
+
bdata = Binary.type_bytes(0b1101,val.value.size) <<
|
552
|
+
Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
|
592
553
|
|
593
554
|
@object_table[saved_object_count] = bdata
|
594
555
|
return saved_object_count
|
595
556
|
end
|
557
|
+
|
558
|
+
=begin It is difficult to reap benefits from an Enumerator
|
559
|
+
# like an array, but we don't know length ahead of time
|
560
|
+
def enum_to_binary(val)
|
561
|
+
saved_object_count = @written_object_count
|
562
|
+
@written_object_count += 1
|
563
|
+
|
564
|
+
size = 0
|
565
|
+
# This destroys our low-memory stream.
|
566
|
+
# However, testing shows that it is faster
|
567
|
+
# Probably because packing this single Array (in pack_int_array_with_size)
|
568
|
+
# is faster than packing each individual array member separately
|
569
|
+
# i.e. to be faster it needs an Enumerator#pack
|
570
|
+
binary = val.value.map do |v|
|
571
|
+
size += 1;
|
572
|
+
v.to_binary(self)
|
573
|
+
end
|
574
|
+
@object_refs += size
|
575
|
+
|
576
|
+
bdata = Binary.type_bytes(0b1010, size) <<
|
577
|
+
Binary.pack_int_array_with_size(object_ref_size(@object_refs), binary)
|
578
|
+
|
579
|
+
@object_table[saved_object_count] = bdata
|
580
|
+
saved_object_count
|
581
|
+
end
|
582
|
+
=end
|
583
|
+
|
596
584
|
end
|
597
585
|
end
|
598
586
|
|
data/lib/rbCFPropertyList.rb
CHANGED
@@ -101,41 +101,55 @@ module CFPropertyList
|
|
101
101
|
#
|
102
102
|
# cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash)
|
103
103
|
def guess(object, options = {})
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
return CFDate.new(object)
|
116
|
-
elsif(object.is_a?(Array)) then
|
104
|
+
case object
|
105
|
+
when Fixnum, Integer then CFInteger.new(object)
|
106
|
+
when Float then CFReal.new(object)
|
107
|
+
when TrueClass, FalseClass then CFBoolean.new(object)
|
108
|
+
|
109
|
+
when String
|
110
|
+
object.blob? ? CFData.new(object, CFData::DATA_RAW) : CFString.new(object)
|
111
|
+
|
112
|
+
when Time, DateTime, Date then CFDate.new(object)
|
113
|
+
|
114
|
+
when Array, Enumerator
|
117
115
|
ary = Array.new
|
118
|
-
object.each do
|
119
|
-
|o|
|
116
|
+
object.each do |o|
|
120
117
|
ary.push CFPropertyList.guess(o, options)
|
121
118
|
end
|
119
|
+
CFArray.new(ary)
|
122
120
|
|
123
|
-
|
124
|
-
elsif(object.is_a?(Hash)) then
|
121
|
+
when Hash
|
125
122
|
hsh = Hash.new
|
126
|
-
object.each_pair do
|
127
|
-
|k,v|
|
123
|
+
object.each_pair do |k,v|
|
128
124
|
k = k.to_s if k.is_a?(Symbol)
|
129
125
|
hsh[k] = CFPropertyList.guess(v, options)
|
130
126
|
end
|
127
|
+
CFDictionary.new(hsh)
|
128
|
+
|
129
|
+
=begin
|
130
|
+
when Enumerator
|
131
|
+
CFEnumerator.new(
|
132
|
+
Enumerator.new do |yielder|
|
133
|
+
object.each do |o|
|
134
|
+
yielder << CFPropertyList.guess(o, options)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
)
|
138
|
+
=end
|
131
139
|
|
132
|
-
return CFDictionary.new(hsh)
|
133
|
-
elsif options[:converter_method] and object.respond_to?(options[:converter_method]) then
|
134
|
-
return CFPropertyList.guess(object.send(options[:converter_method]),options)
|
135
|
-
elsif options[:convert_unknown_to_string] then
|
136
|
-
return CFString.new(object.to_s)
|
137
140
|
else
|
138
|
-
|
141
|
+
case
|
142
|
+
when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
|
143
|
+
CFReal.new(object)
|
144
|
+
when object.respond_to?(:read)
|
145
|
+
CFData.new(object.read(), CFData::DATA_RAW)
|
146
|
+
when options[:converter_method] && object.respond_to?(options[:converter_method])
|
147
|
+
CFPropertyList.guess(object.send(options[:converter_method]),options)
|
148
|
+
when options[:convert_unknown_to_string]
|
149
|
+
CFString.new(object.to_s)
|
150
|
+
else
|
151
|
+
raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
|
152
|
+
end
|
139
153
|
end
|
140
154
|
end
|
141
155
|
|
@@ -337,6 +351,17 @@ class Array
|
|
337
351
|
end
|
338
352
|
end
|
339
353
|
|
354
|
+
class Enumerator
|
355
|
+
# convert an array to plist format
|
356
|
+
def to_plist(options={})
|
357
|
+
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
|
358
|
+
|
359
|
+
plist = CFPropertyList::List.new
|
360
|
+
plist.value = CFPropertyList.guess(self, options)
|
361
|
+
plist.to_str(options[:plist_format])
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
340
365
|
class Hash
|
341
366
|
# convert a hash to plist format
|
342
367
|
def to_plist(options={})
|
data/lib/rbCFTypes.rb
CHANGED
@@ -16,19 +16,13 @@ module CFPropertyList
|
|
16
16
|
# value of the type
|
17
17
|
attr_accessor :value
|
18
18
|
|
19
|
-
|
20
|
-
# set internal value to parameter value by default
|
21
19
|
def initialize(value=nil)
|
22
20
|
@value = value
|
23
21
|
end
|
24
22
|
|
25
|
-
|
26
|
-
def to_xml
|
27
|
-
end
|
23
|
+
def to_xml; end
|
28
24
|
|
29
|
-
|
30
|
-
def to_binary(bplist)
|
31
|
-
end
|
25
|
+
def to_binary(bplist) end
|
32
26
|
end
|
33
27
|
|
34
28
|
# This class holds string values, both, UTF-8 and UTF-16BE
|
@@ -38,12 +32,12 @@ module CFPropertyList
|
|
38
32
|
def to_xml
|
39
33
|
n = LibXML::XML::Node.new('string')
|
40
34
|
n << LibXML::XML::Node.new_text(@value) unless @value.nil?
|
41
|
-
|
35
|
+
n
|
42
36
|
end
|
43
37
|
|
44
38
|
# convert to binary
|
45
39
|
def to_binary(bplist)
|
46
|
-
|
40
|
+
bplist.string_to_binary(@value);
|
47
41
|
end
|
48
42
|
end
|
49
43
|
|
@@ -56,7 +50,7 @@ module CFPropertyList
|
|
56
50
|
|
57
51
|
# convert to binary
|
58
52
|
def to_binary(bplist)
|
59
|
-
|
53
|
+
bplist.num_to_binary(self)
|
60
54
|
end
|
61
55
|
end
|
62
56
|
|
@@ -69,7 +63,7 @@ module CFPropertyList
|
|
69
63
|
|
70
64
|
# convert to binary
|
71
65
|
def to_binary(bplist)
|
72
|
-
|
66
|
+
bplist.num_to_binary(self)
|
73
67
|
end
|
74
68
|
end
|
75
69
|
|
@@ -100,6 +94,10 @@ module CFPropertyList
|
|
100
94
|
def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
|
101
95
|
if(value.is_a?(Time) || value.nil?) then
|
102
96
|
@value = value.nil? ? Time.now : value
|
97
|
+
elsif value.instance_of? Date
|
98
|
+
@value = Time.utc(value.year, value.month, value.day, 0, 0, 0)
|
99
|
+
elsif value.instance_of? DateTime
|
100
|
+
@value = value.to_time.utc
|
103
101
|
else
|
104
102
|
set_value(value,format)
|
105
103
|
end
|
@@ -117,20 +115,20 @@ module CFPropertyList
|
|
117
115
|
# get timestamp, either UNIX or Apple timestamp
|
118
116
|
def get_value(format=CFDate::TIMESTAMP_UNIX)
|
119
117
|
if(format == CFDate::TIMESTAMP_UNIX) then
|
120
|
-
|
118
|
+
@value.to_i
|
121
119
|
else
|
122
|
-
|
120
|
+
@value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
126
124
|
# convert to XML
|
127
125
|
def to_xml
|
128
|
-
|
126
|
+
LibXML::XML::Node.new('date') << LibXML::XML::Node.new_text(CFDate::date_string(@value))
|
129
127
|
end
|
130
128
|
|
131
129
|
# convert to binary
|
132
130
|
def to_binary(bplist)
|
133
|
-
|
131
|
+
bplist.date_to_binary(@value)
|
134
132
|
end
|
135
133
|
end
|
136
134
|
|
@@ -138,12 +136,12 @@ module CFPropertyList
|
|
138
136
|
class CFBoolean < CFType
|
139
137
|
# convert to XML
|
140
138
|
def to_xml
|
141
|
-
|
139
|
+
LibXML::XML::Node.new(@value ? 'true' : 'false')
|
142
140
|
end
|
143
141
|
|
144
142
|
# convert to binary
|
145
143
|
def to_binary(bplist)
|
146
|
-
|
144
|
+
bplist.bool_to_binary(@value);
|
147
145
|
end
|
148
146
|
end
|
149
147
|
|
@@ -156,7 +154,7 @@ module CFPropertyList
|
|
156
154
|
|
157
155
|
# set value to defined state, either base64 encoded or raw
|
158
156
|
def initialize(value=nil,format=DATA_BASE64)
|
159
|
-
if(format == DATA_RAW)
|
157
|
+
if(format == DATA_RAW)
|
160
158
|
@raw_value = value
|
161
159
|
@raw_value.blob = true
|
162
160
|
else
|
@@ -178,12 +176,12 @@ module CFPropertyList
|
|
178
176
|
|
179
177
|
# convert to XML
|
180
178
|
def to_xml
|
181
|
-
|
179
|
+
LibXML::XML::Node.new('data') << LibXML::XML::Node.new_text(encoded_value())
|
182
180
|
end
|
183
181
|
|
184
182
|
# convert to binary
|
185
183
|
def to_binary(bplist)
|
186
|
-
|
184
|
+
bplist.data_to_binary(decoded_value())
|
187
185
|
end
|
188
186
|
end
|
189
187
|
|
@@ -197,20 +195,26 @@ module CFPropertyList
|
|
197
195
|
# convert to XML
|
198
196
|
def to_xml
|
199
197
|
n = LibXML::XML::Node.new('array')
|
200
|
-
@value.each do
|
201
|
-
|v|
|
198
|
+
@value.each do |v|
|
202
199
|
n << v.to_xml
|
203
200
|
end
|
204
|
-
|
205
|
-
return n
|
201
|
+
n
|
206
202
|
end
|
207
203
|
|
208
204
|
# convert to binary
|
209
205
|
def to_binary(bplist)
|
210
|
-
|
206
|
+
bplist.array_to_binary(self)
|
211
207
|
end
|
212
208
|
end
|
213
209
|
|
210
|
+
=begin
|
211
|
+
class CFEnumerator < CFArray
|
212
|
+
def to_binary(bplist)
|
213
|
+
bplist.enum_to_binary(self)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
=end
|
217
|
+
|
214
218
|
# this class contains a hash of values
|
215
219
|
class CFDictionary < CFType
|
216
220
|
# Create new CFDictonary type.
|
@@ -221,19 +225,17 @@ module CFPropertyList
|
|
221
225
|
# convert to XML
|
222
226
|
def to_xml
|
223
227
|
n = LibXML::XML::Node.new('dict')
|
224
|
-
@value.each_pair do
|
225
|
-
|key,value|
|
228
|
+
@value.each_pair do |key,value|
|
226
229
|
k = LibXML::XML::Node.new('key') << LibXML::XML::Node.new_text(key)
|
227
230
|
n << k
|
228
231
|
n << value.to_xml
|
229
232
|
end
|
230
|
-
|
231
|
-
return n
|
233
|
+
n
|
232
234
|
end
|
233
235
|
|
234
236
|
# convert to binary
|
235
237
|
def to_binary(bplist)
|
236
|
-
|
238
|
+
bplist.dict_to_binary(self)
|
237
239
|
end
|
238
240
|
end
|
239
241
|
end
|
data/lib/rbXMLCFPropertyList.rb
CHANGED
@@ -33,8 +33,7 @@ module CFPropertyList
|
|
33
33
|
str = doc.to_s(:indent => opts[:formatted])
|
34
34
|
str1 = String.new
|
35
35
|
first = false
|
36
|
-
str.each_line do
|
37
|
-
|line|
|
36
|
+
str.each_line do |line|
|
38
37
|
str1 << line
|
39
38
|
unless(first) then
|
40
39
|
str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
|
@@ -56,7 +55,7 @@ module CFPropertyList
|
|
56
55
|
else
|
57
56
|
n.content
|
58
57
|
end
|
59
|
-
|
58
|
+
|
60
59
|
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
|
61
60
|
content
|
62
61
|
end
|
@@ -71,8 +70,7 @@ module CFPropertyList
|
|
71
70
|
key = nil
|
72
71
|
|
73
72
|
if node.children? then
|
74
|
-
node.children.each do
|
75
|
-
|n|
|
73
|
+
node.children.each do |n|
|
76
74
|
next if n.text? # avoid a bug of libxml
|
77
75
|
|
78
76
|
if n.name == "key" then
|
@@ -91,8 +89,7 @@ module CFPropertyList
|
|
91
89
|
ary = Array.new
|
92
90
|
|
93
91
|
if node.children? then
|
94
|
-
node.children.each do
|
95
|
-
|n|
|
92
|
+
node.children.each do |n|
|
96
93
|
ary.push import_xml(n)
|
97
94
|
end
|
98
95
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 2
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 2.0.
|
8
|
+
- 17
|
9
|
+
version: 2.0.17
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Christian Kruse
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-07-02 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -57,11 +57,11 @@ extra_rdoc_files:
|
|
57
57
|
- README
|
58
58
|
files:
|
59
59
|
- lib/cfpropertylist.rb
|
60
|
+
- lib/rbCFTypes.rb
|
61
|
+
- lib/rbXMLCFPropertyList.rb
|
60
62
|
- lib/rbBinaryCFPropertyList.rb
|
61
63
|
- lib/rbCFPlistError.rb
|
62
64
|
- lib/rbCFPropertyList.rb
|
63
|
-
- lib/rbCFTypes.rb
|
64
|
-
- lib/rbXMLCFPropertyList.rb
|
65
65
|
- README
|
66
66
|
has_rdoc: true
|
67
67
|
homepage: http://github.com/ckruse/CFPropertyList
|