Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
7323 dpurdie 1
########################################################################
2
# Copyright (c) VIX TECHNOLOGY (AUST) LTD
3
#
4
# Module name   : JatsIgnore.pm
5
# Module type   : JATS Utility
6
# Compiler(s)   : Perl
7
# Environment(s): jats
8
#
9
# Description   : 
10
#
11
# Usage         : See POD at the end of this file
12
#
13
#......................................................................#
14
 
15
require 5.008_002;
16
use strict;
17
use warnings;
18
 
19
package JatsIgnore;
20
 
21
use JatsError;
22
use FileUtils;
23
 
24
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
25
use Exporter;
26
 
27
$VERSION = 1.00;
28
@ISA = qw(Exporter);
29
 
30
# Symbols to autoexport (:DEFAULT tag)
31
@EXPORT = qw(
32
                Test
33
                ReadFilters
34
            );
35
 
36
my  @Filters;                   # List of filters
37
my  $baseUp;                    # Adjust base directory
38
my  $baseDir;                   # Base directory    
39
 
40
#-------------------------------------------------------------------------------
41
# Function        : INIT 
42
#
43
# Description     : Called during startup
44
#                   Init file utils 
45
#
46
sub INIT
47
{
48
    InitFileUtils();
49
}
50
 
51
 
52
#-------------------------------------------------------------------------------
53
# Function        : Test 
54
#
55
# Description     : Test a .jatsignore file
56
#
57
# Inputs          : 
58
#
59
# Returns         : 
60
#
61
sub Test
62
{
63
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
64
    Message("Testing file");
65
}
66
 
67
#-------------------------------------------------------------------------------
68
# Function        : ReadFilters 
69
#
70
# Description     : Read a file of filter specifications
71
#                   Create a list of RE's for later use 
72
#
73
# Inputs          : $file           - Source file
74
#
75
# Returns         : 
76
#
77
sub ReadFilters
78
{
79
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
80
    my ($file) = @_;
81
    open (my $fh, '<', $file) || Error ("Cannot open filter spec: $file. $!");
82
    while (<$fh>)
83
    {
84
        #
85
        #   Strip white space
86
        #   Ignore comment lines
87
        #
88
        $_ =~ s~\s+$~~;
89
        $_ =~ s~^\s+~~;
90
        next unless $_;
91
        next if m~^#~;
92
 
93
        #
94
        #   Extract Meta Data
95
        #
96
        if (m~\[\s*(.*)\s*\]~)
97
        {
98
            my $metaCmd = $1;
99
            $metaCmd =~ s~\s+~ ~g;
100
            Verbose("Meta: $metaCmd");
101
            if ($metaCmd =~ m~ROOT UP (\d+)~)
102
            {
103
                Verbose("Root Directory. Up $1 from here");
104
                $baseUp = $1;
105
                next;
106
            }
107
            ReportError("Uknown MetaData: $_");
108
            next;
109
        }
110
 
111
        my $data = convertToRe($_);
112
        if ($data->{error})
113
        {
114
            ReportError ("Invalid Filter: $_", "Converted to:" . $data->{filter}, $data->{error});
115
        }
116
        else
117
        {
118
            push (@Filters, $data);
119
        }
120
    }
121
    ErrorDoExit();
122
    DebugDumpData("Filters", \@Filters) if IsVerbose(3);
123
}
124
 
125
#-------------------------------------------------------------------------------
126
# Function        : AddFilter 
127
#
128
# Description     : Add one or more filters to the internal filter list
129
#
130
# Inputs          : Array of filters
131
#
132
# Returns         : 
133
#
134
sub AddFilter
135
{
136
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
137
    foreach (@_)
138
    {
139
        my $data = convertToRe($_);
140
        if ($data->{error})
141
        {
142
            ReportError ("Invalid Filter: $_", "Converted to:" . $data->{filter}, $data->{error});
143
        }
144
        else
145
        {
146
            push (@Filters, $data);
147
        }
148
    }
149
    ErrorDoExit();
150
    DebugDumpData("Filters", \@Filters) if IsVerbose(3);
151
}
152
 
153
#-------------------------------------------------------------------------------
154
# Function        : TestFilters 
155
#
156
# Description     : Test the filters against a file of values
157
#
158
# Inputs          : $file       - File to process
159
#
160
# Returns         : 
161
#
162
sub TestFilters
163
{
164
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
165
    my ($file) = @_;
166
    open (my $fh, '<', $file) || Error ("Cannot open test file: $file. $!");
167
    while (<$fh>)
168
    {
169
        #
170
        #   Strip white space
171
        #   Ignore comment lines
172
        #
173
        $_ =~ s~\s+$~~;
174
        $_ =~ s~^\s+~~;
175
        next unless $_;
176
        next if m~^#~;
177
 
178
        my $line = $_;
179
        print("Testing: $line\n");
180
 
181
        foreach my $filter (@Filters)
182
        {
183
            my $rv = $line =~ $filter->{regex};
184
            printf("%1.1s :: %1.1s :: %s, %s\n", $rv, ($filter->{mode} || ''), $filter->{raw}, $filter->{filter});
185
        }
186
 
187
    }
188
}
189
 
