Subversion Repositories DevTools

Rev

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