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.



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
774
775
776
777
778
# 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

Instance Attribute Details

#childObject

Returns the value of attribute child

Returns:

  • (Object)

    the current value of child



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

def child
  @child
end

#childrenObject

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



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

def children
  @children
end

#clsidObject

Returns the value of attribute clsid

Returns:

  • (Object)

    the current value of clsid



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

def clsid
  @clsid
end

#colourObject

Returns the value of attribute colour

Returns:

  • (Object)

    the current value of colour



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

def colour
  @colour
end

#create_timeObject (readonly)

Returns the value of attribute create_time.



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

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



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

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



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

def first_block
  @first_block
end

#flagsObject

Returns the value of attribute flags

Returns:

  • (Object)

    the current value of flags



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

def flags
  @flags
end

#idxObject

i think its just used by the tree building



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

def idx
  @idx
end

#modify_timeObject (readonly)

Returns the value of attribute modify_time.



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

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



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

def modify_time_str
  @modify_time_str
end

#nameObject

Returns the value of attribute name.



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

def name
  @name
end

#name_lenObject

Returns the value of attribute name_len

Returns:

  • (Object)

    the current value of name_len



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

def name_len
  @name_len
end

#name_utf16Object

Returns the value of attribute name_utf16

Returns:

  • (Object)

    the current value of name_utf16



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

def name_utf16
  @name_utf16
end

#nextObject

Returns the value of attribute next

Returns:

  • (Object)

    the current value of next



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

def next
  @next
end

#oleObject (readonly)

Returns the value of attribute ole.



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

def ole
  @ole
end

#parentObject

Returns the value of attribute parent.



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

def parent
  @parent
end

#prevObject

Returns the value of attribute prev

Returns:

  • (Object)

    the current value of prev



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

def prev
  @prev
end

#reservedObject

Returns the value of attribute reserved

Returns:

  • (Object)

    the current value of reserved



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

def reserved
  @reserved
end

#sizeObject

Returns the value of attribute size

Returns:

  • (Object)

    the current value of size



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

def size
  @size
end

#typeObject (readonly)

Returns the value of attribute type.



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

def type
  @type
end

#type_idObject

Returns the value of attribute type_id

Returns:

  • (Object)

    the current value of type_id



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

def type_id
  @type_id
end

Class Method Details

.copy(src, dst) ⇒ Object

Raises:

  • (ArgumentError)


932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
# File 'lib/ole/storage/base.rb', line 932

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.



864
865
866
867
868
869
870
# File 'lib/ole/storage/base.rb', line 864

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.



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

def / name
	@name_lookup[name]
end

#<<(child) ⇒ Object



913
914
915
916
917
# File 'lib/ole/storage/base.rb', line 913

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

#[](idx) ⇒ Object



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

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.



921
922
923
924
925
926
927
928
929
930
# File 'lib/ole/storage/base.rb', line 921

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)


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

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

#each_child(&block) ⇒ Object



842
843
844
# File 'lib/ole/storage/base.rb', line 842

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

#file?Boolean

Returns:

  • (Boolean)


813
814
815
# File 'lib/ole/storage/base.rb', line 813

def file?
	type == :file
end

#flatten(dirents = []) ⇒ Object

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



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

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



898
899
900
901
902
903
904
905
906
907
908
909
910
911
# File 'lib/ole/storage/base.rb', line 898

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)


797
798
799
800
801
802
803
804
805
806
807
# File 'lib/ole/storage/base.rb', line 797

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



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

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

#timeObject

move to ruby-msg. and remove from here



837
838
839
840
# File 'lib/ole/storage/base.rb', line 837

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

#to_sObject



872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
# File 'lib/ole/storage/base.rb', line 872

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