#!/usr/bin/perl

# rename_case_colliders
# This script renames all the files supplied as command-line args
# where necessary so that the filenames are distinct on case-insensitive
# filesystems (e.g. OS X's HFS+)
# If there are filenames that would collide on a case-insensitive filesystem,
# they are renamed to be in lowercase and with numbers appended to make them
# distinct.
# E.g. "tesT", "Test" would be renamed to "test_001", "test_002"
#
# Cameron Hayne (macdev@hayne.net)  Mar 2006

use warnings;
use strict;

sub parseFilename($)
{
    my ($filename) = @_;
    
    use File::Basename;

    my ($name, $dir, $ext) = fileparse($filename, '\..*?');
    $dir = "" if $dir eq "./";
    
    return ($name, $dir, $ext);
}

sub proposeNewFilename($)
{
    my ($filename) = @_;

    my ($name, $dir, $ext) = parseFilename($filename);
    my $lcName = lc($name);
    my $lcExt = lc($ext);
    
    my $num = "000";
    my $proposedName;
    do
    {
        ++$num;
        $proposedName = "$dir$lcName" . "_$num" . $lcExt;
    }
    while (-e $proposedName);

    return $proposedName;
}

sub renameFile($)
{
    my ($origName) = @_;

    my $newName = proposeNewFilename($origName);
    die "Proposed name \"$newName\" already used\n" if -e $newName;

    print "About to rename \"$origName\" to \"$newName\"\n";
    rename($origName, $newName)
       or die "Failed to rename \"$origName\" to \"$newName\": $!\n";
}

MAIN:
{
    my %colliders; # each element in this hash is an array of colliding names
    
    chomp(@ARGV = <STDIN>) unless @ARGV;
    foreach my $filename (@ARGV)
    {
        my ($name, $dir, $ext) = parseFilename($filename);
        my $lcName = lc($name);
        my $lcExt = lc($ext);
    
        push(@{$colliders{"$dir$lcName$lcExt"}}, $filename);
    }
    
    foreach my $key (keys %colliders)
    {
        my @filenames = @{$colliders{$key}}; 
        next unless scalar(@filenames) > 1;
    
        foreach my $filename (@filenames)
        {
            renameFile($filename);
        }
    }
}

