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,
String Data Lpstr Clsid Lpwstr Section Enumerable DateTime FileTime Constants Variant::Constants Storage\n[lib/ole/storage/base.rb\nlib/ole/storage/file_system.rb\nlib/ole/storage/meta_data.rb] PropertySet lib/ole/storage/file_system.rb lib/ole/types/property_set.rb lib/ole/types/base.rb Constants Constants Variant Types Ole dot/m_9_0.png

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.

Methods

/   []   copy   delete   dir?   each_child   file?   flatten   flatten_helper   inspect   new   open   read   time   to_s  

Included Modules

RecursivelyEnumerable

Constants

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

Attributes

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] 

Public Class methods

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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

Public Instance methods

maybe need some options regarding case sensitivity.

[Source]

     # File lib/ole/storage/base.rb, line 801
801:                         def / name
802:                                 children.find { |child| name === child.name }
803:                         end

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/ole/storage/base.rb, line 795
795:                         def dir?
796:                                 # to count root as a dir.
797:                                 !file?
798:                         end

[Source]

     # File lib/ole/storage/base.rb, line 820
820:                         def each_child(&block)
821:                                 @children.each(&block)
822:                         end

[Source]

     # File lib/ole/storage/base.rb, line 791
791:                         def file?
792:                                 type == :file
793:                         end

flattens the tree starting from here into dirents. note it modifies its argument.

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # File lib/ole/storage/base.rb, line 815
815:                         def time
816:                                 #warn 'Dirent#time is deprecated'
817:                                 create_time || modify_time
818:                         end

[Source]

     # 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

[Validate]