#!/bin/bash

# lowercase
# - perform various file rename actions on multiple files
# - author: Steven A (stevenaaus@yahoo.com)

# - tabstops set at 4. Use ":set ts=4" to edit this file in vim.

#########
# Usage #
#########
function Usage () {
	case "$Name" in
		mv_prefix ) ARG2=" PREFIX" ;;
		mv_suffix ) ARG2=" SUFFIX" ;;
		mv_sed    ) ARG2=" SCRIPT" ;;
		mv_char*  ) ARG2=" CHARS" ;;
				* ) ARG2="" ;;
	esac

	echo 
	echo "usage:   $Name [option(s)]$ARG2 file(s)" 
	echo 
	echo "options: -r recurse"
	echo "         -t test"
	echo "         -f force"
	echo 
	echo "(\"man lowercase\" for manpage)"
	echo 

	exit
}

#########
# Error #
#########

function Error () {
	echo "$Name: $*" >&2
	exit 1
}
	
##############
# renamefile #
##############

function renamefile () {

# arg(1) is space padding, arg(2) is filename arg(3) is the num
# mv_lowercase:			mv  "FiLeName"      -> "filename"
# mv_uppercase:			mv  "filename"      -> "FILENAME"
# mv_capitalize:		mv  "filename"      -> "Filename"
# mv_underscore2hyphen:	mv  "file_name"     -> "file-name"
# mv_spaces2underscore: mv  "file name"     -> "file_name"
# mv_prefix:			mv  "file_name"     -> "$prefixfilename"
# mv_suffix:			mv  "file_name"     -> "filename$prefix"
# mv_sed:				mv  "file_name"     -> "script(filename)"
# mv_chars2underscore:	mv  "file[:chars:]" -> "file_____"
# mv_chars2null:		mv  "file[:chars:]" -> "file"
# mv_number:			mv	"filename"		-> "N"
# mv_number.ext:		mv	"filename.ext"	-> "N.ext"

	if [ ! -e "$2" -a ! -L "$2" ] ; then
		# links to nowhere are ok
		echo "${1}\"$2\" No such file" >&2
	else
		case $Name in
			mv_lowercase|lowercase )
				newname=`echo "$2" | tr '[:upper:]' '[:lower:]'` ;;
			mv_uppercase )
				newname=`echo "$2" | tr '[:lower:]' '[:upper:]'` ;;
			mv_capitalize )
				# ${ $2 : string offset (: length) }
				newname=`echo "${2:1}" | tr '[:upper:]' '[:lower:]'`
				newname=`echo "${2:0:1}" | tr '[:lower:]' '[:upper:]'`$newname ;;
			mv_underscore2hyphen )
				newname=`echo "$2" | tr '_' '-'` ;;
			mv_spaces2underscore)
				newname=`echo "$2" | tr '[:blank:]' '_'` ;;

			mv_prefix )
				newname="$Arg$2" ;;
			mv_suffix )
				newname="$2$Arg" ;;

			mv_sed )
				newname=`echo "$2" | sed -e "$Arg"`
				[ $? != "0" ] && Error "'sed' failed, exitting." ;;

			mv_chars2underscore )
				newname=`echo "$2" | tr "$Arg" '_'` 
				[ $? != "0" ] && Error "'tr' failed, exitting." ;;
			mv_chars2null )
				newname=`echo "$2" | tr -d "$Arg"`
				[ $? != "0" ] && Error "'tr' failed, exitting." ;;

			mv_number )
				newname=$3 ;;
			mv_number.ext )
				# removes everything upto the last "." or does nothing if no "."
				tmp="${2##*.}"

				if [ "$2" == "$tmp" ]
				then
					newname="$3"
				else
					newname="$3.$tmp"
				fi;;

			*) Error "unresolved program link error !" ;;

		esac

		if [ "$2" != "$newname" ] ; then
 
			### done in two stages
			### as "mv FileA filea" fails on vfat
			### - the TEST feature will wrongly report fails on vfat

			# this 'mv' failing generally indicate insufficient privilidges
			[ $TEST ] || if ! mv -- "$2" tmp$$; then Error "file mv error, bad :(" ; fi

			if [ ! -e "$newname" -a -n "$newname" ] ; then
				echo "${1}$2->$newname"
				if ! [ $TEST ] ; then
					mv -- tmp$$ "$newname" && total=$((total+1))
				fi
			else
				if [ -n "$FORCE" -a -n "$newname" ] ; then
					if [ ! -d "$newname" -a ! -d tmp$$ ] ; then
						if ! [ $TEST ] ; then
							mv --backup=numbered -- tmp$$ "$newname" && total=$((total+1))
						fi
						echo "${1}${2}->$newname (backup made)"
					else
						echo "${1}$2 FAIL source/dest is a directory" >&2
						[ $TEST ] || mv -- tmp$$ "$2"
					fi
				else
					# not unique or forced or null, so fail and backtrack
					if [ -z "$newname" ] ; then
						echo "${1}$2 FAIL null dest" >&2
					else
						echo "${1}$2 FAIL ($newname) not unique" >&2
					fi
					[ $TEST ] || mv -- tmp$$ "$2"
				fi
			fi
		fi	# names inequal
	fi	# file exists
}

