Subversion Repositories DevTools

Rev

Rev 273 | Rev 379 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
#! perl
2
########################################################################
3
# Copyright (C) 2007 ERG Limited, All rights reserved
4
#
5
# Module name   : jats.sh
6
# Module type   : Makefile system
7
# Compiler(s)   : n/a
8
# Environment(s): jats
9
#
10
# Description   : A class to Locate Files
11
#                 Supportes:
12
#                   Wilcards
13
#                   Exclusions
14
#                   Recursion
15
#
16
# Usage:
17
#               my $search = JatsLocateFiles::new('JatsLocateFiles');
18
#               my $search = JatsLocateFiles->new();
19
#                  $search->recurse ( );
20
#                  $search->filter_in ( );
21
#                  $search->filter_in_re ( );
22
#                  $search->filter_out ( );
23
#                  $search->filter_out_re ( );
24
#                  $search->base_dir ()
25
#                  $search->has_filter ( );
26
#                  $search->full_path ( );
27
#                  $search->dirs_only ( );
28
#                  $search->search ( );
29
#                  $search->results ( );
30
#
31
#......................................................................#
32
 
255 dpurdie 33
require 5.006_001;
227 dpurdie 34
use strict;
35
use warnings;
36
 
37
package JatsLocateFiles;
38
use JatsError;
39
use File::Find;
40
use ArrayHashUtils;
41
 
42
#-------------------------------------------------------------------------------
43
# Function        : new
44
#
45
# Description     : Create a new instance of a searcher
46
#
47
# Inputs          : class           - Class Name
48
#                   options
49
#                   Hash of user options
50
#                   Useful keys are:
51
#                       'recurse'
52
#                       'base_dir'
53
#                       'full_path'
54
#                       'dirs_only'
55
#
56
# Returns         : A reference to the new object of the specified class
57
#
58
sub new {
59
    my $class = shift;
60
    my $self  = {};
61
    $self->{full_path} = 0;
62
    $self->{recurse}  = 0;
63
    $self->{dirs_only}  = 0;
263 dpurdie 64
    $self->{dirs_too} = 0;
227 dpurdie 65
    $self->{exclude}  = [];
66
    $self->{include}  = [];
67
    $self->{base_dir} = undef;
68
    $self->{results}  = [];
69
    bless ($self, $class);
70
 
71
    #
72
    #   Process user arguments.
73
    #   These are treated as options. Leading '--' is optional
74
    #
75
    foreach ( @_ )
76
    {
77
        my $opt = '--' . $_;
78
        $opt =~ s~^----~--~;
79
        $self->option ($opt) || Error( "JatsLocateFiles:new. Unknown initialiser: $_");
80
    }
81
    return $self;
82
}
83
 
84
#-------------------------------------------------------------------------------
85
# Function        : recurse
86
#                   filter_in
87
#                   filter_in_re
88
#                   filter_out
89
#                   filter_out_re
90
#                   base_dir
91
#                   has_filter
92
#                   results
93
#                   full_path
94
#                   dirs_only
263 dpurdie 95
#                   dirs_too                    - Include dirs in the search
227 dpurdie 96
#
97
# Description     : Accessor functions
98
#
99
# Inputs          : class
100
#                   One argument (optional)
101
#
102
# Returns         : Current value of the data item
103
#
104
sub recurse
105
{
106
    my $self = shift;
107
    if (@_) { $self->{recurse} = shift }
108
    return $self->{recurse};
109
}
110
 
111
sub full_path
112
{
113
    my $self = shift;
114
    if (@_) { $self->{full_path} = shift }
115
    return $self->{full_path};
116
}
117
 
118
sub dirs_only
119
{
120
    my $self = shift;
121
    if (@_) { $self->{dirs_only} = shift }
122
    return $self->{dirs_only};
123
}
124
 
263 dpurdie 125
sub dirs_too
126
{
127
    my $self = shift;
128
    if (@_) { $self->{dirs_too} = shift }
129
    return $self->{dirs_too};
130
}
131
 
227 dpurdie 132
sub filter_in
133
{
134
    my $self = shift;
135
    if (@_) { push @{$self->{include}}, glob2pat( shift ) }
136
    return $self->{include};
137
}
138
 
139
sub filter_in_re
140
{
141
    my $self = shift;
142
    if (@_) { push @{$self->{include}}, shift }
143
    return $self->{include};
144
}
145
 
146
sub filter_out
147
{
148
    my $self = shift;
149
    if (@_) { push @{$self->{exclude}}, glob2pat( shift ) }
150
    return $self->{exclude};
151
}
152
 
153
sub filter_out_re
154
{
155
    my $self = shift;
156
    if (@_) { push @{$self->{exclude}}, shift }
157
    return $self->{exclude};
158
}
159
 
