Class: Ole::Storage::Dirent

Inherits:
Struct
  • Object
show all
Includes:
RecursivelyEnumerable
Defined in:
lib/ole/storage/base.rb

Overview

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.

Constant Summary collapse

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 =

something to do with the fact that the tree is supposed to be red-black

{
	0 => :red,
	1 => :black
}
EOT =

used in the next / prev / child stuff to show that the tree ends here. also used for first_block for directory.

0xffffffff
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
]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RecursivelyEnumerable

#each_recursive, #each_recursive_breadth_first, #each_recursive_depth_first, #recursive, #to_tree

Constructor Details

#initialize(ole, values = DEFAULT, params = {}) ⇒ Dirent

Returns a new instance of Dirent.



734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
# File 'lib/ole/storage/base.rb', line 734

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

Instance Attribute Details

#childObject

Returns the value of attribute child

Returns:

  • (Object)

    the current value of child



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def child
  @child
end

#childrenObject (readonly)

This returns all the children of this Dirent. It is filled in when the tree structure is recreated.



721
722
723
# File 'lib/ole/storage/base.rb', line 721

def children
  @children
end

#clsidObject

Returns the value of attribute clsid

Returns:

  • (Object)

    the current value of clsid



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def clsid
  @clsid
end

#colourObject

Returns the value of attribute colour

Returns:

  • (Object)

    the current value of colour



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def colour
  @colour
end

#create_timeObject (readonly)

Returns the value of attribute create_time.



723
724
725
# File 'lib/ole/storage/base.rb', line 723

def create_time
  @create_time
end

#create_time_strObject

Returns the value of attribute create_time_str

Returns:

  • (Object)

    the current value of create_time_str



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def create_time_str
  @create_time_str
end

#first_blockObject

Returns the value of attribute first_block

Returns:

  • (Object)

    the current value of first_block



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def first_block
  @first_block
end

#flagsObject

Returns the value of attribute flags

Returns:

  • (Object)

    the current value of flags



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def flags
  @flags
end

#idxObject

i think its just used by the tree building



727
728
729
# File 'lib/ole/storage/base.rb', line 727

def idx
  @idx
end

#modify_timeObject (readonly)

Returns the value of attribute modify_time.



723
724
725
# File 'lib/ole/storage/base.rb', line 723

def modify_time
  @modify_time
end

#modify_time_strObject

Returns the value of attribute modify_time_str

Returns:

  • (Object)

    the current value of modify_time_str



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def modify_time_str
  @modify_time_str
end

#nameObject

Returns the value of attribute name.



722
723
724
# File 'lib/ole/storage/base.rb', line 722

def name
  @name
end

#name_lenObject

Returns the value of attribute name_len

Returns:

  • (Object)

    the current value of name_len



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def name_len
  @name_len
end

#name_utf16Object

Returns the value of attribute name_utf16

Returns:

  • (Object)

    the current value of name_utf16



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def name_utf16
  @name_utf16
end

#nextObject

Returns the value of attribute next

Returns:

  • (Object)

    the current value of next



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def next
  @next
end

#oleObject (readonly)

Returns the value of attribute ole.



723
724
725
# File 'lib/ole/storage/base.rb', line 723

def ole
  @ole
end

#parentObject

Returns the value of attribute parent.



724
725
726
# File 'lib/ole/storage/base.rb', line 724

def parent
  @parent
end

#prevObject

Returns the value of attribute prev

Returns:

  • (Object)

    the current value of prev



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def prev
  @prev
end

#reservedObject

Returns the value of attribute reserved

Returns:

  • (Object)

    the current value of reserved



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def reserved
  @reserved
end

#sizeObject

Returns the value of attribute size

Returns:

  • (Object)

    the current value of size



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def size
  @size
end

#typeObject (readonly)

Returns the value of attribute type.



723
724
725
# File 'lib/ole/storage/base.rb', line 723

def type
  @type
end

#type_idObject

Returns the value of attribute type_id

Returns:

  • (Object)

    the current value of type_id



687
688
689
# File 'lib/ole/storage/base.rb', line 687

def type_id
  @type_id
end

Class Method Details

.copy(src, dst) ⇒ Object

Raises:

  • (ArgumentError)


919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
# File 'lib/ole/storage/base.rb', line 919

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

.flatten_helper(children) ⇒ Object

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.



851
852
853
854
855
856
857
# File 'lib/ole/storage/base.rb', line 851

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

Instance Method Details

#/(name) ⇒ Object

maybe need some options regarding case sensitivity.



810
811
812
# File 'lib/ole/storage/base.rb', line 810

def / name
	@name_lookup[name]
end

#<<(child) ⇒ Object



900
901
902
903
904
# File 'lib/ole/storage/base.rb', line 900

def << child
	child.parent = self
	@name_lookup[child.name] = child
	@children << child
end

#[](idx) ⇒ Object



814
815
816
817
818
819
820
821
# File 'lib/ole/storage/base.rb', line 814

def [] idx
	if String === idx
		#warn 'String form of Dirent#[] is deprecated'
		self / idx
	else
		super
	end
end

#delete(child, truncate = true) ⇒ Object

remove the Dirent child from the children array, truncating the data by default.



908
909
910
911
912
913
914
915
916
917
# File 'lib/ole/storage/base.rb', line 908

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

#dir?Boolean

Returns:

  • (Boolean)


804
805
806
807
# File 'lib/ole/storage/base.rb', line 804

def dir?
	# to count root as a dir.
	!file?
end

#each_child(&block) ⇒ Object



829
830
831
# File 'lib/ole/storage/base.rb', line 829

def each_child(&block)
	@children.each(&block)
end

#file?Boolean

Returns:

  • (Boolean)


800
801
802
# File 'lib/ole/storage/base.rb', line 800

def file?
	type == :file
end

#flatten(dirents = []) ⇒ Object

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



834
835
836
837
838
839
840
841
842
843
844
# File 'lib/ole/storage/base.rb', line 834

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

#inspectObject



885
886
887
888
889
890
891
892
893
894
895
896
897
898
# File 'lib/ole/storage/base.rb', line 885

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

#open(mode = 'r') ⇒ Object

Raises:

  • (Errno::EISDIR)


784
785
786
787
788
789
790
791
792
793
794
# File 'lib/ole/storage/base.rb', line 784

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

#read(limit = nil) ⇒ Object



796
797
798
# File 'lib/ole/storage/base.rb', line 796

def read limit=nil
	open { |io| io.read limit }
end

#timeObject

move to ruby-msg. and remove from here



824
825
826
827
# File 'lib/ole/storage/base.rb', line 824

def time
	#warn 'Dirent#time is deprecated'
	create_time || modify_time
end

#to_sObject



859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
# File 'lib/ole/storage/base.rb', line 859

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