###########
# recurse #
###########

function recurse () {
### arg(1) is space padding, arg(2) is dir

	local i=
	local num=1
	for i in * ; do
		if [ -d "$i" -a ! -L "$i" ] ; then
			if cd "$i" ; then	# check we have permission
				echo "$1$i:"
				recurse "$1${space}" "$i"
				cd ..
			fi
		fi
		renamefile "$1" "$i" "$num"

		num=$((num+1))
	done
}

########
# main #
########

Name=`basename $0`

### getopt can't handle the '-?' option, so have a special case

[ "$1" == "-?" -o "$1" == "--help" ] && Usage

shopt -s nullglob	# expand "*" to null string
space="    "		# to change tabulation, change this variable

###################
# process options #
###################

FORCE= ; RECURSE= ; TEST=

while getopts "frth" option ; do
	case $option in
		f ) FORCE=1 ;;
		r ) RECURSE=1 ;;
		t ) TEST=1 ;;
		h ) Usage ;;
		\? ) exit 1 ;;
  	esac
done
shift $(($OPTIND - 1))		# OPTIND set by getopt

########################
# handle special cases #
########################

if [ "$Name" == mv_suffix     -o "$Name" == mv_prefix -o "$Name" == mv_sed -o \
	 "$Name" == mv_chars2null -o "$Name" == mv_chars2underscore ] ; then
	Arg="$1"
	shift

	# If the 1st arg is a file they may have mussed up
	if [ -e "$Arg" -a -z "$FORCE" ] ; then
		echo ""
		echo "$Name: warning"
		echo "------------------"
		echo "The prefix/suffix must come first, but intended prefix/suffix \"$Arg\" is also a file"
		echo
		read -p "Continue (y/n) ? " reply
		[ "$reply" != "y" -a "$reply" != "Y" ] && exit
	fi
fi

if [ "$Name" == mv_suffix -o "$Name" == mv_prefix ] ; then
	echo "$Arg" | grep "/" > /dev/null && Error "Prefix/suffix must not contain a slash '/'."
fi

[ -z "$1" ] && Error "missing args(s)" 

#############
# main loop #
#############

num=1 ; total=0

for j in "$@" ; do 

	# ensure links are not dereferenced (see 'info mv')
	# bash trick to remove trailing slashes
	i=${j%/}

	current_dir=$PWD

	# these two lines are to allow "lowercase SomeDir/FileA"
	# else: "mv SomeDir/FileA somedir/filea" -> "mv:somedir , no such dir"
	# in most cases they do nothing
	# the added complexity probably outweighs any functional gain

	## I don't think this 'cd' will ever fail, but if so, bail out
	## - even if $i doesn't exist, it does nothing

	if cd "`dirname \"$i\"`" ; then

		file="`basename \"$i\"`"

		# if directory (and not a link) recurse it if RECURSE is set

		if [ -d "$file" -a -n "$RECURSE" -a ! -L "$file" ] ; then
			# check we have permission
			if cd "$file" ; then
				echo "$file:"
				recurse "$space" "$file"
				cd ..
			fi
		fi

		renamefile "" "$file" "$num"

		num=$((num+1))
		cd "$current_dir"
	fi

done

echo "---------- $total file(s) renamed."

#######
# end #
#######
