Subversion Repositories DevTools

Rev

Rev 7300 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
########################################################################
7300 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
227 dpurdie 3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : A class to Locate Files
10
#                 Supportes:
11
#                   Wilcards
12
#                   Exclusions
13
#                   Recursion
14
#
15
# Usage:
16
#               my $search = JatsLocateFiles::new('JatsLocateFiles');
17
#               my $search = JatsLocateFiles->new();
18
#                  $search->recurse ( );
19
#                  $search->filter_in ( );
20
#                  $search->filter_in_re ( );
21
#                  $search->filter_out ( );
22
#                  $search->filter_out_re ( );
23
#                  $search->base_dir ()
24
#                  $search->has_filter ( );
25
#                  $search->full_path ( );
26
#                  $search->dirs_only ( );
379 dpurdie 27
#                  $search->dirs_too();             # Dirs have '/' appended
227 dpurdie 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
 
4094 dpurdie 206
    } elsif ( $opt =~ m/^--NoFullPath/ ) {              # Default. Returned items relative to search base        
227 dpurdie 207
        $self->full_path(0);
208
 
4094 dpurdie 209
    } elsif ( $opt =~ m/^--FullPath/ ) {                # Prepend base path to returned items
227 dpurdie 210
        $self->full_path(1);
211
 
4094 dpurdie 212
    } elsif ( $opt =~ m/^--FileListOnly/ ) {            # Default. Return files only 
227 dpurdie 213
        $self->full_path(0);
214
        $self->dirs_only(0);
215
 
4094 dpurdie 216
    } elsif ( $opt =~ m/^--DirListOnly/ ) {             # Return dirs that contain selected files
227 dpurdie 217
        $self->dirs_only(1);
218
 
4094 dpurdie 219
    } elsif ( $opt =~ m/^--DirsToo/ ) {                 # Return files and directories
263 dpurdie 220
        $self->dirs_too(1);
221
 
4094 dpurdie 222
    } elsif ( $opt =~ m/^--DirsOnly/ ) {                # Ignore non-directory elements    
223
        $self->dirs_only(2);
224
        $self->dirs_too(1);
225
 
227 dpurdie 226
    } elsif ( $opt =~ m/^--FilterIn=(.+)/ ) {
227
        $self->filter_in ( $1 );
228
 
229
    } elsif ( $opt =~ m/^--FilterInRe=(.+)/ ) {
230
        $self->filter_in_re ( $1 );
231
 
232
    } elsif ( $opt =~ m/^--FilterOut=(.+)/ ) {
233
        $self->filter_out ( $1 );
234
 
235
    } elsif ( $opt =~ m/^--FilterOutRe=(.+)/ ) {
236
        $self->filter_out_re ( $1 );
237
 
238
    } else {
239
        $result = 0;
240
 
241
    }
242
 
243
    return $result;
244
}
245
 
246
#-------------------------------------------------------------------------------
247
# Function        : search
248
#
249
# Description     : This function performs the search for files as specified
250
#                   by the arguments already provided
251
#
252
# Inputs          : base_dir (Optional)
253
#
254
# Returns         : List of files that match the search criteria
255
#                   The base directory is not prepended
256
#                   The list is a simple list of file names
257
#
258
 
259
my @search_list;             # Must be global to avoid closure problems
260
my $search_len;
263 dpurdie 261
my $search_base_dir;
262
my $search_dirs_too;
4094 dpurdie 263
my $search_no_files;
227 dpurdie 264
 
