appsendr 0.0.6 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -16
- data/appsendr.gemspec +18 -43
- data/bin/appsendr +103 -8
- data/lib/appsendr.rb +6 -26
- data/lib/appsendr/app.rb +60 -0
- data/lib/appsendr/version.rb +3 -0
- data/test/units/app_test.rb +9 -0
- metadata +51 -168
- data/Manifest +0 -25
- data/README.rdoc +0 -49
- data/lib/appsendr/binary_plist.rb +0 -494
- data/lib/appsendr/client.rb +0 -258
- data/lib/appsendr/command.rb +0 -81
- data/lib/appsendr/commands/app.rb +0 -241
- data/lib/appsendr/commands/auth.rb +0 -139
- data/lib/appsendr/commands/base.rb +0 -78
- data/lib/appsendr/commands/build.rb +0 -320
- data/lib/appsendr/commands/collaborators.rb +0 -52
- data/lib/appsendr/commands/common.rb +0 -36
- data/lib/appsendr/commands/deploy.rb +0 -56
- data/lib/appsendr/commands/groups.rb +0 -63
- data/lib/appsendr/commands/help.rb +0 -103
- data/lib/appsendr/commands/portal.rb +0 -375
- data/lib/appsendr/commands/testers.rb +0 -118
- data/lib/appsendr/commands/version.rb +0 -7
- data/lib/appsendr/constants.rb +0 -5
- data/lib/appsendr/helpers.rb +0 -144
- data/lib/appsendr/progressbar.rb +0 -236
- data/portal.rb +0 -197
- data/terms +0 -0
data/Manifest
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
Manifest
|
2
|
-
README.rdoc
|
3
|
-
Rakefile
|
4
|
-
bin/appsendr
|
5
|
-
lib/appsendr.rb
|
6
|
-
lib/appsendr/binary_plist.rb
|
7
|
-
lib/appsendr/client.rb
|
8
|
-
lib/appsendr/command.rb
|
9
|
-
lib/appsendr/commands/app.rb
|
10
|
-
lib/appsendr/commands/auth.rb
|
11
|
-
lib/appsendr/commands/base.rb
|
12
|
-
lib/appsendr/commands/build.rb
|
13
|
-
lib/appsendr/commands/collaborators.rb
|
14
|
-
lib/appsendr/commands/common.rb
|
15
|
-
lib/appsendr/commands/deploy.rb
|
16
|
-
lib/appsendr/commands/groups.rb
|
17
|
-
lib/appsendr/commands/help.rb
|
18
|
-
lib/appsendr/commands/portal.rb
|
19
|
-
lib/appsendr/commands/testers.rb
|
20
|
-
lib/appsendr/commands/version.rb
|
21
|
-
lib/appsendr/constants.rb
|
22
|
-
lib/appsendr/helpers.rb
|
23
|
-
lib/appsendr/progressbar.rb
|
24
|
-
portal.rb
|
25
|
-
terms
|
data/README.rdoc
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
# == Synopsis
|
2
|
-
# Ruby command-line app that interacts with Xcode and the AppSendr webservice
|
3
|
-
#
|
4
|
-
# == Examples
|
5
|
-
# Calling this in a Xcode project directory will build the project with an active configuration of AdHoc
|
6
|
-
# appsendr build AdHoc
|
7
|
-
#
|
8
|
-
# Other examples:
|
9
|
-
# appsendr deploy AdHoc
|
10
|
-
#
|
11
|
-
# == Usage
|
12
|
-
# appsendr [options]
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# == Author
|
16
|
-
# Nolan Brown
|
17
|
-
#
|
18
|
-
# == Copyright
|
19
|
-
# Copyright (c) 2010 Nolan Brown. Licensed under the MIT License:
|
20
|
-
# http://www.opensource.org/licenses/mit-license.php
|
21
|
-
|
22
|
-
|
23
|
-
=== General Commands
|
24
|
-
|
25
|
-
help # show this usage
|
26
|
-
version # show the gem version
|
27
|
-
|
28
|
-
list # list your apps
|
29
|
-
link # link your app with an exsiting one in appsendr
|
30
|
-
create <name> # create a new app
|
31
|
-
|
32
|
-
url # get the latest version install url. pass --copy to copy to the clipboard
|
33
|
-
|
34
|
-
build <active configuration> # build your xcode project and deploy
|
35
|
-
build:clean # clean your xcode project
|
36
|
-
|
37
|
-
deploy <active configuration> # deploy the current build. pass --notify to send notification to all testers
|
38
|
-
|
39
|
-
testers # list testers
|
40
|
-
testers:add <email> <name> # add a tester
|
41
|
-
testers:remove <email> # remove a tester
|
42
|
-
testers:clear # remove all testers
|
43
|
-
testers:notify <group> # notify testers about the latest build. group is optional.
|
44
|
-
|
45
|
-
groups # list groups
|
46
|
-
|
47
|
-
collaborators # list collaborators
|
48
|
-
collaborators:add <email> # add collaborators
|
49
|
-
collaborators:remove <email> # add collaborators
|
@@ -1,494 +0,0 @@
|
|
1
|
-
require "date"
|
2
|
-
require "nkf"
|
3
|
-
require "set"
|
4
|
-
require "stringio"
|
5
|
-
|
6
|
-
# Binary Plist implementation is from @schlueter
|
7
|
-
# https://github.com/schlueter/Ipa-Reader/blob/master/lib/ipa_reader/plist_binary.rb
|
8
|
-
#
|
9
|
-
|
10
|
-
module AppSendr
|
11
|
-
module Plist
|
12
|
-
module Binary
|
13
|
-
# Encodes +obj+ as a binary property list. If +obj+ is an Array, Hash, or
|
14
|
-
# Set, the property list includes its contents.
|
15
|
-
def self.binary_plist(obj)
|
16
|
-
encoded_objs = flatten_collection(obj)
|
17
|
-
ref_byte_size = min_byte_size(encoded_objs.length - 1)
|
18
|
-
encoded_objs.collect! {|o| binary_plist_obj(o, ref_byte_size)}
|
19
|
-
# Write header and encoded objects.
|
20
|
-
plist = "bplist00" + encoded_objs.join
|
21
|
-
# Write offset table.
|
22
|
-
offset_table_addr = plist.length
|
23
|
-
offset = 8
|
24
|
-
offset_table = []
|
25
|
-
encoded_objs.each do |o|
|
26
|
-
offset_table << offset
|
27
|
-
offset += o.length
|
28
|
-
end
|
29
|
-
offset_byte_size = min_byte_size(offset)
|
30
|
-
offset_table.each do |offset|
|
31
|
-
plist += pack_int(offset, offset_byte_size)
|
32
|
-
end
|
33
|
-
# Write trailer.
|
34
|
-
plist += "\0\0\0\0\0\0" # Six unused bytes
|
35
|
-
plist += [
|
36
|
-
offset_byte_size,
|
37
|
-
ref_byte_size,
|
38
|
-
encoded_objs.length >> 32, encoded_objs.length & 0xffffffff,
|
39
|
-
0, 0, # Index of root object
|
40
|
-
offset_table_addr >> 32, offset_table_addr & 0xffffffff
|
41
|
-
].pack("CCNNNNNN")
|
42
|
-
plist
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.decode_binary_plist(plist)
|
46
|
-
# Check header.
|
47
|
-
unless plist[0, 6] == "bplist"
|
48
|
-
raise ArgumentError, "argument is not a binary property list"
|
49
|
-
end
|
50
|
-
version = plist[6, 2]
|
51
|
-
unless version == "00"
|
52
|
-
raise ArgumentError,
|
53
|
-
"don't know how to decode format version #{version}"
|
54
|
-
end
|
55
|
-
# Read trailer.
|
56
|
-
trailer = plist[-26, 26].unpack("CCNNNNNN")
|
57
|
-
offset_byte_size = trailer[0]
|
58
|
-
ref_byte_size = trailer[1]
|
59
|
-
encoded_objs_length = combine_ints(32, trailer[2], trailer[3])
|
60
|
-
root_index = combine_ints(32, trailer[4], trailer[5])
|
61
|
-
offset_table_addr = combine_ints(32, trailer[6], trailer[7])
|
62
|
-
# Decode objects.
|
63
|
-
root_offset = offset_for_index(plist, offset_table_addr,
|
64
|
-
offset_byte_size, root_index)
|
65
|
-
root_obj = decode_binary_plist_obj(plist, root_offset, ref_byte_size)
|
66
|
-
unflatten_collection(root_obj, [root_obj], plist, offset_table_addr,
|
67
|
-
offset_byte_size, ref_byte_size)
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
# These marker bytes are prefixed to objects in a binary property list to
|
73
|
-
# indicate the type of the object.
|
74
|
-
CFBinaryPlistMarkerNull = 0x00 # :nodoc:
|
75
|
-
CFBinaryPlistMarkerFalse = 0x08 # :nodoc:
|
76
|
-
CFBinaryPlistMarkerTrue = 0x09 # :nodoc:
|
77
|
-
CFBinaryPlistMarkerFill = 0x0F # :nodoc:
|
78
|
-
CFBinaryPlistMarkerInt = 0x10 # :nodoc:
|
79
|
-
CFBinaryPlistMarkerReal = 0x20 # :nodoc:
|
80
|
-
CFBinaryPlistMarkerDate = 0x33 # :nodoc:
|
81
|
-
CFBinaryPlistMarkerData = 0x40 # :nodoc:
|
82
|
-
CFBinaryPlistMarkerASCIIString = 0x50 # :nodoc:
|
83
|
-
CFBinaryPlistMarkerUnicode16String = 0x60 # :nodoc:
|
84
|
-
CFBinaryPlistMarkerUID = 0x80 # :nodoc:
|
85
|
-
CFBinaryPlistMarkerArray = 0xA0 # :nodoc:
|
86
|
-
CFBinaryPlistMarkerSet = 0xC0 # :nodoc:
|
87
|
-
CFBinaryPlistMarkerDict = 0xD0 # :nodoc:
|
88
|
-
|
89
|
-
# POSIX uses a reference time of 1970-01-01T00:00:00Z; Cocoa's reference
|
90
|
-
# time is in 2001. This interval is for converting between the two.
|
91
|
-
NSTimeIntervalSince1970 = 978307200.0 # :nodoc:
|
92
|
-
|
93
|
-
# Takes an object (nominally a collection, like an Array, Set, or Hash, but
|
94
|
-
# any object is acceptable) and flattens it into a one-dimensional array.
|
95
|
-
# Non-collection objects appear in the array as-is, but the contents of
|
96
|
-
# Arrays, Sets, and Hashes are modified like so: (1) The contents of the
|
97
|
-
# collection are added, one-by-one, to the one-dimensional array. (2) The
|
98
|
-
# collection itself is modified so that it contains indexes pointing to the
|
99
|
-
# objects in the one-dimensional array. Here's an example with an Array:
|
100
|
-
#
|
101
|
-
# ary = [:a, :b, :c]
|
102
|
-
# flatten_collection(ary) # => [[1, 2, 3], :a, :b, :c]
|
103
|
-
#
|
104
|
-
# In the case of a Hash, keys and values are both appended to the one-
|
105
|
-
# dimensional array and then replaced with indexes.
|
106
|
-
#
|
107
|
-
# hsh = {:a => "blue", :b => "purple", :c => "green"}
|
108
|
-
# flatten_collection(hsh)
|
109
|
-
# # => [{1 => 2, 3 => 4, 5 => 6}, :a, "blue", :b, "purple", :c, "green"]
|
110
|
-
#
|
111
|
-
# An object will never be added to the one-dimensional array twice. If a
|
112
|
-
# collection refers to an object more than once, the object will be added
|
113
|
-
# to the one-dimensional array only once.
|
114
|
-
#
|
115
|
-
# ary = [:a, :a, :a]
|
116
|
-
# flatten_collection(ary) # => [[1, 1, 1], :a]
|
117
|
-
#
|
118
|
-
# The +obj_list+ and +id_refs+ parameters are private; they're used for
|
119
|
-
# descending into sub-collections recursively.
|
120
|
-
def self.flatten_collection(collection, obj_list = [], id_refs = {})
|
121
|
-
case collection
|
122
|
-
when Array, Set
|
123
|
-
if id_refs[collection.object_id]
|
124
|
-
return obj_list[id_refs[collection.object_id]]
|
125
|
-
end
|
126
|
-
obj_refs = collection.class.new
|
127
|
-
id_refs[collection.object_id] = obj_list.length
|
128
|
-
obj_list << obj_refs
|
129
|
-
collection.each do |obj|
|
130
|
-
flatten_collection(obj, obj_list, id_refs)
|
131
|
-
obj_refs << id_refs[obj.object_id]
|
132
|
-
end
|
133
|
-
return obj_list
|
134
|
-
when Hash
|
135
|
-
if id_refs[collection.object_id]
|
136
|
-
return obj_list[id_refs[collection.object_id]]
|
137
|
-
end
|
138
|
-
obj_refs = {}
|
139
|
-
id_refs[collection.object_id] = obj_list.length
|
140
|
-
obj_list << obj_refs
|
141
|
-
collection.each do |key, value|
|
142
|
-
key = key.to_s if key.is_a?(Symbol)
|
143
|
-
flatten_collection(key, obj_list, id_refs)
|
144
|
-
flatten_collection(value, obj_list, id_refs)
|
145
|
-
obj_refs[id_refs[key.object_id]] = id_refs[value.object_id]
|
146
|
-
end
|
147
|
-
return obj_list
|
148
|
-
else
|
149
|
-
unless id_refs[collection.object_id]
|
150
|
-
id_refs[collection.object_id] = obj_list.length
|
151
|
-
obj_list << collection
|
152
|
-
end
|
153
|
-
return obj_list
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def self.unflatten_collection(collection, obj_list, plist,
|
158
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
159
|
-
case collection
|
160
|
-
when Array, Set
|
161
|
-
collection.collect! do |index|
|
162
|
-
if obj = obj_list[index]
|
163
|
-
obj
|
164
|
-
else
|
165
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
166
|
-
index)
|
167
|
-
obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
168
|
-
obj_list[index] = obj
|
169
|
-
unflatten_collection(obj, obj_list, plist, offset_table_addr,
|
170
|
-
offset_byte_size, ref_byte_size)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
when Hash
|
174
|
-
hsh = {}
|
175
|
-
collection.each do |key, value|
|
176
|
-
unless key_obj = obj_list[key]
|
177
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
178
|
-
key)
|
179
|
-
key_obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
180
|
-
obj_list[key] = key_obj
|
181
|
-
key_obj = unflatten_collection(key_obj, obj_list, plist,
|
182
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
183
|
-
end
|
184
|
-
unless value_obj = obj_list[value]
|
185
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
186
|
-
value)
|
187
|
-
value_obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
188
|
-
obj_list[value] = value_obj
|
189
|
-
value_obj = unflatten_collection(value_obj, obj_list, plist,
|
190
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
191
|
-
end
|
192
|
-
hsh[key_obj] = value_obj
|
193
|
-
end
|
194
|
-
collection.replace(hsh)
|
195
|
-
end
|
196
|
-
return collection
|
197
|
-
end
|
198
|
-
|
199
|
-
# Returns a binary property list fragment that represents +obj+. The
|
200
|
-
# returned string is not a complete property list, just a fragment that
|
201
|
-
# describes +obj+, and is not useful without a header, offset table, and
|
202
|
-
# trailer.
|
203
|
-
#
|
204
|
-
# The following classes are recognized: String, Float, Integer, the Boolean
|
205
|
-
# classes, Time, IO, StringIO, Array, Set, and Hash. IO and StringIO
|
206
|
-
# objects are rewound, read, and the contents stored as data (i.e., Cocoa
|
207
|
-
# applications will decode them as NSData). All other classes are dumped
|
208
|
-
# with Marshal and stored as data.
|
209
|
-
#
|
210
|
-
# Note that subclasses of the supported classes will be encoded as though
|
211
|
-
# they were the supported superclass. Thus, a subclass of (for example)
|
212
|
-
# String will be encoded and decoded as a String, not as the subclass:
|
213
|
-
#
|
214
|
-
# class ExampleString < String
|
215
|
-
# ...
|
216
|
-
# end
|
217
|
-
#
|
218
|
-
# s = ExampleString.new("disquieting plantlike mystery")
|
219
|
-
# encoded_s = binary_plist_obj(s)
|
220
|
-
# decoded_s = decode_binary_plist_obj(encoded_s)
|
221
|
-
# puts decoded_s.class # => String
|
222
|
-
#
|
223
|
-
# +ref_byte_size+ is the number of bytes to use for storing references to
|
224
|
-
# other objects.
|
225
|
-
def self.binary_plist_obj(obj, ref_byte_size = 4)
|
226
|
-
case obj
|
227
|
-
when String
|
228
|
-
obj = obj.to_s if obj.is_a?(Symbol)
|
229
|
-
# This doesn't really work. NKF's guess method is really, really bad
|
230
|
-
# at discovering UTF8 when only a handful of characters are multi-byte.
|
231
|
-
encoding = NKF.guess2(obj)
|
232
|
-
if encoding == NKF::ASCII && obj =~ /[\x80-\xff]/
|
233
|
-
encoding = NKF::UTF8
|
234
|
-
end
|
235
|
-
if [NKF::ASCII, NKF::BINARY, NKF::UNKNOWN].include?(encoding)
|
236
|
-
result = (CFBinaryPlistMarkerASCIIString |
|
237
|
-
(obj.length < 15 ? obj.length : 0xf)).chr
|
238
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
239
|
-
result += obj
|
240
|
-
return result
|
241
|
-
else
|
242
|
-
# Convert to UTF8.
|
243
|
-
if encoding == NKF::UTF8
|
244
|
-
utf8 = obj
|
245
|
-
else
|
246
|
-
utf8 = NKF.nkf("-m0 -w", obj)
|
247
|
-
end
|
248
|
-
# Decode each character's UCS codepoint.
|
249
|
-
codepoints = []
|
250
|
-
i = 0
|
251
|
-
while i < utf8.length
|
252
|
-
byte = utf8[i]
|
253
|
-
if byte & 0xe0 == 0xc0
|
254
|
-
codepoints << ((byte & 0x1f) << 6) + (utf8[i+1] & 0x3f)
|
255
|
-
i += 1
|
256
|
-
elsif byte & 0xf0 == 0xe0
|
257
|
-
codepoints << ((byte & 0xf) << 12) + ((utf8[i+1] & 0x3f) << 6) +
|
258
|
-
(utf8[i+2] & 0x3f)
|
259
|
-
i += 2
|
260
|
-
elsif byte & 0xf8 == 0xf0
|
261
|
-
codepoints << ((byte & 0xe) << 18) + ((utf8[i+1] & 0x3f) << 12) +
|
262
|
-
((utf8[i+2] & 0x3f) << 6) + (utf8[i+3] & 0x3f)
|
263
|
-
i += 3
|
264
|
-
else
|
265
|
-
codepoints << byte
|
266
|
-
end
|
267
|
-
if codepoints.last > 0xffff
|
268
|
-
raise(ArgumentError, "codepoint too high - only the Basic Multilingual Plane can be encoded")
|
269
|
-
end
|
270
|
-
i += 1
|
271
|
-
end
|
272
|
-
# Return string of 16-bit codepoints.
|
273
|
-
data = codepoints.pack("n*")
|
274
|
-
result = (CFBinaryPlistMarkerUnicode16String |
|
275
|
-
(codepoints.length < 15 ? codepoints.length : 0xf)).chr
|
276
|
-
result += binary_plist_obj(codepoints.length) if codepoints.length >= 15
|
277
|
-
result += data
|
278
|
-
return result
|
279
|
-
end
|
280
|
-
when Float
|
281
|
-
return (CFBinaryPlistMarkerReal | 3).chr + [obj].pack("G")
|
282
|
-
when Integer
|
283
|
-
nbytes = min_byte_size(obj)
|
284
|
-
size_bits = { 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4 }[nbytes]
|
285
|
-
return (CFBinaryPlistMarkerInt | size_bits).chr + pack_int(obj, nbytes)
|
286
|
-
when TrueClass
|
287
|
-
return CFBinaryPlistMarkerTrue.chr
|
288
|
-
when FalseClass
|
289
|
-
return CFBinaryPlistMarkerFalse.chr
|
290
|
-
when Time
|
291
|
-
return CFBinaryPlistMarkerDate.chr +
|
292
|
-
[obj.to_f - NSTimeIntervalSince1970].pack("G")
|
293
|
-
when IO, StringIO
|
294
|
-
obj.rewind
|
295
|
-
return binary_plist_data(obj.read)
|
296
|
-
when Array
|
297
|
-
# Must be an array of object references as returned by flatten_collection.
|
298
|
-
result = (CFBinaryPlistMarkerArray | (obj.length < 15 ? obj.length : 0xf)).chr
|
299
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
300
|
-
result += obj.collect! { |i| pack_int(i, ref_byte_size) }.join
|
301
|
-
when Set
|
302
|
-
# Must be a set of object references as returned by flatten_collection.
|
303
|
-
result = (CFBinaryPlistMarkerSet | (obj.length < 15 ? obj.length : 0xf)).chr
|
304
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
305
|
-
result += obj.to_a.collect! { |i| pack_int(i, ref_byte_size) }.join
|
306
|
-
when Hash
|
307
|
-
# Must be a table of object references as returned by flatten_collection.
|
308
|
-
result = (CFBinaryPlistMarkerDict | (obj.length < 15 ? obj.length : 0xf)).chr
|
309
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
310
|
-
result += obj.keys.collect! { |i| pack_int(i, ref_byte_size) }.join
|
311
|
-
result += obj.values.collect! { |i| pack_int(i, ref_byte_size) }.join
|
312
|
-
else
|
313
|
-
return binary_plist_data(Marshal.dump(obj))
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
def self.decode_binary_plist_obj(plist, offset, ref_byte_size)
|
318
|
-
case plist[offset]
|
319
|
-
when CFBinaryPlistMarkerASCIIString..(CFBinaryPlistMarkerASCIIString | 0xf)
|
320
|
-
length, offset = decode_length(plist, offset)
|
321
|
-
return plist[offset, length]
|
322
|
-
when CFBinaryPlistMarkerUnicode16String..(CFBinaryPlistMarkerUnicode16String | 0xf)
|
323
|
-
length, offset = decode_length(plist, offset)
|
324
|
-
codepoints = plist[offset, length * 2].unpack("n*")
|
325
|
-
str = ""
|
326
|
-
codepoints.each do |codepoint|
|
327
|
-
if codepoint <= 0x7f
|
328
|
-
ch = ' '
|
329
|
-
ch[0] = to_i
|
330
|
-
elsif codepoint <= 0x7ff
|
331
|
-
ch = ' '
|
332
|
-
ch[0] = ((codepoint & 0x7c0) >> 6) | 0xc0
|
333
|
-
ch[1] = codepoint & 0x3f | 0x80
|
334
|
-
else
|
335
|
-
ch = ' '
|
336
|
-
ch[0] = ((codepoint & 0xf000) >> 12) | 0xe0
|
337
|
-
ch[1] = ((codepoint & 0xfc0) >> 6) | 0x80
|
338
|
-
ch[2] = codepoint & 0x3f | 0x80
|
339
|
-
end
|
340
|
-
str << ch
|
341
|
-
end
|
342
|
-
return str
|
343
|
-
when CFBinaryPlistMarkerReal | 3
|
344
|
-
return plist[offset+1, 8].unpack("G").first
|
345
|
-
when CFBinaryPlistMarkerInt..(CFBinaryPlistMarkerInt | 0xf)
|
346
|
-
num_bytes = 2 ** (plist[offset] & 0xf)
|
347
|
-
return unpack_int(plist[offset+1, num_bytes])
|
348
|
-
when CFBinaryPlistMarkerTrue
|
349
|
-
return true
|
350
|
-
when CFBinaryPlistMarkerFalse
|
351
|
-
return false
|
352
|
-
when CFBinaryPlistMarkerDate
|
353
|
-
secs = plist[offset+1, 8].unpack("G").first + NSTimeIntervalSince1970
|
354
|
-
return Time.at(secs)
|
355
|
-
when CFBinaryPlistMarkerData..(CFBinaryPlistMarkerData | 0xf)
|
356
|
-
length, offset = decode_length(plist, offset)
|
357
|
-
return StringIO.new(plist[offset, length])
|
358
|
-
when CFBinaryPlistMarkerArray..(CFBinaryPlistMarkerArray | 0xf)
|
359
|
-
ary = []
|
360
|
-
length, offset = decode_length(plist, offset)
|
361
|
-
length.times do
|
362
|
-
ary << unpack_int(plist[offset, ref_byte_size])
|
363
|
-
offset += ref_byte_size
|
364
|
-
end
|
365
|
-
return ary
|
366
|
-
when CFBinaryPlistMarkerDict..(CFBinaryPlistMarkerDict | 0xf)
|
367
|
-
hsh = {}
|
368
|
-
keys = []
|
369
|
-
length, offset = decode_length(plist, offset)
|
370
|
-
length.times do
|
371
|
-
keys << unpack_int(plist[offset, ref_byte_size])
|
372
|
-
offset += ref_byte_size
|
373
|
-
end
|
374
|
-
length.times do |i|
|
375
|
-
hsh[keys[i]] = unpack_int(plist[offset, ref_byte_size])
|
376
|
-
offset += ref_byte_size
|
377
|
-
end
|
378
|
-
return hsh
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|
382
|
-
# Returns a binary property list fragment that represents a data object
|
383
|
-
# with the contents of the string +data+. A Cocoa application would decode
|
384
|
-
# this fragment as NSData. Like binary_plist_obj, the value returned by
|
385
|
-
# this method is not usable by itself; it is only useful as part of a
|
386
|
-
# complete binary property list with a header, offset table, and trailer.
|
387
|
-
def self.binary_plist_data(data)
|
388
|
-
result = (CFBinaryPlistMarkerData |
|
389
|
-
(data.length < 15 ? data.length : 0xf)).chr
|
390
|
-
result += binary_plist_obj(data.length) if data.length > 15
|
391
|
-
result += data
|
392
|
-
return result
|
393
|
-
end
|
394
|
-
|
395
|
-
# Determines the minimum number of bytes that is a power of two and can
|
396
|
-
# represent the integer +i+. Raises a RangeError if the number of bytes
|
397
|
-
# exceeds 16. Note that the property list format considers integers of 1,
|
398
|
-
# 2, and 4 bytes to be unsigned, while 8- and 16-byte integers are signed;
|
399
|
-
# thus negative integers will always require at least 8 bytes of storage.
|
400
|
-
def self.min_byte_size(i)
|
401
|
-
if i < 0
|
402
|
-
i = i.abs - 1
|
403
|
-
else
|
404
|
-
if i <= 0xff
|
405
|
-
return 1
|
406
|
-
elsif i <= 0xffff
|
407
|
-
return 2
|
408
|
-
elsif i <= 0xffffffff
|
409
|
-
return 4
|
410
|
-
end
|
411
|
-
end
|
412
|
-
if i <= 0x7fffffffffffffff
|
413
|
-
return 8
|
414
|
-
elsif i <= 0x7fffffffffffffffffffffffffffffff
|
415
|
-
return 16
|
416
|
-
end
|
417
|
-
raise(RangeError, "integer too big - exceeds 128 bits")
|
418
|
-
end
|
419
|
-
|
420
|
-
# Packs an integer +i+ into its binary representation in the specified
|
421
|
-
# number of bytes. Byte order is big-endian. Negative integers cannot be
|
422
|
-
# stored in 1, 2, or 4 bytes.
|
423
|
-
def self.pack_int(i, num_bytes)
|
424
|
-
if i < 0 && num_bytes < 8
|
425
|
-
raise(ArgumentError, "negative integers require 8 or 16 bytes of storage")
|
426
|
-
end
|
427
|
-
case num_bytes
|
428
|
-
when 1
|
429
|
-
[i].pack("c")
|
430
|
-
when 2
|
431
|
-
[i].pack("n")
|
432
|
-
when 4
|
433
|
-
[i].pack("N")
|
434
|
-
when 8
|
435
|
-
[(i >> 32) & 0xffffffff, i & 0xffffffff].pack("NN")
|
436
|
-
when 16
|
437
|
-
[i >> 96, (i >> 64) & 0xffffffff, (i >> 32) & 0xffffffff,
|
438
|
-
i & 0xffffffff].pack("NNNN")
|
439
|
-
else
|
440
|
-
raise(ArgumentError, "num_bytes must be 1, 2, 4, 8, or 16")
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
def self.combine_ints(num_bits, *ints)
|
445
|
-
i = ints.pop
|
446
|
-
shift_bits = num_bits
|
447
|
-
ints.reverse.each do |i_part|
|
448
|
-
i += i_part << shift_bits
|
449
|
-
shift_bits += num_bits
|
450
|
-
end
|
451
|
-
return i
|
452
|
-
end
|
453
|
-
|
454
|
-
def self.offset_for_index(plist, table_addr, offset_byte_size, index)
|
455
|
-
offset = plist[table_addr + index * offset_byte_size, offset_byte_size]
|
456
|
-
unpack_int(offset)
|
457
|
-
end
|
458
|
-
|
459
|
-
def self.unpack_int(s)
|
460
|
-
case s.length
|
461
|
-
when 1
|
462
|
-
s.unpack("C").first
|
463
|
-
when 2
|
464
|
-
s.unpack("n").first
|
465
|
-
when 4
|
466
|
-
s.unpack("N").first
|
467
|
-
when 8
|
468
|
-
i = combine_ints(32, *(s.unpack("NN")))
|
469
|
-
(i & 0x80000000_00000000 == 0) ?
|
470
|
-
i :
|
471
|
-
-(i ^ 0xffffffff_ffffffff) - 1
|
472
|
-
when 16
|
473
|
-
i = combine_ints(32, *(s.unpack("NNNN")))
|
474
|
-
(i & 0x80000000_00000000_00000000_00000000 == 0) ?
|
475
|
-
i :
|
476
|
-
-(i ^ 0xffffffff_ffffffff_ffffffff_ffffffff) - 1
|
477
|
-
else
|
478
|
-
raise(ArgumentError, "length must be 1, 2, 4, 8, or 16 bytes")
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
def self.decode_length(plist, offset)
|
483
|
-
if plist[offset] & 0xf == 0xf
|
484
|
-
offset += 1
|
485
|
-
length = decode_binary_plist_obj(plist, offset, 0)
|
486
|
-
offset += min_byte_size(length) + 1
|
487
|
-
return length, offset
|
488
|
-
else
|
489
|
-
return (plist[offset] & 0xf), (offset + 1)
|
490
|
-
end
|
491
|
-
end
|
492
|
-
end
|
493
|
-
end
|
494
|
-
end
|