Class | Ole::Storage::Dirent |
In: |
lib/ole/storage/base.rb
|
Parent: | 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.
PACK | = | 'a64 v C C V3 a16 V a8 a8 V2 a4' | ||
SIZE | = | 128 | ||
TYPE_MAP | = | { # this is temporary 0 => :empty, 1 => :dir, 2 => :file, 5 => :root | ||
COLOUR_MAP | = | { 0 => :red, 1 => :black | something to do with the fact that the tree is supposed to be red-black | |
EOT | = | 0xffffffff | used in the next / prev / child stuff to show that the tree ends here. also used for first_block for directory. | |
DEFAULT | = | [ 0.chr * 2, 2, 0, # will get overwritten 1, EOT, EOT, EOT, 0.chr * 16, 0, nil, nil, AllocationTable::EOC, 0, 0.chr * 4 |
children | [RW] | This returns all the children of this Dirent. It is filled in when the tree structure is recreated. |
create_time | [R] | |
idx | [RW] | i think its just used by the tree building |
modify_time | [R] | |
name | [RW] | |
ole | [R] | |
type | [R] |
# File lib/ole/storage/base.rb, line 898 898: def self.copy src, dst 899: # copies the contents of src to dst. must be the same type. this will throw an 900: # error on copying to root. maybe this will recurse too much for big documents?? 901: raise ArgumentError, 'differing types' if src.file? and !dst.file? 902: dst.name = src.name 903: if src.dir? 904: src.children.each do |src_child| 905: dst_child = Dirent.new dst.ole, :type => src_child.type 906: dst.children << dst_child 907: Dirent.copy src_child, dst_child 908: end 909: else 910: src.open do |src_io| 911: dst.open { |dst_io| IO.copy src_io, dst_io } 912: end 913: end 914: 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 842 842: def self.flatten_helper children 843: return EOT if children.empty? 844: i = children.length / 2 845: this = children[i] 846: this.prev, this.next = [(0...i), (i+1..-1)].map { |r| flatten_helper children[r] } 847: this.idx 848: end
# File lib/ole/storage/base.rb, line 722 722: def initialize ole, values=DEFAULT, params={} 723: @ole = ole 724: values, params = DEFAULT, values if Hash === values 725: values = values.unpack(PACK) if String === values 726: super(*values) 727: 728: # extra parsing from the actual struct values 729: @name = params[:name] || Types::Variant.load(Types::VT_LPWSTR, name_utf16[0...name_len]) 730: @type = if params[:type] 731: unless TYPE_MAP.values.include?(params[:type]) 732: raise ArgumentError, "unknown type #{params[:type].inspect}" 733: end 734: params[:type] 735: else 736: TYPE_MAP[type_id] or raise FormatError, "unknown type_id #{type_id.inspect}" 737: end 738: 739: # further extra type specific stuff 740: if file? 741: default_time = @ole.params[:update_timestamps] ? Time.now : nil 742: @create_time ||= default_time 743: @modify_time ||= default_time 744: @create_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if create_time_str 745: @modify_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if modify_time_str 746: @children = nil 747: else 748: @create_time = nil 749: @modify_time = nil 750: self.size = 0 unless @type == :root 751: @children = [] 752: end 753: 754: # to silence warnings. used for tree building at load time 755: # only. 756: @idx = nil 757: end
maybe need some options regarding case sensitivity.
# File lib/ole/storage/base.rb, line 801 801: def / name 802: children.find { |child| name === child.name } 803: end
# File lib/ole/storage/base.rb, line 805 805: def [] idx 806: if String === idx 807: #warn 'String form of Dirent#[] is deprecated' 808: self / idx 809: else 810: super 811: end 812: end
# File lib/ole/storage/base.rb, line 891 891: def delete child 892: # remove from our child array, so that on reflatten and re-creation of @dirents, it will be gone 893: raise ArgumentError, "#{child.inspect} not a child of #{self.inspect}" unless @children.delete child 894: # free our blocks 895: child.open { |io| io.truncate 0 } 896: end
# File lib/ole/storage/base.rb, line 795 795: def dir? 796: # to count root as a dir. 797: !file? 798: end
# File lib/ole/storage/base.rb, line 820 820: def each_child(&block) 821: @children.each(&block) 822: end
flattens the tree starting from here into dirents. note it modifies its argument.
# File lib/ole/storage/base.rb, line 825 825: def flatten dirents=[] 826: @idx = dirents.length 827: dirents << self 828: if file? 829: self.prev = self.next = self.child = EOT 830: else 831: children.each { |child| child.flatten dirents } 832: self.child = Dirent.flatten_helper children 833: end 834: dirents 835: end
# File lib/ole/storage/base.rb, line 876 876: def inspect 877: str = "#<Dirent:#{name.inspect}" 878: # perhaps i should remove the data snippet. its not that useful anymore. 879: # there is also some dir specific stuff. like clsid, flags, that i should 880: # probably include 881: if file? 882: tmp = read 9 883: data = tmp.length == 9 ? tmp[0, 5] + '...' : tmp 884: str << " size=#{size}" + 885: "#{modify_time ? ' modify_time=' + modify_time.to_s.inspect : nil}" + 886: " data=#{data.inspect}" 887: end 888: str + '>' 889: end
# File lib/ole/storage/base.rb, line 759 759: def open mode='r' 760: raise Errno::EISDIR unless file? 761: io = RangesIOMigrateable.new self, mode 762: # TODO work on the mode string stuff a bit more. 763: # maybe let the io object know about the mode, so it can refuse 764: # to work for read/write appropriately. maybe redefine all unusable 765: # methods using singleton class to throw errors. 766: # for now, i just want to implement truncation on use of 'w'. later, 767: # i need to do 'a' etc. 768: case mode 769: when 'r', 'r+' 770: # as i don't enforce reading/writing, nothing changes here. kind of 771: # need to enforce tt if i want modify times to work better. 772: @modify_time = Time.now if mode == 'r+' 773: when 'w' 774: @modify_time = Time.now 775: # io.truncate 0 776: #else 777: # raise NotImplementedError, "unsupported mode - #{mode.inspect}" 778: end 779: if block_given? 780: begin yield io 781: ensure; io.close 782: end 783: else io 784: end 785: end
# File lib/ole/storage/base.rb, line 787 787: def read limit=nil 788: open { |io| io.read limit } 789: end
move to ruby-msg. and remove from here
# File lib/ole/storage/base.rb, line 815 815: def time 816: #warn 'Dirent#time is deprecated' 817: create_time || modify_time 818: end
# File lib/ole/storage/base.rb, line 850 850: def to_s 851: tmp = Types::Variant.dump(Types::VT_LPWSTR, name) 852: tmp = tmp[0, 62] if tmp.length > 62 853: tmp += 0.chr * 2 854: self.name_len = tmp.length 855: self.name_utf16 = tmp + 0.chr * (64 - tmp.length) 856: # type_id can perhaps be set in the initializer, as its read only now. 857: self.type_id = TYPE_MAP.to_a.find { |id, name| @type == name }.first 858: # for the case of files, it is assumed that that was handled already 859: # note not dir?, so as not to override root's first_block 860: self.first_block = Dirent::EOT if type == :dir 861: if file? 862: # this is messed up. it changes the time stamps regardless of whether the file 863: # was actually touched. instead, any open call with a writeable mode, should update 864: # the modify time. create time would be set in new. 865: if @ole.params[:update_timestamps] 866: self.create_time_str = Types::Variant.dump Types::VT_FILETIME, @create_time 867: self.modify_time_str = Types::Variant.dump Types::VT_FILETIME, @modify_time 868: end 869: else 870: self.create_time_str = 0.chr * 8 871: self.modify_time_str = 0.chr * 8 872: end 873: to_a.pack PACK 874: end