#!/usr/local/bin/perl

##########################################################################
#
#  Author: Stuart Robinson
#  Description: This script automates the onerous chore of renaming files 
#    in a directory (recursively or non-recursively).
#
#  Copyright (C) 2002 Stuart P. Robinson
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
##########################################################################

use Carp;
use English (-no_match_vars);
use File::Copy;
use File::Find;
use Getopt::Long;
use strict;
use vars qw($dryrun $backup $help $verbose $recursive 
	    $lcase $ucase
            $old_string $new_string
            @processed_files @directories);

##########################################################################
#
# Deal with command-line stuff
#
##########################################################################

# Deal w/ options
GetOptions('n' => \$dryrun,
	   'v' => \$verbose,
	   'b' => \$backup,
	   'r' => \$recursive,
	   'l' => \$lcase,
	   'u' => \$ucase,
	   'h' => \$help);
if ($help) {
    help();
}

# Deal w/ arguments
if (@ARGV == 0) {
    ($old_string, $new_string) = get_user_input();
} elsif (@ARGV == 1) {
    my @directories = @ARGV;
    ($old_string, $new_string) = get_user_input();
} elsif (@ARGV == 2) {
    ($old_string, $new_string) = @ARGV;
} else {
    ($old_string, $new_string, @directories) = @ARGV;
}

@directories = qw(.) unless defined @directories;

#print "OLD = $old_string\n"; # DEBUG
#print "NEW = $new_string\n"; # DEBUG
#print "DIRS = " . join(',', @directories) . "\n"; # DEBUG

main(\@directories);


#####################################################################
#
# Subroutines
#
#####################################################################

sub main {
    my $rDirlist = shift;

    find(\&process_file, @$rDirlist);
	
    print_results() if $verbose;
}


sub help {
    print "\nUSAGE:\n";
    print "$PROGRAM_NAME [-n -h -v -b -r -l -u] (<OLD> <NEW>) (<FILE|DIR>)\n";
    print "\n";
    print "ARGUMENTS:\n";
    print "<OLD>           The matching regex.\n";
    print "<NEW>           The replacement regex.\n";
    print "<FILE|DIR>      The files being renamed or the directories where files will be renamed.\n";
    print "If no argument is provided, the current directory is used by default.\n";
    print "\n"; 
    print "OPTIONS:\n"; 
    print "-n   noexec     Don't actually rename files; but summarize what would happen if the files were renamed.\n";
    print "-b   backup     Backup all renamed files with .bak.\n";
    print "-h   help       Get this help message.\n";
    print "-r   recursive  Run recursively.\n";
    print "-v   verbose    Verbose.\n";
    print "-l   lowercase  Renamed files have all-lowercase filenames.\n";
    print "-u   uppercase  Renamed files have all-uppercase filenames.\n";
    print "\n";
    exit;
}


sub get_user_input {
    # Get the to-be-replaced string
    print "Enter what you wish to replace as a regular expression:\n";
    my $old_string = <STDIN>;
    chomp $old_string;
    
    # Get the replacement string
    print "\nEnter the replacement as a regular expression:\n";
    my $new_string = <STDIN>;
    chomp $new_string;

    return ($old_string, $new_string);
}


sub print_results {
    # See whether any files were processed
    if ( @processed_files == 0 ) {
	print "\nYour regular expression /$old_string/ did not match any filenames.\n";
    } elsif ($dryrun) {
	print "\nWithout the 'dryrun' flag, these files would have been affected:\n";  
	# List processed files
	foreach my $item (@processed_files) {
	    print "  $item\n";
	}
    } else {
	print "\nThese files were affected:\n";
	# List processed files
	foreach my $item (@processed_files) {
	    print "  $item\n";
	}
    }
}


sub process_file {
    # If not running recursively, then you've got to make sure that nothing
    # get's processed that isn't under the top directory
    if (!$recursive && -d && $File::Find::name ne $File::Find::topdir) {
	$File::Find::prune = 1;
	return 1;
    }

    # Ignore various file types
    if ( -d $_ and $_ =~ /CVS/) { $File::Find::prune = 1; return 1 };
    if ( -d $_ ) { return 1 };
    if ( -l $_ ) { return 1 };
    if ( $_ =~ /\.class/ ) { return 1 };
    if ( $_ =~ /\.jar$/ ) { return 1 };
    if ( $_ =~ /\.bak/) { return 1 };
    if ( $_ =~ /~$/) { return 1 };
    
    # Get old filename
    my $old_filename = $_;

    # If regex matches filename, rename it
    my $new_filename = $old_filename;
    if ($new_filename =~ m/$old_string/) {
	# Create backup filename
	if ($backup) {
	    my $backup_filename = $old_filename . '.bak';
	    copy("$old_filename", "$backup_filename") 
		or confess "Couldn't back up $old_filename as $backup_filename: $OS_ERROR";
	}

	# Determine new filename and rename file
	$new_filename =~ s/$old_string/$new_string/g;

	# See whether to change case
	if ($lcase) {
	    $new_filename = lc($new_filename);
	} elsif ($ucase) {
	    $new_filename = ucc($new_filename);	    
	}

	rename("$old_filename", "$new_filename") 
	    or confess "Couldn't rename $old_filename as $new_filename: $OS_ERROR";
	push(@processed_files, $old_filename);
    }

    #print "TARGET REGEX = $old_string\n"; # DEBUG
    #print "REPLACEMENT REGEX = $new_string\n"; # DEBUG
    #print "OLD = $old_filename\n"; # DEBUG
    #print "BACKUP = $backup_filename\n"; # DEBUG
    #print "NEW = $new_filename\n\n"; # DEBUG
}



