160
sub base_dir
161
{
162
    my $self = shift;
163
    if (@_) { $self->{base_dir} = shift }
164
    return $self->{base_dir};
165
}
166
 
167
sub has_filter
168
{
169
    my $self = shift;
170
    return ( ( @{$self->{include}} || @{$self->{exclude}} ) );
171
}
172
 
173
sub results
174
{
175
    my $self = shift;
176
    $self->{results} = \() unless ( exists $self->{results} );
177
    return wantarray ? @{$self->{results}} : 1 + $#{$self->{results}};
178
}
179
 
180
#-------------------------------------------------------------------------------
181
# Function        : option
182
#
183
# Description     : Function to simplify the processing of search arguments
184
#                   Given an argument this function will act on it or
185
#                   return false
186
#
187
# Inputs          : option          - One possible standard search option
188
#
189
# Returns         : True            - Option is a search option and its been
190
#                                     processed
191
#
192
sub option
193
{
194
    my ($self, $opt) = @_;
195
    my $result = 1;
196
 
197
    if ( $opt =~ m/^--Dir=(.+)/ ) {
198
        $self->base_dir ($1);
199
 
200
    } elsif ( $opt =~ m/^--Recurse/ ) {
201
        $self->recurse(1);
202
 
203
    } elsif ( $opt =~ m/^--NoRecurse/ ) {
331 dpurdie 204
        $self->recurse(0);
227 dpurdie 205
 
206
    } elsif ( $opt =~ m/^--NoFullPath/ ) {
207
        $self->full_path(0);
208
 
209
    } elsif ( $opt =~ m/^--FullPath/ ) {
210
        $self->full_path(1);
211
 
212
    } elsif ( $opt =~ m/^--FileListOnly/ ) {
213
        $self->full_path(0);
214
        $self->dirs_only(0);
215
 
216
    } elsif ( $opt =~ m/^--DirListOnly/ ) {
217
        $self->dirs_only(1);
218
 
263 dpurdie 219
    } elsif ( $opt =~ m/^--DirsToo/ ) {
220
        $self->dirs_too(1);
221
 
227 dpurdie 222
    } elsif ( $opt =~ m/^--FilterIn=(.+)/ ) {
223
        $self->filter_in ( $1 );
224
 
225
    } elsif ( $opt =~ m/^--FilterInRe=(.+)/ ) {
226
        $self->filter_in_re ( $1 );
227
 
228
    } elsif ( $opt =~ m/^--FilterOut=(.+)/ ) {
229
        $self->filter_out ( $1 );
230
 
231
    } elsif ( $opt =~ m/^--FilterOutRe=(.+)/ ) {
232
        $self->filter_out_re ( $1 );
233
 
234
    } else {
235
        $result = 0;
236
 
237
    }
238
 
239
    return $result;
240
}
241
 
242
#-------------------------------------------------------------------------------
243
# Function        : search
244
#
245
# Description     : This function performs the search for files as specified
246
#                   by the arguments already provided
247
#
248
# Inputs          : base_dir (Optional)
249
#
250
# Returns         : List of files that match the search criteria
251
#                   The base directory is not prepended
252
#                   The list is a simple list of file names
253
#
254
 
255
my @search_list;             # Must be global to avoid closure problems
256
my $search_len;
263 dpurdie 257
my $search_base_dir;
258
my $search_dirs_too;
227 dpurdie 259
 