190
#-------------------------------------------------------------------------------
191
# Function        : FilterPath 
192
#
193
# Description     : Examine the provided path and determine if it should be filtered
194
#
195
# Inputs          : $path       - Path to process
196
#
197
# Returns         : 0: Keep, D: Delete, P: Prune
198
#
199
sub FilterPath
200
{
201
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
202
    my ($path) = @_;
203
 
204
    study $path;
205
    foreach my $filter (@Filters)
206
    {
207
        if ($path=~ $filter->{regex} )
208
        {
209
            return $filter->{mode} || 'D';
210
        }
211
    }
212
    return 0;
213
}
214
 
215
#-------------------------------------------------------------------------------
216
# Function        : ScanDir 
217
#
218
# Description     : Scan a directory
219
#
220
# Inputs          : $SrcDir           - Start of path to scan
221
#                   $callback         - Expect a code Ref
222
#                                       Called with:
223
#                                           Code: P, D, 0
224
#                                           AbsPath:
225
#
226
#
227
# Returns         : 
228
#
229
sub ScanDir
230
{
231
    my ($SrcDir, $callback) = @_;
232
 
233
    $SrcDir = AbsPath($SrcDir);
234
    Verbose("ScanDir: $SrcDir");
235
 
236
    #
237
    #   Sanity check callback
238
    #
239
    Error("ScanDir, not provided with a code reference")
240
        if ($callback && ref($callback) ne 'CODE' );
241
 
242
    #
243
    #   Create a list of subdirs to scan
244
    #       Elements do not contain the SrcDir
245
    #       Elements have a '/' suffix - simplify joining
246
    #
247
    my @dirs = '';
248
 
249
    #
250
    #   Process all directories in the list
251
    #   Pop them off so we do a depth first search
252
    #
253
    while ( @dirs )
254
    {
255
        my $root = pop( @dirs );
256
 
257
        my $dir = $SrcDir . '/' . $root;
258
        unless (opendir DIR, $dir )
259
        {
260
            ::Warning ("File Find. Can't opendir($dir): $!\n");
261
            next;
262
        }
263
        my @filenames = readdir DIR;
264
        closedir(DIR);
265
 
266
        foreach my $file ( @filenames )
267
        {
268
            #
269
            #   Ignore filesystem house keeping directories
270
            #
271
            next if ( $file eq '.' || $file eq '..' );
272
 
273
            #
274
            #   Determine the type of element
275
            #       1)Link
276
            #           - Link to a File
277
            #           - Link to a directory
278
            #       2)File
279
            #       3)Directory
280
            #
281
            my $filename = $dir . $file;
282
            my $relname = $root . $file;
283
            my $type;
284
            my $rv;
285
 
286
            #
287
            #   Stat the file
288
            #   Use speed trick. (-f _) will use into from last stat/lstat
289
            #
290
            stat ( $filename );
291
            if ( -f _ )
292
            {
293
                $rv = FilterPath ($relname);
294
                $type = 'f';
295
            }
296
            elsif ( -d _ )
297
            {
298
                #
299
                #   Add to the list of future dirs to process
300
                #   Place on end to ensure depth first
301
                #   Algorithm requires dirname has a trailing /
302
                #
303
                $rv = FilterPath ($relname . '/');
304
                push @dirs, $relname . '/' unless $rv eq 'P';
305
                $type = 'd';
306
            }
307
            else
308
            {
309
                ::Verbose ("Find File: Unknown type skipped: $filename");
310
                next;
311
            }
312
 
313
            #
314
            #   Have a valid element to process
315
            #   Setup parameters for later users
316
            #
317
            #print("Examine $rv : $relname\n");
318
            #print("Exlude: $filename\n") if $rv;
319
            $callback->($rv, $filename) if $callback && $rv;
320
        }
321
    }
322
}
323
 
