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.
- data/Gemfile +23 -8
- data/bin/facter +4 -1
- data/ext/build_defaults.yaml +1 -1
- data/ext/debian/changelog.erb +4 -22
- data/ext/debian/control +2 -2
- data/ext/project_data.yaml +1 -1
- data/ext/redhat/facter.spec.erb +10 -9
- data/install.rb +27 -53
- data/lib/facter.rb +23 -5
- data/lib/facter/application.rb +21 -0
- data/lib/facter/blockdevices.rb +105 -0
- data/lib/facter/domain.rb +19 -7
- data/lib/facter/filesystems.rb +38 -0
- data/lib/facter/hardwaremodel.rb +3 -2
- data/lib/facter/ipaddress6.rb +1 -2
- data/lib/facter/kernelrelease.rb +10 -2
- data/lib/facter/ldom.rb +47 -0
- data/lib/facter/macaddress.rb +1 -1
- data/lib/facter/manufacturer.rb +7 -1
- data/lib/facter/memory.rb +58 -160
- data/lib/facter/operatingsystem.rb +17 -2
- data/lib/facter/operatingsystemmajrelease.rb +33 -0
- data/lib/facter/operatingsystemrelease.rb +88 -37
- data/lib/facter/osfamily.rb +6 -2
- data/lib/facter/processor.rb +2 -2
- data/lib/facter/ps.rb +5 -0
- data/lib/facter/ssh.rb +50 -12
- data/lib/facter/util/cfpropertylist.rb +6 -0
- data/lib/facter/util/cfpropertylist/LICENSE +19 -0
- data/lib/facter/util/cfpropertylist/README +44 -0
- data/lib/facter/util/cfpropertylist/Rakefile +44 -0
- data/lib/facter/util/cfpropertylist/THANKS +7 -0
- data/lib/facter/util/cfpropertylist/lib/cfpropertylist.rb +6 -0
- data/lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb +562 -0
- data/lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb +26 -0
- data/lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb +402 -0
- data/lib/facter/util/cfpropertylist/lib/rbCFTypes.rb +244 -0
- data/lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb +135 -0
- data/lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb +140 -0
- data/lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb +136 -0
- data/lib/facter/util/collection.rb +36 -14
- data/lib/facter/util/composite_loader.rb +12 -0
- data/lib/facter/util/config.rb +36 -0
- data/lib/facter/util/confine.rb +1 -6
- data/lib/facter/util/directory_loader.rb +83 -0
- data/lib/facter/util/fact.rb +49 -42
- data/lib/facter/util/file_read.rb +32 -0
- data/lib/facter/util/ip.rb +2 -9
- data/lib/facter/util/loader.rb +16 -2
- data/lib/facter/util/macosx.rb +15 -2
- data/lib/facter/util/memory.rb +154 -27
- data/lib/facter/util/nothing_loader.rb +15 -0
- data/lib/facter/util/parser.rb +141 -0
- data/lib/facter/util/processor.rb +27 -35
- data/lib/facter/util/resolution.rb +97 -26
- data/lib/facter/util/solaris_zones.rb +153 -0
- data/lib/facter/util/virtual.rb +32 -3
- data/lib/facter/version.rb +72 -2
- data/lib/facter/virtual.rb +56 -3
- data/lib/facter/zfs_version.rb +10 -0
- data/lib/facter/zonename.rb +6 -0
- data/lib/facter/zones.rb +17 -0
- data/lib/facter/zpool_version.rb +10 -0
- data/spec/fixtures/ifconfig/ifconfig_net_tools_1.60.txt +19 -0
- data/spec/fixtures/ifconfig/ifconfig_ubuntu_1204.txt +16 -0
- data/spec/fixtures/ldom/ldom_v1 +6 -0
- data/spec/fixtures/unit/filesystems/linux +28 -0
- data/spec/fixtures/unit/util/manufacturer/intel_linux_dmidecode +549 -0
- data/spec/fixtures/unit/virtual/sysfs_dmi_entries_raw.txt +0 -0
- data/spec/fixtures/unit/zfs_version/freebsd_8.2 +14 -0
- data/spec/fixtures/unit/zfs_version/freebsd_9.0 +13 -0
- data/spec/fixtures/unit/zfs_version/linux-fuse_0.6.9 +14 -0
- data/spec/fixtures/unit/zfs_version/solaris_10 +10 -0
- data/spec/fixtures/unit/zfs_version/solaris_11 +12 -0
- data/spec/fixtures/unit/zpool_version/freebsd_8.2 +26 -0
- data/spec/fixtures/unit/zpool_version/freebsd_9.0 +38 -0
- data/spec/fixtures/unit/zpool_version/linux-fuse_0.6.9 +35 -0
- data/spec/fixtures/unit/zpool_version/solaris_10 +31 -0
- data/spec/fixtures/unit/zpool_version/solaris_11 +43 -0
- data/spec/integration/facter_spec.rb +12 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/architecture_spec.rb +1 -1
- data/spec/unit/blockdevices_spec.rb +109 -0
- data/spec/unit/domain_spec.rb +189 -81
- data/spec/unit/ec2_spec.rb +15 -8
- data/spec/unit/filesystems_spec.rb +50 -0
- data/spec/unit/hardwaremodel_spec.rb +8 -1
- data/spec/unit/id_spec.rb +6 -5
- data/spec/unit/ipaddress6_spec.rb +14 -2
- data/spec/unit/ipaddress_spec.rb +1 -1
- data/spec/unit/kernel_spec.rb +24 -0
- data/spec/unit/kernelmajversion_spec.rb +17 -0
- data/spec/unit/kernelrelease_spec.rb +53 -0
- data/spec/unit/kernelversion_spec.rb +32 -0
- data/spec/unit/ldom_spec.rb +74 -0
- data/spec/unit/macaddress_spec.rb +3 -1
- data/spec/unit/manufacturer_spec.rb +115 -0
- data/spec/unit/memory_spec.rb +442 -75
- data/spec/unit/operatingsystem_spec.rb +16 -2
- data/spec/unit/operatingsystemmajrelease_spec.rb +16 -0
- data/spec/unit/operatingsystemrelease_spec.rb +110 -1
- data/spec/unit/processor_spec.rb +22 -7
- data/spec/unit/ps_spec.rb +42 -0
- data/spec/unit/ssh_spec.rb +76 -0
- data/spec/unit/util/collection_spec.rb +94 -118
- data/spec/unit/util/config_spec.rb +36 -5
- data/spec/unit/util/confine_spec.rb +31 -43
- data/spec/unit/util/directory_loader_spec.rb +87 -0
- data/spec/unit/util/fact_spec.rb +37 -25
- data/spec/unit/util/file_read_spec.rb +29 -0
- data/spec/unit/util/ip_spec.rb +4 -2
- data/spec/unit/util/loader_spec.rb +102 -45
- data/spec/unit/util/macosx_spec.rb +40 -9
- data/spec/unit/util/manufacturer_spec.rb +12 -1
- data/spec/unit/util/parser_spec.rb +135 -0
- data/spec/unit/util/resolution_spec.rb +136 -4
- data/spec/unit/util/solaris_zones_spec.rb +127 -0
- data/spec/unit/util/virtual_spec.rb +54 -0
- data/spec/unit/version_spec.rb +42 -0
- data/spec/unit/virtual_spec.rb +102 -27
- data/spec/unit/zfs_version_spec.rb +76 -0
- data/spec/unit/zonename_spec.rb +14 -0
- data/spec/unit/zones_spec.rb +55 -0
- data/spec/unit/zpool_version_spec.rb +76 -0
- metadata +113 -11
- 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,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
|