1: use strict; 2: use warnings; 3: 4: use XML::Simple; 5: use Getopt::Std; 6: use File::Basename; 7: use File::Find::Rule; 8: use Data::Dumper; 9: 10: $::PROGRAM = basename $0; 11: 12: getopts('n:t:hr', my $opts = {}); 13: 14: usage() if $opts->{h} or @ARGV == 0; 15: 16: my @dirs = $opts->{r} ? @ARGV : map dirname($_), @ARGV; 17: my @files = $opts->{r} ? '*.xml' : map basename($_), @ARGV; 18: my $callback = gensub($opts); 19: 20: my @found = find( 21: file => 22: name => \@files, 23: ## handy callback which wraps around the callback created above 24: exec => sub { $callback->( XMLin $_[-1] ) }, 25: in => [ @dirs ] 26: ); 27: 28: print "$::PROGRAM: no files matched the search criteria\n" and exit(0) 29: if @found == 0; 30: 31: print "$::PROGRAM: the following files matched the search criteria\n", 32: map "\t$_\n", @found; 33: 34: exit(0); 35: 36: sub usage { 37: print "Usage: $::PROGRAM -t TEXT [-n NODE -h -r] FILES\n"; 38: exit(0); 39: } 40: 41: sub gensub { 42: my $opts = shift; 43: 44: ## basic matcher wraps around the program options 45: return sub { Dumper($_[0]) =~ /\Q$opts->{t}/sm } 46: unless exists $opts->{n}; 47: 48: ## node based matcher wraps around options and itself! 49: my $self; $self = sub { 50: my($tree, $seennode) = @_; 51: 52: for(keys %$tree) { 53: $seennode = 1 if $_ eq $opts->{n}; 54: 55: if( ref $tree->{$_} eq 'HASH') { 56: return $self->($tree->{$_}, $seennode); 57: } elsif( ref $tree->{$_} eq 'ARRAY') { 58: return !!grep $self->($_, $seennode), @{ $tree->{$_} }; 59: } else { 60: next unless $seennode; 61: return !!1 62: if $tree->{$_} =~ /\Q$opts->{t}/; 63: } 64: } 65: return; 66: }; 67: 68: return $self; 69: }