File Coverage

blib/lib/Finnigan/OLE2DirectoryEntry.pm
Criterion Covered Total %
statement 110 131 83.9
branch 25 44 56.8
condition 4 6 66.6
subroutine 12 14 85.7
pod 10 10 100.0
total 161 205 78.5


line stmt bran cond sub pod time code
1             package Finnigan::OLE2DirectoryEntry;
2              
3 2     2   20 use strict;
  2         4  
  2         81  
4 2     2   12 use warnings FATAL => qw( all );
  2         4  
  2         95  
5             our $VERSION = 0.0206;
6              
7 2     2   11 use Finnigan;
  2         3  
  2         62  
8 2     2   9 use base 'Finnigan::Decoder';
  2         5  
  2         3295  
9              
10             my $UNUSED = 0xFFFFFFFF; # -1
11             my $END_OF_CHAIN = 0xFFFFFFFE; # -2
12             my $FAT_SECTOR = 0xFFFFFFFD; # -3
13             my $DIF_SECTOR = 0xFFFFFFFC; # -4
14             my $STORAGE = 1;
15             my $ROOT = 5;
16              
17             my $DEPTH = -1; # for recursive directory listing
18              
19             my %SPECIAL = ($END_OF_CHAIN => 1, $UNUSED => 1, $FAT_SECTOR => 1, $DIF_SECTOR => 1);
20              
21             sub new {
22 5     5 1 11 my ($class, $file, $index) = @_;
23 5         14 my $self = {file => $file, index => $index};
24 5         12 bless $self, $class;
25              
26             # The directory entries are organized in a red-black tree (for
27             # efficiency of access). The following piece of code does an ordered
28             # traversal of such a tree and creates a new tree of
29             # OLE2DirectoryEntry objects whose child lists are properly ordered,
30             # to simplify the code
31              
32 5         12 my $p = $file->{properties}->[$index];
33            
34             # copy the property's data
35 5         16 $self->{name} = $p->name;
36 5         17 $self->{type} = $p->type;
37 5         14 $self->{start} = $p->start;
38 5         15 $self->{size} = $p->data_size;
39            
40             # process child nodes, if any
41 5         11 my @stack = ($index);
42 5         6 my ($left, $right, $child);
43 5         12 $index = $p->child;
44 5 100       14 unless ( $index == $UNUSED ) {
45             # start at the leftmost position
46 2         8 $left = $file->{properties}->[$index]->left;
47 2         9 $right = $file->{properties}->[$index]->right;
48 2         9 $child = $file->{properties}->[$index]->child;
49              
50 2         7 while ( $left != $UNUSED ) {
51 1         2 push @stack, $index;
52 1         2 $index = $left;
53 1         5 $left = $file->{properties}->[$index]->left;
54 1         6 $right = $file->{properties}->[$index]->right;
55 1         5 $child = $file->{properties}->[$index]->child;
56             }
57              
58 2         7 while ( $index != $self->{index} ) { # while sid != self.sid:
59 4         6 push @{$self->{children}}, new Finnigan::OLE2DirectoryEntry($file, $index);
  4         37  
60              
61             # try to move right
62 4         14 $left = $file->{properties}->[$index]->left;
63 4         14 $right = $file->{properties}->[$index]->right;
64 4         13 $child = $file->{properties}->[$index]->child;
65 4 100       9 if ( $right != $UNUSED ) {
66             # and then back to the left
67 1         2 $index = $right;
68 1         2 while ( 1 ) {
69 1         6 $left = $file->{properties}->[$index]->left;
70 1         6 $right = $file->{properties}->[$index]->right;
71 1         10 $child = $file->{properties}->[$index]->child;
72 1 50       6 last if $left == $UNUSED;
73 0         0 push @stack, $index;
74 0         0 $index = $left;
75             }
76             }
77             else {
78             # couldn't move right; move up instead
79 3         5 my $ptr;
80 3         4 while ( 1 ) {
81 3         4 $ptr = $stack[-1];
82 3         3 pop @stack;
83 3         12 $left = $file->{properties}->[$ptr]->left;
84 3         9 $right = $file->{properties}->[$ptr]->right;
85 3         10 $child = $file->{properties}->[$ptr]->child;
86 3 50       10 last if $right != $index;
87 0         0 $index = $right;
88             }
89 3         9 $left = $file->{properties}->[$index]->left;
90 3         12 $right = $file->{properties}->[$index]->right;
91 3         10 $child = $file->{properties}->[$index]->child;
92 3 50       28 $index = $ptr if $right != $ptr;
93             }
94             # in the OLE file, entries are sorted on (length, name).
95             # for convenience, we sort them on name instead.
96            
97             #self.kids.sort()
98             }
99             }
100              
101 5         15 return $self;
102             }
103              
104             sub list {
105 0     0 1 0 my ( $self, $style ) = @_;
106 0 0       0 $self->render_list_item($style, $DEPTH) unless $self->type == $ROOT;
107 0 0       0 if ( $self->{children} ) {
108 0         0 $DEPTH++;
109 0         0 $_->list($style) for @{$self->{children}};
  0         0  
110 0         0 $DEPTH--;
111             }
112             }
113              
114             sub find {
115 1     1 1 3 my ( $self, $query) = @_;
116              
117 1 50       6 die "find() must be called on the root entry" unless $self->type == $ROOT;
118              
119 1 50       5 return $self if $query eq "/";
120              
121 1         3 $query =~ s%^/+%%;
122 1         5 $query =~ s%/+$%%;
123              
124 1         5 my @name = split "\/", $query;
125              
126 1         3 my $node = $self;
127 1         4 foreach my $i ( 0 .. $#name ) {
128 2 50       11 if ( $node->{children} ) {
129 2         3 my $match = 0;
130 2         3 foreach my $child ( @{$node->{children}} ) {
  2         7  
131 3 100       10 if ( $child->name eq $name[$i]) {
132 2         3 $node = $child;
133 2         3 $match = 1;
134 2         6 last;
135             }
136             }
137 2 50       8 return undef unless $match;
138             }
139             else {
140 0         0 return undef;
141             }
142             }
143 1 50       4 return $node unless $node->name eq $self->name;
144 0         0 return undef; # not found
145             }
146              
147             sub render_list_item {
148 0     0 1 0 my ($self, $style) = @_;
149 0         0 my $size = $self->size;
150 0 0       0 my $size_text = $self->type == $STORAGE ? "" : "($size bytes)";
151 0         0 print " " x $DEPTH, $self->name, " $size_text\n";
152             }
153              
154             sub data {
155 3     3 1 7 my $self = shift;
156 3         6 my $data;
157              
158             # get the data
159             my $stream_size;
160 3 50       18 if ( $self->size ) {
161 3 50 66     8 if ( $self->size > $self->file->header->ministream_max
162             or
163             $self->type == $ROOT ) { # the data in the root entry is always in big blocks
164 3         6 $stream_size = 'big';
165             }
166             else {
167 0         0 $stream_size = 'mini';
168             }
169              
170 3         7 my $first = undef;
171 3         4 my $previous = undef;
172 3         4 my $size = 0;
173 3         10 my $fragment_group = undef;
174 3         8 my @chain = $self->file->get_chain($self->start, $stream_size);
175              
176             # assemble contiguous fragments
177 3         8 my $contiguous;
178 3         5 while ( 1 ) {
179 44         64 my $block = shift @chain;
180 44 100       89 if ( defined $block ) {
181 41         52 $contiguous = 0;
182 41 100       72 if ( not defined $first ) {
183 3         3 $first = $block;
184 3         6 $contiguous = 1;
185             }
186 41 100 66     160 if ( defined $previous and $block == $previous + 1 ) {
187 38         48 $contiguous = 1;
188             }
189 41 50       81 if ( $contiguous ) {
190 41         51 $previous = $block;
191 41         80 $size += $self->file->sector_size($stream_size);
192 41         71 next;
193             }
194             }
195 3 50       10 last unless defined $first;
196              
197 3         8 $data .= $self->file->read(
198             $stream_size, # which depot
199             $first, # where
200             $previous - $first + 1 # how many sectors
201             );
202              
203             # my $desc = sprintf "$stream_size blocks %s..%s (%s)", $first, $previous, $previous-$first+1;
204             # $desc .= sprintf " of %s bytes", $self->file->sector_size($stream_size);
205             # print "$desc\n";
206              
207 3 50       22 last unless $block;
208              
209 0         0 $first = $block;
210 0         0 $previous = $block;
211 0         0 $size = $self->file->sector_size;
212             }
213 3         10 return substr($data, 0, $self->size);
214             }
215 0         0 return undef;
216             }
217              
218             sub file {
219 50     50 1 170 shift->{file};
220             }
221              
222             sub name {
223 6     6 1 539 shift->{name};
224             }
225              
226             sub type {
227 2     2 1 14 shift->{type};
228             }
229              
230             sub size {
231 9     9 1 57 shift->{size};
232             }
233              
234             sub start {
235 3     3 1 13 shift->{start};
236             }
237              
238             1;
239             __END__