line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package File::Find::Repository ; |
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
34905
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
250
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings ; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
133
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
BEGIN |
8
|
|
|
|
|
|
|
{ |
9
|
1
|
|
|
1
|
|
7
|
use vars qw ($VERSION); |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
79
|
|
10
|
1
|
|
|
1
|
|
17
|
$VERSION = '0.03'; |
11
|
|
|
|
|
|
|
} |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
14
|
|
|
|
|
|
|
|
15
|
1
|
|
|
1
|
|
4
|
use Carp qw(carp croak confess) ; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
242
|
|
16
|
1
|
|
|
1
|
|
855
|
use English qw( -no_match_vars ) ; |
|
1
|
|
|
|
|
4363
|
|
|
1
|
|
|
|
|
4
|
|
17
|
|
|
|
|
|
|
|
18
|
1
|
|
|
1
|
|
1106
|
use Readonly ; |
|
1
|
|
|
|
|
2820
|
|
|
1
|
|
|
|
|
66
|
|
19
|
|
|
|
|
|
|
Readonly my $EMPTY_STRING => q{} ; |
20
|
|
|
|
|
|
|
|
21
|
1
|
|
|
1
|
|
6
|
use File::Spec; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
17
|
|
22
|
1
|
|
|
1
|
|
389
|
use Tie::Hash::Indexed ; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 NAME |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
File::Find::Repository - Find files in your repositories. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 SYNOPSIS |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
use File::Find::Repository ; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $locator = new File::Find::Repository |
35
|
|
|
|
|
|
|
( |
36
|
|
|
|
|
|
|
NAME => 'name you want to see when messages are displayed', |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
REPOSITORIES => |
39
|
|
|
|
|
|
|
[ |
40
|
|
|
|
|
|
|
'path', |
41
|
|
|
|
|
|
|
\&sub, |
42
|
|
|
|
|
|
|
... |
43
|
|
|
|
|
|
|
], |
44
|
|
|
|
|
|
|
) ; |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# single scalar argument |
47
|
|
|
|
|
|
|
my $located_file = $locator->Find($file_to_locate) ; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# multiple arguments |
50
|
|
|
|
|
|
|
my $located_files = $locator->Find |
51
|
|
|
|
|
|
|
( |
52
|
|
|
|
|
|
|
FILES => [...], |
53
|
|
|
|
|
|
|
REPOSITORIES => ['path', \&sub, ...], |
54
|
|
|
|
|
|
|
VERBOSE => 1, |
55
|
|
|
|
|
|
|
) ; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 DESCRIPTION |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
This module will find files in a set of repositories. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 DOCUMENTATION |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
This module will try to locate a file in the repositories you define. The repositories are either |
64
|
|
|
|
|
|
|
a string representing a local filesystem path or a sub. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
When locating a file, multiple file match can occur (each in a different repository). The default behavior is |
67
|
|
|
|
|
|
|
to return the first match. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
You can customize the behavior of the search with two callbacks. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
B will be called to allow you to add relevant information to the files that have been located. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
B will be called to let you decide which found files is returned. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=head3 Advanced example |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
This module was extracted from B, a build system, made generic and will be re-integrated in B |
78
|
|
|
|
|
|
|
in next version. Here is how it could be used for a more advanced repository search. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Let's imagine we have multiple matches for an object file in our repositories. The goal here is to not rebuild the object |
81
|
|
|
|
|
|
|
file. Selecting the first object file in the list would be too naive so we define a B callback that will select |
82
|
|
|
|
|
|
|
the most appropriate. In this case, it might involve looking in the object file digest and/or check what configuration was |
83
|
|
|
|
|
|
|
used when the object file was build. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $located_file = $locator->Find |
86
|
|
|
|
|
|
|
( |
87
|
|
|
|
|
|
|
FILES => [$file_to_locate], |
88
|
|
|
|
|
|
|
REPOSITORIES => [$build_directory, @repositories], |
89
|
|
|
|
|
|
|
WHICH => FIND_NODE_WITH_DEPENDENCIES($information_needed_to_select_the_found_file) |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# bote that FIND_NODE_WITH_DEPENDENCIES returns a sub reference |
92
|
|
|
|
|
|
|
) ; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
$located_file ||= "$build_directory/$located_file" ; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=cut |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub new |
105
|
|
|
|
|
|
|
{ |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head2 new |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Create a File::Find::Repository . |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my $locator = new File::Find::Repository |
112
|
|
|
|
|
|
|
( |
113
|
|
|
|
|
|
|
# all arguments are optional |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
NAME => 'name you want to see when messages are displayed', |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
REPOSITORIES => |
118
|
|
|
|
|
|
|
[ |
119
|
|
|
|
|
|
|
'path', |
120
|
|
|
|
|
|
|
\&sub, |
121
|
|
|
|
|
|
|
... |
122
|
|
|
|
|
|
|
], |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
INTERACTION => |
125
|
|
|
|
|
|
|
{ |
126
|
|
|
|
|
|
|
INFO = \&OnMyTerminal, |
127
|
|
|
|
|
|
|
WARN = \&WithBlinkingRedLetters, |
128
|
|
|
|
|
|
|
DIE = \&QuickAndPainless, |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
VERBOSE => 1, |
132
|
|
|
|
|
|
|
FULL_INFO => \&File::Find::Repository::TIME_AND_SIZE, |
133
|
|
|
|
|
|
|
WHICH => \&File::Find::Repository::FIRST_FOUND, |
134
|
|
|
|
|
|
|
) ; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=head3 Options |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=over 2 |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item * NAME |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
Name you want to see when messages are displayed. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=item * REPOSITORIES |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
An array reference. The elements are either scalars representing a local filesystem path or a code |
147
|
|
|
|
|
|
|
reference. The code references are passed a single argument, the file to locate, and should either |
148
|
|
|
|
|
|
|
return the located file or undef. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
This allows you to, for example, to locate the files on servers. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=item * INTERACTION |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Lets you define subs used to interact with the user. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
INTERACTION => |
157
|
|
|
|
|
|
|
{ |
158
|
|
|
|
|
|
|
INFO => \&sub, |
159
|
|
|
|
|
|
|
WARN => \&sub, |
160
|
|
|
|
|
|
|
DIE => \&sub, |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=over 4 |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item INFO |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
This sub will be used when displaying L information. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item WARN |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
This sub will be used when a warning is displayed. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item DIE |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
Used when an error occurs. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=back |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
The functions default to: |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=over 2 |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item * INFO => print |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item * WARN => Carp::carp |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item * DIE => Carp::confess |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=back |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item * VERBOSE |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
When set, informative messages will be displayed. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=item * FULL_INFO |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
This is set to a sub ref which is called for all the found files, this allows you to add information. |
198
|
|
|
|
|
|
|
See L for an example. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Passed arguments: |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=over 4 |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item * the File::Find::Repository object. |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
This is useful when you want to display a message; use the subroutines defined in $object->{INTERACTION}. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=item * The file name |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item * a hash reference. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
The found file. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=back |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=item * WHICH |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
By defaults, B will set I to I which |
219
|
|
|
|
|
|
|
return the first file found in the repositories. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Define this callback if you wish to return something else, e.g. the newest file or the largest file. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
I subroutine will be called with these arguments: |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=over 4 |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=item * the File::Find::Repository object. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
This is useful when you want to display a message; use the subroutines defined in $object->{INTERACTION}. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=item * a hash reference. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
Containing all the found files, after processing with L. The hash is ordered. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=back |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
The subroutine should return one of the array elements or undef. Note that you could also return an element |
238
|
|
|
|
|
|
|
not present in the hash. In this case, a proper documentation of your algorithm will help maintenance. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=back |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=cut |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
my ($invocant, @setup_data) = @_ ; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my $class = ref($invocant) || $invocant ; |
247
|
|
|
|
|
|
|
confess 'Invalid constructor call!' unless defined $class ; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
my $object = {} ; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my ($package, $file_name, $line) = caller() ; |
252
|
|
|
|
|
|
|
bless $object, $class ; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
$object->Setup($package, $file_name, $line, @setup_data) ; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
return($object) ; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub Setup |
262
|
|
|
|
|
|
|
{ |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=head2 Setup |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
Helper sub called by new. This is a private sub. |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=cut |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
my ($object, $package, $file_name, $line, @setup_data) = @_ ; |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
%{$object} = |
273
|
|
|
|
|
|
|
( |
274
|
|
|
|
|
|
|
NAME => "Anonymous created at $file_name:$line", |
275
|
|
|
|
|
|
|
WHICH => \&FIRST_FOUND, |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
@setup_data, |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
AT_FILE => $file_name, |
280
|
|
|
|
|
|
|
AT_LINE => $line, |
281
|
|
|
|
|
|
|
) ; |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
my $location = "$object->{AT_FILE}:$object->{AT_LINE}" ; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
$object->{VALID_OPTIONS} = |
286
|
|
|
|
|
|
|
{ |
287
|
|
|
|
|
|
|
map{$_ => 1} |
288
|
|
|
|
|
|
|
qw( |
289
|
|
|
|
|
|
|
FILES |
290
|
|
|
|
|
|
|
FULL_INFO |
291
|
|
|
|
|
|
|
INTERACTION |
292
|
|
|
|
|
|
|
REPOSITORIES |
293
|
|
|
|
|
|
|
VERBOSE |
294
|
|
|
|
|
|
|
WHICH |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
AT_FILE |
297
|
|
|
|
|
|
|
AT_LINE |
298
|
|
|
|
|
|
|
) |
299
|
|
|
|
|
|
|
} ; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
#~ $object->{INTERACTION}{INFO} ||= \&CORE::print ; |
302
|
|
|
|
|
|
|
$object->{INTERACTION}{INFO} ||= sub{print(@_) or croak "Can't print! $!"}; |
303
|
|
|
|
|
|
|
$object->{INTERACTION}{WARN} ||= \&Carp::carp ; |
304
|
|
|
|
|
|
|
$object->{INTERACTION}{DIE} ||= \&Carp::confess ; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
if(defined $object->{REPOSITORIES}) |
307
|
|
|
|
|
|
|
{ |
308
|
|
|
|
|
|
|
if('ARRAY' ne ref $object->{REPOSITORIES}) |
309
|
|
|
|
|
|
|
{ |
310
|
|
|
|
|
|
|
$object->{INTERACTION}{DIE}->("$object->{NAME}: REPOSITORIES must be an array reference at '$location'!") ; |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
for my $repository (@{$object->{REPOSITORIES}}) |
314
|
|
|
|
|
|
|
{ |
315
|
|
|
|
|
|
|
if(defined $repository) |
316
|
|
|
|
|
|
|
{ |
317
|
|
|
|
|
|
|
my $type = ref $repository ; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
if($EMPTY_STRING ne $type && 'CODE' ne $type) |
320
|
|
|
|
|
|
|
{ |
321
|
|
|
|
|
|
|
$object->{INTERACTION}{DIE}->("$object->{NAME}: invalid repository type '$type' at '$location'!") ; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
else |
325
|
|
|
|
|
|
|
{ |
326
|
|
|
|
|
|
|
$object->{INTERACTION}{DIE}->("$object->{NAME}: invalid repository [undef] at '$location'!") ; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
return(1) ; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
sub Find |
337
|
|
|
|
|
|
|
{ ## no critic (ProhibitExcessComplexity) |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=head2 Find |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# single scalar argument |
342
|
|
|
|
|
|
|
my $located_file = $locator->Find($file_to_locate) ; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# multiple arguments |
345
|
|
|
|
|
|
|
my $located_files = $locator->Find |
346
|
|
|
|
|
|
|
( |
347
|
|
|
|
|
|
|
FILES => [...], |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# optional |
350
|
|
|
|
|
|
|
REPOSITORIES => ['path', \&sub, ...], |
351
|
|
|
|
|
|
|
VERBOSE => 1, |
352
|
|
|
|
|
|
|
INTERACTION => { INFO = \&OnMyTerminal,}, |
353
|
|
|
|
|
|
|
FULL_INFO => \&File::Find::Repository::TIME_AND_SIZE, |
354
|
|
|
|
|
|
|
WHICH => \&File::Find::Repository::FIRST_FOUND, |
355
|
|
|
|
|
|
|
) ; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head3 SCALAR calling context |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
Only SCALAR calling context is allowed. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head3 Arguments |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
If a single string argument is passed to Find, a string or undef is returned. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
If multiple arguments are passed, they will override the object's values for the call duration. |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Valid arguments: |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=over 2 |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=item * FILES |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
An array ref with scalar elements. Each element represents a file to locate. The returned value will be an |
374
|
|
|
|
|
|
|
ordered hash reference. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=item * AT_FILE and AT_LINE |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
These will be used in the information message and the history information if set. If not set, the values |
379
|
|
|
|
|
|
|
returned by I will be used. B that report the |
380
|
|
|
|
|
|
|
callers location properly. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
All arguments passed to L, except B are also valid arguments to L. |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=back |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=cut |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
my ($self, @arguments) = @_ ; |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
my $single_file_to_find = $EMPTY_STRING ; |
391
|
|
|
|
|
|
|
my ($number_of_arguments) = scalar(@arguments) ; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my $location = "$self->{AT_FILE}:$self->{AT_LINE}" ; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
if($number_of_arguments <= 0) |
396
|
|
|
|
|
|
|
{ |
397
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: No argument at '$location'!") ; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
elsif($number_of_arguments == 1) |
400
|
|
|
|
|
|
|
{ |
401
|
|
|
|
|
|
|
if($EMPTY_STRING eq ref $arguments[0]) |
402
|
|
|
|
|
|
|
{ |
403
|
|
|
|
|
|
|
$single_file_to_find = $arguments[0] ; |
404
|
|
|
|
|
|
|
@arguments = (FILES => [@arguments]) ; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
else |
407
|
|
|
|
|
|
|
{ |
408
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: single argument must be scalar at '$location'!") ; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
my %arguments = @arguments ; |
413
|
|
|
|
|
|
|
$self->CheckOptions(\%arguments) ; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
## no critic (ProhibitLocalVars ProhibitConditionalDeclarations) |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
local $self->{FILES} = $arguments{FILES} ; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
local $self->{FULL_INFO} = $arguments{FULL_INFO} if exists $arguments{FULL_INFO} ; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
local $self->{INTERACTION}{INFO} = $arguments{INTERACTION}{INFO} if exists $arguments{INTERACTION}{INFO} ; |
422
|
|
|
|
|
|
|
local $self->{INTERACTION}{WARN} = $arguments{INTERACTION}{WARN} if exists $arguments{INTERACTION}{WARN} ; |
423
|
|
|
|
|
|
|
local $self->{INTERACTION}{DIE} = $arguments{INTERACTION}{DIE} if exists $arguments{INTERACTION}{DIE} ; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
local $self->{REPOSITORIES} = $arguments{REPOSITORIES} if exists $arguments{REPOSITORIES} ; |
426
|
|
|
|
|
|
|
local $self->{VERBOSE} = $arguments{VERBOSE} if exists $arguments{VERBOSE} ; |
427
|
|
|
|
|
|
|
local $self->{WHICH} = $arguments{WHICH} if exists $arguments{WHICH} ; |
428
|
|
|
|
|
|
|
local $self->{AT_FILE } = $arguments{AT_FILE } if exists $arguments{AT_FILE } ; |
429
|
|
|
|
|
|
|
local $self->{AT_LINE } = $arguments{AT_LINE } if exists $arguments{AT_LINE }; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
## use critic |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
$location = "$self->{AT_FILE}:$self->{AT_LINE}" ; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
if(! defined wantarray) |
436
|
|
|
|
|
|
|
{ |
437
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: not called in scalar context at '$location'!") ; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
if(wantarray) |
441
|
|
|
|
|
|
|
{ |
442
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: not called in scalar context at '$location'!") ; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
my %located_files ; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
for my $file_to_locate (@{$arguments{FILES}}) |
448
|
|
|
|
|
|
|
{ |
449
|
|
|
|
|
|
|
my $located_files = $self->FindFiles($file_to_locate) ; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
if(keys %{$located_files}) |
452
|
|
|
|
|
|
|
{ |
453
|
|
|
|
|
|
|
if($self->{FULL_INFO}) |
454
|
|
|
|
|
|
|
{ |
455
|
|
|
|
|
|
|
while (my ($file_name, $file) = each %{$located_files}) |
456
|
|
|
|
|
|
|
{ |
457
|
|
|
|
|
|
|
$self->{FULL_INFO}->($self, $file_to_locate, $file) ; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
$located_files{$file_to_locate} = $self->{WHICH}->($self, $located_files) ; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
else |
464
|
|
|
|
|
|
|
{ |
465
|
|
|
|
|
|
|
$located_files{$file_to_locate} = undef ; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
if($number_of_arguments == 1) |
470
|
|
|
|
|
|
|
{ |
471
|
|
|
|
|
|
|
return($located_files{$single_file_to_find}{FOUND_AT}) ; |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
else |
474
|
|
|
|
|
|
|
{ |
475
|
|
|
|
|
|
|
return(\%located_files) ; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub FindFiles |
482
|
|
|
|
|
|
|
{ |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=head2 FindFiles |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
This is a private sub. Do not use directly. |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Finds all the files in the repositories. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=cut |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
my ($self, $file_to_locate) = @_ ; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
my $location = "$self->{AT_FILE}:$self->{AT_LINE}" ; |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
tie my %files_found, 'Tie::Hash::Indexed' ; ## no critic |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
if(File::Spec->file_name_is_absolute($file_to_locate)) |
499
|
|
|
|
|
|
|
{ |
500
|
|
|
|
|
|
|
$self->{INTERACTION}{WARN}->("$self->{NAME}: passed absolute file path '$file_to_locate' at $location.\n") ; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
else |
503
|
|
|
|
|
|
|
{ |
504
|
|
|
|
|
|
|
$self->{INTERACTION}{INFO}->("Searching for '$file_to_locate':\n") if $self->{VERBOSE} ; |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
for my $source_directory (@{$self->{REPOSITORIES}}) |
507
|
|
|
|
|
|
|
{ |
508
|
|
|
|
|
|
|
my $searched_file = "$source_directory/$file_to_locate" ; |
509
|
|
|
|
|
|
|
my $file_found ; |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
my $type = ref $source_directory ; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
if($EMPTY_STRING eq $type) |
514
|
|
|
|
|
|
|
{ |
515
|
|
|
|
|
|
|
$file_found = $searched_file if( -e $searched_file) ; |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
elsif('CODE' eq $type) |
518
|
|
|
|
|
|
|
{ |
519
|
|
|
|
|
|
|
$file_found = $source_directory->($file_to_locate); |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
else |
522
|
|
|
|
|
|
|
{ |
523
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid repository type '$type' at $location.\n") ; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
if(defined $file_found) |
527
|
|
|
|
|
|
|
{ |
528
|
|
|
|
|
|
|
$files_found{$file_found} = {FOUND_AT => $file_found, EXISTS => (-e $file_found)} ; |
529
|
|
|
|
|
|
|
$self->{INTERACTION}{INFO}->(" Found in '$source_directory'\n.") if $self->{VERBOSE} ; |
530
|
|
|
|
|
|
|
} |
531
|
|
|
|
|
|
|
else |
532
|
|
|
|
|
|
|
{ |
533
|
|
|
|
|
|
|
$self->{INTERACTION}{INFO}->(" Not found in '$source_directory'.\n") if $self->{VERBOSE} ; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
return(\%files_found) ; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
sub CheckOptions |
544
|
|
|
|
|
|
|
{ |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=head2 CheckOptions |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Verifies the options passed to the members of this class. Calls B<{INTERACTION}{DIE}> in case |
549
|
|
|
|
|
|
|
of error. This shall not be used directly. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=cut |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
my ($self, $options) = @_ ; |
554
|
|
|
|
|
|
|
my $location = "$self->{AT_FILE}:$self->{AT_LINE}" ; |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
for my $option_name (keys %{$options}) |
557
|
|
|
|
|
|
|
{ |
558
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid Option '$option_name' at '$self->{AT_FILE}:$self->{AT_LINE}'!") unless exists $self->{VALID_OPTIONS}{$option_name} ; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
if |
562
|
|
|
|
|
|
|
( |
563
|
|
|
|
|
|
|
(defined $options->{AT_FILE} && ! defined $options->{AT_LINE}) |
564
|
|
|
|
|
|
|
|| (!defined $options->{AT_FILE} && defined $options->{AT_LINE}) |
565
|
|
|
|
|
|
|
) |
566
|
|
|
|
|
|
|
{ |
567
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: Incomplete option AT_FILE::AT_LINE!") ; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# check we have enough to work with |
571
|
|
|
|
|
|
|
unless(exists $options->{FILES}) |
572
|
|
|
|
|
|
|
{ |
573
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: No FILES to find at '$location'!") ; |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
if('ARRAY' ne ref $options->{FILES}) |
577
|
|
|
|
|
|
|
{ |
578
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: Invalid FILES at '$location'!") ; |
579
|
|
|
|
|
|
|
} |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
if(0 == scalar(@{$options->{FILES}})) |
582
|
|
|
|
|
|
|
{ |
583
|
|
|
|
|
|
|
$self->{INTERACTION}{DIE}->("$self->{NAME}: no entries in FILES at '$location'!") ; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
return(1) ; |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
sub FIRST_FOUND |
592
|
|
|
|
|
|
|
{ |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=head2 FIRST_FOUND |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Returns the first matching file. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=cut |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my ($object, $located_files) = @_ ; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my (@keys) = keys %{$located_files} ; |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
return($located_files->{$keys[0]}) ; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
sub TIME_AND_SIZE |
610
|
|
|
|
|
|
|
{ |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 TIME_AND_SIZE |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Adds time and size information to the matched file. |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=cut |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
my ($object, $file_name, $file) = @_ ; |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
Readonly my $YEAR_1900 => 1900 ; |
621
|
|
|
|
|
|
|
Readonly my $STAT_SIZE => 7 ; |
622
|
|
|
|
|
|
|
Readonly my $STAT_CTIME => 10 ; |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
if($file->{EXISTS}) |
625
|
|
|
|
|
|
|
{ |
626
|
|
|
|
|
|
|
my ($file_size, undef, undef, $modification_time) = (stat($file->{FOUND_AT}))[$STAT_SIZE..$STAT_CTIME]; |
627
|
|
|
|
|
|
|
my ($sec, $min, $hour, $month_day, $month, $year, $week_day, $year_day) = gmtime($modification_time) ; |
628
|
|
|
|
|
|
|
$year += $YEAR_1900 ; |
629
|
|
|
|
|
|
|
$month++ ; |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
$file->{SIZE} = $file_size ; |
632
|
|
|
|
|
|
|
$file->{DATE} = |
633
|
|
|
|
|
|
|
{ |
634
|
|
|
|
|
|
|
DAY => $month_day, |
635
|
|
|
|
|
|
|
MONTH => $month, |
636
|
|
|
|
|
|
|
YEAR => $year, |
637
|
|
|
|
|
|
|
HOUR => $hour, |
638
|
|
|
|
|
|
|
MINUTE => $min, |
639
|
|
|
|
|
|
|
SECOND => $sec, |
640
|
|
|
|
|
|
|
}; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
return(1) ; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
1 ; |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
None so far. |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
=head1 AUTHOR |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
Khemir Nadim ibn Hamouda |
657
|
|
|
|
|
|
|
CPAN ID: NKH |
658
|
|
|
|
|
|
|
mailto:nadim@khemir.net |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
This program is free software; you can redistribute |
663
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=head1 SUPPORT |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
perldoc File::Find::Repository |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
You can also look for information at: |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=over 4 |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
L |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Please report any bugs or feature requests to L . |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
We will be notified, and then you'll automatically be notified of progress on |
684
|
|
|
|
|
|
|
your bug as we make changes. |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
=item * Search CPAN |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
L |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
=back |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=head1 SEE ALSO |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
L |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=cut |