1b39c5158Smillertpackage File::GlobMapper;
2b39c5158Smillert
3b39c5158Smillertuse strict;
4b39c5158Smillertuse warnings;
5b39c5158Smillertuse Carp;
6b39c5158Smillert
7b39c5158Smillertour ($CSH_GLOB);
8b39c5158Smillert
9b39c5158SmillertBEGIN
10b39c5158Smillert{
11b39c5158Smillert    if ($] < 5.006)
12b39c5158Smillert    {
13*3d61058aSafresh1        require File::BSDGlob; File::BSDGlob->import(':glob');
14b39c5158Smillert        $CSH_GLOB = File::BSDGlob::GLOB_CSH();
15b39c5158Smillert        *globber = \&File::BSDGlob::csh_glob;
16b39c5158Smillert    }
17b39c5158Smillert    else
18b39c5158Smillert    {
19*3d61058aSafresh1        require File::Glob; File::Glob->import(':glob');
20b39c5158Smillert        $CSH_GLOB = File::Glob::GLOB_CSH();
21b39c5158Smillert        #*globber = \&File::Glob::bsd_glob;
22b39c5158Smillert        *globber = \&File::Glob::csh_glob;
23b39c5158Smillert    }
24b39c5158Smillert}
25b39c5158Smillert
26b39c5158Smillertour ($Error);
27b39c5158Smillert
28b39c5158Smillertour ($VERSION, @EXPORT_OK);
29b46d8ef2Safresh1$VERSION = '1.001';
30b39c5158Smillert@EXPORT_OK = qw( globmap );
31b39c5158Smillert
32b39c5158Smillert
33b39c5158Smillertour ($noPreBS, $metachars, $matchMetaRE, %mapping, %wildCount);
34898184e3Ssthen$noPreBS = '(?<!\\\)' ; # no preceding backslash
35b39c5158Smillert$metachars = '.*?[](){}';
36b39c5158Smillert$matchMetaRE = '[' . quotemeta($metachars) . ']';
37b39c5158Smillert
38b39c5158Smillert%mapping = (
39b39c5158Smillert                '*' => '([^/]*)',
40b39c5158Smillert                '?' => '([^/])',
41b39c5158Smillert                '.' => '\.',
42b39c5158Smillert                '[' => '([',
43b39c5158Smillert                '(' => '(',
44b39c5158Smillert                ')' => ')',
45b39c5158Smillert           );
46b39c5158Smillert
47b39c5158Smillert%wildCount = map { $_ => 1 } qw/ * ? . { ( [ /;
48b39c5158Smillert
49b39c5158Smillertsub globmap ($$;)
50b39c5158Smillert{
51b39c5158Smillert    my $inputGlob = shift ;
52b39c5158Smillert    my $outputGlob = shift ;
53b39c5158Smillert
54eac174f2Safresh1    my $obj = File::GlobMapper->new($inputGlob, $outputGlob, @_)
55b39c5158Smillert        or croak "globmap: $Error" ;
56b39c5158Smillert    return $obj->getFileMap();
57b39c5158Smillert}
58b39c5158Smillert
59b39c5158Smillertsub new
60b39c5158Smillert{
61b39c5158Smillert    my $class = shift ;
62b39c5158Smillert    my $inputGlob = shift ;
63b39c5158Smillert    my $outputGlob = shift ;
64b39c5158Smillert    # TODO -- flags needs to default to whatever File::Glob does
65b39c5158Smillert    my $flags = shift || $CSH_GLOB ;
66b39c5158Smillert    #my $flags = shift ;
67b39c5158Smillert
68b39c5158Smillert    $inputGlob =~ s/^\s*\<\s*//;
69b39c5158Smillert    $inputGlob =~ s/\s*\>\s*$//;
70b39c5158Smillert
71b39c5158Smillert    $outputGlob =~ s/^\s*\<\s*//;
72b39c5158Smillert    $outputGlob =~ s/\s*\>\s*$//;
73b39c5158Smillert
74b39c5158Smillert    my %object =
75b39c5158Smillert            (   InputGlob   => $inputGlob,
76b39c5158Smillert                OutputGlob  => $outputGlob,
77b39c5158Smillert                GlobFlags   => $flags,
78b39c5158Smillert                Braces      => 0,
79b39c5158Smillert                WildCount   => 0,
80b39c5158Smillert                Pairs       => [],
81b39c5158Smillert                Sigil       => '#',
82b39c5158Smillert            );
83b39c5158Smillert
84b39c5158Smillert    my $self = bless \%object, ref($class) || $class ;
85b39c5158Smillert
86b39c5158Smillert    $self->_parseInputGlob()
87b39c5158Smillert        or return undef ;
88b39c5158Smillert
89b39c5158Smillert    $self->_parseOutputGlob()
90b39c5158Smillert        or return undef ;
91b39c5158Smillert
92b39c5158Smillert    my @inputFiles = globber($self->{InputGlob}, $flags) ;
93b39c5158Smillert
94b39c5158Smillert    if (GLOB_ERROR)
95b39c5158Smillert    {
96b39c5158Smillert        $Error = $!;
97b39c5158Smillert        return undef ;
98b39c5158Smillert    }
99b39c5158Smillert
100b39c5158Smillert    #if (whatever)
101b39c5158Smillert    {
102b39c5158Smillert        my $missing = grep { ! -e $_ } @inputFiles ;
103b39c5158Smillert
104b39c5158Smillert        if ($missing)
105b39c5158Smillert        {
106b39c5158Smillert            $Error = "$missing input files do not exist";
107b39c5158Smillert            return undef ;
108b39c5158Smillert        }
109b39c5158Smillert    }
110b39c5158Smillert
111b39c5158Smillert    $self->{InputFiles} = \@inputFiles ;
112b39c5158Smillert
113b39c5158Smillert    $self->_getFiles()
114b39c5158Smillert        or return undef ;
115b39c5158Smillert
116b39c5158Smillert    return $self;
117b39c5158Smillert}
118b39c5158Smillert
119b39c5158Smillertsub _retError
120b39c5158Smillert{
121b39c5158Smillert    my $string = shift ;
122b39c5158Smillert    $Error = "$string in input fileglob" ;
123b39c5158Smillert    return undef ;
124b39c5158Smillert}
125b39c5158Smillert
126b39c5158Smillertsub _unmatched
127b39c5158Smillert{
128b39c5158Smillert    my $delimeter = shift ;
129b39c5158Smillert
130b39c5158Smillert    _retError("Unmatched $delimeter");
131b39c5158Smillert    return undef ;
132b39c5158Smillert}
133b39c5158Smillert
134b39c5158Smillertsub _parseBit
135b39c5158Smillert{
136b39c5158Smillert    my $self = shift ;
137b39c5158Smillert
138b39c5158Smillert    my $string = shift ;
139b39c5158Smillert
140b39c5158Smillert    my $out = '';
141b39c5158Smillert    my $depth = 0 ;
142b39c5158Smillert
143b39c5158Smillert    while ($string =~ s/(.*?)$noPreBS(,|$matchMetaRE)//)
144b39c5158Smillert    {
145b39c5158Smillert        $out .= quotemeta($1) ;
146b39c5158Smillert        $out .= $mapping{$2} if defined $mapping{$2};
147b39c5158Smillert
148b39c5158Smillert        ++ $self->{WildCount} if $wildCount{$2} ;
149b39c5158Smillert
150b39c5158Smillert        if ($2 eq ',')
151b39c5158Smillert        {
1529f11ffb7Safresh1            return _unmatched("(")
153b39c5158Smillert                if $depth ;
154b39c5158Smillert
155b39c5158Smillert            $out .= '|';
156b39c5158Smillert        }
157b39c5158Smillert        elsif ($2 eq '(')
158b39c5158Smillert        {
159b39c5158Smillert            ++ $depth ;
160b39c5158Smillert        }
161b39c5158Smillert        elsif ($2 eq ')')
162b39c5158Smillert        {
1639f11ffb7Safresh1            return _unmatched(")")
164b39c5158Smillert                if ! $depth ;
165b39c5158Smillert
166b39c5158Smillert            -- $depth ;
167b39c5158Smillert        }
168b39c5158Smillert        elsif ($2 eq '[')
169b39c5158Smillert        {
170b39c5158Smillert            # TODO -- quotemeta & check no '/'
171b39c5158Smillert            # TODO -- check for \]  & other \ within the []
172b39c5158Smillert            $string =~ s#(.*?\])##
1739f11ffb7Safresh1                or return _unmatched("[");
174b39c5158Smillert            $out .= "$1)" ;
175b39c5158Smillert        }
176b39c5158Smillert        elsif ($2 eq ']')
177b39c5158Smillert        {
1789f11ffb7Safresh1            return _unmatched("]");
179b39c5158Smillert        }
180b39c5158Smillert        elsif ($2 eq '{' || $2 eq '}')
181b39c5158Smillert        {
1829f11ffb7Safresh1            return _retError("Nested {} not allowed");
183b39c5158Smillert        }
184b39c5158Smillert    }
185b39c5158Smillert
186b39c5158Smillert    $out .= quotemeta $string;
187b39c5158Smillert
1889f11ffb7Safresh1    return _unmatched("(")
189b39c5158Smillert        if $depth ;
190b39c5158Smillert
191b39c5158Smillert    return $out ;
192b39c5158Smillert}
193b39c5158Smillert
194b39c5158Smillertsub _parseInputGlob
195b39c5158Smillert{
196b39c5158Smillert    my $self = shift ;
197b39c5158Smillert
198b39c5158Smillert    my $string = $self->{InputGlob} ;
199b39c5158Smillert    my $inGlob = '';
200b39c5158Smillert
201b39c5158Smillert    # Multiple concatenated *'s don't make sense
202b39c5158Smillert    #$string =~ s#\*\*+#*# ;
203b39c5158Smillert
204b39c5158Smillert    # TODO -- Allow space to delimit patterns?
205b39c5158Smillert    #my @strings = split /\s+/, $string ;
206b39c5158Smillert    #for my $str (@strings)
207b39c5158Smillert    my $out = '';
208b39c5158Smillert    my $depth = 0 ;
209b39c5158Smillert
210b39c5158Smillert    while ($string =~ s/(.*?)$noPreBS($matchMetaRE)//)
211b39c5158Smillert    {
212b39c5158Smillert        $out .= quotemeta($1) ;
213b39c5158Smillert        $out .= $mapping{$2} if defined $mapping{$2};
214b39c5158Smillert        ++ $self->{WildCount} if $wildCount{$2} ;
215b39c5158Smillert
216b39c5158Smillert        if ($2 eq '(')
217b39c5158Smillert        {
218b39c5158Smillert            ++ $depth ;
219b39c5158Smillert        }
220b39c5158Smillert        elsif ($2 eq ')')
221b39c5158Smillert        {
2229f11ffb7Safresh1            return _unmatched(")")
223b39c5158Smillert                if ! $depth ;
224b39c5158Smillert
225b39c5158Smillert            -- $depth ;
226b39c5158Smillert        }
227b39c5158Smillert        elsif ($2 eq '[')
228b39c5158Smillert        {
229b39c5158Smillert            # TODO -- quotemeta & check no '/' or '(' or ')'
230b39c5158Smillert            # TODO -- check for \]  & other \ within the []
231b39c5158Smillert            $string =~ s#(.*?\])##
2329f11ffb7Safresh1                or return _unmatched("[");
233b39c5158Smillert            $out .= "$1)" ;
234b39c5158Smillert        }
235b39c5158Smillert        elsif ($2 eq ']')
236b39c5158Smillert        {
2379f11ffb7Safresh1            return _unmatched("]");
238b39c5158Smillert        }
239b39c5158Smillert        elsif ($2 eq '}')
240b39c5158Smillert        {
2419f11ffb7Safresh1            return _unmatched("}");
242b39c5158Smillert        }
243b39c5158Smillert        elsif ($2 eq '{')
244b39c5158Smillert        {
245b39c5158Smillert            # TODO -- check no '/' within the {}
246b39c5158Smillert            # TODO -- check for \}  & other \ within the {}
247b39c5158Smillert
248b39c5158Smillert            my $tmp ;
249b39c5158Smillert            unless ( $string =~ s/(.*?)$noPreBS\}//)
250b39c5158Smillert            {
2519f11ffb7Safresh1                return _unmatched("{");
252b39c5158Smillert            }
253b39c5158Smillert            #$string =~ s#(.*?)\}##;
254b39c5158Smillert
255b39c5158Smillert            #my $alt = join '|',
256b39c5158Smillert            #          map { quotemeta $_ }
257b39c5158Smillert            #          split "$noPreBS,", $1 ;
258b39c5158Smillert            my $alt = $self->_parseBit($1);
259b39c5158Smillert            defined $alt or return 0 ;
260b39c5158Smillert            $out .= "($alt)" ;
261b39c5158Smillert
262b39c5158Smillert            ++ $self->{Braces} ;
263b39c5158Smillert        }
264b39c5158Smillert    }
265b39c5158Smillert
2669f11ffb7Safresh1    return _unmatched("(")
267b39c5158Smillert        if $depth ;
268b39c5158Smillert
269b39c5158Smillert    $out .= quotemeta $string ;
270b39c5158Smillert
271b39c5158Smillert
272b39c5158Smillert    $self->{InputGlob} =~ s/$noPreBS[\(\)]//g;
273b39c5158Smillert    $self->{InputPattern} = $out ;
274b39c5158Smillert
275b39c5158Smillert    #print "# INPUT '$self->{InputGlob}' => '$out'\n";
276b39c5158Smillert
277b39c5158Smillert    return 1 ;
278b39c5158Smillert
279b39c5158Smillert}
280b39c5158Smillert
281b39c5158Smillertsub _parseOutputGlob
282b39c5158Smillert{
283b39c5158Smillert    my $self = shift ;
284b39c5158Smillert
285b39c5158Smillert    my $string = $self->{OutputGlob} ;
286b39c5158Smillert    my $maxwild = $self->{WildCount};
287b39c5158Smillert
288b39c5158Smillert    if ($self->{GlobFlags} & GLOB_TILDE)
289b39c5158Smillert    #if (1)
290b39c5158Smillert    {
291b39c5158Smillert        $string =~ s{
292b39c5158Smillert              ^ ~             # find a leading tilde
293b39c5158Smillert              (               # save this in $1
294b39c5158Smillert                  [^/]        # a non-slash character
295b39c5158Smillert                        *     # repeated 0 or more times (0 means me)
296b39c5158Smillert              )
297b39c5158Smillert            }{
298b39c5158Smillert              $1
299b39c5158Smillert                  ? (getpwnam($1))[7]
300b39c5158Smillert                  : ( $ENV{HOME} || $ENV{LOGDIR} )
301b39c5158Smillert            }ex;
302b39c5158Smillert
303b39c5158Smillert    }
304b39c5158Smillert
305b39c5158Smillert    # max #1 must be == to max no of '*' in input
306b39c5158Smillert    while ( $string =~ m/#(\d)/g )
307b39c5158Smillert    {
308b39c5158Smillert        croak "Max wild is #$maxwild, you tried #$1"
309b39c5158Smillert            if $1 > $maxwild ;
310b39c5158Smillert    }
311b39c5158Smillert
312898184e3Ssthen    my $noPreBS = '(?<!\\\)' ; # no preceding backslash
313b39c5158Smillert    #warn "noPreBS = '$noPreBS'\n";
314b39c5158Smillert
315b39c5158Smillert    #$string =~ s/${noPreBS}\$(\d)/\${$1}/g;
316b39c5158Smillert    $string =~ s/${noPreBS}#(\d)/\${$1}/g;
317b39c5158Smillert    $string =~ s#${noPreBS}\*#\${inFile}#g;
318b39c5158Smillert    $string = '"' . $string . '"';
319b39c5158Smillert
320b39c5158Smillert    #print "OUTPUT '$self->{OutputGlob}' => '$string'\n";
321b39c5158Smillert    $self->{OutputPattern} = $string ;
322b39c5158Smillert
323b39c5158Smillert    return 1 ;
324b39c5158Smillert}
325b39c5158Smillert
326b39c5158Smillertsub _getFiles
327b39c5158Smillert{
328b39c5158Smillert    my $self = shift ;
329b39c5158Smillert
330b39c5158Smillert    my %outInMapping = ();
331b39c5158Smillert    my %inFiles = () ;
332b39c5158Smillert
333b39c5158Smillert    foreach my $inFile (@{ $self->{InputFiles} })
334b39c5158Smillert    {
335b39c5158Smillert        next if $inFiles{$inFile} ++ ;
336b39c5158Smillert
337b39c5158Smillert        my $outFile = $inFile ;
338b39c5158Smillert
339b39c5158Smillert        if ( $inFile =~ m/$self->{InputPattern}/ )
340b39c5158Smillert        {
341b39c5158Smillert            no warnings 'uninitialized';
342b39c5158Smillert            eval "\$outFile = $self->{OutputPattern};" ;
343b39c5158Smillert
344b39c5158Smillert            if (defined $outInMapping{$outFile})
345b39c5158Smillert            {
346b39c5158Smillert                $Error =  "multiple input files map to one output file";
347b39c5158Smillert                return undef ;
348b39c5158Smillert            }
349b39c5158Smillert            $outInMapping{$outFile} = $inFile;
350b39c5158Smillert            push @{ $self->{Pairs} }, [$inFile, $outFile];
351b39c5158Smillert        }
352b39c5158Smillert    }
353b39c5158Smillert
354b39c5158Smillert    return 1 ;
355b39c5158Smillert}
356b39c5158Smillert
357b39c5158Smillertsub getFileMap
358b39c5158Smillert{
359b39c5158Smillert    my $self = shift ;
360b39c5158Smillert
361b39c5158Smillert    return $self->{Pairs} ;
362b39c5158Smillert}
363b39c5158Smillert
364b39c5158Smillertsub getHash
365b39c5158Smillert{
366b39c5158Smillert    my $self = shift ;
367b39c5158Smillert
368b39c5158Smillert    return { map { $_->[0] => $_->[1] } @{ $self->{Pairs} } } ;
369b39c5158Smillert}
370b39c5158Smillert
371b39c5158Smillert1;
372b39c5158Smillert
373b39c5158Smillert__END__
374b39c5158Smillert
375b39c5158Smillert=head1 NAME
376b39c5158Smillert
377b39c5158SmillertFile::GlobMapper - Extend File Glob to Allow Input and Output Files
378b39c5158Smillert
379b39c5158Smillert=head1 SYNOPSIS
380b39c5158Smillert
381b39c5158Smillert    use File::GlobMapper qw( globmap );
382b39c5158Smillert
383b39c5158Smillert    my $aref = globmap $input => $output
384b39c5158Smillert        or die $File::GlobMapper::Error ;
385b39c5158Smillert
386eac174f2Safresh1    my $gm = File::GlobMapper->new( $input => $output )
387b39c5158Smillert        or die $File::GlobMapper::Error ;
388b39c5158Smillert
389b39c5158Smillert
390b39c5158Smillert=head1 DESCRIPTION
391b39c5158Smillert
392b39c5158SmillertThis module needs Perl5.005 or better.
393b39c5158Smillert
394b39c5158SmillertThis module takes the existing C<File::Glob> module as a starting point and
395b39c5158Smillertextends it to allow new filenames to be derived from the files matched by
396b39c5158SmillertC<File::Glob>.
397b39c5158Smillert
398b39c5158SmillertThis can be useful when carrying out batch operations on multiple files that
399b39c5158Smillerthave both an input filename and output filename and the output file can be
400b39c5158Smillertderived from the input filename. Examples of operations where this can be
401b39c5158Smillertuseful include, file renaming, file copying and file compression.
402b39c5158Smillert
403b39c5158Smillert
404b39c5158Smillert=head2 Behind The Scenes
405b39c5158Smillert
406b39c5158SmillertTo help explain what C<File::GlobMapper> does, consider what code you
407b39c5158Smillertwould write if you wanted to rename all files in the current directory
408b39c5158Smillertthat ended in C<.tar.gz> to C<.tgz>. So say these files are in the
409b39c5158Smillertcurrent directory
410b39c5158Smillert
411b39c5158Smillert    alpha.tar.gz
412b39c5158Smillert    beta.tar.gz
413b39c5158Smillert    gamma.tar.gz
414b39c5158Smillert
415b39c5158Smillertand they need renamed to this
416b39c5158Smillert
417b39c5158Smillert    alpha.tgz
418b39c5158Smillert    beta.tgz
419b39c5158Smillert    gamma.tgz
420b39c5158Smillert
421b39c5158SmillertBelow is a possible implementation of a script to carry out the rename
422b39c5158Smillert(error cases have been omitted)
423b39c5158Smillert
424b39c5158Smillert    foreach my $old ( glob "*.tar.gz" )
425b39c5158Smillert    {
426b39c5158Smillert        my $new = $old;
427b39c5158Smillert        $new =~ s#(.*)\.tar\.gz$#$1.tgz# ;
428b39c5158Smillert
429b39c5158Smillert        rename $old => $new
430b39c5158Smillert            or die "Cannot rename '$old' to '$new': $!\n;
431b39c5158Smillert    }
432b39c5158Smillert
433b39c5158SmillertNotice that a file glob pattern C<*.tar.gz> was used to match the
434b39c5158SmillertC<.tar.gz> files, then a fairly similar regular expression was used in
435b39c5158Smillertthe substitute to allow the new filename to be created.
436b39c5158Smillert
437b39c5158SmillertGiven that the file glob is just a cut-down regular expression and that it
438b39c5158Smillerthas already done a lot of the hard work in pattern matching the filenames,
439b39c5158Smillertwouldn't it be handy to be able to use the patterns in the fileglob to
440b39c5158Smillertdrive the new filename?
441b39c5158Smillert
442b39c5158SmillertWell, that's I<exactly> what C<File::GlobMapper> does.
443b39c5158Smillert
444b39c5158SmillertHere is same snippet of code rewritten using C<globmap>
445b39c5158Smillert
446b39c5158Smillert    for my $pair (globmap '<*.tar.gz>' => '<#1.tgz>' )
447b39c5158Smillert    {
448b39c5158Smillert        my ($from, $to) = @$pair;
449b39c5158Smillert        rename $from => $to
450b39c5158Smillert            or die "Cannot rename '$old' to '$new': $!\n;
451b39c5158Smillert    }
452b39c5158Smillert
453b39c5158SmillertSo how does it work?
454b39c5158Smillert
455b39c5158SmillertBehind the scenes the C<globmap> function does a combination of a
456b39c5158Smillertfile glob to match existing filenames followed by a substitute
457b39c5158Smillertto create the new filenames.
458b39c5158Smillert
459b39c5158SmillertNotice how both parameters to C<globmap> are strings that are delimited by <>.
460b39c5158SmillertThis is done to make them look more like file globs - it is just syntactic
461b39c5158Smillertsugar, but it can be handy when you want the strings to be visually
462b39c5158Smillertdistinctive. The enclosing <> are optional, so you don't have to use them - in
463b39c5158Smillertfact the first thing globmap will do is remove these delimiters if they are
464b39c5158Smillertpresent.
465b39c5158Smillert
466b39c5158SmillertThe first parameter to C<globmap>, C<*.tar.gz>, is an I<Input File Glob>.
467b39c5158SmillertOnce the enclosing "< ... >" is removed, this is passed (more or
468b39c5158Smillertless) unchanged to C<File::Glob> to carry out a file match.
469b39c5158Smillert
470b39c5158SmillertNext the fileglob C<*.tar.gz> is transformed behind the scenes into a
471b39c5158Smillertfull Perl regular expression, with the additional step of wrapping each
472b39c5158Smillerttransformed wildcard metacharacter sequence in parenthesis.
473b39c5158Smillert
474b39c5158SmillertIn this case the input fileglob C<*.tar.gz> will be transformed into
475b39c5158Smillertthis Perl regular expression
476b39c5158Smillert
477b39c5158Smillert    ([^/]*)\.tar\.gz
478b39c5158Smillert
479b39c5158SmillertWrapping with parenthesis allows the wildcard parts of the Input File
480b39c5158SmillertGlob to be referenced by the second parameter to C<globmap>, C<#1.tgz>,
481b39c5158Smillertthe I<Output File Glob>. This parameter operates just like the replacement
482b39c5158Smillertpart of a substitute command. The difference is that the C<#1> syntax
483b39c5158Smillertis used to reference sub-patterns matched in the input fileglob, rather
484b39c5158Smillertthan the C<$1> syntax that is used with perl regular expressions. In
485b39c5158Smillertthis case C<#1> is used to refer to the text matched by the C<*> in the
486b39c5158SmillertInput File Glob. This makes it easier to use this module where the
487b39c5158Smillertparameters to C<globmap> are typed at the command line.
488b39c5158Smillert
489b39c5158SmillertThe final step involves passing each filename matched by the C<*.tar.gz>
490b39c5158Smillertfile glob through the derived Perl regular expression in turn and
491b39c5158Smillertexpanding the output fileglob using it.
492b39c5158Smillert
493b39c5158SmillertThe end result of all this is a list of pairs of filenames. By default
494b39c5158Smillertthat is what is returned by C<globmap>. In this example the data structure
495b39c5158Smillertreturned will look like this
496b39c5158Smillert
497b39c5158Smillert     ( ['alpha.tar.gz' => 'alpha.tgz'],
498b39c5158Smillert       ['beta.tar.gz'  => 'beta.tgz' ],
499b39c5158Smillert       ['gamma.tar.gz' => 'gamma.tgz']
500b39c5158Smillert     )
501b39c5158Smillert
502b39c5158Smillert
503b39c5158SmillertEach pair is an array reference with two elements - namely the I<from>
504b39c5158Smillertfilename, that C<File::Glob> has matched, and a I<to> filename that is
505b39c5158Smillertderived from the I<from> filename.
506b39c5158Smillert
507b39c5158Smillert
508b39c5158Smillert
509b39c5158Smillert=head2 Limitations
510b39c5158Smillert
511b39c5158SmillertC<File::GlobMapper> has been kept simple deliberately, so it isn't intended to
512b39c5158Smillertsolve all filename mapping operations. Under the hood C<File::Glob> (or for
513b39c5158Smillertolder versions of Perl, C<File::BSDGlob>) is used to match the files, so you
514b39c5158Smillertwill never have the flexibility of full Perl regular expression.
515b39c5158Smillert
516b39c5158Smillert=head2 Input File Glob
517b39c5158Smillert
518b39c5158SmillertThe syntax for an Input FileGlob is identical to C<File::Glob>, except
519b39c5158Smillertfor the following
520b39c5158Smillert
521b39c5158Smillert=over 5
522b39c5158Smillert
523b39c5158Smillert=item 1.
524b39c5158Smillert
525b39c5158SmillertNo nested {}
526b39c5158Smillert
527b39c5158Smillert=item 2.
528b39c5158Smillert
529b39c5158SmillertWhitespace does not delimit fileglobs.
530b39c5158Smillert
531b39c5158Smillert=item 3.
532b39c5158Smillert
533b39c5158SmillertThe use of parenthesis can be used to capture parts of the input filename.
534b39c5158Smillert
535b39c5158Smillert=item 4.
536b39c5158Smillert
537b39c5158SmillertIf an Input glob matches the same file more than once, only the first
538b39c5158Smillertwill be used.
539b39c5158Smillert
540b39c5158Smillert=back
541b39c5158Smillert
542b39c5158SmillertThe syntax
543b39c5158Smillert
544b39c5158Smillert=over 5
545b39c5158Smillert
546b39c5158Smillert=item B<~>
547b39c5158Smillert
548b39c5158Smillert=item B<~user>
549b39c5158Smillert
550b39c5158Smillert
551b39c5158Smillert=item B<.>
552b39c5158Smillert
553b39c5158SmillertMatches a literal '.'.
554b39c5158SmillertEquivalent to the Perl regular expression
555b39c5158Smillert
556b39c5158Smillert    \.
557b39c5158Smillert
558b39c5158Smillert=item B<*>
559b39c5158Smillert
560b39c5158SmillertMatches zero or more characters, except '/'. Equivalent to the Perl
561b39c5158Smillertregular expression
562b39c5158Smillert
563b39c5158Smillert    [^/]*
564b39c5158Smillert
565b39c5158Smillert=item B<?>
566b39c5158Smillert
567b39c5158SmillertMatches zero or one character, except '/'. Equivalent to the Perl
568b39c5158Smillertregular expression
569b39c5158Smillert
570b39c5158Smillert    [^/]?
571b39c5158Smillert
572b39c5158Smillert=item B<\>
573b39c5158Smillert
574b39c5158SmillertBackslash is used, as usual, to escape the next character.
575b39c5158Smillert
576b39c5158Smillert=item  B<[]>
577b39c5158Smillert
578b39c5158SmillertCharacter class.
579b39c5158Smillert
580b39c5158Smillert=item  B<{,}>
581b39c5158Smillert
582b39c5158SmillertAlternation
583b39c5158Smillert
584b39c5158Smillert=item  B<()>
585b39c5158Smillert
586b39c5158SmillertCapturing parenthesis that work just like perl
587b39c5158Smillert
588b39c5158Smillert=back
589b39c5158Smillert
590b39c5158SmillertAny other character it taken literally.
591b39c5158Smillert
592b39c5158Smillert=head2 Output File Glob
593b39c5158Smillert
594b39c5158SmillertThe Output File Glob is a normal string, with 2 glob-like features.
595b39c5158Smillert
596b39c5158SmillertThe first is the '*' metacharacter. This will be replaced by the complete
597b39c5158Smillertfilename matched by the input file glob. So
598b39c5158Smillert
599b39c5158Smillert    *.c *.Z
600b39c5158Smillert
601b39c5158SmillertThe second is
602b39c5158Smillert
603b39c5158SmillertOutput FileGlobs take the
604b39c5158Smillert
605b39c5158Smillert=over 5
606b39c5158Smillert
607b39c5158Smillert=item "*"
608b39c5158Smillert
609b39c5158SmillertThe "*" character will be replaced with the complete input filename.
610b39c5158Smillert
611b39c5158Smillert=item #1
612b39c5158Smillert
613b39c5158SmillertPatterns of the form /#\d/ will be replaced with the
614b39c5158Smillert
615b39c5158Smillert=back
616b39c5158Smillert
617b39c5158Smillert=head2 Returned Data
618b39c5158Smillert
619b39c5158Smillert
620b39c5158Smillert=head1 EXAMPLES
621b39c5158Smillert
622b39c5158Smillert=head2 A Rename script
623b39c5158Smillert
624b39c5158SmillertBelow is a simple "rename" script that uses C<globmap> to determine the
625b39c5158Smillertsource and destination filenames.
626b39c5158Smillert
627b39c5158Smillert    use File::GlobMapper qw(globmap) ;
628b39c5158Smillert    use File::Copy;
629b39c5158Smillert
630b39c5158Smillert    die "rename: Usage rename 'from' 'to'\n"
631b39c5158Smillert        unless @ARGV == 2 ;
632b39c5158Smillert
633b39c5158Smillert    my $fromGlob = shift @ARGV;
634b39c5158Smillert    my $toGlob   = shift @ARGV;
635b39c5158Smillert
636b39c5158Smillert    my $pairs = globmap($fromGlob, $toGlob)
637b39c5158Smillert        or die $File::GlobMapper::Error;
638b39c5158Smillert
639b39c5158Smillert    for my $pair (@$pairs)
640b39c5158Smillert    {
641b39c5158Smillert        my ($from, $to) = @$pair;
642b39c5158Smillert        move $from => $to ;
643b39c5158Smillert    }
644b39c5158Smillert
645b39c5158Smillert
646b39c5158Smillert
647b39c5158SmillertHere is an example that renames all c files to cpp.
648b39c5158Smillert
649b39c5158Smillert    $ rename '*.c' '#1.cpp'
650b39c5158Smillert
651b39c5158Smillert=head2 A few example globmaps
652b39c5158Smillert
653b39c5158SmillertBelow are a few examples of globmaps
654b39c5158Smillert
655b39c5158SmillertTo copy all your .c file to a backup directory
656b39c5158Smillert
657b39c5158Smillert    '</my/home/*.c>'    '</my/backup/#1.c>'
658b39c5158Smillert
659b39c5158SmillertIf you want to compress all
660b39c5158Smillert
661b39c5158Smillert    '</my/home/*.[ch]>'    '<*.gz>'
662b39c5158Smillert
663b39c5158SmillertTo uncompress
664b39c5158Smillert
665b39c5158Smillert    '</my/home/*.[ch].gz>'    '</my/home/#1.#2>'
666b39c5158Smillert
667b39c5158Smillert=head1 SEE ALSO
668b39c5158Smillert
669b39c5158SmillertL<File::Glob|File::Glob>
670b39c5158Smillert
671b39c5158Smillert=head1 AUTHOR
672b39c5158Smillert
673b39c5158SmillertThe I<File::GlobMapper> module was written by Paul Marquess, F<pmqs@cpan.org>.
674b39c5158Smillert
675b39c5158Smillert=head1 COPYRIGHT AND LICENSE
676b39c5158Smillert
677b39c5158SmillertCopyright (c) 2005 Paul Marquess. All rights reserved.
678b39c5158SmillertThis program is free software; you can redistribute it and/or
679b39c5158Smillertmodify it under the same terms as Perl itself.
680