facter 1.6.18 → 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of facter might be problematic. Click here for more details.

Files changed (126) hide show
  1. data/Gemfile +23 -8
  2. data/bin/facter +4 -1
  3. data/ext/build_defaults.yaml +1 -1
  4. data/ext/debian/changelog.erb +4 -22
  5. data/ext/debian/control +2 -2
  6. data/ext/project_data.yaml +1 -1
  7. data/ext/redhat/facter.spec.erb +10 -9
  8. data/install.rb +27 -53
  9. data/lib/facter.rb +23 -5
  10. data/lib/facter/application.rb +21 -0
  11. data/lib/facter/blockdevices.rb +105 -0
  12. data/lib/facter/domain.rb +19 -7
  13. data/lib/facter/filesystems.rb +38 -0
  14. data/lib/facter/hardwaremodel.rb +3 -2
  15. data/lib/facter/ipaddress6.rb +1 -2
  16. data/lib/facter/kernelrelease.rb +10 -2
  17. data/lib/facter/ldom.rb +47 -0
  18. data/lib/facter/macaddress.rb +1 -1
  19. data/lib/facter/manufacturer.rb +7 -1
  20. data/lib/facter/memory.rb +58 -160
  21. data/lib/facter/operatingsystem.rb +17 -2
  22. data/lib/facter/operatingsystemmajrelease.rb +33 -0
  23. data/lib/facter/operatingsystemrelease.rb +88 -37
  24. data/lib/facter/osfamily.rb +6 -2
  25. data/lib/facter/processor.rb +2 -2
  26. data/lib/facter/ps.rb +5 -0
  27. data/lib/facter/ssh.rb +50 -12
  28. data/lib/facter/util/cfpropertylist.rb +6 -0
  29. data/lib/facter/util/cfpropertylist/LICENSE +19 -0
  30. data/lib/facter/util/cfpropertylist/README +44 -0
  31. data/lib/facter/util/cfpropertylist/Rakefile +44 -0
  32. data/lib/facter/util/cfpropertylist/THANKS +7 -0
  33. data/lib/facter/util/cfpropertylist/lib/cfpropertylist.rb +6 -0
  34. data/lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb +562 -0
  35. data/lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb +26 -0
  36. data/lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb +402 -0
  37. data/lib/facter/util/cfpropertylist/lib/rbCFTypes.rb +244 -0
  38. data/lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb +135 -0
  39. data/lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb +140 -0
  40. data/lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb +136 -0
  41. data/lib/facter/util/collection.rb +36 -14
  42. data/lib/facter/util/composite_loader.rb +12 -0
  43. data/lib/facter/util/config.rb +36 -0
  44. data/lib/facter/util/confine.rb +1 -6
  45. data/lib/facter/util/directory_loader.rb +83 -0
  46. data/lib/facter/util/fact.rb +49 -42
  47. data/lib/facter/util/file_read.rb +32 -0
  48. data/lib/facter/util/ip.rb +2 -9
  49. data/lib/facter/util/loader.rb +16 -2
  50. data/lib/facter/util/macosx.rb +15 -2
  51. data/lib/facter/util/memory.rb +154 -27
  52. data/lib/facter/util/nothing_loader.rb +15 -0
  53. data/lib/facter/util/parser.rb +141 -0
  54. data/lib/facter/util/processor.rb +27 -35
  55. data/lib/facter/util/resolution.rb +97 -26
  56. data/lib/facter/util/solaris_zones.rb +153 -0
  57. data/lib/facter/util/virtual.rb +32 -3
  58. data/lib/facter/version.rb +72 -2
  59. data/lib/facter/virtual.rb +56 -3
  60. data/lib/facter/zfs_version.rb +10 -0
  61. data/lib/facter/zonename.rb +6 -0
  62. data/lib/facter/zones.rb +17 -0
  63. data/lib/facter/zpool_version.rb +10 -0
  64. data/spec/fixtures/ifconfig/ifconfig_net_tools_1.60.txt +19 -0
  65. data/spec/fixtures/ifconfig/ifconfig_ubuntu_1204.txt +16 -0
  66. data/spec/fixtures/ldom/ldom_v1 +6 -0
  67. data/spec/fixtures/unit/filesystems/linux +28 -0
  68. data/spec/fixtures/unit/util/manufacturer/intel_linux_dmidecode +549 -0
  69. data/spec/fixtures/unit/virtual/sysfs_dmi_entries_raw.txt +0 -0
  70. data/spec/fixtures/unit/zfs_version/freebsd_8.2 +14 -0
  71. data/spec/fixtures/unit/zfs_version/freebsd_9.0 +13 -0
  72. data/spec/fixtures/unit/zfs_version/linux-fuse_0.6.9 +14 -0
  73. data/spec/fixtures/unit/zfs_version/solaris_10 +10 -0
  74. data/spec/fixtures/unit/zfs_version/solaris_11 +12 -0
  75. data/spec/fixtures/unit/zpool_version/freebsd_8.2 +26 -0
  76. data/spec/fixtures/unit/zpool_version/freebsd_9.0 +38 -0
  77. data/spec/fixtures/unit/zpool_version/linux-fuse_0.6.9 +35 -0
  78. data/spec/fixtures/unit/zpool_version/solaris_10 +31 -0
  79. data/spec/fixtures/unit/zpool_version/solaris_11 +43 -0
  80. data/spec/integration/facter_spec.rb +12 -0
  81. data/spec/spec_helper.rb +9 -0
  82. data/spec/unit/architecture_spec.rb +1 -1
  83. data/spec/unit/blockdevices_spec.rb +109 -0
  84. data/spec/unit/domain_spec.rb +189 -81
  85. data/spec/unit/ec2_spec.rb +15 -8
  86. data/spec/unit/filesystems_spec.rb +50 -0
  87. data/spec/unit/hardwaremodel_spec.rb +8 -1
  88. data/spec/unit/id_spec.rb +6 -5
  89. data/spec/unit/ipaddress6_spec.rb +14 -2
  90. data/spec/unit/ipaddress_spec.rb +1 -1
  91. data/spec/unit/kernel_spec.rb +24 -0
  92. data/spec/unit/kernelmajversion_spec.rb +17 -0
  93. data/spec/unit/kernelrelease_spec.rb +53 -0
  94. data/spec/unit/kernelversion_spec.rb +32 -0
  95. data/spec/unit/ldom_spec.rb +74 -0
  96. data/spec/unit/macaddress_spec.rb +3 -1
  97. data/spec/unit/manufacturer_spec.rb +115 -0
  98. data/spec/unit/memory_spec.rb +442 -75
  99. data/spec/unit/operatingsystem_spec.rb +16 -2
  100. data/spec/unit/operatingsystemmajrelease_spec.rb +16 -0
  101. data/spec/unit/operatingsystemrelease_spec.rb +110 -1
  102. data/spec/unit/processor_spec.rb +22 -7
  103. data/spec/unit/ps_spec.rb +42 -0
  104. data/spec/unit/ssh_spec.rb +76 -0
  105. data/spec/unit/util/collection_spec.rb +94 -118
  106. data/spec/unit/util/config_spec.rb +36 -5
  107. data/spec/unit/util/confine_spec.rb +31 -43
  108. data/spec/unit/util/directory_loader_spec.rb +87 -0
  109. data/spec/unit/util/fact_spec.rb +37 -25
  110. data/spec/unit/util/file_read_spec.rb +29 -0
  111. data/spec/unit/util/ip_spec.rb +4 -2
  112. data/spec/unit/util/loader_spec.rb +102 -45
  113. data/spec/unit/util/macosx_spec.rb +40 -9
  114. data/spec/unit/util/manufacturer_spec.rb +12 -1
  115. data/spec/unit/util/parser_spec.rb +135 -0
  116. data/spec/unit/util/resolution_spec.rb +136 -4
  117. data/spec/unit/util/solaris_zones_spec.rb +127 -0
  118. data/spec/unit/util/virtual_spec.rb +54 -0
  119. data/spec/unit/version_spec.rb +42 -0
  120. data/spec/unit/virtual_spec.rb +102 -27
  121. data/spec/unit/zfs_version_spec.rb +76 -0
  122. data/spec/unit/zonename_spec.rb +14 -0
  123. data/spec/unit/zones_spec.rb +55 -0
  124. data/spec/unit/zpool_version_spec.rb +76 -0
  125. metadata +113 -11
  126. data/lib/facter/arp.rb +0 -28
