]> git.vpit.fr Git - perl/modules/Regexp-Wildcards.git/blob - lib/Regexp/Wildcards.pm
Major rewrite
[perl/modules/Regexp-Wildcards.git] / lib / Regexp / Wildcards.pm
1 package Regexp::Wildcards;
2
3 use strict;
4 use warnings;
5
6 use Carp qw/croak/;
7 use Text::Balanced qw/extract_bracketed/;
8
9 =head1 NAME
10
11 Regexp::Wildcards - Converts wildcard expressions to Perl regular expressions.
12
13 =head1 VERSION
14
15 Version 0.08
16
17 =cut
18
19 our $VERSION = '0.08';
20
21 =head1 SYNOPSIS
22
23     use Regexp::Wildcards;
24
25     my $rw = Regexp::Wildcards->new(type => 'unix');
26
27     my $re;
28     $re = $rw->convert('a{b?,c}*');          # Do it Unix shell style.
29     $re = $rw->convert('a?,b*',   'win32');  # Do it Windows shell style.
30     $re = $rw->convert('*{x,y}?', 'jokers'); # Process the jokers and escape the rest.
31     $re = $rw->convert('%a_c%',    'sql');   # Turn SQL wildcards into regexps.
32
33     $rw = Regexp::Wildcards->new(
34      do      => [ qw/jokers brackets/ ], # Do jokers and brackets.
35      capture => [ qw/any greedy/ ],      # Capture *'s greedily.
36     );
37
38     $rw->do(add => 'groups');            # Don't escape groups.
39     $rw->capture(rem => [ qw/greedy/ ]); # Actually we want non-greedy matches.
40     $re = $rw->convert('*a{,(b)?}?c*');  # '(.*?)a(?:|(b).).c(.*?)'
41     $rw->capture();                      # No more captures.
42
43 =head1 DESCRIPTION
44
45 In many situations, users may want to specify patterns to match but don't need the full power of regexps. Wildcards make one of those sets of simplified rules. This module converts wildcard expressions to Perl regular expressions, so that you can use them for matching.
46
47 It handles the C<*> and C<?> shell jokers, as well as Unix bracketed alternatives C<{,}>, but also C<%> and C<_> SQL wildcards. It can also keep original C<(...)> groups. Backspace (C<\>) is used as an escape character.
48
49 Typesets that mimic the behaviour of Windows and Unix shells are also provided.
50
51 =head1 METHODS
52
53 =cut
54
55 sub _check_self {
56  croak 'First argument isn\'t a valid ' . __PACKAGE__ . ' object'
57   unless ref $_[0] and $_[0]->isa(__PACKAGE__);
58 }
59
60 my %types = (
61  jokers   => [ qw/jokers/ ],
62  sql      => [ qw/sql/ ],
63  commas   => [ qw/commas/ ],
64  brackets => [ qw/brackets/ ],
65  unix     => [ qw/jokers brackets/ ],
66  win32    => [ qw/jokers commas/ ],
67 );
68 $types{$_} = $types{win32} for qw/dos os2 MSWin32 cygwin/;
69
70 my %escapes = (
71  jokers   => '?*',
72  sql      => '%_',
73  commas   => ',',
74  brackets => '{},',
75  groups   => '()',
76 );
77
78 my %captures = (
79  single   => sub { $_[1] ? '(.)' : '.' },
80  any      => sub { $_[1] ? ($_[0]->{greedy} ? '(.*)'
81                                             : '(.*?)')
82                          : '.*' },
83  brackets => sub { $_[1] ? '(' : '(?:'; },
84  greedy   => undef
85 );
86
87 sub _validate {
88  my $self  = shift;
89  _check_self $self;
90  my $valid = shift;
91  my $old   = shift;
92  $old = { } unless defined $old;
93  my $c;
94  if (@_ <= 1) {
95   $c = { set => $_[0] };
96  } elsif (@_ % 2) {
97   croak 'Arguments must be passed as an unique scalar or as key => value pairs';
98  } else {
99   my %args = @_;
100   $c = { map { (exists $args{$_}) ? ($_ => $args{$_}) : () } qw/set add rem/ };
101  }
102  for (qw/set add rem/) {
103   my $v = $c->{$_};
104   next unless defined $v;
105   my $cb = {
106    ''      => sub { +{ ($_[0] => 1) x (exists $valid->{$_[0]}) } },
107    'ARRAY' => sub { +{ map { ($_ => 1) x (exists $valid->{$_}) } @{$_[0]} } },
108    'HASH'  => sub { +{ map { ($_ => $_[0]->{$_}) x (exists $valid->{$_}) }
109                         keys %{$_[0]} } }
110   }->{ ref $v };
111   croak 'Wrong option set' unless $cb;
112   $c->{$_} = $cb->($v);
113  }
114  my $config = (exists $c->{set}) ? $c->{set} : $old;
115  $config->{$_} = $c->{add}->{$_} for grep $c->{add}->{$_},
116                                                 keys %{$c->{add} || {}};
117  delete $config->{$_} for grep $c->{rem}->{$_}, keys %{$c->{rem} || {}};
118  $config;
119 }
120
121 sub _do {
122  my $self = shift;
123  my $config;
124  $config->{do} = $self->_validate(\%escapes, $self->{do}, @_);
125  $config->{escape} = '';
126  $config->{escape} .= $escapes{$_} for keys %{$config->{do}};
127  $config->{escape} = quotemeta $config->{escape};
128  $config;
129 }
130
131 sub do {
132  my $self = shift;
133  _check_self $self;
134  my $config = $self->_do(@_);
135  $self->{$_} = $config->{$_} for keys %$config;
136  $self;
137 }
138
139 sub _capture {
140  my $self = shift;
141  my $config;
142  $config->{capture} = $self->_validate(\%captures, $self->{capture}, @_);
143  $config->{greedy}  = delete $config->{capture}->{greedy};
144  for (keys %captures) {
145   $config->{'c_' . $_} = $captures{$_}->($config, $config->{capture}->{$_})
146                                                if $captures{$_}; # Skip 'greedy'
147  }
148  $config;
149 }
150
151 sub capture {
152  my $self = shift;
153  _check_self $self;
154  my $config = $self->_capture(@_);
155  $self->{$_} = $config->{$_} for keys %$config;
156  $self;
157 }
158
159 sub _type {
160  my ($self, $type) = @_;
161  $type = 'unix'      unless defined $type;
162  croak 'Wrong type'  unless exists $types{$type};
163  my $config = $self->_do($types{$type});
164  $config->{type} = $type;
165  $config;
166 }
167
168 sub type {
169  my $self = shift;
170  _check_self $self;
171  my $config = $self->_type(@_);
172  $self->{$_} = $config->{$_} for keys %$config;
173  $self;
174 }
175
176 sub new {
177  my $class = shift;
178  $class = ref($class) || $class || __PACKAGE__;
179  croak 'Optional arguments must be passed as key => value pairs' if @_ % 2;
180  my %args = @_;
181  my $self = { };
182  bless $self, $class;
183  if (defined $args{do}) {
184   $self->do($args{do});
185  } else {
186   $self->type($args{type});
187  }
188  $self->capture($args{capture});
189 }
190
191 =head2 C<< new [ do => $what | type => $type ], capture => $captures >>
192
193 Constructs a new L<Regexp::Wildcard> object.
194
195 C<do> lists all features that should be enabled when converting wildcards to regexps. Refer to L</do> for details on what can be passed in C<$what>.
196
197 The C<type> specifies a predefined set of C<do> features to use. 
198
199
200 C<$type> can be any of C<'jokers'>, C<'sql'>, C<'commas'>, C<'brackets'>, C<'win32'> or C<'unix'>. An unknown value defaults to C<'unix'>, except for C<'dos'>, C<'os2'>, C<'MSWin32'> and C<'cygwin'> that default to C<'win32'>. With this set of options, you can pass C<$^O> as the C<$type> so that you get the corresponding shell behaviour.
201
202 =over 4
203
204 =item C<>
205
206 For the C<$capture> syntax, refer to the L</capture> method.
207
208 =head3 C<type>
209
210 =head3 C<capture>
211
212 =over 4
213
214 =head2 C<$CaptureSingle>
215
216 When this variable is true, each occurence of unescaped I<"exactly one"> wildcards (i.e. C<?> jokers or C<_> for SQL wildcards) are made capturing in the resulting regexp (they are be replaced by C<(.)>). Otherwise, they are just replaced by C<.>. Default is the latter.
217
218     For jokers :
219     'a???b\\??' is translated to 'a(.)(.)(.)b\\?(.)' if $CaptureSingle is true
220                                  'a...b\\?.'         otherwise (default)
221
222     For SQL wildcards :
223     'a___b\\__' is translated to 'a(.)(.)(.)b\\_(.)' if $CaptureSingle is true
224                                  'a...b\\_.'         otherwise (default)
225
226
227 =item C<any>
228
229 By default this variable is false, and successions of unescaped I<"any"> wildcards (i.e. C<*> jokers or C<%> for SQL wildcards) are replaced by B<one> single C<.*>. When it evalutes to true, those sequences of I<"any"> wildcards are made into B<one> capture, which is greedy (C<(.*)>) for C<$CaptureAny E<gt> 0> and otherwise non-greedy (C<(.*?)>).
230
231     For jokers :
232     'a***b\\**' is translated to 'a.*b\\*.*'       if $CaptureAny is false (default)
233                                  'a(.*)b\\*(.*)'   if $CaptureAny > 0
234                                  'a(.*?)b\\*(.*?)' otherwise
235
236     For SQL wildcards :
237     'a%%%b\\%%' is translated to 'a.*b\\%.*'       if $CaptureAny is false (default)
238                                  'a(.*)b\\%(.*)'   if $CaptureAny > 0
239                                  'a(.*?)b\\%(.*?)' otherwise
240
241 =item C<brackets>
242
243 If this variable is set to true, valid brackets constructs are made into C<( | )> captures, and otherwise they are replaced by non-capturing alternations (C<(?: | >)), which is the default.
244
245     'a{b\\},\\{c}' is translated to 'a(b\\}|\\{c)'   if $CaptureBrackets is true
246                                     'a(?:b\\}|\\{c)' otherwise (default)
247
248 =back
249
250 =head2 C<jokers>
251
252 This function takes as its only argument the wildcard string to process, and returns the corresponding regular expression where the jokers C<?> (I<"exactly one">) and C<*> (I<"any">) have been translated into their regexp equivalents (see L</VARIABLES> for more details). All other unprotected regexp metacharacters are escaped.
253
254     # Everything is escaped.
255     print 'ok' if wc2re_jokers('{a{b,c}d,e}') eq '\\{a\\{b\\,c\\}d\\,e\\}';
256
257 =cut
258
259 =head2 C<sql>
260
261 Similar to the precedent, but for the SQL wildcards C<_> (I<"exactly one">) and C<%> (I<"any">). All other unprotected regexp metacharacters are escaped.
262  
263 =cut
264   
265 =head2 C<shell>
266
267 This function conforms to standard Unix shell wildcard rules. It successively escapes all unprotected regexp special characters that doesn't hold any meaning for wildcards, turns C<?> and C<*> jokers into their regexp equivalents (see L</wc2re_jokers>), and changes bracketed blocks into (possibly capturing) alternations as described in L</VARIABLES>. If brackets are unbalanced, it tries to substitute as many of them as possible, and then escape the remaining C<{> and C<}>. Commas outside of any bracket-delimited block are also escaped.
268
269     # This is a valid bracket expression, and is completely translated.
270     print 'ok' if wc2re_unix('{a{b,c}d,e}') eq '(?:a(?:b|c)d|e)';
271
272 The function handles unbalanced bracket expressions, by escaping everything it can't recognize. For example :
273
274     # The first comma is replaced, and the remaining brackets and comma are escaped.
275     print 'ok' if wc2re_unix('{a\\{b,c}d,e}') eq '(?:a\\{b|c)d\\,e\\}';
276
277     # All the brackets and commas are escaped.
278     print 'ok' if wc2re_unix('{a{b,c\\}d,e}') eq '\\{a\\{b\\,c\\}d\\,e\\}';
279
280 This one works just like the one before, but for Windows wildcards. Bracketed blocks are no longer handled (which means that brackets are escaped), but you can provide a comma-separated list of items.
281
282     # All the brackets are escaped, and commas are seen as list delimiters.
283     print 'ok' if wc2re_win32('{a{b,c}d,e}') eq '(?:\\{a\\{b|c\\}d|e\\})';
284
285 =cut
286
287 =head2 C<convert>
288
289 A generic function that wraps around all the different rules. The first argument is the wildcard expression, and the second one is the type of rules to apply which can be :
290
291 =over 4
292
293 =item C<'unix'>, C<'win32'>, C<'jokers'>, C<'sql'>
294
295 For one of those raw rule names, C<wc2re> simply maps to C<wc2re_unix>, C<wc2re_win32>, C<wc2re_jokers> and C<wc2re_sql> respectively.
296
297 =item C<$^O>
298
299 If you supply the Perl operating system name, the call is deferred to C<wc2re_win32> for C< $^O> equal to C<'dos'>, C<'os2'>, C<'MSWin32'> or C<'cygwin'>, and to C<wc2re_unix> in all the other cases.
300
301 =back
302
303 If the type is undefined or not supported, it defaults to C<'unix'>.
304
305      # Wraps to wc2re_jokers ($re eq 'a\\{b\\,c\\}.*').
306      $re = wc2re 'a{b,c}*' => 'jokers';
307
308      # Wraps to wc2re_win32 ($re eq '(?:a\\{b|c\\}.*)')
309      #       or wc2re_unix  ($re eq 'a(?:b|c).*')       depending on $^O.
310      $re = wc2re 'a{b,c}*' => $^O;
311
312 =cut
313
314 sub convert {
315  my ($self, $wc, $type) = @_;
316  _check_self $self;
317  my $config;
318  if (defined $type) {
319   $config = $self->_type($type);
320  } else {
321   $config = $self;
322  }
323  return unless defined $wc;
324  my $do = $config->{do};
325  my $e  = $config->{escape};
326  $wc =~ s/(?<!\\)((?:\\\\)*[^\w\s\\$e])/\\$1/g;
327  return $self->_sql($wc)   if $do->{sql};
328  $wc = $self->_jokers($wc) if $do->{jokers};
329  if ($do->{brackets}) {
330   $wc = $self->_bracketed($wc);
331  } elsif ($do->{commas}) {
332   if ($wc =~ /(?<!\\)(?:\\\\)*,/) { # win32 allows comma-separated lists
333    $wc = $self->{'c_brackets'} . $self->_commas($wc) . ')';
334   }
335  }
336  return $wc;
337 }
338
339 =head1 EXPORT
340
341 An object module shouldn't export any function, and so does this one.
342
343 =head1 DEPENDENCIES
344
345 L<Text::Balanced>, which is bundled with perl since version 5.7.3
346
347 =head1 CAVEATS
348
349 This module does not implement the strange behaviours of Windows shell that result from the special handling of the three last characters (for the file extension). For example, Windows XP shell matches C<*a> like C<.*a>, C<*a?> like C<.*a.?>, C<*a??> like C<.*a.{0,2}> and so on.
350
351 =head1 SEE ALSO
352
353 Some modules provide incomplete alternatives as helper functions :
354
355 L<Net::FTPServer> has a method for that. Only jokers are translated, and escaping won't preserve them.
356
357 L<File::Find::Match::Util> has a C<wildcard> function that compiles a matcher. It only handles C<*>.
358
359 L<Text::Buffer> has the C<convertWildcardToRegex> class method that handles jokers.
360
361 =head1 AUTHOR
362
363 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
364
365 You can contact me by mail or on #perl @ FreeNode (vincent or Prof_Vince).
366
367 =head1 BUGS
368
369 Please report any bugs or feature requests to C<bug-regexp-wildcards at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Regexp-Wildcards>. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
370
371 =head1 SUPPORT
372
373 You can find documentation for this module with the perldoc command.
374
375     perldoc Regexp::Wildcards
376
377 Tests code coverage report is available at L<http://www.profvince.com/perl/cover/Regexp-Wildcards>.
378
379 =head1 COPYRIGHT & LICENSE
380
381 Copyright 2007-2008 Vincent Pit, all rights reserved.
382
383 This program is free software; you can redistribute it and/or modify it
384 under the same terms as Perl itself.
385
386 =cut
387
388 sub _extract ($) { extract_bracketed $_[0], '{',  qr/.*?(?<!\\)(?:\\\\)*(?={)/ }
389
390 sub _jokers {
391  my $self = shift;
392  local $_ = $_[0];
393  # escape an odd number of \ that doesn't protect a regexp/wildcard special char
394  s/(?<!\\)((?:\\\\)*\\(?:[\w\s]|$))/\\$1/g;
395  # substitute ? preceded by an even number of \
396  my $s = $self->{c_single};
397  s/(?<!\\)((?:\\\\)*)\?/$1$s/g;
398  # substitute * preceded by an even number of \
399  $s = $self->{c_any};
400  s/(?<!\\)((?:\\\\)*)\*+/$1$s/g;
401  return $_;
402 }
403
404 sub _sql {
405  my $self = shift;
406  local $_ = $_[0];
407  # escape an odd number of \ that doesn't protect a regexp/wildcard special char
408  s/(?<!\\)((?:\\\\)*\\(?:[^\W_]|\s|$))/\\$1/g;
409  # substitute _ preceded by an even number of \
410  my $s = $self->{c_single};
411  s/(?<!\\)((?:\\\\)*)_/$1$s/g;
412  # substitute % preceded by an even number of \
413  $s = $self->{c_any};
414  s/(?<!\\)((?:\\\\)*)%+/$1$s/g;
415  return $_;
416 }
417
418 sub _commas {
419  local $_ = $_[1];
420  # substitute , preceded by an even number of \
421  s/(?<!\\)((?:\\\\)*),/$1|/g;
422  return $_;
423 }
424
425 sub _brackets {
426  my ($self, $rest) = @_;
427  substr $rest, 0, 1, '';
428  chop $rest;
429  my ($re, $bracket, $prefix) = ('');
430  while (do { ($bracket, $rest, $prefix) = _extract $rest; $bracket }) {
431   $re .= $self->_commas($prefix) . $self->_brackets($bracket);
432  }
433  $re .= $self->_commas($rest);
434  return $self->{c_brackets} . $re . ')';
435 }
436
437 sub _bracketed {
438  my ($self, $rest) = @_;
439  my ($re, $bracket, $prefix) = ('');
440  while (do { ($bracket, $rest, $prefix) = _extract $rest; $bracket }) {
441   $re .= $prefix . $self->_brackets($bracket);
442  }
443  $re .= $rest;
444  $re =~ s/(?<!\\)((?:\\\\)*[\{\},])/\\$1/g;
445  return $re;
446 }
447
448 1; # End of Regexp::Wildcards