Subversion Repositories DevTools

Rev

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