#!/bin/ash
# 0.1.0 - July 10, 2005 - Dan Van Wormer - Puppy Linux
# 0.1.1 - Updated by disciple August 19 2007, to handle files with spaces in their path or name, add "permanently delete item" and "open item" features, and change behaviour on click.
# 0.1.2 - BK oct 2007: updated for rawpup (icon paths and stuff).
# 0.2.0 - Updated by disciple 8 April 2008, to fix some bugs in Barry's update and merge some features from the version by Achaw over at Rox.
# 0.3.0 - Updated by disciple 8 June 2008, for minor tweaks and to finish summary / info file feature.  Note that this feature does not display files trashed by earlier versions.
# 0.3.1 - Updated by disciple 9 August 2008, for minor changes to summary feature and help file.
# 0.3.2 - Updated by disciple 4 September 2008, to make summary feature display items with spaces in the path, add minor changes to help file and retrospectively version number everything, so it isn't still claiming to be version 0.1.0 from 2005 :)
# 0.3.3 - hairywill 5 January 2009, fixed instances of unquoted APPDIR so that the app works with spaces in its path 
# jamesbond 2012 - $HOME is only $SPOT_HOME/Downloads for root, otherwise it is their own $HOME
# jamesbond 2012 - change location of PuppyPin
# jamesbond 2016 - no more special $HOME for root
# JakeSFR 2022 - style changes (indentations), simplifications, fixes, improvements, corner cases, gettext, etc.

# std localisation stanza
export TEXTDOMAIN=fatdog
. gettext.sh

PINBOARD_FILE="$HOME/.config/rox.sourceforge.net/ROX-Filer/PuppyPin"
#SPOT_HOME=$(awk -F: '$1=="spot" {print $6}' /etc/passwd)
#[ $(id -u) -eq 0 ] && HOME=$SPOT_HOME/Downloads

TRASH_PATH="$HOME/.Trash"

# Create the Trash directory if it does not already exist.
[ -d "$TRASH_PATH" ] || mkdir "$TRASH_PATH"

# Determine the path to this application.
cd "$(readlink -f "$(dirname "$0")")"
TRASH_APPDIR="$(pwd)"

# Set icon paths
#FULLICON="$TRASH_APPDIR/trashcan_full.png"
#EMPTYICON="$TRASH_APPDIR/trashcan_empty.png"
FULLICON="/usr/share/pixmaps/midi-icons/trashcan_full48.png"
EMPTYICON="/usr/share/pixmaps/midi-icons/trashcan_empty48.png"

# Whether to use gxmessage or xmessage
MSG="$(which gxmessage 2>/dev/null)" || MSG="xmessage"

# -----------------------------------------------------------------------------

# Play the trash sound
PlaySound() {
	# See if /dev/dsp is in use. If it is then do not play the sound file.
	fuser /dev/dsp || aplay -q "$TRASH_APPDIR/systemmsg.wav"
}

# $1-icon (full/empty)
SetIcon() {
	local icon
	
	case "$1" in
		full|FULL)	icon="$FULLICON"	;;
		*)			icon="$EMPTYICON"	;;
	esac

	#ln -sf "$icon" "$TRASH_APPDIR/.DirIcon"
	#cd "${HOME}"
	#rox -x "$TRASH_APPDIR"

	# Set the icon on per-user basis
	rox --RPC <<- EOF
	<?xml version="1.0"?>
	<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
		<env:Body xmlns="http://rox.sourceforge.net/SOAP/ROX-Filer">
			<SetIcon>
				<Path>$TRASH_APPDIR</Path>
				<Icon>$icon</Icon>
			</SetIcon>
		</env:Body>
	</env:Envelope>
	EOF
}

EmptyIcon() {
	SetIcon empty
}

FullIcon() {
	SetIcon full
}

UpdateIcon() {
	if [ "$(ls "${TRASH_PATH}")" = "" ]; then
		EmptyIcon
	else
		FullIcon
	fi
}

# $1-filename (basename only)
GetFileExtension() {
	local name='' ext=''

	#case "${1,,}" in	# for Bash >= 4, IIRC
	case "$(echo "$1" | tr [:upper:] [:lower:])" in
		# special cases
		*.7z.[0-9][0-9][0-9]|*.tar.7z|*.tar.br|*.tar.bz|*.tar.bz2|*.tar.bzip|*.tar.bzip2|*.tar.gz2|*.tar.f|*.tar.gz|*.tar.gzip|*.tar.lrz|*.tar.lz|*.tar.lz4|*.tar.lzma|*.tar.lzo|*.tar.xz|*.tar.z|*.tar.zip|*.tar.zst|*.cpio.7z|*.cpio.br|*.cpio.bz|*.cpio.bz2|*.cpio.bzip|*.cpio.bzip2|*.cpio.f|*.cpio.gz|*.cpio.lrz|*.cpio.lz|*.cpio.lz4|*.cpio.lzma|*.cpio.lzo|*.cpio.xz|*.cpio.z|*.cpio.zip|*.cpio.zst)
			name="${1%.*}"
			ext="${name##*.}.${1##*.}"
			name="${name%.*}"
			;;
		# not so special cases
		*.*)
			name="${1%.*}"
			ext="${1##*.}"
			;;
	esac

	[ "$name" = "" ] && ext=''	# hidden files without extension

	echo "$ext"
}

