ruby-ole 1.2.6 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -99,6 +99,8 @@ module RecursivelyEnumerable
99
99
  end
100
100
  end
101
101
 
102
+ # don't think this is actually a proper breadth first recursion. only first
103
+ # level is breadth first.
102
104
  def each_recursive_breadth_first(&block)
103
105
  children = []
104
106
  each_child do |child|
@@ -131,41 +133,35 @@ module RecursivelyEnumerable
131
133
  # streams a "tree" form of the recursively enumerable structure to +io+, or
132
134
  # return a string form instead if +io+ is not specified.
133
135
  #
134
- # mostly a debugging aid. can specify a different +method+ to call if desired,
135
- # though it should return a single line string.
136
- def to_tree io=nil, method=:inspect
137
- unless io
138
- to_tree io = StringIO.new
139
- return io.string
140
- end
141
- io << "- #{send method}\n"
142
- to_tree_helper io, method, ' '
143
- end
144
-
145
- def to_tree_helper io, method, prefix
146
- # i need to know when i get to the last child. use delay to know
147
- child = nil
148
- each_child do |next_child|
149
- if child
150
- io << "#{prefix}|- #{child.send method}\n"
151
- if child.respond_to? :to_tree_helper
152
- child.to_tree_helper io, method, prefix + '| '
136
+ # mostly a debugging aid. can specify a different block which will be called
137
+ # to provide the string form for each node.
138
+ def to_tree io='', &inspect
139
+ inspect ||= :inspect.to_proc
140
+ io << "- #{inspect[self]}\n"
141
+ recurse = proc do |node, prefix|
142
+ child = nil
143
+ node.each_child do |next_child|
144
+ if child
145
+ io << "#{prefix}|- #{inspect[child]}\n"
146
+ recurse.call child, prefix + '| '
153
147
  end
148
+ child = next_child
149
+ end if node.respond_to?(:each_child)
150
+ if child
151
+ io << "#{prefix}\\- #{inspect[child]}\n"
152
+ recurse.call child, prefix + ' '
154
153
  end
155
- child = next_child
156
- end
157
- return unless child
158
- # child is the last child
159
- io << "#{prefix}\\- #{child.send method}\n"
160
- if child.respond_to? :to_tree_helper
161
- child.to_tree_helper io, method, prefix + ' '
162
154
  end
155
+ recurse.call self, ' '
156
+ io
163
157
  end
164
- protected :to_tree_helper
165
158
  end
166
159
 
167
160
  # can include File::Constants
168
161
  class IO
162
+ # this is for jruby
163
+ include File::Constants unless defined?(RDONLY)
164
+
169
165
  # nabbed from rubinius, and modified
170
166
  def self.parse_mode mode
171
167
  ret = 0