324
#-------------------------------------------------------------------------------
325
# Function        : TestFile 
326
#
327
# Description     : Test against file data
328
#                   Each line has 3 comma sep values
329
#                   result, filter, path
330
#
331
# Inputs          : $file       - File to process
332
#
333
# Returns         : 
334
#
335
sub TestFile
336
{
337
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
338
    my ($file) = @_;
339
    open (my $fh, '<', $file) || Error ("Cannot open test file: $file. $!");
340
    while (<$fh>)
341
    {
342
        #
343
        #   Strip white space
344
        #   Ignore comment lines
345
        #
346
        $_ =~ s~\s+$~~;
347
        $_ =~ s~^\s+~~;
348
        next unless $_;
349
        next if m~^#~;
350
 
351
        my $line = $_;
352
        #print("Testing: $line\n");
353
 
354
        my ($rv, $re, $path) = split(/\s*,\s*/, $line);
355
        TestRe($., $rv, $re, $path);
356
    }
357
}
358
 
359
 
360
#-------------------------------------------------------------------------------
361
# Function        : TestRe 
362
#
363
# Description     : Test an RE against a line
364
#                   Test utility - should do the same job as the main filtering
365
#                   Will display stuff
366
#
367
# Inputs          : result          - expected result (P:Prune, D:Delete, X:Bad Filter, 0:No Match
368
#                   re              - Re to test against 
369
#                   path            - Patch to test
370
#
371
# Returns         : result
372
#
373
sub TestRe
374
{
375
    my ($lineNo, $result, $re, $line) = @_;
376
    my $error = ErrorReConfig( 'name'    =>'IGNORE' );
377
 
378
    my $rv = 'X';
379
    my $filter = convertToRe($re);
380
    if ($filter->{regex})
381
    {
382
        if ($line =~ $filter->{regex}) {
383
            $rv = $filter->{mode} || 'D'; 
384
        } else {
385
            $rv = 0;
386
        }
387
    }
388
 
389
    #
390
    #   Report results
391
    #
392
    printf "%3.3s : %1.1s, %20.20s, %30.30s, %s\n",
393
            $lineNo, $result, $re, $line, ($rv eq $result) ? 'Good' : ('----Error: Got ' . $rv . ' : ' . $filter->{filter});
394
    return $rv;
395
}
396
 
397
 
398
#-------------------------------------------------------------------------------
399
# Function        : convertToRe 
400
#
401
# Description     : Convert a filter to an RE
402
#
403
# Inputs          : Filter expression
404
#
405
# Returns         : Data item filled in
406
#
407
sub convertToRe
408
{
409
    my ($_) = @_;
410
    my $data;
411
    my $filter;
412
 
413
    $data->{raw} = $_;
414
    if (m~^\+(.*)~)
415
    {
416
        #   Process Regexps
417
        #   If end in / - then its a pruning match
418
        $filter = $1;
419
        if ($filter =~ m~(.*)/$~)
420
        {
421
            $data->{mode} = 'P' ;
422
            $filter = $1 . '(/|$)';
423
        }
424
    }
425
    else
426
    {
427
        #
428
        #   Process non-regexps
429
        #   If end in ** - then its a pruning match
430
        #
431
        $filter = $_;
432
        $data->{mode} = 'P' if ($filter =~ m~\*{2}$~);
433
        $filter = glob2pat ($filter);
434
    }
435
    #
436
    #   Ensure we have a sane Regular expression
437
    #   If not, then clean up the reported error before giving to the user
438
    #   Use delayed error reporting
439
    #
440
    $data->{filter} = $filter;
441
    $data->{regex} = eval { qr/$filter/i };
442
    if ($@)
443
    {
444
        my $etext = $@;
445
        $etext =~ s~/ at .*~~;
446
        $data->{error} = $etext;
447
        $data->{regex} = undef
448
    }
449
    else
450
    {
451
        Verbose2 ("FilterLine: '$_' -> $filter");
452
    }
453
 
454
    return $data;
455
}
456
 
457
#-------------------------------------------------------------------------------
458
# Function        : glob2pat
459
#
460
# Description     : Convert shell wildcard characters into their equivalent
461
#                   regular expression; all other characters are quoted to
462
#                   render them literals.
463
#
464
#                   Treat '**' as match anything
465
#                   Treat '*' as match within a filename
466
#
467
# Inputs          : Shell style wildcard pattern
468
#
469
# Returns         : Perl RE
470
#
471
sub glob2pat
472
{
473
    my $globstr = shift;
474
    $globstr =~ s~^/~~;
475
    $globstr =~ s~\*{2,}~$;~g;
476
    my %patmap = (
477
        '*' => '[^/]+',
478
        '?' => '.',
479
        '[' => '[',
480
        ']' => ']',
481
        '-' => '-',
482
        "$;" => "$;"
483
    );
484
    $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
485
    $globstr =~ s~$;~.*~g;
486
    $globstr = '(/|^)' . $globstr;
487
    $globstr .= '$' unless $globstr =~ m~/$~;
488
    return  $globstr;
489
}
490
 
491
1;