Subversion Repositories DevTools

Rev

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