@@ -1,243 +1,2 @@
1
- require 'iconv'
2
- require 'date'
3
-
4
- require 'ole/base'
5
-
6
- module Ole # :nodoc:
7
- #
8
- # The Types module contains all the serialization and deserialization code for standard ole
9
- # types.
10
- #
11
- # It also defines all the variant type constants, and symbolic names.
12
- #
13
- module Types
14
- # for anything that we don't have serialization code for
15
- class Data < String
16
- def self.load str
17
- new str
18
- end
19
-
20
- def self.dump str
21
- str.to_s
22
- end
23
- end
24
-
25
- class Lpstr < String
26
- def self.load str
27
- # not sure if its always there, but there is often a trailing
28
- # null byte.
29
- new str.chomp(0.chr)
30
- end
31
-
32
- def self.dump str
33
- # do i need to append the null byte?
34
- str.to_s
35
- end
36
- end
37
-
38
- # for VT_LPWSTR
39
- class Lpwstr < String
40
- FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
41
- TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
42
-
43
- def self.load str
44
- new FROM_UTF16.iconv(str).chomp(0.chr)
45
- end
46
-
47
- def self.dump str
48
- # need to append nulls?
49
- TO_UTF16.iconv str
50
- end
51
- end
52
-
53
- # for VT_FILETIME
54
- class FileTime < DateTime
55
- SIZE = 8
56
- EPOCH = new 1601, 1, 1
57
-
58
- # Create a +DateTime+ object from a struct +FILETIME+
59
- # (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
60
- #
61
- # Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
62
- # the 100's of nanoseconds since 1st january 1601 (Epoch).
63
- def self.load str
64
- low, high = str.to_s.unpack 'V2'
65
- # we ignore these, without even warning about it
66
- return nil if low == 0 and high == 0
67
- # switched to rational, and fixed the off by 1 second error i sometimes got.
68
- # time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
69
- # use const_get to ensure we can return anything which subclasses this (VT_DATE?)
70
- const_get('EPOCH') + Rational(high * (1 << 32) + low, 1e7.to_i * 86400) rescue return
71
- # extra sanity check...
72
- #unless (1800...2100) === time.year
73
- # Log.warn "ignoring unlikely time value #{time.to_s}"
74
- # return nil
75
- #end
76
- #time
77
- end
78
-
79
- # +time+ should be able to be either a Time, Date, or DateTime.
80
- def self.dump time
81
- # i think i'll convert whatever i get to be a datetime, because of
82
- # the covered range.
83
- return 0.chr * SIZE unless time
84
- time = time.send(:to_datetime) if Time === time
85
- # don't bother to use const_get here
86
- bignum = (time - EPOCH) * 86400 * 1e7.to_i
87
- high, low = bignum.divmod 1 << 32
88
- [low, high].pack 'V2'
89
- end
90
- end
91
-
92
- # for VT_CLSID
93
- # Unlike most of the other conversions, the Guid's are serialized/deserialized by actually
94
- # doing nothing! (eg, _load & _dump are null ops)
95
- # Rather, its just a string with a different inspect string, and it includes a
96
- # helper method for creating a Guid from that readable form (#format).
97
- class Clsid < String
98
- SIZE = 16
99
- PACK = 'V v v CC C6'
100
-
101
- def self.load str
102
- new str.to_s
103
- end
104
-
105
- def self.dump guid
106
- return 0.chr * SIZE unless guid
107
- # allow use of plain strings in place of guids.
108
- guid['-'] ? parse(guid) : guid
109
- end
110
-
111
- def self.parse str
112
- vals = str.scan(/[a-f\d]+/i).map(&:hex)
113
- if vals.length == 5
114
- # this is pretty ugly
115
- vals[3] = ('%04x' % vals[3]).scan(/../).map(&:hex)
116
- vals[4] = ('%012x' % vals[4]).scan(/../).map(&:hex)
117
- guid = new vals.flatten.pack(PACK)
118
- return guid unless guid.delete('{}') == str.downcase.delete('{}')
119
- end
120
- raise ArgumentError, 'invalid guid - %p' % str
121
- end
122
-
123
- def format
124
- "%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}" % unpack(PACK)
125
- end
126
-
127
- def inspect
128
- "#<#{self.class}:{#{format}}>"
129
- end
130
- end
131
-
132
- #
133
- # The OLE variant types, extracted from
134
- # http://www.marin.clara.net/COM/variant_type_definitions.htm.
135
- #
136
- # A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
137
- #
138
- # Use like:
139
- #
140
- # p Ole::Types::Variant::NAMES[0x001f] => 'VT_LPWSTR'
141
- # p Ole::Types::VT_DATE # => 7
142
- #
143
- # The serialization / deserialization functions should be fixed to make it easier
144
- # to work with. like
145
- #
146
- # Ole::Types.from_str(VT_DATE, data) # and
147
- # Ole::Types.to_str(VT_DATE, data)
148
- #
149
- # Or similar, rather than having to do VT_* <=> ad hoc class name etc as it is
150
- # currently.
151
- #
152
- module Variant
153
- NAMES = {
154
- 0x0000 => 'VT_EMPTY',
155
- 0x0001 => 'VT_NULL',
156
- 0x0002 => 'VT_I2',
157
- 0x0003 => 'VT_I4',
158
- 0x0004 => 'VT_R4',
159
- 0x0005 => 'VT_R8',
160
- 0x0006 => 'VT_CY',
161
- 0x0007 => 'VT_DATE',
162
- 0x0008 => 'VT_BSTR',
163
- 0x0009 => 'VT_DISPATCH',
164
- 0x000a => 'VT_ERROR',
165
- 0x000b => 'VT_BOOL',
166
- 0x000c => 'VT_VARIANT',
167
- 0x000d => 'VT_UNKNOWN',
168
- 0x000e => 'VT_DECIMAL',
169
- 0x0010 => 'VT_I1',
170
- 0x0011 => 'VT_UI1',
171
- 0x0012 => 'VT_UI2',
172
- 0x0013 => 'VT_UI4',
173
- 0x0014 => 'VT_I8',
174
- 0x0015 => 'VT_UI8',
175
- 0x0016 => 'VT_INT',
176
- 0x0017 => 'VT_UINT',
177
- 0x0018 => 'VT_VOID',
178
- 0x0019 => 'VT_HRESULT',
179
- 0x001a => 'VT_PTR',
180
- 0x001b => 'VT_SAFEARRAY',
181
- 0x001c => 'VT_CARRAY',
182
- 0x001d => 'VT_USERDEFINED',
183
- 0x001e => 'VT_LPSTR',
184
- 0x001f => 'VT_LPWSTR',
185
- 0x0040 => 'VT_FILETIME',
186
- 0x0041 => 'VT_BLOB',
187
- 0x0042 => 'VT_STREAM',
188
- 0x0043 => 'VT_STORAGE',
189
- 0x0044 => 'VT_STREAMED_OBJECT',
190
- 0x0045 => 'VT_STORED_OBJECT',
191
- 0x0046 => 'VT_BLOB_OBJECT',
192
- 0x0047 => 'VT_CF',
193
- 0x0048 => 'VT_CLSID',
194
- 0x0fff => 'VT_ILLEGALMASKED',
195
- 0x0fff => 'VT_TYPEMASK',
196
- 0x1000 => 'VT_VECTOR',
197
- 0x2000 => 'VT_ARRAY',
198
- 0x4000 => 'VT_BYREF',
199
- 0x8000 => 'VT_RESERVED',
200
- 0xffff => 'VT_ILLEGAL'
201
- }
202
-
203
- CLASS_MAP = {
204
- # haven't seen one of these. wonder if its same as FILETIME?
205
- #'VT_DATE' => ?,
206
- 'VT_LPSTR' => Lpstr,
207
- 'VT_LPWSTR' => Lpwstr,
208
- 'VT_FILETIME' => FileTime,
209
- 'VT_CLSID' => Clsid
210
- }
211
-
212
- module Constants
213
- NAMES.each { |num, name| const_set name, num }
214
- end
215
-
216
- def self.load type, str
217
- type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
218
- (CLASS_MAP[type] || Data).load str
219
- end
220
-
221
- def self.dump type, variant
222
- type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
223
- (CLASS_MAP[type] || Data).dump variant
224
- end
225
- end
226
-
227
- include Variant::Constants
228
-
229
- # deprecated aliases, kept mostly for the benefit of ruby-msg, until
230
- # i release a new version.
231
- def self.load_guid str
232
- Variant.load VT_CLSID, str
233
- end
234
-
235
- def self.load_time str
236
- Variant.load VT_FILETIME, str
237
- end
238
-
239
- FROM_UTF16 = Lpwstr::FROM_UTF16
240
- TO_UTF16 = Lpwstr::TO_UTF16
241
- end
242
- end
243
-
1
+ require 'ole/types/base'
2
+ require 'ole/types/property_set'
@@ -0,0 +1,247 @@
1
+ require 'iconv'
2
+ require 'date'
3
+
4
+ require 'ole/base'
5
+
6
+ module Ole # :nodoc:
7
+ #
8
+ # The Types module contains all the serialization and deserialization code for standard ole
9
+ # types.
10
+ #
11
+ # It also defines all the variant type constants, and symbolic names.
12
+ #
13
+ module Types
14
+ # for anything that we don't have serialization code for
15
+ class Data < String
16
+ def self.load str
17
+ new str
18
+ end
19
+
20
+ def self.dump str
21
+ str.to_s
22
+ end
23
+ end
24
+
25
+ class Lpstr < String
26
+ def self.load str
27
+ # not sure if its always there, but there is often a trailing
28
+ # null byte.
29
+ new str.chomp(0.chr)
30
+ end
31
+
32
+ def self.dump str
33
+ # do i need to append the null byte?
34
+ str.to_s
35
+ end
36
+ end
37
+
38
+ # for VT_LPWSTR
39
+ class Lpwstr < String
40
+ FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
41
+ TO_UTF16 = Iconv.new 'utf-16le', 'utf-8'
42
+
43
+ def self.load str
44
+ new FROM_UTF16.iconv(str).chomp(0.chr)
45
+ end
46
+
47
+ def self.dump str
48
+ # need to append nulls?
49
+ TO_UTF16.iconv str
50
+ end
51
+ end
52
+
53
+ # for VT_FILETIME
54
+ class FileTime < DateTime
55
+ SIZE = 8
56
+ EPOCH = new 1601, 1, 1
57
+
58
+ # Create a +DateTime+ object from a struct +FILETIME+
59
+ # (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
60
+ #
61
+ # Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
62
+ # the 100's of nanoseconds since 1st january 1601 (Epoch).
63
+ def self.load str
64
+ low, high = str.to_s.unpack 'V2'
65
+ # we ignore these, without even warning about it
66
+ return nil if low == 0 and high == 0
67
+ # switched to rational, and fixed the off by 1 second error i sometimes got.
68
+ # time = EPOCH + (high * (1 << 32) + low) / 1e7 / 86400 rescue return
69
+ # use const_get to ensure we can return anything which subclasses this (VT_DATE?)
70
+ const_get('EPOCH') + Rational(high * (1 << 32) + low, 1e7.to_i * 86400) rescue return
71
+ # extra sanity check...
72
+ #unless (1800...2100) === time.year
73
+ # Log.warn "ignoring unlikely time value #{time.to_s}"
74
+ # return nil
75
+ #end
76
+ #time
77
+ end
78
+
79
+ # +time+ should be able to be either a Time, Date, or DateTime.
80
+ def self.dump time
81
+ # i think i'll convert whatever i get to be a datetime, because of
82
+ # the covered range.
83
+ return 0.chr * SIZE unless time
84
+ time = time.send(:to_datetime) if Time === time
85
+ # don't bother to use const_get here
86
+ bignum = (time - EPOCH) * 86400 * 1e7.to_i
87
+ high, low = bignum.divmod 1 << 32
88
+ [low, high].pack 'V2'
89
+ end
90
+
91
+ def inspect
92
+ "#<#{self.class} #{to_s}>"
93
+ end
94
+ end
95
+
96
+ # for VT_CLSID
97
+ # Unlike most of the other conversions, the Guid's are serialized/deserialized by actually
98
+ # doing nothing! (eg, _load & _dump are null ops)
99
+ # Rather, its just a string with a different inspect string, and it includes a
100
+ # helper method for creating a Guid from that readable form (#format).
101
+ class Clsid < String
102
+ SIZE = 16
103
+ PACK = 'V v v CC C6'
104
+
105
+ def self.load str
106
+ new str.to_s
107
+ end
108
+
109
+ def self.dump guid
110
+ return 0.chr * SIZE unless guid
111
+ # allow use of plain strings in place of guids.
112
+ guid['-'] ? parse(guid) : guid
113
+ end
114
+
115
+ def self.parse str
116
+ vals = str.scan(/[a-f\d]+/i).map(&:hex)
117
+ if vals.length == 5
118
+ # this is pretty ugly
119
+ vals[3] = ('%04x' % vals[3]).scan(/../).map(&:hex)
120
+ vals[4] = ('%012x' % vals[4]).scan(/../).map(&:hex)
121
+ guid = new vals.flatten.pack(PACK)
122
+ return guid if guid.format.delete('{}') == str.downcase.delete('{}')
123
+ end
124
+ raise ArgumentError, 'invalid guid - %p' % str
125
+ end
126
+
127
+ def format
128
+ "%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}" % unpack(PACK)
129
+ end
130
+
131
+ def inspect
132
+ "#<#{self.class}:{#{format}}>"
133
+ end
134
+ end
135
+
136
+ #
137
+ # The OLE variant types, extracted from
138
+ # http://www.marin.clara.net/COM/variant_type_definitions.htm.
139
+ #
140
+ # A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
141
+ #
142
+ # Use like:
143
+ #
144
+ # p Ole::Types::Variant::NAMES[0x001f] => 'VT_LPWSTR'
145
+ # p Ole::Types::VT_DATE # => 7
146
+ #
147
+ # The serialization / deserialization functions should be fixed to make it easier
148
+ # to work with. like
149
+ #
150
+ # Ole::Types.from_str(VT_DATE, data) # and
151
+ # Ole::Types.to_str(VT_DATE, data)
152
+ #
153
+ # Or similar, rather than having to do VT_* <=> ad hoc class name etc as it is
154
+ # currently.
155
+ #
156
+ module Variant
157
+ NAMES = {
158
+ 0x0000 => 'VT_EMPTY',
159
+ 0x0001 => 'VT_NULL',
160
+ 0x0002 => 'VT_I2',
161
+ 0x0003 => 'VT_I4',
162
+ 0x0004 => 'VT_R4',
163
+ 0x0005 => 'VT_R8',
164
+ 0x0006 => 'VT_CY',
165
+ 0x0007 => 'VT_DATE',
166
+ 0x0008 => 'VT_BSTR',
167
+ 0x0009 => 'VT_DISPATCH',
168
+ 0x000a => 'VT_ERROR',
169
+ 0x000b => 'VT_BOOL',
170
+ 0x000c => 'VT_VARIANT',
171
+ 0x000d => 'VT_UNKNOWN',
172
+ 0x000e => 'VT_DECIMAL',
173
+ 0x0010 => 'VT_I1',
174
+ 0x0011 => 'VT_UI1',
175
+ 0x0012 => 'VT_UI2',
176
+ 0x0013 => 'VT_UI4',
177
+ 0x0014 => 'VT_I8',
178
+ 0x0015 => 'VT_UI8',
179
+ 0x0016 => 'VT_INT',
180
+ 0x0017 => 'VT_UINT',
181
+ 0x0018 => 'VT_VOID',
182
+ 0x0019 => 'VT_HRESULT',
183
+ 0x001a => 'VT_PTR',
184
+ 0x001b => 'VT_SAFEARRAY',
185
+ 0x001c => 'VT_CARRAY',
186
+ 0x001d => 'VT_USERDEFINED',
187
+ 0x001e => 'VT_LPSTR',
188
+ 0x001f => 'VT_LPWSTR',
189
+ 0x0040 => 'VT_FILETIME',
190
+ 0x0041 => 'VT_BLOB',
191
+ 0x0042 => 'VT_STREAM',
192
+ 0x0043 => 'VT_STORAGE',
193
+ 0x0044 => 'VT_STREAMED_OBJECT',
194
+ 0x0045 => 'VT_STORED_OBJECT',
195
+ 0x0046 => 'VT_BLOB_OBJECT',
196
+ 0x0047 => 'VT_CF',
197
+ 0x0048 => 'VT_CLSID',
198
+ 0x0fff => 'VT_ILLEGALMASKED',
199
+ 0x0fff => 'VT_TYPEMASK',
200
+ 0x1000 => 'VT_VECTOR',
201
+ 0x2000 => 'VT_ARRAY',
202
+ 0x4000 => 'VT_BYREF',
203
+ 0x8000 => 'VT_RESERVED',
204
+ 0xffff => 'VT_ILLEGAL'
205
+ }
206
+
207
+ CLASS_MAP = {
208
+ # haven't seen one of these. wonder if its same as FILETIME?
209
+ #'VT_DATE' => ?,
210
+ 'VT_LPSTR' => Lpstr,
211
+ 'VT_LPWSTR' => Lpwstr,
212
+ 'VT_FILETIME' => FileTime,
213
+ 'VT_CLSID' => Clsid
214
+ }
215
+
216
+ module Constants
217
+ NAMES.each { |num, name| const_set name, num }
218
+ end
219
+
220
+ def self.load type, str
221
+ type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
222
+ (CLASS_MAP[type] || Data).load str
223
+ end
224
+
225
+ def self.dump type, variant
226
+ type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
227
+ (CLASS_MAP[type] || Data).dump variant
228
+ end
229
+ end
230
+
231
+ include Variant::Constants
232
+
233
+ # deprecated aliases, kept mostly for the benefit of ruby-msg, until
234
+ # i release a new version.
235
+ def self.load_guid str
236
+ Variant.load VT_CLSID, str
237
+ end
238
+
239
+ def self.load_time str
240
+ Variant.load VT_FILETIME, str
241
+ end
242
+
243
+ FROM_UTF16 = Lpwstr::FROM_UTF16
244
+ TO_UTF16 = Lpwstr::TO_UTF16
245
+ end
246
+ end
247
+