File Coverage

blib/lib/File/GlobMapper.pm
Criterion Covered Total %
statement 130 142 91.5
branch 62 76 81.5
condition 3 12 25.0
subroutine 15 15 100.0
pod 0 4 0.0
total 210 249 84.3


line stmt bran cond sub pod time code
1             package File::GlobMapper;
2              
3 84     84   505 use strict;
  84         137  
  84         2151  
4 84     84   341 use warnings;
  84         143  
  84         1687  
5 84     84   361 use Carp;
  84         147  
  84         14875  
6              
7             our ($CSH_GLOB);
8              
9             BEGIN
10             {
11 84 50   84   536 if ($] < 5.006)
12             {
13 0         0 require File::BSDGlob; import File::BSDGlob qw(:glob) ;
  0         0  
14 0         0 $CSH_GLOB = File::BSDGlob::GLOB_CSH() ;
15 0         0 *globber = \&File::BSDGlob::csh_glob;
16             }
17             else
18             {
19 84         441 require File::Glob; import File::Glob qw(:glob) ;
  84         14754  
20 84         288 $CSH_GLOB = File::Glob::GLOB_CSH() ;
21             #*globber = \&File::Glob::bsd_glob;
22 84         124429 *globber = \&File::Glob::csh_glob;
23             }
24             }
25              
26             our ($Error);
27              
28             our ($VERSION, @EXPORT_OK);
29             $VERSION = '1.001';
30             @EXPORT_OK = qw( globmap );
31              
32              
33             our ($noPreBS, $metachars, $matchMetaRE, %mapping, %wildCount);
34             $noPreBS = '(?
35             $metachars = '.*?[](){}';
36             $matchMetaRE = '[' . quotemeta($metachars) . ']';
37              
38             %mapping = (
39             '*' => '([^/]*)',
40             '?' => '([^/])',
41             '.' => '\.',
42             '[' => '([',
43             '(' => '(',
44             ')' => ')',
45             );
46              
47             %wildCount = map { $_ => 1 } qw/ * ? . { ( [ /;
48              
49             sub globmap ($$;)
50             {
51 1     1 0 1420 my $inputGlob = shift ;
52 1         5 my $outputGlob = shift ;
53              
54 1 50       8 my $obj = File::GlobMapper->new($inputGlob, $outputGlob, @_)
55             or croak "globmap: $Error" ;
56 1         4 return $obj->getFileMap();
57             }
58              
59             sub new
60             {
61 56     56 0 21540 my $class = shift ;
62 56         107 my $inputGlob = shift ;
63 56         88 my $outputGlob = shift ;
64             # TODO -- flags needs to default to whatever File::Glob does
65 56   33     206 my $flags = shift || $CSH_GLOB ;
66             #my $flags = shift ;
67              
68 56         234 $inputGlob =~ s/^\s*\<\s*//;
69 56         185 $inputGlob =~ s/\s*\>\s*$//;
70              
71 56         143 $outputGlob =~ s/^\s*\<\s*//;
72 56         157 $outputGlob =~ s/\s*\>\s*$//;
73              
74 56         377 my %object =
75             ( InputGlob => $inputGlob,
76             OutputGlob => $outputGlob,
77             GlobFlags => $flags,
78             Braces => 0,
79             WildCount => 0,
80             Pairs => [],
81             Sigil => '#',
82             );
83              
84 56   33     1045 my $self = bless \%object, ref($class) || $class ;
85              
86 56 100       178 $self->_parseInputGlob()
87             or return undef ;
88              
89 31 50       96 $self->_parseOutputGlob()
90             or return undef ;
91              
92 31         2288 my @inputFiles = globber($self->{InputGlob}, $flags) ;
93              
94 31 50       186 if (GLOB_ERROR)
95             {
96 0         0 $Error = $!;
97 0         0 return undef ;
98             }
99              
100             #if (whatever)
101             {
102 31         51 my $missing = grep { ! -e $_ } @inputFiles ;
  31         70  
  74         840  
103              
104 31 50       113 if ($missing)
105             {
106 0         0 $Error = "$missing input files do not exist";
107 0         0 return undef ;
108             }
109             }
110              
111 31         86 $self->{InputFiles} = \@inputFiles ;
112              
113 31 100       114 $self->_getFiles()
114             or return undef ;
115              
116 30         114 return $self;
117             }
118              
119             sub _retError
120             {
121 25     25   41 my $string = shift ;
122 25         53 $Error = "$string in input fileglob" ;
123 25         40 return undef ;
124             }
125              
126             sub _unmatched
127             {
128 25     25   45 my $delimeter = shift ;
129              
130 25         105 _retError("Unmatched $delimeter");
131 25         158 return undef ;
132             }
133              
134             sub _parseBit
135             {
136 7     7   10 my $self = shift ;
137              
138 7         14 my $string = shift ;
139              
140 7         12 my $out = '';
141 7         10 my $depth = 0 ;
142              
143 7         55 while ($string =~ s/(.*?)$noPreBS(,|$matchMetaRE)//)
144             {
145 9         22 $out .= quotemeta($1) ;
146 9 100       22 $out .= $mapping{$2} if defined $mapping{$2};
147              
148 9 100       21 ++ $self->{WildCount} if $wildCount{$2} ;
149              
150 9 100 33     54 if ($2 eq ',')
    100          
    100          
    100          
    100          
    50          
151             {
152 3 50       8 return _unmatched("(")
153             if $depth ;
154              
155 3         16 $out .= '|';
156             }
157             elsif ($2 eq '(')
158             {
159 1         7 ++ $depth ;
160             }
161             elsif ($2 eq ')')
162             {
163 1 50       5 return _unmatched(")")
164             if ! $depth ;
165              
166 0         0 -- $depth ;
167             }
168             elsif ($2 eq '[')
169             {
170             # TODO -- quotemeta & check no '/'
171             # TODO -- check for \] & other \ within the []
172 1 50       6 $string =~ s#(.*?\])##
173             or return _unmatched("[");
174 0         0 $out .= "$1)" ;
175             }
176             elsif ($2 eq ']')
177             {
178 1         4 return _unmatched("]");
179             }
180             elsif ($2 eq '{' || $2 eq '}')
181             {
182 0         0 return _retError("Nested {} not allowed");
183             }
184             }
185              
186 4         106 $out .= quotemeta $string;
187              
188 4 100       84 return _unmatched("(")
189             if $depth ;
190              
191 3         7 return $out ;
192             }
193              
194             sub _parseInputGlob
195             {
196 56     56   95 my $self = shift ;
197              
198 56         135 my $string = $self->{InputGlob} ;
199 56         95 my $inGlob = '';
200              
201             # Multiple concatenated *'s don't make sense
202             #$string =~ s#\*\*+#*# ;
203              
204             # TODO -- Allow space to delimit patterns?
205             #my @strings = split /\s+/, $string ;
206             #for my $str (@strings)
207 56         83 my $out = '';
208 56         79 my $depth = 0 ;
209              
210 56         628 while ($string =~ s/(.*?)$noPreBS($matchMetaRE)//)
211             {
212 124         344 $out .= quotemeta($1) ;
213 124 100       365 $out .= $mapping{$2} if defined $mapping{$2};
214 124 100       288 ++ $self->{WildCount} if $wildCount{$2} ;
215              
216 124 100       928 if ($2 eq '(')
    100          
    100          
    100          
    100          
    100          
217             {
218 3         17 ++ $depth ;
219             }
220             elsif ($2 eq ')')
221             {
222 18 100       66 return _unmatched(")")
223             if ! $depth ;
224              
225 2         9 -- $depth ;
226             }
227             elsif ($2 eq '[')
228             {
229             # TODO -- quotemeta & check no '/' or '(' or ')'
230             # TODO -- check for \] & other \ within the []
231 2 100       11 $string =~ s#(.*?\])##
232             or return _unmatched("[");
233 1         9 $out .= "$1)" ;
234             }
235             elsif ($2 eq ']')
236             {
237 1         4 return _unmatched("]");
238             }
239             elsif ($2 eq '}')
240             {
241 1         4 return _unmatched("}");
242             }
243             elsif ($2 eq '{')
244             {
245             # TODO -- check no '/' within the {}
246             # TODO -- check for \} & other \ within the {}
247              
248 8         11 my $tmp ;
249 8 100       54 unless ( $string =~ s/(.*?)$noPreBS\}//)
250             {
251 1         4 return _unmatched("{");
252             }
253             #$string =~ s#(.*?)\}##;
254              
255             #my $alt = join '|',
256             # map { quotemeta $_ }
257             # split "$noPreBS,", $1 ;
258 7         20 my $alt = $self->_parseBit($1);
259 7 100       35 defined $alt or return 0 ;
260 3         6 $out .= "($alt)" ;
261              
262 3         16 ++ $self->{Braces} ;
263             }
264             }
265              
266 32 100       89 return _unmatched("(")
267             if $depth ;
268              
269 31         61 $out .= quotemeta $string ;
270              
271              
272 31         215 $self->{InputGlob} =~ s/$noPreBS[\(\)]//g;
273 31         102 $self->{InputPattern} = $out ;
274              
275             #print "# INPUT '$self->{InputGlob}' => '$out'\n";
276              
277 31         101 return 1 ;
278              
279             }
280              
281             sub _parseOutputGlob
282             {
283 31     31   55 my $self = shift ;
284              
285 31         60 my $string = $self->{OutputGlob} ;
286 31         52 my $maxwild = $self->{WildCount};
287              
288 31 50       95 if ($self->{GlobFlags} & GLOB_TILDE)
289             #if (1)
290             {
291 31         62 $string =~ s{
292             ^ ~ # find a leading tilde
293             ( # save this in $1
294             [^/] # a non-slash character
295             * # repeated 0 or more times (0 means me)
296             )
297             }{
298             $1
299             ? (getpwnam($1))[7]
300             : ( $ENV{HOME} || $ENV{LOGDIR} )
301 0 0 0     0 }ex;
302              
303             }
304              
305             # max #1 must be == to max no of '*' in input
306 31         153 while ( $string =~ m/#(\d)/g )
307             {
308 30 50       132 croak "Max wild is #$maxwild, you tried #$1"
309             if $1 > $maxwild ;
310             }
311              
312 31         58 my $noPreBS = '(?
313             #warn "noPreBS = '$noPreBS'\n";
314              
315             #$string =~ s/${noPreBS}\$(\d)/\${$1}/g;
316 31         247 $string =~ s/${noPreBS}#(\d)/\${$1}/g;
317 31         140 $string =~ s#${noPreBS}\*#\${inFile}#g;
318 31         88 $string = '"' . $string . '"';
319              
320             #print "OUTPUT '$self->{OutputGlob}' => '$string'\n";
321 31         75 $self->{OutputPattern} = $string ;
322              
323 31         99 return 1 ;
324             }
325              
326             sub _getFiles
327             {
328 31     31   56 my $self = shift ;
329              
330 31         88 my %outInMapping = ();
331 31         55 my %inFiles = () ;
332              
333 31         53 foreach my $inFile (@{ $self->{InputFiles} })
  31         94  
334             {
335 74 100       252 next if $inFiles{$inFile} ++ ;
336              
337 73         111 my $outFile = $inFile ;
338              
339 73 50       1082 if ( $inFile =~ m/$self->{InputPattern}/ )
340             {
341 84     84   686 no warnings 'uninitialized';
  84         175  
  84         18522  
342 73         4318 eval "\$outFile = $self->{OutputPattern};" ;
343              
344 73 100       319 if (defined $outInMapping{$outFile})
345             {
346 1         3 $Error = "multiple input files map to one output file";
347 1         26 return undef ;
348             }
349 72         164 $outInMapping{$outFile} = $inFile;
350 72         96 push @{ $self->{Pairs} }, [$inFile, $outFile];
  72         254  
351             }
352             }
353              
354 30         124 return 1 ;
355             }
356              
357             sub getFileMap
358             {
359 30     30 0 1968 my $self = shift ;
360              
361 30         88 return $self->{Pairs} ;
362             }
363              
364             sub getHash
365             {
366 4     4 0 3668 my $self = shift ;
367              
368 4         8 return { map { $_->[0] => $_->[1] } @{ $self->{Pairs} } } ;
  5         21  
  4         13  
369             }
370              
371             1;
372              
373             __END__