Struct.new( :name_utf16, :name_len, :type_id, :colour, :prev, :next, :child, :clsid, :flags,
A class which wraps an ole directory entry. Can be either a directory (Dirent#dir?) or a file (Dirent#file?)
Most interaction with Ole::Storage is through this class. The 2 most important functions are Dirent#children, and Dirent#data.
was considering separate classes for dirs and files. some methods/attrs only applicable to one or the other.
As with the other classes, to_s performs the serialization.
# File lib/ole/storage/base.rb, line 924 def self.copy src, dst # copies the contents of src to dst. must be the same type. this will throw an # error on copying to root. maybe this will recurse too much for big documents?? raise ArgumentError, 'differing types' if src.file? and !dst.file? dst.name = src.name if src.dir? src.children.each do |src_child| dst_child = Dirent.new dst.ole, :type => src_child.type dst << dst_child Dirent.copy src_child, dst_child end else src.open do |src_io| dst.open { |dst_io| IO.copy src_io, dst_io } end end end
i think making the tree structure optimized is actually more complex than this, and requires some intelligent ordering of the children based on names, but as long as it is valid its ok. actually, i think its ok. gsf for example only outputs a singly-linked-list, where prev is always EOT.
# File lib/ole/storage/base.rb, line 856 def self.flatten_helper children return EOT if children.empty? i = children.length / 2 this = children[i] this.prev, this.next = [(0...i), (i+1..-1)].map { |r| flatten_helper children[r] } this.idx end
# File lib/ole/storage/base.rb, line 739 def initialize ole, values=DEFAULT, params={} @ole = ole values, params = DEFAULT, values if Hash === values values = values.unpack(PACK) if String === values super(*values) # extra parsing from the actual struct values @name = params[:name] || Types::Variant.load(Types::VT_LPWSTR, name_utf16[0...name_len]) @type = if params[:type] unless TYPE_MAP.values.include?(params[:type]) raise ArgumentError, "unknown type #{params[:type].inspect}" end params[:type] else TYPE_MAP[type_id] or raise FormatError, "unknown type_id #{type_id.inspect}" end # further extra type specific stuff if file? default_time = @ole.params[:update_timestamps] ? Types::FileTime.now : nil @create_time ||= default_time @modify_time ||= default_time @create_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if create_time_str @modify_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if modify_time_str @children = nil @name_lookup = nil else @create_time = nil @modify_time = nil self.size = 0 unless @type == :root @children = [] @name_lookup = {} end @parent = nil # to silence warnings. used for tree building at load time # only. @idx = nil end
maybe need some options regarding case sensitivity.
# File lib/ole/storage/base.rb, line 815 def / name @name_lookup[name] end
# File lib/ole/storage/base.rb, line 905 def << child child.parent = self @name_lookup[child.name] = child @children << child end
# File lib/ole/storage/base.rb, line 819 def [] idx if String === idx #warn 'String form of Dirent#[] is deprecated' self / idx else super end end
remove the Dirent child from the children array, truncating the data by default.
# File lib/ole/storage/base.rb, line 913 def delete child, truncate=true # remove from our child array, so that on reflatten and re-creation of @dirents, it will be gone unless @children.delete(child) raise ArgumentError, "#{child.inspect} not a child of #{self.inspect}" end @name_lookup.delete(child.name) child.parent = nil # free our blocks child.open { |io| io.truncate 0 } if child.file? end
# File lib/ole/storage/base.rb, line 809 def dir? # to count root as a dir. !file? end
# File lib/ole/storage/base.rb, line 834 def each_child(&block) @children.each(&block) if dir? end
flattens the tree starting from here into dirents. note it modifies its argument.
# File lib/ole/storage/base.rb, line 839 def flatten dirents=[] @idx = dirents.length dirents << self if file? self.prev = self.next = self.child = EOT else children.each { |child| child.flatten dirents } self.child = Dirent.flatten_helper children end dirents end
# File lib/ole/storage/base.rb, line 890 def inspect str = "#<Dirent:#{name.inspect}" # perhaps i should remove the data snippet. its not that useful anymore. # there is also some dir specific stuff. like clsid, flags, that i should # probably include if file? tmp = read 9 data = tmp.length == 9 ? tmp[0, 5] + '...' : tmp str << " size=#{size}" + "#{modify_time ? ' modify_time=' + modify_time.to_s.inspect : nil}" + " data=#{data.inspect}" end str + '>' end
# File lib/ole/storage/base.rb, line 780 def name= name if @parent map = @parent.instance_variable_get :@name_lookup map.delete @name map[name] = self end @name = name end
# File lib/ole/storage/base.rb, line 789 def open mode='r' raise Errno::EISDIR unless file? io = RangesIOMigrateable.new self, mode @modify_time = Types::FileTime.now if io.mode.writeable? if block_given? begin yield io ensure; io.close end else io end end
# File lib/ole/storage/base.rb, line 801 def read limit=nil open { |io| io.read limit } end
move to ruby-msg. and remove from here
# File lib/ole/storage/base.rb, line 829 def time #warn 'Dirent#time is deprecated' create_time || modify_time end
# File lib/ole/storage/base.rb, line 864 def to_s tmp = Types::Variant.dump(Types::VT_LPWSTR, name) tmp = tmp[0, 62] if tmp.length > 62 tmp += 0.chr * 2 self.name_len = tmp.length self.name_utf16 = tmp + 0.chr * (64 - tmp.length) # type_id can perhaps be set in the initializer, as its read only now. self.type_id = TYPE_MAP.to_a.find { |id, name| @type == name }.first # for the case of files, it is assumed that that was handled already # note not dir?, so as not to override root's first_block self.first_block = Dirent::EOT if type == :dir if file? # this is messed up. it changes the time stamps regardless of whether the file # was actually touched. instead, any open call with a writeable mode, should update # the modify time. create time would be set in new. if @ole.params[:update_timestamps] self.create_time_str = Types::Variant.dump Types::VT_FILETIME, @create_time self.modify_time_str = Types::Variant.dump Types::VT_FILETIME, @modify_time end else self.create_time_str = 0.chr * 8 self.modify_time_str = 0.chr * 8 end to_a.pack PACK end
Generated with the Darkfish Rdoc Generator 2.