#!/usr/bin/bash

# Trash
# Command line trash can (kde < 3.4 compatible)
# Author: Steven A (stevenaaus@yahoo.com)
# Freebsd port available at www.sourceforge.net/projects/filerenameutils

# TRASH is the directory
# EXCLUDE is a regexp of files that are never deleted from TRASH
# - add a dot-slash ( "./" ) to all filenames to make find happy
# - place a backslashed logical "or" ( "|" ) between filenames
# - and dots and or's should be prefixed by a backslash :(

####### start user configuration ###########

### copying one of the following into ~/.Trashrc will overide the below defaults

### KDE ### ( exclude "./.directory" and "." )
# TRASH=${HOME}/Desktop/Trash
# EXCLUDE="\./\.directory\|\."

### Gnome/other ###
TRASH=${HOME}/.Trash
EXCLUDE="\."

######### end user configuration ###########

function Usage () {
	echo "usage:	$NAME [--] FILE(s): trash files
	$NAME             : list contents of trashcan
	$NAME -l|-s       : list contents of trashcan and sizes
	$NAME -r FILE(s)  : recover files
	$NAME -r DIR/FILE : recover single file to directory DIR
	$NAME -e          : empty trashcan"
}

function Error () {
	echo "$NAME: $*"
	exit 1
}

function IsEmpty () {
	[ -z "`find -maxdepth 1 -regex \"$EXCLUDE\" -o -print `" ]
	return $?
}

function Shuffle () {
	# we want to:   mv   $1.~$2~   $1.~(++$2)~
	local FROM="$1.~${2}~"
	local NEXTINDEX=$(($2 + 1))
	local TO="$1.~${NEXTINDEX}~"

	# recursively move it along , then move ourself
	if [ -e "$TO" -o -L "$TO" ] ; then
		Shuffle "$1" $NEXTINDEX
	fi
	mv -- "$FROM" "$TO"
}

function ShowSummary () {
	echo -------------------
	printf "%s     %i %s\n" `du -sh | cut -f 1` `find -regex "$EXCLUDE" -o -print | wc -l` "file(s)" 
}

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

NAME=`basename "$0"`

### setting "~/.Trashrc" will override the default at the top of this script

[ -f "$HOME/.Trashrc" ] && source $HOME/.Trashrc &> /dev/null

# exit straight away if TRASH isn't a directory

if [ ! -d "$TRASH" ] ; then
	echo
	echo "$NAME: \"$TRASH\" no such directory."
	echo "Trash does not know where your trash-can is."
	echo "Type \"man Trash\" , or edit $0 directly."
	echo
	exit 1
fi

case "$1" in

	-)  Error "'-' missing options" ;;
	-r) shift
		[ "$1" == "" ] && Error "missing arg(s)"

		# recover a single file to the directory it came from (if applicable)
		# this works by simply cd-ing to "dirname" and then pretending it's 
		# just a normal Trash -r "basename"
		remote_dir=`dirname -- "$1"`
		if [ $# == 1 -a "$remote_dir" != "."  ] ; then
			cd  "$remote_dir" 2> /dev/null || \
				Error "Error restoring $1. 'cd $remote_dir' failed."
			set `basename -- "$1"`
			echo "$NAME: restoring $1 -> $remote_dir"
		fi

		# test the trash for files matching pattern/name $@
		cd $TRASH
		[ -z "`ls -1d -- \"$@\" 2>/dev/null`" ] && Error "no such file(s): $@"

		while read LINE ; do
			LINE=${LINE%/}    # bash trick to remove trailing slashes
			LINEDEST="$OLDPWD/$LINE"
			if [ -e "$LINEDEST" -o -L "$LINEDEST" ] ; then
				echo "** $LINE already exists **"
			else
				if mv -- "$LINE" "$LINEDEST" ; then
					echo Untrashed: "$LINE"
				else
					echo "** failed to restore $LINE **"
				fi
			fi
		done <<EOF
                `ls -1d -- "$@" 2>/dev/null`
EOF

		;;

	-l|-s )

		cd $TRASH

		IsEmpty && exit 0

		# find -maxdepth 1  -regex "$EXCLUDE" -o -printf "%f\000" | xargs -0 ls -d --color --

		i=0 ; a_max=0 ; b_max=0
		while read LINE ; do
			[ "$LINE" == "." -o "$LINE" == ".." ] && continue
			a[$i]="$LINE"
			b[$i]="$(du -s -- "$LINE" | cut -f 1)"
			[ "${#LINE}" -gt $a_max -a "${#LINE}" -lt 30 ] && a_max="${#LINE}"
			[ "${#b[$i]}" -gt $b_max ] && b_max="${#b[$i]}"
			((i++))
		done <<EOF
		`ls -1a 2>/dev/null`
EOF

		# display results
		((a_max++))
		for (( j=0 ; $j < $i ; j++ )) ; do
				printf "%${b_max}s  %-${a_max}s \n" "${b[$j]}" "${a[$j]}" 
		done | sort -n

		ShowSummary

		;;
	"")
		cd $TRASH

		IsEmpty && exit 0

		### list contents of trash
		# this does a null terminated "ls" which "xargs -0" can handle
		# listing all files except EXCLUDE ("-o" means or)
		find -maxdepth 1  -regex "$EXCLUDE" -o -printf "%f\000" | xargs -0 ls -d --color --
		# -printf "%f\000" is the same as "-print0" except the prepended "./" is removed

		ShowSummary
		;;

	-e) if [ "$2" == "" ] ; then
			cd $TRASH
			IsEmpty && exit 0
			find -maxdepth 1  -regex "$EXCLUDE" -o -printf "%f\000" \
			| xargs -0 rm -r --
		else
			Error "unexpected arg(s)."
		fi ;;

	--?* | -[^-]* ) Usage ;; # matches "-*" and "--*" but not "--"

	* )	### previously was
		#   mv --backup=numbered --target-directory=$TRASH \
		#   -- "$@" && echo Trashed: "$@"
		# but couldn't backup directories

		[ "$1" == "mv" ] && Error "*** '$NAME mv' found, exitting ***"
		[ "$1" == "--" ] && shift

		# if unique mv FILE -> $TRASH/FILE
		# else shuffle FILE -> $TRASH/FILE -> $TRASH/FILE.~1~
		#                   -> .......     -> $TRASH/FILE.~N~

		for j in "$@" ; do

			i=${j%/}    # bash trick to remove trailing slashes

			# test if the file exists (or is a link)
			if [ -e "$i" -o -L "$i" ] ; then

				BASENAME=$(basename -- "$i")
				DEST=$TRASH/$BASENAME
				INDEX=""
				FILENAME="$DEST"

				if [ -e "$FILENAME" -o -L "$FILENAME" ] ; then
					# handle the first case separately, but after that
					# shuffle along the bastards

					NEXT="$DEST.~1~"

					if  [ -e "$NEXT" -o -L "$NEXT" ] ; then
						cd $TRASH
						Shuffle "$BASENAME" 1
						cd "$OLDPWD"
					fi

					mv -- "$FILENAME" "$NEXT"
				fi

				if mv -- "$i" "$FILENAME" ; then
					echo "Trashed: $i"
				else
					# if not enough permissions will arrive here
					# mv will have already reported an error message
					echo -n ""
				fi
			else
				echo "'$i': no such file"
			fi
		done ;;
esac