# $1-filepath
GetFileType() {
	if [ -h "$1" ]; then
		echo "$(gettext "symlink")"
	elif [ -d "$1" ]; then
		echo "$(gettext "folder")"
	else
		rox -m "$1" 2>/dev/null
	fi
}

# $1-filepath_uri
GetShortcut() {
	# See if the item has a link to it on the pinboard; strip tags to have only x y label name
	# Btw, using '>' as a field separator, because ROX uses '&gt;' in Pinboard file, so it's safe
	grep -F ">$1<" "$PINBOARD_FILE" | sed -n 's|.*x="\(.*\)".*y="\(.*\)" label="\(.*\)">\(.*\)<.*|\1>\2>\3>\4|p'
}

# $1-path
MoveToTrash() {
	local suffix=1 rpc_rem_paths='' had_shortcut="No"
	local filepath filepath_uri filename filesize filetype
	local itemname extension shortcut shortcut_cnt deldate mntpoint response

	filepath="$1"

	# (Ab)use the '-U' option to convert $filepath to a string with a proper ROX URI escaping
	filepath_uri="$(LANG=C rox -U "$filepath" | sed -n "s|.*<faultstring>'\(.*\)' is not a valid URI</faultstring>.*|\1|p")"

	filename="$(basename "$filepath")"
	filesize="$(du -hs "$filepath" | cut -f1)"
	filetype="$(GetFileType "$filepath")"
	shortcut="$(GetShortcut "$filepath_uri")"
	
	if [ "$shortcut" ]; then
		$MSG -center \
			-buttons "$(gettext "No"),$(gettext "Yes"):21" \
			-default "$(gettext "No")" \
			-title "$(gettext "Confirm Action")" \
			"$(eval_gettext "There is one or more Desktop shortcuts pointing to '\$m'.
This will move the original item to the Trash and remove the shortcut(s) from the Desktop.
Do you want to continue?")"
		[ $? -ne 21 ] && return 1
	fi

	shortcut_cnt="$(echo "$shortcut" | grep -c .)"
	deldate="$(date +%F' '%H:%M:%S)"
	mntpoint="$(stat -c %m "$filepath")"

	# Find file extension
	if [ -d "$filepath" ]; then
		extension=''
	else
		extension="$(GetFileExtension "$filename")"
	fi

	# Add a prefix if the item is a hidden file/directory
	itemname="$filename"
	if [ "${itemname%${itemname#?}*}" = '.' ]; then
		itemname="HIDDEN.${itemname#?}"
	fi
	
	# Check if File dir exists
	while test -e "${TRASH_PATH}/${itemname}-${suffix}"; do
		suffix=$((suffix+1))
	done
	itemname="${itemname}-${suffix}"

	# Create a directory to put the item itself in
	mkdir -p "${TRASH_PATH}/$itemname/Files"

	# Create the shortcut file info and remove the shortcut from the pinboard
	if [ "$shortcut" ]; then
		had_shortcut="Yes"
		echo "$shortcut" > "${TRASH_PATH}/$itemname/shortcut"

		while [ $shortcut_cnt -gt 0 ]; do
			rpc_rem_paths="${rpc_rem_paths}<PinboardRemove><Path>$filepath_uri</Path></PinboardRemove>"
			shortcut_cnt=$((shortcut_cnt-1))
		done

		rox --RPC <<- EOF
		<?xml version="1.0"?>
		<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
			<env:Body xmlns="http://rox.sourceforge.net/SOAP/ROX-Filer">
				${rpc_rem_paths}
			</env:Body>
		</env:Envelope>
		EOF
	fi

	# Create the info file
	cat > "${TRASH_PATH}/$itemname/Info" <<- EOF
	$(gettext "Item") = $itemname
	$(gettext "Name") = $filename
	$(gettext "Path") = $filepath
	$(gettext "Mountpoint") = $mntpoint
	$(gettext "Size") = $filesize
	$(gettext "Deletion Date") = $deldate
	$(gettext "Type") = $filetype
	$(gettext "Extension") = $extension
	$(gettext "Had desktop shortcut") = $had_shortcut
	EOF

	# Store this AppDir's path, the item's original path and mountpoint
	cat > "${TRASH_PATH}/$itemname/.info" <<- EOF
	$TRASH_APPDIR
	$filepath
	$mntpoint
	EOF

	# move the item to the Trash directory.
	if ! response="$(mv "$filepath" "${TRASH_PATH}/$itemname/Files" 2>&1)"; then
		$MSG -center \
			-title "Error" \
			"$(gettext "There was an error while moving the item to Trash!")
$response"
		return 1
	fi
	
	# Link the icon for the trashed item.
	ln -sf "$TRASH_APPDIR/template/trash.png" "${TRASH_PATH}/$itemname/.DirIcon"
	
	# Link the AppInfo template.
	ln -sf "$TRASH_APPDIR/template/temp-AppInfo.xml" "${TRASH_PATH}/$itemname/AppInfo.xml"

	# Link the AppRun template.
	ln -sf "$TRASH_APPDIR/template/temp-AppRun" "${TRASH_PATH}/$itemname/AppRun"

	# Make it executable.
	chmod 755 "${TRASH_PATH}/$itemname"
}

