#!/usr/bin/perl
# From: ilya@math.ohio-state.edu (Ilya Zakharevich)
# So if you do not want to
# descend into directories, just do
# 	rnm '$_ = lc'
# (with 5.004 or close, with perls which are older than a week you need
# 	rnm '$_ = lc $_'
# ).
# 
# I wrote a similarly nice tool which does a similar thing to
# "find". Use it like this:
# 	pfind . '$_ = lc'
# 
# To lowercase only the files which contain `blah', do
# 	pfind . /blah/ '$_ = lc'

$version = 1.5;
# 1.5: correctly open files with leading/trailing spaces in names.

sub usage {
  print <<EOU;
usage: $0 [-debug] startdir rule1 rule2 ...
			>>>> Version: $version. <<<< 
	Rules are perl statements to execute. Statements starting with
`-', `/', or `!' are considered filters, a file will be discarded unless
the statement returns true. The rest is executed "as is".
	If only filters are given the default action is `prt' (see below).
	Variables \$_, \$name, \$dir contain the file name, full file name
and the name of the directory. Statements are executed in the directory
of the each processed file.
	If file is not discarded, and \$_ is changed after the perl
statements are executed, the file is renamed to the new value of \$_.
	Convenience function `prt' prints \\n-terminated name of the file.

	If a rule starts with `=~', it is considered as a filter to
match contents of the file. The rest is the regular expression to
match. If regexp contains modifiers `s' or `m', it is matched against
the file as a whole, otherwise it is matched line-by-line. 
	If the regexp-filter fails, file processing stops. Otherwise 
whatever was matched against is available in the variable \$line.
EOU
  ;                             # `; # for Emacs
  exit;
}

sub prt {print "$File::Find::name\n"}
sub wrapper {
  my $regexp = shift;
  return <<EOF if $regexp !~ /\w*[sm]\w*\s*$/; # Single line processing

  return unless -f;
  local \$_ = \$_;
  s|^(\\s)|./\$1|;
  s/(\\s+)\$/\$1\\0/;
  open FILE, \$_ or die "cannot open \$_: \$!";
 LINE: while (defined (\$line = <FILE>)) {
    \$found = 1, last if \$line =~ $regexp;
  }
  close FILE or die "cannot close \$_: \$!";
  return unless \$found;
EOF
  # Multiline processing:
  return <<'EOF' . <<EOF1;

  {
    return unless -f;
    local $/ = undef;
    local \$_ = \$_;
    s|^(\\s)|./\$1|;
    s/(\\s+)\$/\$1\\0/;
    open FILE, $_ or die "cannot open $_: $!";
    $line = <FILE>;
    close FILE or die "cannot close $_: $!";
EOF
    return unless \$line =~ $regexp;
  }
EOF1
}

my $start = shift;
usage unless @ARGV;
$debug = 1, $start = shift if $start eq '-debug';
die "Starting directory `$start' not found.\n" unless -d $start;

my $NF = 0;  # no of filter arguments
map { $NF++ if  m:^([-/!]):  || m/^=~/ } @ARGV;
my @rows = map { s:^([-/!]):return unless $1: ; $_ } @ARGV;
@rows = map { s! ^ =~ (.*) ! wrapper($1) !xes ; $_ } @rows;

my $text = join ";\n  ", @rows;
   $text.= ";\n  prt"  if $NF == @ARGV;
@ARGV = ();

my $setup = <<'EOS';
  my $name = $File::Find::name;
  my $dir  = $File::Find::dir;
  my $was = $_;
  my ($line, $found);
EOS

my $finish = <<'EOS';
  rename($was,$_) unless $was eq $_;
EOS

my $wanted = <<EOW;
sub {
$setup
  $text;
$finish
}
EOW

print "$wanted\n" if $debug;
my $sub = eval $wanted;
die $@ if $@;

use File::Find;

File::Find::find ( $sub, $start );