Class: Ole::Storage::Dirent

### Home Classes Methods

### In Files

- lib/ole/storage/base.rb

### Parent

Struct.new( :name_utf16, :name_len, :type_id, :colour, :prev, :next, :child, :clsid, :flags,

### Methods

- ::copy
- ::flatten_helper
- ::new
- #/
- #<<
- #[]
- #delete
- #dir?
- #each_child
- #file?
- #flatten
- #inspect
- #name=
- #open
- #read
- #time
- #to_s

### Files

- ChangeLog
- README

### Class/Module Index

Quicksearch

- Ole
- Ole::IOMode
- Ole::IOMode::Constants
- Ole::Storage
- Ole::Storage::AllocationTable
- Ole::Storage::AllocationTable::Big
- Ole::Storage::AllocationTable::Small
- Ole::Storage::DirClass
- Ole::Storage::DirClass::Dir
- Ole::Storage::Dirent
- Ole::Storage::FileClass
- Ole::Storage::FileClass::Stat
- Ole::Storage::Header
- Ole::Storage::MetaData
- Ole::Storage::RangesIOMigrateable
- Ole::Storage::RangesIOResizeable
- Ole::Types
- Ole::Types::Clsid
- Ole::Types::Data
- Ole::Types::FileTime
- Ole::Types::Lpstr
- Ole::Types::Lpwstr
- Ole::Types::PropertySet
- Ole::Types::PropertySet::Constants
- Ole::Types::PropertySet::Section
- Ole::Types::Variant
- Ole::Types::Variant::Constants
- RangesIO

No matching classes.

# Ole::Storage::Dirent

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.

### Constants

COLOUR_MAP

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

DEFAULT
EOT

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

PACK
SIZE
TYPE_MAP

### Attributes

children[R]

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[R]
name_lookup[R]

these are for internal use and are used for faster lookup.

ole[R]
parent[RW]
type[R]

### Public Class Methods

copy(src, dst) click to toggle source

    # File lib/ole/storage/base.rb, line 924
    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) click to toggle source

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 856
    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

new(ole, values=DEFAULT, params={}) click to toggle source

    # 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

### Public Instance Methods

/(name) click to toggle source

maybe need some options regarding case sensitivity.

    # File lib/ole/storage/base.rb, line 815
    def / name
      @name_lookup[name]
    end

<<(child) click to toggle source

    # File lib/ole/storage/base.rb, line 905
    def << child
      child.parent = self
      @name_lookup[child.name] = child
      @children << child
    end

[](idx) click to toggle source

    # File lib/ole/storage/base.rb, line 819
    def [] idx
      if String === idx
        #warn 'String form of Dirent#[] is deprecated'
        self / idx
      else
        super
      end
    end

delete(child, truncate=true) click to toggle source

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

    # File lib/ole/storage/base.rb, line 913
    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?() click to toggle source

    # File lib/ole/storage/base.rb, line 809
    def dir?
      # to count root as a dir.
      !file?
    end

each_child(&block) click to toggle source

    # File lib/ole/storage/base.rb, line 834
    def each_child(&block)
      @children.each(&block) if dir?
    end

file?() click to toggle source

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

flatten(dirents=[]) click to toggle source

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

    # File lib/ole/storage/base.rb, line 839
    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

inspect() click to toggle source

    # File lib/ole/storage/base.rb, line 890
    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

name=(name) click to toggle source

    # File lib/ole/storage/base.rb, line 780
    def name= name
      if @parent
        map = @parent.instance_variable_get :@name_lookup
        map.delete @name
        map[name] = self
      end
      @name = name
    end

open(mode='r') click to toggle source

    # File lib/ole/storage/base.rb, line 789
    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) click to toggle source

    # File lib/ole/storage/base.rb, line 801
    def read limit=nil
      open { |io| io.read limit }
    end

time() click to toggle source

move to ruby-msg. and remove from here

    # File lib/ole/storage/base.rb, line 829
    def time
      #warn 'Dirent#time is deprecated'
      create_time || modify_time
    end

to_s() click to toggle source

    # File lib/ole/storage/base.rb, line 864
    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

[Validate]

Generated with the Darkfish Rdoc Generator 2.