File Coverage

blib/lib/Regexp/Shellish.pm
Criterion Covered Total %
statement 42 42 100.0
branch 31 34 91.1
condition 35 39 89.7
subroutine 6 6 100.0
pod 2 2 100.0
total 116 123 94.3


line stmt bran cond sub pod time code
1             package Regexp::Shellish ;
2              
3             #
4             # Copyright 1999, Barrie Slaymaker
5             #
6             # You may distribute under the terms of either the GNU General Public
7             # License or the Artistic License, as specified in the README file.
8             #
9              
10             =head1 NAME
11              
12             Regexp::Shellish - Shell-like regular expressions
13              
14             =head1 SYNOPSIS
15              
16             use Regexp::Shellish qw( :all ) ;
17              
18             $re = compile_shellish( 'a/c*d' ) ;
19              
20             ## This next one's like 'a*d' except that it'll
21             ## match 'a/d'.
22             $re = compile_shellish( 'a**d' ) ;
23              
24             ## And here '**' won't match 'a/d', but behaves
25             ## like 'a*d', except for the possibility of high
26             ## cpu time consumption.
27             $re = compile_shellish( 'a**d', { star_star => 0 } ) ;
28              
29             ## The next two result in identical $re1 and $re2.
30             ## The second is a noop so that Regexp references can
31             ## be easily accomodated.
32             $re1 = compile_shellish( 'a{b,c}d' ) ;
33             $re2 = compile_shellish( qr/\A(?:a(?:b|c)d)\Z/ ) ;
34              
35             @matches = shellish_glob( $re, @possibilities ) ;
36              
37              
38             =head1 DESCRIPTION
39              
40             Provides shell-like regular expressions. The wildcards provided
41             are C, C<*> and C<**>, where C<**> is like C<*> but matches C. See
42             L for details.
43              
44             Case sensitivity and constructs like <**>, C<(a*b)>, and C<{a,b,c}>
45             can be disabled.
46              
47             =over
48              
49             =cut
50              
51 1     1   6470 use strict ;
  1         1  
  1         86  
52              
53 1     1   7 use Carp ;
  1         3  
  1         81  
54 1     1   6 use Exporter ;
  1         6  
  1         42  
55              
56 1     1   7 use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS ) ;
  1         2  
  1         731  
