nixenvironment 0.0.118 → 0.0.119

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Exceptions used:
4
+ # CFPlistError:: General base exception
5
+ # CFFormatError:: Format error
6
+ # CFTypeError:: Type error
7
+ #
8
+ # Easy and simple :-)
9
+ #
10
+ # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
11
+ # Copyright:: Copyright (c) 2010
12
+ # License:: MIT License
13
+
14
+ module CFPropertyList228
15
+ # general plist error. All exceptions thrown are derived from this class.
16
+ class CFPlistError < Exception
17
+ end
18
+
19
+ # Exception thrown when format errors occur
20
+ class CFFormatError < CFPlistError
21
+ end
22
+
23
+ # Exception thrown when type errors occur
24
+ class CFTypeError < CFPlistError
25
+ end
26
+ end
27
+
28
+ # eof
@@ -0,0 +1,432 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'kconv'
4
+ require 'date'
5
+ require 'time'
6
+
7
+ #
8
+ # CFPropertyList228 implementation
9
+ #
10
+ # class to read, manipulate and write both XML and binary property list
11
+ # files (plist(5)) as defined by Apple. Have a look at CFPropertyList228::List
12
+ # for more documentation.
13
+ #
14
+ # == Example
15
+ # require 'CFPropertyList228'
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 CFPropertyList228::List object
29
+ # plist = CFPropertyList228::List.new
30
+ #
31
+ # # call CFPropertyList228.guess() to create corresponding CFType values
32
+ # # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
33
+ # plist.value = CFPropertyList228.guess(data)
34
+ #
35
+ # # write plist to file
36
+ # plist.save("example.plist", CFPropertyList228::List::FORMAT_BINARY)
37
+ #
38
+ # # … later, read it again
39
+ # plist = CFPropertyList228::List.new(:file => "example.plist")
40
+ # data = CFPropertyList228.native_types(plist.value)
41
+ #
42
+ # Author:: Christian Kruse (mailto:cjk@wwwtech.de)
43
+ # Copyright:: Copyright (c) 2010
44
+ # License:: MIT License
45
+ module CFPropertyList228
46
+ class << self
47
+ attr_accessor :xml_parser_interface
48
+ end
49
+
50
+ # interface class for PList parsers
51
+ class ParserInterface
52
+ # load a plist
53
+ def load(opts={})
54
+ return ""
55
+ end
56
+
57
+ # convert a plist to string
58
+ def to_str(opts={})
59
+ return true
60
+ end
61
+ end
62
+
63
+ class XMLParserInterface < ParserInterface
64
+ def new_node(name)
65
+ end
66
+
67
+ def new_text(val)
68
+ end
69
+
70
+ def append_node(parent, child)
71
+ end
72
+ end
73
+ end
74
+
75
+ class String
76
+ unless("".respond_to?(:bytesize)) then
77
+ def bytesize
78
+ self.length
79
+ end
80
+ end
81
+ end
82
+
83
+ dirname = File.dirname(__FILE__)
84
+ require dirname + '/rbCFPlistError.rb'
85
+ require dirname + '/rbCFTypes.rb'
86
+ require dirname + '/rbBinaryCFPropertyList.rb'
87
+
88
+ require 'iconv' unless "".respond_to?("encode")
89
+
90
+ # ensure that the module and class exist
91
+ module Enumerable
92
+ class Enumerator
93
+ end
94
+ end
95
+
96
+ begin
97
+ require dirname + '/rbLibXMLParser.rb'
98
+ temp = LibXML::XML::Parser::Options::NOBLANKS; # check if we have a version with parser options
99
+ try_nokogiri = false
100
+ CFPropertyList228.xml_parser_interface = CFPropertyList228::LibXMLParser
101
+ rescue LoadError, NameError
102
+ try_nokogiri = true
103
+ end
104
+
105
+ if try_nokogiri then
106
+ begin
107
+ require dirname + '/rbNokogiriParser.rb'
108
+ CFPropertyList228.xml_parser_interface = CFPropertyList228::NokogiriXMLParser
109
+ rescue LoadError => e
110
+ require dirname + '/rbREXMLParser.rb'
111
+ CFPropertyList228.xml_parser_interface = CFPropertyList228::ReXMLParser
112
+ end
113
+ end
114
+
115
+
116
+ module CFPropertyList228
117
+ # Create CFType hierarchy by guessing the correct CFType, e.g.
118
+ #
119
+ # x = {
120
+ # 'a' => ['b','c','d']
121
+ # }
122
+ # cftypes = CFPropertyList228.guess(x)
123
+ #
124
+ # pass optional options hash. Only possible value actually:
125
+ # +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
126
+ # +converter_method+:: Convert unknown objects to known objects calling +method_name+
127
+ #
128
+ # cftypes = CFPropertyList228.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash, :converter_with_opts => true)
129
+ def guess(object, options = {})
130
+ case object
131
+ when Fixnum, Integer then CFInteger.new(object)
132
+ when UidFixnum then CFUid.new(object)
133
+ when Float then CFReal.new(object)
134
+ when TrueClass, FalseClass then CFBoolean.new(object)
135
+
136
+ when Blob
137
+ CFData.new(object, CFData::DATA_RAW)
138
+
139
+ when String
140
+ CFString.new(object)
141
+
142
+ when Time, DateTime, Date then CFDate.new(object)
143
+
144
+ when Array, Enumerator, Enumerable::Enumerator
145
+ ary = Array.new
146
+ object.each do |o|
147
+ ary.push CFPropertyList228.guess(o, options)
148
+ end
149
+ CFArray.new(ary)
150
+
151
+ when Hash
152
+ hsh = Hash.new
153
+ object.each_pair do |k,v|
154
+ k = k.to_s if k.is_a?(Symbol)
155
+ hsh[k] = CFPropertyList228.guess(v, options)
156
+ end
157
+ CFDictionary.new(hsh)
158
+ else
159
+ case
160
+ when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
161
+ CFReal.new(object)
162
+ when object.respond_to?(:read)
163
+ raw_data = object.read
164
+ # treat the data as a bytestring (ASCII-8BIT) if Ruby supports it. Do this by forcing
165
+ # the encoding, on the assumption that the bytes were read correctly, and just tagged with
166
+ # an inappropriate encoding, rather than transcoding.
167
+ raw_data.force_encoding(Encoding::ASCII_8BIT) if raw_data.respond_to?(:force_encoding)
168
+ CFData.new(raw_data, CFData::DATA_RAW)
169
+ when options[:converter_method] && object.respond_to?(options[:converter_method])
170
+ if options[:converter_with_opts]
171
+ CFPropertyList228.guess(object.send(options[:converter_method],options),options)
172
+ else
173
+ CFPropertyList228.guess(object.send(options[:converter_method]),options)
174
+ end
175
+ when options[:convert_unknown_to_string]
176
+ CFString.new(object.to_s)
177
+ else
178
+ raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
179
+ end
180
+ end
181
+ end
182
+
183
+ # Converts a CFType hiercharchy to native Ruby types
184
+ def native_types(object,keys_as_symbols=false)
185
+ return if object.nil?
186
+
187
+ if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) || object.is_a?(CFUid) then
188
+ return object.value
189
+ elsif(object.is_a?(CFData)) then
190
+ return CFPropertyList228::Blob.new(object.decoded_value)
191
+ elsif(object.is_a?(CFArray)) then
192
+ ary = []
193
+ object.value.each do
194
+ |v|
195
+ ary.push CFPropertyList228.native_types(v)
196
+ end
197
+
198
+ return ary
199
+ elsif(object.is_a?(CFDictionary)) then
200
+ hsh = {}
201
+ object.value.each_pair do
202
+ |k,v|
203
+ k = k.to_sym if keys_as_symbols
204
+ hsh[k] = CFPropertyList228.native_types(v)
205
+ end
206
+
207
+ return hsh
208
+ end
209
+ end
210
+
211
+ module_function :guess, :native_types
212
+
213
+ # Class representing a CFPropertyList228. Instanciate with #new
214
+ class List
215
+ # Format constant for binary format
216
+ FORMAT_BINARY = 1
217
+
218
+ # Format constant for XML format
219
+ FORMAT_XML = 2
220
+
221
+ # Format constant for automatic format recognizing
222
+ FORMAT_AUTO = 0
223
+
224
+ @@parsers = [Binary, CFPropertyList228.xml_parser_interface]
225
+
226
+ # Path of PropertyList
227
+ attr_accessor :filename
228
+ # the original format of the PropertyList
229
+ attr_accessor :format
230
+ # the root value in the plist file
231
+ attr_accessor :value
232
+ # default value for XML generation; if true generate formatted XML
233
+ attr_accessor :formatted
234
+
235
+ # initialize a new CFPropertyList228, arguments are:
236
+ #
237
+ # :file:: Parse a file
238
+ # :format:: Format is one of FORMAT_BINARY or FORMAT_XML. Defaults to FORMAT_AUTO
239
+ # :data:: Parse a string
240
+ #
241
+ # All arguments are optional
242
+ def initialize(opts={})
243
+ @filename = opts[:file]
244
+ @format = opts[:format] || FORMAT_AUTO
245
+ @data = opts[:data]
246
+ @formatted = opts[:formatted]
247
+
248
+ load(@filename) unless @filename.nil?
249
+ load_str(@data) unless @data.nil?
250
+ end
251
+
252
+ # returns a list of registered parsers
253
+ def self.parsers
254
+ @@parsers
255
+ end
256
+
257
+ # set a list of parsers
258
+ def self.parsers=(val)
259
+ @@parsers = val
260
+ end
261
+
262
+ # Load an XML PropertyList
263
+ # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
264
+ def load_xml(filename=nil)
265
+ load(filename,List::FORMAT_XML)
266
+ end
267
+
268
+ # read a binary plist file
269
+ # filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
270
+ def load_binary(filename=nil)
271
+ load(filename,List::FORMAT_BINARY)
272
+ end
273
+
274
+ # load a plist from a XML string
275
+ # str:: The string containing the plist
276
+ def load_xml_str(str=nil)
277
+ load_str(str,List::FORMAT_XML)
278
+ end
279
+
280
+ # load a plist from a binary string
281
+ # str:: The string containing the plist
282
+ def load_binary_str(str=nil)
283
+ load_str(str,List::FORMAT_BINARY)
284
+ end
285
+
286
+ # load a plist from a string
287
+ # str = nil:: The string containing the plist
288
+ # format = nil:: The format of the plist
289
+ def load_str(str=nil,format=nil)
290
+ str = @data if str.nil?
291
+ format = @format if format.nil?
292
+
293
+ @value = {}
294
+ case format
295
+ when List::FORMAT_BINARY, List::FORMAT_XML then
296
+ prsr = @@parsers[format-1].new
297
+ @value = prsr.load({:data => str})
298
+
299
+ when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
300
+ filetype = str[0..5]
301
+ version = str[6..7]
302
+
303
+ prsr = nil
304
+ if filetype == "bplist" then
305
+ raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
306
+ prsr = Binary.new
307
+ @format = List::FORMAT_BINARY
308
+ else
309
+ prsr = CFPropertyList228.xml_parser_interface.new
310
+ @format = List::FORMAT_XML
311
+ end
312
+
313
+ @value = prsr.load({:data => str})
314
+ end
315
+ end
316
+
317
+ # Read a plist file
318
+ # file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
319
+ # format = nil:: The format of the plist file. Auto-detect if nil
320
+ def load(file=nil,format=nil)
321
+ file = @filename if file.nil?
322
+ format = @format if format.nil?
323
+ @value = {}
324
+
325
+ raise IOError.new("File #{file} not readable!") unless File.readable? file
326
+
327
+ case format
328
+ when List::FORMAT_BINARY, List::FORMAT_XML then
329
+ prsr = @@parsers[format-1].new
330
+ @value = prsr.load({:file => file})
331
+
332
+ when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
333
+ magic_number = IO.read(file,8)
334
+ raise IOError.new("File #{file} is empty.") unless magic_number
335
+ filetype = magic_number[0..5]
336
+ version = magic_number[6..7]
337
+
338
+ prsr = nil
339
+ if filetype == "bplist" then
340
+ raise CFFormatError.new("Wong file version #{version}") unless version == "00"
341
+ prsr = Binary.new
342
+ @format = List::FORMAT_BINARY
343
+ else
344
+ prsr = CFPropertyList228.xml_parser_interface.new
345
+ @format = List::FORMAT_XML
346
+ end
347
+
348
+ @value = prsr.load({:file => file})
349
+ end
350
+
351
+ raise CFFormatError.new("Invalid format or parser error!") if @value.nil?
352
+ end
353
+
354
+ # Serialize CFPropertyList228 object to specified format and write it to file
355
+ # file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
356
+ # format = nil:: The format to save in. Uses +format+ instance variable if nil
357
+ def save(file=nil,format=nil,opts={})
358
+ format = @format if format.nil?
359
+ file = @filename if file.nil?
360
+
361
+ raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML") if format != FORMAT_BINARY && format != FORMAT_XML
362
+
363
+ if(!File.exists?(file)) then
364
+ raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
365
+ elsif(!File.writable?(file)) then
366
+ raise IOError.new("File #{file} not writable!")
367
+ end
368
+
369
+ opts[:root] = @value
370
+ opts[:formatted] = @formatted unless opts.has_key?(:formatted)
371
+
372
+ prsr = @@parsers[format-1].new
373
+
374
+ content = prsr.to_str(opts)
375
+
376
+ File.open(file, 'wb') {
377
+ |fd|
378
+ fd.write content
379
+ }
380
+ end
381
+
382
+ # convert plist to string
383
+ # format = List::FORMAT_BINARY:: The format to save the plist
384
+ # opts={}:: Pass parser options
385
+ def to_str(format=List::FORMAT_BINARY,opts={})
386
+ raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML") if format != FORMAT_BINARY && format != FORMAT_XML
387
+
388
+ prsr = @@parsers[format-1].new
389
+
390
+ opts[:root] = @value
391
+ opts[:formatted] = @formatted unless opts.has_key?(:formatted)
392
+
393
+ return prsr.to_str(opts)
394
+ end
395
+ end
396
+ end
397
+
398
+
399
+ class Array
400
+ # convert an array to plist format
401
+ def to_plist(options={})
402
+ options[:plist_format] ||= CFPropertyList228::List::FORMAT_BINARY
403
+
404
+ plist = CFPropertyList228::List.new
405
+ plist.value = CFPropertyList228.guess(self, options)
406
+ plist.to_str(options[:plist_format], options)
407
+ end
408
+ end
409
+
410
+ class Enumerator
411
+ # convert an array to plist format
412
+ def to_plist(options={})
413
+ options[:plist_format] ||= CFPropertyList228::List::FORMAT_BINARY
414
+
415
+ plist = CFPropertyList228::List.new
416
+ plist.value = CFPropertyList228.guess(self, options)
417
+ plist.to_str(options[:plist_format], options)
418
+ end
419
+ end
420
+
421
+ class Hash
422
+ # convert a hash to plist format
423
+ def to_plist(options={})
424
+ options[:plist_format] ||= CFPropertyList228::List::FORMAT_BINARY
425
+
426
+ plist = CFPropertyList228::List.new
427
+ plist.value = CFPropertyList228.guess(self, options)
428
+ plist.to_str(options[:plist_format], options)
429
+ end
430
+ end
431
+
432
+ # eof