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   581 use strict;
  84         161  
  84         2427  
4 84     84   437 use warnings;
  84         198  
  84         1861  
5 84     84   394 use Carp;
  84         164  
  84         16958  
6              
7             our ($CSH_GLOB);
8              
9             BEGIN
10             {
11 84 50   84   791 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         486 require File::Glob; import File::Glob qw(:glob) ;
  84         16740  
20 84         288 $CSH_GLOB = File::Glob::GLOB_CSH() ;
21             #*globber = \&File::Glob::bsd_glob;
22 84         143105 *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 1442 my $inputGlob = shift ;
52 1         2 my $outputGlob = shift ;
53              
54 1 50       7 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 21663 my $class = shift ;
62 56         107 my $inputGlob = shift ;
63 56         98 my $outputGlob = shift ;
64             # TODO -- flags needs to default to whatever File::Glob does
65 56   33     225 my $flags = shift || $CSH_GLOB ;
66             #my $flags = shift ;
67              
68 56         268 $inputGlob =~ s/^\s*\<\s*//;
69 56         199 $inputGlob =~ s/\s*\>\s*$//;
70              
71 56         196 $outputGlob =~ s/^\s*\<\s*//;
72 56         211 $outputGlob =~ s/\s*\>\s*$//;
73              
74 56         445 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     251 my $self = bless \%object, ref($class) || $class ;
85              
86 56 100       224 $self->_parseInputGlob()
87             or return undef ;
88              
89 31 50       111 $self->_parseOutputGlob()
90             or return undef ;
91              
92 31         2656 my @inputFiles = globber($self->{InputGlob}, $flags) ;
93              
94 31 50       247 if (GLOB_ERROR)
95             {
96 0         0 $Error = $!;
97 0         0 return undef ;
98             }
99              
100             #if (whatever)
101             {
102 31         63 my $missing = grep { ! -e $_ } @inputFiles ;
  31         77  
  74         857  
103              
104 31 50       144 if ($missing)
105             {
106 0         0 $Error = "$missing input files do not exist";
107 0         0 return undef ;
108             }
109             }
110              
111 31         119 $self->{InputFiles} = \@inputFiles ;
112              
113 31 100       137 $self->_getFiles()
114             or return undef ;
115              
116 30         128 return $self;
117             }
118              
119             sub _retError
120             {
121 25     25   49 my $string = shift ;
122 25         62 $Error = "$string in input fileglob" ;
123 25         50 return undef ;
124             }
125              
126             sub _unmatched
127             {
128 25     25   45 my $delimeter = shift ;
129              
130 25         98 _retError("Unmatched $delimeter");
131 25         195 return undef ;
132             }
133              
134             sub _parseBit
135             {
136 7     7   12 my $self = shift ;
137              
138 7         15 my $string = shift ;
139              
140 7         14 my $out = '';
141 7         11 my $depth = 0 ;
142              
143 7         56 while ($string =~ s/(.*?)$noPreBS(,|$matchMetaRE)//)
144             {
145 9         21 $out .= quotemeta($1) ;
146 9 100       27 $out .= $mapping{$2} if defined $mapping{$2};
147              
148 9 100       22 ++ $self->{WildCount} if $wildCount{$2} ;
149              
150 9 100 33     56 if ($2 eq ',')
    100          
    100          
    100          
    100          
    50          
151             {
152 3 50       8 return _unmatched("(")
153             if $depth ;
154              
155 3         15 $out .= '|';
156             }
157             elsif ($2 eq '(')
158             {
159 1         7 ++ $depth ;
160             }
161             elsif ($2 eq ')')
162             {
163 1 50       6 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       8 $string =~ s#(.*?\])##
173             or return _unmatched("[");
174 0         0 $out .= "$1)" ;
175             }
176             elsif ($2 eq ']')
177             {
178 1         103 return _unmatched("]");
179             }
180             elsif ($2 eq '{' || $2 eq '}')
181             {
182 0         0 return _retError("Nested {} not allowed");
183             }
184             }
185              
186 4         10 $out .= quotemeta $string;
187              
188 4 100       12 return _unmatched("(")
189             if $depth ;
190              
191 3         7 return $out ;
192             }
193              
194             sub _parseInputGlob
195             {
196 56     56   104 my $self = shift ;
197              
198 56         150 my $string = $self->{InputGlob} ;
199 56         94 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         104 my $out = '';
208 56         85 my $depth = 0 ;
209              
210 56         704 while ($string =~ s/(.*?)$noPreBS($matchMetaRE)//)
211             {
212 124         364 $out .= quotemeta($1) ;
213 124 100       415 $out .= $mapping{$2} if defined $mapping{$2};
214 124 100       402 ++ $self->{WildCount} if $wildCount{$2} ;
215              
216 124 100       992 if ($2 eq '(')
    100          
    100          
    100          
    100          
    100          
217             {
218 3         16 ++ $depth ;
219             }
220             elsif ($2 eq ')')
221             {
222 18 100       72 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       12 $string =~ s#(.*?\])##
232             or return _unmatched("[");
233 1         7 $out .= "$1)" ;
234             }
235             elsif ($2 eq ']')
236             {
237 1         5 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         14 my $tmp ;
249 8 100       57 unless ( $string =~ s/(.*?)$noPreBS\}//)
250             {
251 1         6 return _unmatched("{");
252             }
253             #$string =~ s#(.*?)\}##;
254              
255             #my $alt = join '|',
256             # map { quotemeta $_ }
257             # split "$noPreBS,", $1 ;
258 7         22 my $alt = $self->_parseBit($1);
259 7 100       36 defined $alt or return 0 ;
260 3         6 $out .= "($alt)" ;
261              
262 3         16 ++ $self->{Braces} ;
263             }
264             }
265              
266 32 100       142 return _unmatched("(")
267             if $depth ;
268              
269 31         157 $out .= quotemeta $string ;
270              
271              
272 31         267 $self->{InputGlob} =~ s/$noPreBS[\(\)]//g;
273 31         115 $self->{InputPattern} = $out ;
274              
275             #print "# INPUT '$self->{InputGlob}' => '$out'\n";
276              
277 31         113 return 1 ;
278              
279             }
280              
281             sub _parseOutputGlob
282             {
283 31     31   61 my $self = shift ;
284              
285 31         72 my $string = $self->{OutputGlob} ;
286 31         61 my $maxwild = $self->{WildCount};
287              
288 31 50       107 if ($self->{GlobFlags} & GLOB_TILDE)
289             #if (1)
290             {
291 31         81 $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         159 while ( $string =~ m/#(\d)/g )
307             {
308 30 50       144 croak "Max wild is #$maxwild, you tried #$1"
309             if $1 > $maxwild ;
310             }
311              
312 31         73 my $noPreBS = '(?
313             #warn "noPreBS = '$noPreBS'\n";
314              
315             #$string =~ s/${noPreBS}\$(\d)/\${$1}/g;
316 31         296 $string =~ s/${noPreBS}#(\d)/\${$1}/g;
317 31         162 $string =~ s#${noPreBS}\*#\${inFile}#g;
318 31         96 $string = '"' . $string . '"';
319              
320             #print "OUTPUT '$self->{OutputGlob}' => '$string'\n";
321 31         84 $self->{OutputPattern} = $string ;
322              
323 31         104 return 1 ;
324             }
325              
326             sub _getFiles
327             {
328 31     31   72 my $self = shift ;
329              
330 31         81 my %outInMapping = ();
331 31         69 my %inFiles = () ;
332              
333 31         53 foreach my $inFile (@{ $self->{InputFiles} })
  31         105  
334             {
335 74 100       263 next if $inFiles{$inFile} ++ ;
336              
337 73         138 my $outFile = $inFile ;
338              
339 73 50       1336 if ( $inFile =~ m/$self->{InputPattern}/ )
340             {
341 84     84   767 no warnings 'uninitialized';
  84         251  
  84         20855  
342 73         4846 eval "\$outFile = $self->{OutputPattern};" ;
343              
344 73 100       371 if (defined $outInMapping{$outFile})
345             {
346 1         3 $Error = "multiple input files map to one output file";
347 1         10 return undef ;
348             }
349 72         202 $outInMapping{$outFile} = $inFile;
350 72         111 push @{ $self->{Pairs} }, [$inFile, $outFile];
  72         308  
351             }
352             }
353              
354 30         142 return 1 ;
355             }
356              
357             sub getFileMap
358             {
359 30     30 0 1954 my $self = shift ;
360              
361 30         94 return $self->{Pairs} ;
362             }
363              
364             sub getHash
365             {
366 4     4 0 3669 my $self = shift ;
367              
368 4         8 return { map { $_->[0] => $_->[1] } @{ $self->{Pairs} } } ;
  5         21  
  4         12  
369             }
370              
371             1;
372              
373             __END__