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.



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
774
775
776
777
778
779
# File 'lib/ole/storage/base.rb', line 740

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



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def child
  @child
end

#childrenObject (readonly)

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



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

def children
  @children
end

#clsidObject

Returns the value of attribute clsid

Returns:

  • (Object)

    the current value of clsid



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def clsid
  @clsid
end

#colourObject

Returns the value of attribute colour

Returns:

  • (Object)

    the current value of colour



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def colour
  @colour
end

#create_timeObject (readonly)

Returns the value of attribute create_time.



729
730
731
# File 'lib/ole/storage/base.rb', line 729

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



693
694
695
# File 'lib/ole/storage/base.rb', line 693

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



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def first_block
  @first_block
end

#flagsObject

Returns the value of attribute flags

Returns:

  • (Object)

    the current value of flags



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def flags
  @flags
end

#idxObject

i think its just used by the tree building



733
734
735
# File 'lib/ole/storage/base.rb', line 733

def idx
  @idx
end

#modify_timeObject (readonly)

Returns the value of attribute modify_time.



729
730
731
# File 'lib/ole/storage/base.rb', line 729

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



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def modify_time_str
  @modify_time_str
end

#nameObject

Returns the value of attribute name.



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

def name
  @name
end

#name_lenObject

Returns the value of attribute name_len

Returns:

  • (Object)

    the current value of name_len



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def name_len
  @name_len
end

#name_utf16Object

Returns the value of attribute name_utf16

Returns:

  • (Object)

    the current value of name_utf16



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def name_utf16
  @name_utf16
end

#nextObject

Returns the value of attribute next

Returns:

  • (Object)

    the current value of next



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def next
  @next
end

#oleObject (readonly)

Returns the value of attribute ole.



729
730
731
# File 'lib/ole/storage/base.rb', line 729

def ole
  @ole
end

#parentObject

Returns the value of attribute parent.



730
731
732
# File 'lib/ole/storage/base.rb', line 730

def parent
  @parent
end

#prevObject

Returns the value of attribute prev

Returns:

  • (Object)

    the current value of prev



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def prev
  @prev
end

#reservedObject

Returns the value of attribute reserved

Returns:

  • (Object)

    the current value of reserved



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def reserved
  @reserved
end

#sizeObject

Returns the value of attribute size

Returns:

  • (Object)

    the current value of size



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def size
  @size
end

#typeObject (readonly)

Returns the value of attribute type.



729
730
731
# File 'lib/ole/storage/base.rb', line 729

def type
  @type
end

#type_idObject

Returns the value of attribute type_id

Returns:

  • (Object)

    the current value of type_id



693
694
695
# File 'lib/ole/storage/base.rb', line 693

def type_id
  @type_id
end

Class Method Details

.copy(src, dst) ⇒ Object

Raises:

  • (ArgumentError)


925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
# File 'lib/ole/storage/base.rb', line 925

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.



857
858
859
860
861
862
863
# File 'lib/ole/storage/base.rb', line 857

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.



816
817
818
# File 'lib/ole/storage/base.rb', line 816

def / name
	@name_lookup[name]
end

#<<(child) ⇒ Object



906
907
908
909
910
# File 'lib/ole/storage/base.rb', line 906

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

#[](idx) ⇒ Object



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

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.



914
915
916
917
918
919
920
921
922
923
# File 'lib/ole/storage/base.rb', line 914

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)


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

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

#each_child(&block) ⇒ Object



835
836
837
# File 'lib/ole/storage/base.rb', line 835

def each_child(&block)
	@children.each(&block) if dir?
end

#file?Boolean

Returns:

  • (Boolean)


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

def file?
	type == :file
end

#flatten(dirents = []) ⇒ Object

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



840
841
842
843
844
845
846
847
848
849
850
# File 'lib/ole/storage/base.rb', line 840

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



891
892
893
894
895
896
897
898
899
900
901
902
903
904
# File 'lib/ole/storage/base.rb', line 891

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)


790
791
792
793
794
795
796
797
798
799
800
# File 'lib/ole/storage/base.rb', line 790

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



802
803
804
# File 'lib/ole/storage/base.rb', line 802

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

#timeObject

move to ruby-msg. and remove from here



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

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

#to_sObject



865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
# File 'lib/ole/storage/base.rb', line 865

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