265
sub search
266
{
267
    my $self = shift;
268
    $self->{base_dir} = $_[0] if (defined $_[0] );
269
    $self->{results} = ();
270
 
271
    #
272
    #   Ensure user has provided enough info
273
    #
274
    Error ("JatsLocateFiles: No base directory provided") unless ( $self->{base_dir} );
275
 
276
    #
277
    #   Clean up the user dir. Remove any trailing / as we will be adding it back
278
    #
279
    $self->{base_dir} =~ s~/*$~~g;
280
 
281
    #
282
    #   Init recursion information
283
    #   Needed to avoid closure interactions
284
    #
285
    @search_list = ();
286
    $search_len = 1 + length( $self->{base_dir} );
287
 
288
    #
289
    #   Create a list of candidate files
290
    #   If we are recursing the subtree, then this is a little harder
291
    #   If we are not recursing then we can't simply glob the directory as
292
    #   not all files are processed.
293
    #
294
    #   Will end up with a list of files that don't include $dir
295
    #
296
    if ( -d $self->{base_dir} )
297
    {
298
        if ( $self->{recurse} )
299
        {
263 dpurdie 300
            $search_dirs_too = $self->{dirs_too};
301
            $search_base_dir = $self->{base_dir};
4094 dpurdie 302
            $search_no_files = ($self->{dirs_only} == 2);
227 dpurdie 303
            sub find_file_wanted
304
            {
4094 dpurdie 305
                return if ( $search_no_files && ! -d $_ );              # skip if current is not a dir (assume file) and we only match dirs
263 dpurdie 306
                return if ( !$search_dirs_too && -d $_ );               # skip if current is dir and we are not including dirs
307
                return if ( $search_base_dir eq $File::Find::name );    # skip if current is base_dir as we dont include it
227 dpurdie 308
                my $file = $File::Find::name;
4094 dpurdie 309
                $file .= '/' if ( -d $_ && ! $search_no_files);
227 dpurdie 310
                push @search_list, substr($file, $search_len );
311
            }
312
 
313
            #
314
            #       Under Unix we need to follow symbolic links, but Perl's
315
            #       Find:find does not work with -follow under windows if the source
316
            #       path contains a drive letter.
317
            #
318
            #       Solution. Only use follow under non-windows systems.
319
            #                 Works as Windows does not have symlinks (yet).
320
            #
321
            my $follow_opt =  ($ENV{GBE_UNIX}  > 0);
322
 
3832 dpurdie 323
            File::Find::find( {wanted => \&find_file_wanted, follow_fast => $follow_opt, follow_skip => 2 }, $self->{base_dir} );
227 dpurdie 324
        }
325
        else
326
        {
327
            local *DIR ;
328
            opendir DIR, $self->{base_dir} || die ("Cannot open $self->{base_dir}");
329
            foreach ( readdir( DIR ) )
330
            {
331
                next if /^\Q.\E$/;
332
                next if /^\Q..\E$/;
263 dpurdie 333
                next if ( !$self->{dirs_too} && -d "$self->{base_dir}/$_" );
227 dpurdie 334
                push @search_list, $_;
335
            }
336
            closedir DIR;
337
        }
338
    }
339
 
340
 
341
    my @result;
342
    if ( @{$self->{include}} || @{$self->{exclude}} )
343
    {
344
        #
345
        #   Filtering is present
346
        #   Apply the filterin rules and then the filter out rules
347
        #   If no filter-in rules, then assume that all files are allowed in and
348
        #   simply apply the filter-out rules.
349
        #
350
        my @patsin  = map { qr/$_/ } @{$self->{include}};
351
        my @patsout = map { qr/$_/ } @{$self->{exclude}};
352
 
353
    #    map { print "Include:$_\n"; } @{$self->{include}};
354
    #    map { print "Exclude:$_\n"; } @{$self->{exclude}};
355
 
356
 
357
        file:
358
        foreach my $rfile ( @search_list )
359
        {
360
            my $file = '/' . $rfile;
361
            if ( @{$self->{include}} )
362
            {
363
                my $in = 0;
364
                for my $pat (@patsin)
365
                {
366
                    if ( $file =~ /$pat/ )
367
                    {
368
                        $in = 1;
369
                        last;
370
                    }
371
                }
372
    #print "------- Not included $file\n" unless $in;
373
                next unless ( $in );
374
            }
375
 
376
            for my $pat (@patsout)
377
            {
378
    #print "------- REJECT $file :: $pat \n" if ( $file =~ /$pat/ );
379
                next file if ( $file =~ /$pat/ );
380
            }
381
            push @result, $rfile;
382
        }
383
    }
384
    else
385
    {
386
        @result = @search_list ;
387
    }
388
    @search_list = ();
389
    $self->{results} = [];;
390
 
391
    #
392
    #   Reattach the base directory, if required
393
    #       full_path  : Prepend full path
394
    #       dirs_only  : return list of dirs that have files
395
    #   Extract dirs only
396
    #
7322 dpurdie 397
    my %fileList;
227 dpurdie 398
    foreach  ( @result )
399
    {
400
        my $path;
7322 dpurdie 401
        if ( $self->{full_path} ) {
227 dpurdie 402
            $path = $self->{base_dir} . '/' . $_;
7322 dpurdie 403
        } else {
227 dpurdie 404
            $path = $_;
405
        }
406
 
7322 dpurdie 407
        if ( $self->{dirs_only} == 1 ) {
227 dpurdie 408
            $path =~ s~/[^/]*$~~;
409
        }
410
 
7322 dpurdie 411
        #
412
        #   Add to results - if not already present
413
        #   Dont use UniquePush - its slow over large lists
414
        #
415
        unless (exists $fileList{$path} ) {
416
            push( @{$self->{results}}, $path);
417
            $fileList{$path} = 1;
418
        }
227 dpurdie 419
    }
420
#DebugDumpData ("Search", $self);
421
    return @{$self->{results}};
422
}
423
 
424
#-------------------------------------------------------------------------------
425
# Function        : glob2pat
426
#
427
# Description     : Convert four shell wildcard characters into their equivalent
428
#                   regular expression; all other characters are quoted to
429
#                   render them literals.
430
#
431
# Inputs          : Shell style wildcard pattern
432
#
433
# Returns         : Perl RE
434
#
435
 
436
sub glob2pat
437
{
438
    my $globstr = shift;
439
    $globstr =~ s~^/~~;
440
    my %patmap = (
441
        '*' => '[^/]*',
442
        '?' => '[^/]',
443
        '[' => '[',
444
        ']' => ']',
273 dpurdie 445
        '-' => '-',
227 dpurdie 446
    );
447
    $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
448
    return '/' . $globstr . '$';
449
}
450
 
451
#------------------------------------------------------------------------------
452
1;