57              
58             $VERSION = '0.93' ;
59              
60             @ISA = qw( Exporter ) ;
61              
62             @EXPORT_OK = qw(
63             compile_shellish
64             shellish_glob
65             ) ;
66              
67             %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ) ;
68              
69             =item compile_shellish
70              
71             Compiles a string containing a 'shellish' regular expression, returning a
72             Regexp reference. Regexp references passed in are passed through
73             unmolested.
74              
75             Here are the transformation rules from shellish expression terms to
76             perl regular expression terms:
77              
78             Shellish Perl RE
79             ======== =======
80             * [^/]*
81             ? .
82             ** .* ## unless { star_star => 0 }
83             ... .* ## unless { dot_dot_dot => 0 }
84              
85             ( ( ## unless { parens => 0 }
86             ) ) ## unless { parens => 0 }
87              
88             {a,b,c} (?:a|b|c) ## unless { braces => 0 }
89              
90             \a a ## These are de-escaped and
91             \* \* ## passed to quotemeta()
92              
93             The wildcards treat newlines as normal characters.
94              
95             Parens group in to $1..$n, since they are passed through unmolested
96             (unless option parens => 0 is passed). This is useless when using
97             glob_shellish(), though.
98              
99             The final parameter can be a hash reference containing options:
100              
101             compile_shellish(
102             '**',
103             {
104             anchors => 0, ## Doesn't put ^ and $ around the
105             ## resulting regexp
106             case_sensitive => 0, ## Make case insensitive
107             dot_dot_dot => 0, ## '...' is now just three '.' chars
108             star_star => 0, ## '**' is now two '*' wildcards
109             parens => 0, ## '(', ')' are now regular chars
110             braces => 0, ## '{', '}' are now regular chars
111             }
112             ) ;
113              
114             No option affects Regexps passed through.
115              
116             =cut
117              
118             sub compile_shellish {
119 62 100 66 62 1 7713 my $o = @_ && ref $_[-1] eq 'HASH' ? pop : {} ;
120 62         80 my $re = shift ;
121              
122 62 100       157 return $re if ref $re eq 'Regexp' ;
123              
124 33 100 100     97 my $star_star = ( ! exists $o->{star_star} || $o->{star_star} )
125             ? '.*'
126             : '[^/]*[^/]*' ;
127              
128 33 100 100     95 my $dot_dot_dot = ( ! exists $o->{dot_dot_dot} || $o->{dot_dot_dot} )
129             ? '.*'
130             : '\.\.\.' ;
131              
132 33 100 100     82 my $case = ( ! exists $o->{case_sensitive} || $o->{case_sensitive} )
133             ? ''
134             : 'i' ;
135              
136 33   66     73 my $anchors = ( ! exists $o->{anchors} || $o->{anchors} ) ;
137 33   100     70 my $pass_parens = ( ! exists $o->{parens} || $o->{parens} ) ;
138 33   100     72 my $pass_braces = ( ! exists $o->{braces} || $o->{braces} ) ;
139              
140 33         33 my $brace_depth = 0 ;
141              
142 33         37 my $orig = $re ;
143              
144 33         126 $re =~ s@
145             ( \\.
146             | \*\*
147             | \.\.\.
148             | .
149             )
150             @
151 120 100 100     1519 if ( $1 eq '?' ) {
    100 100        
    100 100        
    100 100        
    100 100        
    100          
    100          
    100          
152 3         7 '[^/]' ;
153             }
154             elsif ( $1 eq '*' ) {
155 4         11 '[^/]*' ;
156             }
157             elsif ( $1 eq '**' ) {
158 6         14 $star_star ;
159             }
160             elsif ( $1 eq '...' ) {
161 4         13 $dot_dot_dot;
162             }
163             elsif ( $pass_braces && $1 eq '{' ) {
164 4         5 ++$brace_depth ;
165 4         13 '(?:' ;
166             }
167             elsif ( $pass_braces && $1 eq '}' ) {
168 4 50       21 croak "Unmatched '}' in '$orig'" unless $brace_depth-- ;
169 4         12 ')' ;
170             }
171             elsif ( $pass_braces && $brace_depth && $1 eq ',' ) {
172 2         7 '|' ;
173             }
174             elsif ( $pass_parens && index( '()', $1 ) >= 0 ) {
175 4         14 $1 ;
176             }
177             else {
178 89         258 quotemeta(substr( $1, -1 ) );
179             }
180             @gexs ;
181              
182 33 50       86 croak "Unmatched '{' in '$orig'" if $brace_depth ;
183              
184 33 100       651 return $anchors ? qr/\A(?$case:$re)\Z/s : qr/(?$case:$re)/s ;
185             }
186              
187              
188             =item shellish_glob
189              
190             Pass a regular expression and a list of possible values, get back a list of
191             matching values.
192              
193             my @matches = shellish_glob( '*/*', @possibilities ) ;
194             my @matches = shellish_glob( '*/*', @possibilities, \%options ) ;
195              
196             =cut
197              
198             sub shellish_glob {
199 28 50 33 28 1 192 my $o = @_ > 1 && ref $_[-1] eq 'HASH' ? pop : {} ;
200 28         51 my $re = compile_shellish( shift, $o ) ;
201 28         48 return grep { m/$re/ } @_ ;
  280         917  
202             }
203              
204             =back
205              
206             =head1 AUTHOR
207              
208             Barrie Slaymaker
209              
210             =cut
211              
212              
213             1 ;