#!/bin/bash
#
# Copyright (C) 2008-2011 bu1137
#
# Distributed under the terms of the GNU General Public License v2
#

PROG_NAME="fixnames"

declare -a opt_args
opt_path=
opt_dryrun=
opt_verbose=
opt_print=
do_trim=
do_colon=
do_question=
do_dotdot=
do_traildot=

function pb_usage()
{
	echo "${PROG_NAME} - Filename fixing utility"
	echo
	echo "Usage: ${PROG_NAME} FIXUP [OPTIONS] [PATH]"
	echo
	echo "Startup:"
	echo "  -h, --help          Display this help"
	echo
	echo "Options:"
	echo "  -v, --verbose       Show what is being done"
	echo "  -d, --dry-run       Show what would be done without really doing it"
	echo "  -p, --print-only    Only print the paths of matching files"
	echo
	echo "Fixups:"
	echo "  -a, --all           Enable all the following fixups"
	echo "  -t, --trim          Trim spaces at begining/end"
	echo "  -c, --colon         Replace all ':' with '-'"
	echo "  -q, --questionANDstar      Replace all '?' and '*' with '_'"
	echo "  -s, --horizontal_slash      Replace all '|' with '-'"
	echo "  -8, --fix_brackets      Replace all '<' and '>' with '(' and ')'"
	echo "  -o, --dotdot        Replace all '..' with '.'"
	echo "  -l, --traildot      Remove all trailing dot's"
	echo
}

# ======================================================================
if [ $# -eq 0 ]; then
	echo "Usage: ${PROG_NAME} [OPTIONS] FIXUP [PATH]" 2>&1
	exit 1
fi

while [ -n "$1" ]; do
	case "$1" in
	-t|--trim) do_trim=1 ;;
	-c|--colon) do_colon=1 ;;
	-q|--questionANDstar) do_questionANDstar=1 ;; 
	-s|-- horizontal_slash) do_hslash=1 ;;
	-8|-- fix_brackets) do_bracketsA=1; do_bracketsB=1 ;;
	-o|--dotdot) do_dotdot=1 ;;
	-l|--traildot) do_traildot=1 ;;
	-a|--all) do_trim=1; do_colon=1; do_questionANDstar=1; do_hslash; do_bracketsA; do_bracketsB; do_dotdot=1; do_traildot=1 ;;

	-v|--verbose) opt_verbose=1 ;;
	-d|--dry-run) opt_dryrun=1 ;;
	-p|--print-only) opt_print=1 ;;

	-h|--help)
		pb_usage
		exit 0
	;;
	--) shift; break ;;
	-*)
		echo "Unrecognized option: $1" 2>&1
		echo "Try '${PROG_NAME} --help' for more information." 2>&1
		exit 1
	;;
	*)
		opt_args[${#opt_args[*]}]="$1"
	;;
	esac
	shift
done

if [ ${#opt_args[*]} -eq 0 ]; then
	opt_path="./"
elif [ ${#opt_args[*]} -eq 1 ]; then
	opt_path="${opt_args[0]}"
fi
if [ -z "$opt_path" ]; then
	echo "Usage: ${PROG_NAME} [OPTIONS] FIXUP [PATH]" 2>&1
	exit 1
fi
if [ ! -d "$opt_path" ]; then
	echo "Error: Not a directory: $opt_path" 2>&1
	exit 1
fi

[ -n "$opt_dryrun" ] && opt_verbose=1

declare -a find_args
if [ -n "$do_trim" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]=" *"
	find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="* "
	find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="._ *"
fi
if [ -n "$do_colon" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*:*"
fi
if [ -n "$do_questionANDstar" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*\?*"
	find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*\**"
fi
if [ -n "$do_hslash" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*\|*"
fi
if [ -n "$do_bracketsA" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*<*"
fi
if [ -n "$do_bracketsB" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*>*"
fi
if [ -n "$do_dotdot" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*..*"
fi
if [ -n "$do_traildot" ]; then
	[ ${#find_args[@]} -gt 0 ] && find_args[${#find_args[@]}]="-o"
	find_args[${#find_args[@]}]="-name"
	find_args[${#find_args[@]}]="*."
fi
if [ -z "$find_args" ]; then
	echo "At least fixup action has to be used" 2>&1
	exit 1
fi

# NOTE: the while loop is run in a subshell
# therefore, no variables can be passed up to this/parent script
IFS=$'\n'
find "$opt_path" \( "${find_args[@]}" \) | sort -r |
while read line; do
	[ "$line" = ".." ] && continue
	[ "$line" = "." ] && continue
	[ "$line" = "../" ] && continue
	[ "$line" = "./" ] && continue

	if [ -n "$opt_print" ]; then
		echo "'$line'"
		continue
	fi

	fullname="$line"
	file_path="${fullname%/*}"
	file_name="${fullname##*/}"
	fixed_name="$file_name"
	fixed_full=

	if [ -n "$do_trim" ]; then
		x="$fixed_name"
		double_prefix=
		if [[ "$x" = ._* ]]; then
			double_prefix="._"
			x=${x#._}
		fi
		while [ "${x:0:1}" = " " ]; do
			x="${x# }"
		done
		while [ "${x: -1}" = " " ]; do
			x="${x% }"
		done
		fixed_name="${double_prefix}$x"
	fi

	if [ -n "$do_dotdot" ]; then
		fixed_name="${fixed_name//..../.}"
		fixed_name="${fixed_name//.../.}"
		fixed_name="${fixed_name//../.}"
	fi

	if [ -n "$do_traildot" ] && [[ "$fixed_name" = *. ]]; then
		x="$fixed_name"
		while [[ "$fixed_name" = *. ]]; do
			fixed_name="${fixed_name%.}"
		done
	fi

	if [ -n "$do_colon" ]; then
		x="${fixed_name// : / - }"
		x="${x//: /-}"
		x="${x//:/-}"
		while [ "${x:0:1}" = "-" ]; do
			x="${x#-}"
		done
		while [ "${x: -1}" = "-" ]; do
			x="${x%-}"
		done
		fixed_name="$x"
	fi

	if [ -n "$do_question" ]; then
		fixed_name="${fixed_name//\?/_}"
		fixed_name="${fixed_name//\*/_}"
	fi

	if [ -n "$do_hslash" ]; then
		fixed_name="${fixed_name//\|/-}"
	fi

	if [ -n "$do_bracketsA" ]; then
		fixed_name="${fixed_name//</(}"
	fi

	if [ -n "$do_bracketsB" ]; then
		fixed_name="${fixed_name//>/)}"
	fi

	fixed_full="$file_path/$fixed_name"
	[ "$fixed_name" = "$file_name" ] && continue
	if [ -e "$fixed_full" ]; then
		echo "Error: target already exists: '$fixed_full'" 2>&1
		continue
	fi

	[ -n "$opt_verbose" ] && echo mv "'$fullname'" "'$fixed_full'"
	[ -z "$opt_dryrun" ] && mv "$fullname" "$fixed_full"
done