#$1-quick = no confirmation
EmptyTrash() {
	local stuff

	if [ "$1" != 'quick' ]; then
		# See what is in the trash.
		stuff="$(ls "${TRASH_PATH}")"

		# Write the confirmation message.
		$MSG -center \
			-buttons "$(gettext "Cancel"),$(gettext "Delete"):21" \
			-default "$(gettext "Cancel")" \
			-title "$(gettext "Confirm Delete")" \
			"$stuff"

		# If they chose to cancel.
		[ $? -eq 21 ] || return
	fi

	# If they chose to delete.
	rm -r "${TRASH_PATH}"/*

	EmptyIcon
	PlaySound
}

ViewSummary() {
	local items size

	items=$(ls -1 ${TRASH_PATH} | wc -l)
	size=$(du -hsc ${TRASH_PATH} | tail -n 1 | cut -f1)
	
	$MSG -center \
		-title "$(gettext "Summary")" \
		"$(eval_gettext "Trashed items: \$items
Total size: \$size")"

}

# -----------------------------------------------------------------------------

### START

# If they just clicked on the icon.
if [ "$1" = "" ]; then

	UpdateIcon
	exec rox "${TRASH_PATH}"

# If they chose to update the icon.
elif [ "$1" = '-update-icon' ]; then

	UpdateIcon

# If they chose the "Look in the Trash" option.
elif [ "$1" = '-look' ]; then

	exec rox "${TRASH_PATH}"

# If they chose the "Empty the Trash" option.
elif [ "$1" = '-empty' ]; then

	EmptyTrash

# If they chose to "Quickly Empty the Trash".
elif [ "$1" = '-empty-quick' ]; then

	EmptyTrash quick

# If they chose to view summary.
elif [ "$1" = '-view-summary' ]; then

	ViewSummary

else

	was_trashed='false'
	yes_all='false'
	
	# If they sent something to the trash can.
	for m in "$@"; do

		# Check if the item exists at all.
		[ -e "$m" ] || continue
		
		# It's still possible to invoke AppRun with / or // (etc.) directly.
		if [ "${m#${m%%[!/]*}}" = '' ]; then
			$MSG -center \
				-title "$(gettext "Error")" \
				"$(gettext "Nope, I won't let you trash the / directory.")"
			continue
		fi

		# Check if they're trying to trash an item from within the Trash.
		case "$m" in
			"${TRASH_PATH}"*)
				$MSG -center \
					-title "$(gettext "Error")" \
					"$(eval_gettext "'\$m' is already in Trash, skipping...")"
				continue
				;;
		esac

		# Check if an item is on another filesystem.
		if [ "$(stat -c %m "$m")" != '/' ]; then

			# Check the item's size
			freespace=$(df -P / | awk 'NR==2 {print $4}')
			itemsize=$(( $(du -s "$m" | cut -f1) + 1024 ))	# add 1024K as a safety buffer
			if [ $itemsize -ge $freespace ]; then
				$MSG -center \
					-title "$(gettext "Error")" \
					"$(eval_gettext "The size of '\$m' is greater than the available space, skipping...")"
				continue
			fi

			if [ "$yes_all" = 'false' ]; then
				$MSG -center \
					-buttons "$(gettext "No"),$(gettext "Yes"):21,$(gettext "Yes to All"):22" \
					-default "$(gettext "No")" \
					-title "$(gettext "Confirm Action")" \
"$(eval_gettext "'\$m' is on another device/partition.
Trashing it will move it to your savefile/savedir or RAM layer.
Are you sure you want to do it?")"
				case $? in
					21) true			;;
					22) yes_all='true'	;;
					*)	continue		;;
				esac
			fi
		fi

		MoveToTrash "$m" && was_trashed='true'
	done

	if [ "$was_trashed" = 'true' ]; then
		FullIcon
		PlaySound
	fi

fi