@@ -0,0 +1,44 @@
1
+ CFPropertyList implementation
2
+ class to read, manipulate and write both XML and binary property list
3
+ files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List
4
+ for more documentation.
5
+
6
+ == Installation
7
+
8
+ You could either use ruby gems and install it via
9
+
10
+ gem install CFPropertyList
11
+
12
+ or you could clone this repository and place it somewhere in your load path.
13
+
14
+ == Example
15
+ require 'cfpropertylist'
16
+
17
+ # create a arbitrary data structure of basic data types
18
+ data = {
19
+ 'name' => 'John Doe',
20
+ 'missing' => true,
21
+ 'last_seen' => Time.now,
22
+ 'friends' => ['Jane Doe','Julian Doe'],
23
+ 'likes' => {
24
+ 'me' => false
25
+ }
26
+ }
27
+
28
+ # create CFPropertyList::List object
29
+ plist = CFPropertyList::List.new
30
+
31
+ # call CFPropertyList.guess() to create corresponding CFType values
32
+ plist.value = CFPropertyList.guess(data)
33
+
34
+ # write plist to file
35
+ plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
36
+
37
+ # … later, read it again
38
+ plist = CFPropertyList::List.new(:file => "example.plist")
39
+ data = CFPropertyList.native_types(plist.value)
40
+
41
+ Author:: Christian Kruse (mailto:cjk@wwwtech.de)
42
+ Copyright:: Copyright (c) 2010
43
+ License:: MIT License
44
+
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'rake/testtask'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = "CFPropertyList"
9
+ s.version = "2.1"
10
+ s.author = "Christian Kruse"
11
+ s.email = "cjk@wwwtech.de"
12
+ s.homepage = "http://github.com/ckruse/CFPropertyList"
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = "Read, write and manipulate both binary and XML property lists as defined by apple"
15
+ s.description = "This is a module to read, write and manipulate both binary and XML property lists as defined by apple."
16
+ s.files = FileList["lib/*"].to_a
17
+ s.require_path = "lib"
18
+ #s.autorequire = "name"
19
+ #s.test_files = FileList["{test}/**/*test.rb"].to_a
20
+ s.has_rdoc = true
21
+ s.extra_rdoc_files = ["README"]
22
+ s.add_development_dependency("rake",">=0.7.0")
23
+ end
24
+
25
+ desc 'Generate RDoc documentation for the CFPropertyList module.'
26
+ Rake::RDocTask.new do |rdoc|
27
+ files = ['README', 'LICENSE', 'lib/*.rb']
28
+ rdoc.rdoc_files.add(files)
29
+ rdoc.main = 'README'
30
+ rdoc.title = 'CFPropertyList RDoc'
31
+ rdoc.rdoc_dir = 'doc'
32
+ rdoc.options << '--line-numbers' << '--inline-source' << '-c utf8'
33
+ end
34
+
35
+ Gem::PackageTask.new(spec) do |pkg|
36
+ pkg.need_tar = true
37
+ end
38
+
39
+ Rake::TestTask.new do |test|
40
+ test.libs << 'test'
41
+ test.test_files = Dir.glob('test/test*.rb')
42
+ end
43
+
44
+ # eof
@@ -0,0 +1,7 @@
1
+ Special thanks to:
2
+
3
+ Steve Madsen for providing a lot of performance patches and bugfixes!
4
+ Have a look at his Github account: <http://github.com/sjmadsen>
5
+
6
+
7
+
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require File.dirname(__FILE__) + '/rbCFPropertyList.rb'
4
+
5
+
6
+ # eof
@@ -0,0 +1,562 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Facter::Util::CFPropertyList
4
+ # Binary PList parser class
5
+ class Binary
6
+ # Read a binary plist file
7
+ def load(opts)
8
+ @unique_table = {}
9
+ @count_objects = 0
10
+ @object_refs = 0
11
+
12
+ @written_object_count = 0
13
+ @object_table = []
14
+ @object_ref_size = 0
15
+
16
+ @offsets = []
17
+
18
+ fd = nil
19
+ if(opts.has_key?(:file))
20
+ fd = File.open(opts[:file],"rb")
21
+ file = opts[:file]
22
+ else
23
+ fd = StringIO.new(opts[:data],"rb")
24
+ file = "<string>"
25
+ end
26
+
27
+ # first, we read the trailer: 32 byte from the end
28
+ fd.seek(-32,IO::SEEK_END)
29
+ buff = fd.read(32)
30
+
31
+ offset_size, object_ref_size, number_of_objects, top_object, table_offset = buff.unpack "x6CCx4Nx4Nx4N"
32
+
33
+ # after that, get the offset table
34
+ fd.seek(table_offset, IO::SEEK_SET)
35
+ coded_offset_table = fd.read(number_of_objects * offset_size)
36
+ raise CFFormatError.new("#{file}: Format error!") unless coded_offset_table.bytesize == number_of_objects * offset_size
37
+
38
+ @count_objects = number_of_objects
39
+
40
+ # decode offset table
41
+ formats = ["","C*","n*","(H6)*","N*"]
42
+ @offsets = coded_offset_table.unpack(formats[offset_size])
43
+ if(offset_size == 3)
44
+ 0.upto(@offsets.size-1) { |i| @offsets[i] = @offsets[i].to_i(16) }
45
+ end
46
+
47
+ @object_ref_size = object_ref_size
48
+ val = read_binary_object_at(file,fd,top_object)
49
+
50
+ fd.close
51
+ val
52
+ end
53
+
54
+
55
+ # Convert Facter::Util::CFPropertyList to binary format; since we have to count our objects we simply unique CFDictionary and CFArray
56
+ def to_str(opts={})
57
+ @unique_table = {}
58
+ @count_objects = 0
59
+ @object_refs = 0
60
+
61
+ @written_object_count = 0
62
+ @object_table = []
63
+
64
+ @offsets = []
65
+
66
+ binary_str = "bplist00"
67
+
68
+ @object_refs = count_object_refs(opts[:root])
69
+
70
+ opts[:root].to_binary(self)
71
+
72
+ next_offset = 8
73
+ offsets = @object_table.map do |object|
74
+ offset = next_offset
75
+ next_offset += object.bytesize
76
+ offset
77
+ end
78
+ binary_str << @object_table.join
79
+
80
+ table_offset = next_offset
81
+ offset_size = Binary.bytes_needed(table_offset)
82
+
83
+ if offset_size < 8
84
+ # Fast path: encode the entire offset array at once.
85
+ binary_str << offsets.pack((%w(C n N N)[offset_size - 1]) + '*')
86
+ else
87
+ # Slow path: host may be little or big endian, must pack each offset
88
+ # separately.
89
+ offsets.each do |offset|
90
+ binary_str << "#{Binary.pack_it_with_size(offset_size,offset)}"
91
+ end
92
+ end
93
+
94
+ binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
95
+ binary_str << [@object_table.size].pack("x4N")
96
+ binary_str << [0].pack("x4N")
97
+ binary_str << [table_offset].pack("x4N")
98
+
99
+ binary_str
100
+ end
101
+
102
+ def object_ref_size object_refs
103
+ Binary.bytes_needed(object_refs)
104
+ end
105
+
106
+ # read a „null” type (i.e. null byte, marker byte, bool value)
107
+ def read_binary_null_type(length)
108
+ case length
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}")
115
+ end
116
+ end
117
+ protected :read_binary_null_type
118
+
119
+ # read a binary int value
120
+ def read_binary_int(fname,fd,length)
121
+ if length > 3
122
+ raise CFFormatError.new("Integer greater than 8 bytes: #{length}")
123
+ end
124
+
125
+ nbytes = 1 << length
126
+
127
+ buff = fd.read(nbytes)
128
+
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
145
+ end
146
+ )
147
+ end
148
+ protected :read_binary_int
149
+
150
+ # read a binary real value
151
+ def read_binary_real(fname,fd,length)
152
+ raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
153
+
154
+ nbytes = 1 << length
155
+ buff = fd.read(nbytes)
156
+
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
+ end
172
+ protected :read_binary_real
173
+
174
+ # read a binary date value
175
+ def read_binary_date(fname,fd,length)
176
+ raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
177
+
178
+ nbytes = 1 << length
179
+ buff = fd.read(nbytes)
180
+
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
+ )
194
+ end
195
+ protected :read_binary_date
196
+
197
+ # Read a binary data value
198
+ def read_binary_data(fname,fd,length)
199
+ CFData.new(read_fd(fd, length), CFData::DATA_RAW)
200
+ end
201
+ protected :read_binary_data
202
+
203
+ def read_fd fd, length
204
+ length > 0 ? fd.read(length) : ""
205
+ end
206
+
207
+ # Read a binary string value
208
+ def read_binary_string(fname,fd,length)
209
+ buff = read_fd fd, length
210
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
211
+ CFString.new(buff)
212
+ end
213
+ protected :read_binary_string
214
+
215
+ # Convert the given string from one charset to another
216
+ def Binary.charset_convert(str,from,to="UTF-8")
217
+ return str.clone.force_encoding(from).encode(to) if str.respond_to?("encode")
218
+ Iconv.conv(to,from,str)
219
+ end
220
+
221
+ # Count characters considering character set
222
+ def Binary.charset_strlen(str,charset="UTF-8")
223
+ if str.respond_to?(:encode)
224
+ size = str.length
225
+ else
226
+ utf8_str = Iconv.conv("UTF-8",charset,str)
227
+ size = utf8_str.scan(/./mu).size
228
+ end
229
+
230
+ # UTF-16 code units in the range D800-DBFF are the beginning of
231
+ # a surrogate pair, and count as one additional character for
232
+ # length calculation.
233
+ if charset =~ /^UTF-16/
234
+ if str.respond_to?(:encode)
235
+ str.bytes.to_a.each_slice(2) { |pair| size += 1 if (0xd8..0xdb).include?(pair[0]) }
236
+ else
237
+ str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
238
+ end
239
+ end
240
+
241
+ size
242
+ end
243
+
244
+ # Read a unicode string value, coded as UTF-16BE
245
+ def read_binary_unicode_string(fname,fd,length)
246
+ # The problem is: we get the length of the string IN CHARACTERS;
247
+ # since a char in UTF-16 can be 16 or 32 bit long, we don't really know
248
+ # how long the string is in bytes
249
+ buff = fd.read(2*length)
250
+
251
+ @unique_table[buff] = true unless @unique_table.has_key?(buff)
252
+ CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
253
+ end
254
+ protected :read_binary_unicode_string
255
+
256
+ # Read an binary array value, including contained objects
257
+ def read_binary_array(fname,fd,length)
258
+ ary = []
259
+
260
+ # first: read object refs
261
+ if(length != 0)
262
+ buff = fd.read(length * @object_ref_size)
263
+ objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
264
+
265
+ # now: read objects
266
+ 0.upto(length-1) do |i|
267
+ object = read_binary_object_at(fname,fd,objects[i])
268
+ ary.push object
269
+ end
270
+ end
271
+
272
+ CFArray.new(ary)
273
+ end
274
+ protected :read_binary_array
275
+
276
+ # Read a dictionary value, including contained objects
277
+ def read_binary_dict(fname,fd,length)
278
+ dict = {}
279
+
280
+ # first: read keys
281
+ if(length != 0) then
282
+ buff = fd.read(length * @object_ref_size)
283
+ keys = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
284
+
285
+ # second: read object refs
286
+ buff = fd.read(length * @object_ref_size)
287
+ objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
288
+
289
+ # read real keys and objects
290
+ 0.upto(length-1) do |i|
291
+ key = read_binary_object_at(fname,fd,keys[i])
292
+ object = read_binary_object_at(fname,fd,objects[i])
293
+ dict[key.value] = object
294
+ end
295
+ end
296
+
297
+ CFDictionary.new(dict)
298
+ end
299
+ protected :read_binary_dict
300
+
301
+ # Read an object type byte, decode it and delegate to the correct reader function
302
+ def read_binary_object(fname,fd)
303
+ # first: read the marker byte
304
+ buff = fd.read(1)
305
+
306
+ object_length = buff.unpack("C*")
307
+ object_length = object_length[0] & 0xF
308
+
309
+ buff = buff.unpack("H*")
310
+ object_type = buff[0][0].chr
311
+
312
+ if(object_type != "0" && object_length == 15) then
313
+ object_length = read_binary_object(fname,fd)
314
+ object_length = object_length.value
315
+ end
316
+
317
+ case object_type
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)
336
+ end
337
+ end
338
+ protected :read_binary_object
339
+
340
+ # Read an object type byte at position $pos, decode it and delegate to the correct reader function
341
+ def read_binary_object_at(fname,fd,pos)
342
+ position = @offsets[pos]
343
+ fd.seek(position,IO::SEEK_SET)
344
+ read_binary_object(fname,fd)
345
+ end
346
+ protected :read_binary_object_at
347
+
348
+ # pack an +int+ of +nbytes+ with size
349
+ def Binary.pack_it_with_size(nbytes,int)
350
+ case nbytes
351
+ when 1 then [int].pack('c')
352
+ when 2 then [int].pack('n')
353
+ when 4 then [int].pack('N')
354
+ when 8
355
+ [int >> 32, int & 0xFFFFFFFF].pack('NN')
356
+ else
357
+ raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
358
+ end
359
+ end
360
+
361
+ def Binary.pack_int_array_with_size(nbytes, array)
362
+ case nbytes
363
+ when 1 then array.pack('C*')
364
+ when 2 then array.pack('n*')
365
+ when 4 then array.pack('N*')
366
+ when 8
367
+ array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
368
+ else
369
+ raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
370
+ end
371
+ end
372
+
373
+ # calculate how many bytes are needed to save +count+
374
+ def Binary.bytes_needed(count)
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
380
+ else
381
+ raise CFFormatError.new("Data size too large: #{count}")
382
+ end
383
+ end
384
+
385
+ # Create a type byte for binary format as defined by apple
386
+ def Binary.type_bytes(type, length)
387
+ if length < 15
388
+ [(type << 4) | length].pack('C')
389
+ else
390
+ bytes = [(type << 4) | 0xF]
391
+ if length <= 0xFF
392
+ bytes.push(0x10, length).pack('CCC') # 1 byte length
393
+ elsif length <= 0xFFFF
394
+ bytes.push(0x11, length).pack('CCn') # 2 byte length
395
+ elsif length <= 0xFFFFFFFF
396
+ bytes.push(0x12, length).pack('CCN') # 4 byte length
397
+ elsif length <= 0x7FFFFFFFFFFFFFFF
398
+ bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
399
+ else
400
+ raise CFFormatError.new("Integer too large: #{int}")
401
+ end
402
+ end
403
+ end
404
+
405
+ def count_object_refs(object)
406
+ case object
407
+ when CFArray
408
+ contained_refs = 0
409
+ object.value.each do |element|
410
+ if CFArray === element || CFDictionary === element
411
+ contained_refs += count_object_refs(element)
412
+ end
413
+ end
414
+ return object.value.size + contained_refs
415
+ when CFDictionary
416
+ contained_refs = 0
417
+ object.value.each_value do |value|
418
+ if CFArray === value || CFDictionary === value
419
+ contained_refs += count_object_refs(value)
420
+ end
421
+ end
422
+ return object.value.keys.size * 2 + contained_refs
423
+ else
424
+ return 0
425
+ end
426
+ end
427
+
428
+ def Binary.ascii_string?(str)
429
+ if str.respond_to?(:ascii_only?)
430
+ str.ascii_only?
431
+ else
432
+ str !~ /[\x80-\xFF]/mn
433
+ end
434
+ end
435
+
436
+ # Uniques and transforms a string value to binary format and adds it to the object table
437
+ def string_to_binary(val)
438
+ val = val.to_s
439
+
440
+ @unique_table[val] ||= begin
441
+ if !Binary.ascii_string?(val)
442
+ utf8_strlen = Binary.charset_strlen(val, "UTF-8")
443
+ val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
444
+ bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
445
+
446
+ val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
447
+ @object_table[@written_object_count] = bdata << val
448
+ else
449
+ utf8_strlen = val.bytesize
450
+ bdata = Binary.type_bytes(0b0101,val.bytesize)
451
+ @object_table[@written_object_count] = bdata << val
452
+ end
453
+ @written_object_count += 1
454
+ @written_object_count - 1
455
+ end
456
+ end
457
+
458
+ # Codes an integer to binary format
459
+ def int_to_binary(value)
460
+ nbytes = 0
461
+ nbytes = 1 if value > 0xFF # 1 byte integer
462
+ nbytes += 1 if value > 0xFFFF # 4 byte integer
463
+ nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
464
+ nbytes = 3 if value < 0 # 8 byte integer, since signed
465
+
466
+ Binary.type_bytes(0b0001, nbytes) <<
467
+ if nbytes < 3
468
+ [value].pack(
469
+ if nbytes == 0 then "C"
470
+ elsif nbytes == 1 then "n"
471
+ else "N"
472
+ end
473
+ )
474
+ else
475
+ # 64 bit signed integer; we need the higher and the lower 32 bit of the value
476
+ high_word = value >> 32
477
+ low_word = value & 0xFFFFFFFF
478
+ [high_word,low_word].pack("NN")
479
+ end
480
+ end
481
+
482
+ # Codes a real value to binary format
483
+ def real_to_binary(val)
484
+ Binary.type_bytes(0b0010,3) << [val].pack("d").reverse
485
+ end
486
+
487
+ # Converts a numeric value to binary and adds it to the object table
488
+ def num_to_binary(value)
489
+ @object_table[@written_object_count] =
490
+ if value.is_a?(CFInteger)
491
+ int_to_binary(value.value)
492
+ else
493
+ real_to_binary(value.value)
494
+ end
495
+
496
+ @written_object_count += 1
497
+ @written_object_count - 1
498
+ end
499
+
500
+ # Convert date value (apple format) to binary and adds it to the object table
501
+ def date_to_binary(val)
502
+ 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
503
+
504
+ @object_table[@written_object_count] =
505
+ (Binary.type_bytes(0b0011, 3) << [val].pack("d").reverse)
506
+
507
+ @written_object_count += 1
508
+ @written_object_count - 1
509
+ end
510
+
511
+ # Convert a bool value to binary and add it to the object table
512
+ def bool_to_binary(val)
513
+
514
+ @object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
515
+ @written_object_count += 1
516
+ @written_object_count - 1
517
+ end
518
+
519
+ # Convert data value to binary format and add it to the object table
520
+ def data_to_binary(val)
521
+ @object_table[@written_object_count] =
522
+ (Binary.type_bytes(0b0100, val.bytesize) << val)
523
+
524
+ @written_object_count += 1
525
+ @written_object_count - 1
526
+ end
527
+
528
+ # Convert array to binary format and add it to the object table
529
+ def array_to_binary(val)
530
+ saved_object_count = @written_object_count
531
+ @written_object_count += 1
532
+ #@object_refs += val.value.size
533
+
534
+ values = val.value.map { |v| v.to_binary(self) }
535
+ bdata = Binary.type_bytes(0b1010, val.value.size) <<
536
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs),
537
+ values)
538
+
539
+ @object_table[saved_object_count] = bdata
540
+ saved_object_count
541
+ end
542
+
543
+ # Convert dictionary to binary format and add it to the object table
544
+ def dict_to_binary(val)
545
+ saved_object_count = @written_object_count
546
+ @written_object_count += 1
547
+
548
+ #@object_refs += val.value.keys.size * 2
549
+
550
+ keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
551
+ keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
552
+
553
+ bdata = Binary.type_bytes(0b1101,val.value.size) <<
554
+ Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
555
+
556
+ @object_table[saved_object_count] = bdata
557
+ return saved_object_count
558
+ end
559
+ end
560
+ end
561
+
562
+ # eof