260
sub search
261
{
262
    my $self = shift;
263
    $self->{base_dir} = $_[0] if (defined $_[0] );
264
    $self->{results} = ();
265
 
266
    #
267
    #   Ensure user has provided enough info
268
    #
269
    Error ("JatsLocateFiles: No base directory provided") unless ( $self->{base_dir} );
270
 
271
    #
272
    #   Clean up the user dir. Remove any trailing / as we will be adding it back
273
    #
274
    $self->{base_dir} =~ s~/*$~~g;
275
 
276
    #
277
    #   Init recursion information
278
    #   Needed to avoid closure interactions
279
    #
280
    @search_list = ();
281
    $search_len = 1 + length( $self->{base_dir} );
282
 
283
    #
284
    #   Create a list of candidate files
285
    #   If we are recursing the subtree, then this is a little harder
286
    #   If we are not recursing then we can't simply glob the directory as
287
    #   not all files are processed.
288
    #
289
    #   Will end up with a list of files that don't include $dir
290
    #
291
    if ( -d $self->{base_dir} )
292
    {
293
        if ( $self->{recurse} )
294
        {
263 dpurdie 295
            $search_dirs_too = $self->{dirs_too};
296
            $search_base_dir = $self->{base_dir};
227 dpurdie 297
            sub find_file_wanted
298
            {
263 dpurdie 299
                return if ( !$search_dirs_too && -d $_ );               # skip if current is dir and we are not including dirs
300
                return if ( $search_base_dir eq $File::Find::name );    # skip if current is base_dir as we dont include it
227 dpurdie 301
                my $file = $File::Find::name;
302
                push @search_list, substr($file, $search_len );
303
            }
304
 
305
            #
306
            #       Under Unix we need to follow symbolic links, but Perl's
307
            #       Find:find does not work with -follow under windows if the source
308
            #       path contains a drive letter.
309
            #
310
            #       Solution. Only use follow under non-windows systems.
311
            #                 Works as Windows does not have symlinks (yet).
312
            #
313
            my $follow_opt =  ($ENV{GBE_UNIX}  > 0);
314
 
315
            File::Find::find( {wanted => \&find_file_wanted, follow_fast => $follow_opt }, $self->{base_dir} );
316
        }
317
        else
318
        {
319
            local *DIR ;
320
            opendir DIR, $self->{base_dir} || die ("Cannot open $self->{base_dir}");
321
            foreach ( readdir( DIR ) )
322
            {
323
                next if /^\Q.\E$/;
324
                next if /^\Q..\E$/;
263 dpurdie 325
                next if ( !$self->{dirs_too} && -d "$self->{base_dir}/$_" );
227 dpurdie 326
                push @search_list, $_;
327
            }
328
            closedir DIR;
329
        }
330
    }
331
 
332
 
333
    my @result;
334
    if ( @{$self->{include}} || @{$self->{exclude}} )
335
    {
336
        #
337
        #   Filtering is present
338
        #   Apply the filterin rules and then the filter out rules
339
        #   If no filter-in rules, then assume that all files are allowed in and
340
        #   simply apply the filter-out rules.
341
        #
342
        my @patsin  = map { qr/$_/ } @{$self->{include}};
343
        my @patsout = map { qr/$_/ } @{$self->{exclude}};
344
 
345
    #    map { print "Include:$_\n"; } @{$self->{include}};
346
    #    map { print "Exclude:$_\n"; } @{$self->{exclude}};
347
 
348
 
349
        file:
350
        foreach my $rfile ( @search_list )
351
        {
352
            my $file = '/' . $rfile;
353
            if ( @{$self->{include}} )
354
            {
355
                my $in = 0;
356
                for my $pat (@patsin)
357
                {
358
                    if ( $file =~ /$pat/ )
359
                    {
360
                        $in = 1;
361
                        last;
362
                    }
363
                }
364
    #print "------- Not included $file\n" unless $in;
365
                next unless ( $in );
366
            }
367
 
368
            for my $pat (@patsout)
369
            {
370
    #print "------- REJECT $file :: $pat \n" if ( $file =~ /$pat/ );
371
                next file if ( $file =~ /$pat/ );
372
            }
373
 
374
            push @result, $rfile;
375
        }
376
    }
377
    else
378
    {
379
        @result = @search_list ;
380
    }
381
    @search_list = ();
382
    $self->{results} = [];;
383
 
384
    #
385
    #   Reattach the base directory, if required
386
    #       full_path  : Prepend full path
387
    #       dirs_only  : return list of dirs that have files
388
    #   Extract dirs only
389
    #
390
    foreach  ( @result )
391
    {
392
        my $path;
393
        if ( $self->{full_path} )
394
        {
395
            $path = $self->{base_dir} . '/' . $_;
396
        }
397
        else
398
        {
399
            $path = $_;
400
        }
401
 
402
        if ( $self->{dirs_only} )
403
        {
404
            $path =~ s~/[^/]*$~~;
405
        }
406
 
407
        UniquePush( $self->{results}, $path);
408
    }
409
#DebugDumpData ("Search", $self);
410
    return @{$self->{results}};
411
}
412
 
413
#-------------------------------------------------------------------------------
414
# Function        : glob2pat
415
#
416
# Description     : Convert four shell wildcard characters into their equivalent
417
#                   regular expression; all other characters are quoted to
418
#                   render them literals.
419
#
420
# Inputs          : Shell style wildcard pattern
421
#
422
# Returns         : Perl RE
423
#
424
 
425
sub glob2pat
426
{
427
    my $globstr = shift;
428
    $globstr =~ s~^/~~;
429
    my %patmap = (
430
        '*' => '[^/]*',
431
        '?' => '[^/]',
432
        '[' => '[',
433
        ']' => ']',
273 dpurdie 434
        '-' => '-',
227 dpurdie 435
    );
436
    $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
437
    return '/' . $globstr . '$';
438
}
439
 
440
#------------------------------------------------------------------------------
441
1;