Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

263 lines
6.8 KiB

package PPM::Search;
use strict;
use Data::Dumper;
$PPM::Search::VERSION = '3.00';
# Convert a glob into a regex.
sub glob_to_regex {
my ($glob, $casei) = @_;
my $i = $casei ? '(?i)': '';
# If the user specified any globs, remove the implicit globs surrounding
# their query:
my $globs = ($glob =~ /[?*]/);
my $l = $globs ? '^' : '';
my $r = $globs ? '$' : '';
$glob =~ s/\./\\./g;
$glob =~ s/\?/./g;
$glob =~ s/\*/.*?/g;
return qr/$l$i$glob$r/;
}
sub new {
my ($pkg, $query, $casei) = @_;
$pkg = ref($pkg) || $pkg;
my $self = bless {
'query' => $query,
'casei' => $casei,
}, $pkg;
my ($terms, $left) = $self->_query($self->{'query'});
$self->{'terms'} = $terms;
$self;
}
sub match {
die "Must override match() method in subclass!";
}
sub search {
my ($self, @pkgs) = @_;
$self->do_search($self->{'terms'}, \@pkgs);
}
sub do_search {
my ($self, $terms, $matches) = @_;
my $op = shift @$terms;
return $self->do_and($terms, $matches) if $op eq 'and';
return $self->do_or ($terms, $matches) if $op eq 'or';
warn "Invalid search.\n";
return ();
}
sub do_and {
my $self = shift;
my ($terms, $matches) = @_;
my @matches = @$matches;
for my $term (@$terms) {
if (ref $term eq 'HASH') {
@matches =
grep { my $o = $self->match($_, $term->{field}, $term->{value});
$term->{not} ? not $o : $o
} @matches;
}
elsif (ref $term eq 'ARRAY') {
@matches = $self->do_search($term, \@matches);
}
}
return @matches;
}
sub do_or {
my $self = shift;
my ($terms, $matches) = @_;
my @matches;
my %matches;
for my $term (@$terms) {
my @new;
if (ref $term eq 'HASH') {
@new = (grep {my $o = $self->match($_, $term->{field}, $term->{value});
$term->{not} ? not $o : $o }
grep { not $matches{$_->name} }
@$matches);
}
elsif (ref $term eq 'ARRAY') {
@new = $self->do_search($term, $matches);
}
for my $n (@new) {
$matches{$n->name}++ and next;
push @matches, $n;
}
}
return @matches;
}
# Parse the query:
sub _query {
my $self = shift;
my $query = shift;
my ($terms, $left) = $self->_terms($query);
return ($terms, $left) if ref $terms eq 'ARRAY';
($terms, $left) = $self->_termopterms($query);
return ($terms, $left) if ref $terms eq 'ARRAY';
return (undef, $query);
}
sub _termopterms {
my $self = shift;
my $query = shift;
my @terms = ('or', ['and']);
my ($yes1, $yes2, $left) = (undef, undef, $query);
while(1) {
($yes1, $left) = $self->_term($left);
warn "Query syntax error: '$left'\n" and return (undef, $left)
unless defined $yes1;
($yes2, $left) = $self->_op($left);
push @{$terms[$#terms]}, $yes1;
last unless defined $yes2;
push @terms, ['and'] if $yes2 =~ /or/i;
}
return \@terms, $left;
}
sub _terms {
my $self = shift;
my $query = shift;
my @terms = ('and');
my ($yes, $left) = (undef, $query);
while (1) {
($yes, $left)=$self->_term($left);
last unless defined $yes;
push @terms, $yes;
}
return undef, $query unless $left eq '';
return \@terms, $left;
}
sub _term {
my $self = shift;
my $query = shift;
my ($yes, $left) = $self->_term_1($query);
return ($yes, $left) if defined $yes;
($yes, $left) = $self->_term_2($query);
return ($yes, $left) if defined $yes;
($yes, $left) = $self->_term_3($query);
return ($yes, $left) if defined $yes;
return (undef, $query);
}
sub _term_1 {
my $self = shift;
my $query = shift;
my $term = { not => 0 };
my ($yes, $left) = (undef, $query);
($yes, $left) = $self->_not($left);
$term->{not} = 1 if defined $yes;
($yes, $left) = $self->_field($left);
return (undef, $query) unless defined $yes;
return (undef, $query) unless $left =~ /^=/;
$term->{field} = $yes;
($yes, $left) = $self->_glob2regex($self->_glob(substr($left, 1)));
return (undef, $query) unless defined $yes;
$term->{value} = $yes;
return ($term, $left);
}
sub _term_2 {
my $self = shift;
my $query = shift;
my $term = { not => 0 };
my ($yes, $left) = (undef, $query);
($yes, $left) = $self->_not($left);
$term->{not} = 1 if defined $yes;
($yes, $left) = $self->_glob2regex($self->_glob($left));
return (undef, $query) unless defined $yes;
$term->{value} = $yes;
$term->{field} = "NAME";
return ($term, $left);
}
sub _term_3 {
my $self = shift;
my $query = shift;
my ($yes, $left) = (undef, $query);
return (undef, $query) unless $left =~ s/^\s*\(//;
($yes, $left) = $self->_query($left);
return (undef, $query) unless defined $yes;
return (undef, $query) unless $left =~ s/^\s*\)//;
return ($yes, $left);
}
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _op {
my $self = shift;
my $query = shift;
return 'and', $query if $query =~ s/^\s*and\s+//i;
return 'or', $query if $query =~ s/^\s*or\s+//i;
return undef, $query;
}
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _not {
my $self = shift;
my $query = shift;
return 'not', $query if $query =~ s/^\s*not\s+//i;
return undef, $query;
}
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _field {
my $self = shift;
my $query = shift;
return $1, $query
if $query =~ s/^\s*([A-Za-z_][A-Za-z0-9_]*)//;
return undef, $query;
}
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _glob {
my $self = shift;
my $query = shift;
my ($yes, $left);
($yes, $left) = $self->_glob_1($query);
return ($yes, $left) if defined $yes;
($yes, $left) = $self->_glob_2($query);
return ($yes, $left) if defined $yes;
return undef, $query;
}
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _glob_1 {
my $self = shift;
my $query = shift;
return $1, substr($query, length($1))
if $query =~ /^([][\-:\.^\$,\w*?\\]+)/;
return undef, $query;
}
my $quoted_re = qr'(?:(?:\")(?:[^\\\"]*(?:\\.[^\\\"]*)*)(?:\")|(?:\')(?:[^\\\']*(?:\\.[^\\\']*)*)(?:\')|(?:\`)(?:[^\\\`]*(?:\\.[^\\\`]*)*)(?:\`))';
# Returns (OP, REMAINDER) or (undef, QUERY) on failure
sub _glob_2 {
my $self = shift;
my $query = shift;
if ($query =~ s/^($quoted_re)//) {
my $quoted = $1;
substr($quoted, 0, 1) = "";
substr($quoted, -1) = "";
return $quoted, $query;
}
return undef, $query;
}
sub _glob2regex {
my $self = shift;
my $glob = shift;
return (undef, @_) unless defined $glob;
return glob_to_regex($glob, $self->{casei}), @_;
}
1;