#!/bin/bash
# rmdirtrash - `rmdir' compatible layer for `trash'
# Version 1.7 (build 20131129)
#
#
# SHORT DESCRIPTION:
#   Put empty directories in trash using the `trash-put' command in a way
#   that is, otherwise as `trash-put' itself, compatible to GNUs `rmdir'.
#
#
# DEPENDENCIES:
#   - `trash' or `trash-put', provided by the package `trash-cli'
#   - `dpkg', provided by the package `dpkg'
#
#   Note that there are many more dependencies. Nearly every distribution meets
#   those dependencies by default, so they are not listed here. Please also
#   note that this script uses options of POSIX commands, which are not part of
#   the POSIX standard. The extended variants provided by GNU are recommended.
#
#   This script was tested with Ubuntu 10.04 LTS (Lucid Lynx) and
#   Ubuntu 12.04 LTS (Precise Pangolin) only. It SHOULD work great with all
#   Debian-based distributions.
#
#
# EXIT CODES:
#   An exit status of zero indicates success, a nonzero value indicates the
#   occurence of an error. The following exit codes are fatal, rmtrash stops
#   execution.
#
#      1  unknown error
#      2  invalid options
#      4  requirements of this layer weren't met
#         (`trash-put` and/or `rm` wasn't found, is not installed or
#          is not executable)
#
#   The following exit codes are non-fatal, thus rmtrash aborted execution of
#   the corresponding argument only. All other arguments (prior and posterior
#   the failed argument) will be handled regularly. All following exit codes
#   are bitmasks.
#
#      8  `trash-put` returned a nonzero exit status
#     16  `rm` returned a nonzero exit status
#     32  user interaction requiered in non-interactive mode
#     64  cannot remove . or ..
#    128  no such file or directory
#    256  not a directory
#    512  directory not empty
#   1024  unable to create trashcan: permission denied
#   2048  unable to trash the trashcan
#
#
# KNOWN BUGS:
#   If you use rmtrash as an bash alias, you maybe noticed, that the alias
#   doesn't work when using sudo. You can catch up on that by adding
#       alias sudo='sudo '
#   to the bashrc. Note the space before the closing quote. Consider the
#   manpage of bash:
#       "A trailing space in  value causes the next word to be checked for alias
#       substitution when the alias is expanded."
#
#
# COPYRIGHT AND LICENSING:
#   Copyright (C) 2011-2013  Daniel Rudolf <http://www.daniel-rudolf.de/>
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, version 3 of the License only.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# CHANGELOG:
#   v1.1 - 2011-09-11 00:35:00+0200
#       * printing errors with relative, not absolute paths
#       * cleaning up the code
#   v1.2 - 2011-09-12 22:08:00+0200
#       + adding replacement options --forbid-root and --forbid-root-force
#       * fixing unexpected behaviour of filenames with special characters
#         Many thanks to ubuntuusers.de member Lasall!
#       * improving code styling
#   v1.3 - 2011-09-13 16:17:00+0200
#       * fixing filenames with special characters when using --parents
#   v1.4 - 2012-12-11 23:47:00+0100
#       + detecting the renaming of `trash` to `trash-put`
#       + reading paths of `rm` and `trash-put` from $PATH
#       + checking for executability of `rm` and `trash-put`
#       * minor code improvements
#   v1.5 - skipped to sync version numbering with rmtrash
#   v1.6 - 2013-08-07 14:57:00+0200
#	+ adapting some new features of rmtrash 1.4
#       * minor code improvements
#   v1.7 - 2013-11-29 22:54:00+0100
#       + adding function showUsage()
#       * moving --help and --version handling
#       * improving documentation

LC_ALL=C
APP_NAME=$(basename "$0")

function ShowUsage() {
	echo "Usage:"
	echo "  $APP_NAME [OPTION]... DIRECTORY..."
}

function getOptionsAsCmdString() {
	local CMD=""
	
	if [ $IGNORE_FAIL_ON_NON_EMPTY == true ]; then
		CMD+=" --ignore-fail-on-non-empty"
	fi
	if [ $PARENTS == true ]; then
		CMD+=" --parents"
	fi
	if [ $VERBOSE == true ]; then
		CMD+=" --verbose"
	fi
	
	if [ -n "$CMD" ]; then
		echo "${CMD:1}"
	fi
}

# get path of rmdir
RMDIR_CMD="$(which "rmdir")"

# check if rmdir is installed
if [ -z "$RMDIR_CMD" ]; then
	echo "$APP_NAME: command \`rmdir' was not found." >&2
	exit 4
fi

# check if rmdir is executable
if [ ! -x "$RMDIR_CMD" ]; then
	echo "$APP_NAME: \`$RMDIR_CMD' is not executable." >&2
	exit 4
fi

# check if trash-cli package is installed
TRASH_PACKAGE_INFORMATION="$(dpkg -p trash-cli)"
if [ "$?" -ne 0 ] || [ -z "$TRASH_PACKAGE_INFORMATION" ]; then
	echo "$APP_NAME: This program requires a command line interface trashcan utility." >&2
	echo "It seems that the required program is not installed yet.  You can catch up on" >&2
	echo "that by typing:  sudo apt-get install trash-cli" >&2
	exit 4
fi

# get version of the trash-cli package
TRASH_VERSION="$(echo "$TRASH_PACKAGE_INFORMATION" | sed -n 's/^Version: \(.*\)/\1/p')"
if [ -z "$TRASH_VERSION" ]; then
	echo "$APP_NAME: Unable to determine version of the \`trash-cli' package." >&2
	exit 4
fi

# get path of trash
# the path depends on the installed version of the trash-cli package	
dpkg --compare-versions "$TRASH_VERSION" "ge" "0.11"
if [ "$?" -eq "0" ]; then
	TRASH_CMD="$(which "trash-put")"
else
	TRASH_CMD="$(which "trash")"
fi

# check if trash command is found
if [ -z "$TRASH_CMD" ]; then
	echo "$APP_NAME: Unable to find the trashcan command within \$PATH." >&2
	exit 4
fi

# check if trash is executable
if [ ! -x "$TRASH_CMD" ]; then
	echo "$APP_NAME: \`$TRASH_CMD' is not executable." >&2
	exit 4
fi

# check if shell is running in interactive mode
SHELL_IN_INTERACTIVE_MODE=false
tty --quiet
if [ $? -eq 0 ]; then
	SHELL_IN_INTERACTIVE_MODE=true
fi

# use getopt to parse parameters
if ! OPTIONS=$(getopt -n "$APP_NAME" -o pv -l "ignore-fail-on-non-empty" -l "parents" -l "verbose" -l "forbid-root" -l "forbid-root-force" -l "help" -l "version" -- "$@"); then
	ShowUsage
	exit 2
fi
eval set -- "${OPTIONS}"

# default option values
IGNORE_FAIL_ON_NON_EMPTY=false	# --ignore-fail-on-non-empty		bool
PARENTS=false			# -p / --parents			bool
VERBOSE=false			# -v / --verbose			bool
FORBID_ROOT="no"		# --forbid-root / --forbid-root-force	string ( no / yes / force )

# parse options
while true; do
	case "$1" in
		"--ignore-fail-on-non-empty")
			IGNORE_FAIL_ON_NON_EMPTY=true
			shift
			;;

		"-p"|"--parents")
			PARENTS=true
			shift
			;;

		"-v"|"--verbose")
			VERBOSE=true
			shift
			;;

		"--forbid-root")
			FORBID_ROOT="yes"
			shift
			;;

		"--forbid-root-force")
			FORBID_ROOT="force"
			shift
			;;

		"--help")
			ShowUsage
			echo
			echo "Put empty directories in trash using the \`$TRASH_CMD' command in a way"
			echo "that is, otherwise as \`$TRASH_CMD' itself, compatible to GNUs \`rmdir'."
			echo "  see $RMDIR_CMD --help"
			echo "  see $TRASH_CMD --help"
			echo
			echo "Help Options:"
			echo "      --help                display this help and exit"
			echo "      --version             output version information and exit"
			echo
			echo "Application Options:"
			echo "      --ignore-fail-on-non-empty"
			echo "                 ignore each failure that is solely because a directory is"
			echo "                    non-empty"
			echo "  -p, --parents  remove DIRECTORY and its ancestors; e.g., \`$APP_NAME -p a/b/c'"
			echo "                    is similar to \`$APP_NAME a/b/c a/b a'"
			echo "  -v, --verbose  output a diagnostic for every directory processed"
			echo
			echo "Replacement options:"
			echo "  These options are not supposed to be used when calling $APP_NAME, they help"
			echo "  you to control how and in which cases \`rmdir' is replaced."
			echo "      --forbid-root         forbid user \`root' to trash directories.  When"
			echo "                              standard input is a terminal, the user is asked"
			echo "                              to pass the command to \`$RMDIR_CMD', otherwise"
			echo "                              the entire command is aborted"
			echo "      --forbid-root-force   when user \`root' trys to trash directories, the"
			echo "                              entire command is automatically passed to"
			echo "                              \`$RMDIR_CMD'."
			echo
			echo "To remove a directory whose name starts with a \`-', for example \`-foo', use"
			echo "one of these commands:"
			echo "  $APP_NAME -- -foo"
			echo "  $APP_NAME ./-foo"
			echo
			echo "See also \`trash-list' (or \`list-trash'), \`trash-empty' (or \`empty-trash'),"
			echo "\`restore-trash', \`trash-rm', and the FreeDesktop.org Trash Specification"
			echo "at <http://www.ramendik.ru/docs/trashspec.html>."
			echo
			echo "Please report bugs using the contact form at <http://www.daniel-rudolf.de/>"
			echo "Besides, you will find general help and information about $APP_NAME there."
			exit 0
			;;

		"--version")
			echo "rmdirtrash 1.7 (build 20131129)"
			echo "Copyright (C) 2011-2013 Daniel Rudolf"
			echo "License GPLv3: GNU GPL version 3 only <http://gnu.org/licenses/gpl.html>."
			echo "This is free software: you are free to change and redistribute it."
			echo "There is NO WARRANTY, to the extent permitted by law."
			echo
			echo "Written by Daniel Rudolf <http://www.daniel-rudolf.de/>"
			exit 0
			;;

		"--")
			shift
			break
			;;

		*)
			echo "$APP_NAME: execution of getopt failed" >&2
			ShowUsage
			exit 2
			;;
	esac
done

# no arguments given
if [ $# -eq 0 ]; then
	echo "$APP_NAME: too few arguments" >&2
	showUsage
	exit 2
fi

# forbid root?
if [ "$FORBID_ROOT" != "no" ] && [ $(id -u) -eq 0 ]; then
	echo "$APP_NAME: user \`root' should never trash directories" >&2

	PASS_COMMAND=false
	if [ "$FORBID_ROOT" == "yes" ]; then
		# prompt
		if [ $SHELL_IN_INTERACTIVE_MODE == true ]; then
			echo -n "pass entire command to \`$RMDIR_CMD' (delete arguments instead of trashing)? "
			read PASS_COMMAND_ANSWER

			if [ "$PASS_COMMAND_ANSWER" == "y" ] || [ "$PASS_COMMAND_ANSWER" == "yes" ]; then
				PASS_COMMAND=true
			fi
		else
			# exit if shell is not running in interactive mode
			exit 32
		fi
	else
		# force
		PASS_COMMAND=true
		echo "$APP_NAME: entire command will be passed to \`$RMDIR_CMD'..."
	fi

	if [ $PASS_COMMAND == true ]; then
		# create command
		CMD="$RMDIR_CMD"
		CMD_OPTIONS="$(getOptionsAsCmdString)"
		CMD_ARGUMENTS=""
		while [ $# -gt 0 ]; do
			CMD_ARGUMENTS+=" \"$1\""
			shift
		done

		# execute command	
		if [ $VERBOSE == true ]; then
			echo "$APP_NAME: executing \`$CMD $CMD_OPTIONS ${CMD_ARGUMENTS:1}'"
		fi
		eval "$CMD $CMD_OPTIONS ${CMD_ARGUMENTS:1}"
		RMDIR_EXIT_STATUS=$?
		
		if [ "$RMDIR_EXIT_STATUS" -ne 0 ]; then
			echo "$APP_NAME: execution of \`$RMDIR_CMD' failed (exit status $RMDIR_EXIT_STATUS)" >&2
			exit 16
		fi
		exit 0
	fi
fi

# handle each argument in a subprocess
EXIT=0
if [ $# -gt 1 ]; then
	# create command
	CMD="$0"
	CMD_OPTIONS="$(getOptionsAsCmdString)"

	# parse arguments
	while [ $# -gt 1 ]; do
		# execute command
		eval "$CMD $CMD_OPTIONS \"$1\""

		# get return value
		EXIT_STATUS=$?
		if [ $EXIT_STATUS -ne 0 ]; then
			EXIT=$(( $EXIT | $EXIT_STATUS ))
		fi

		# process the next argument
		shift
	done
fi

# there's only one argument (left)
ARGUMENT="$1"

# remove trailing slash
let "ARGUMENT_LENGTH_TRAILING_SLASH_TEST_INDEX = ${#ARGUMENT} - 1"
if [ "${ARGUMENT:$ARGUMENT_LENGTH_TRAILING_SLASH_TEST_INDEX}" == "/" ]; then
	ARGUMENT="${1:0:$ARGUMENT_LENGTH_TRAILING_SLASH_TEST_INDEX}"
fi

# you can't remove . or ..
ARGUMENT_BASENAME="$(basename "$ARGUMENT")"
if [ "$ARGUMENT" == "." ] || [ "$ARGUMENT" == ".." ]; then
	echo "$APP_NAME: cannot remove directory \`$ARGUMENT'" >&2
	exit 64
fi
if [ "$ARGUMENT_BASENAME" == "." ] || [ "$ARGUMENT_BASENAME" == ".." ]; then
	echo "$APP_NAME: cannot remove \`$ARGUMENT_BASENAME' directory \`$ARGUMENT'" >&2
	exit 64
fi

# get full path
if [ "${ARGUMENT:0:1}" == "/" ]; then
	DIRECTORY="$ARGUMENT"
else
	DIRECTORY="$PWD/$ARGUMENT"
fi

# no such file or directory
if [ ! -e "$DIRECTORY" ]; then
	echo "$APP_NAME: failed to remove \`$ARGUMENT': No such file or directory" >&2
	exit 128
fi

# not a directory
if [ ! -d "$DIRECTORY" ]; then
	echo "$APP_NAME: failed to remove \`$ARGUMENT': Not a directory" >&2
	exit 256
fi

# directory not empty
if [ -z "$(find "$DIRECTORY" -maxdepth 0 -empty)" ] && [ $IGNORE_FAIL_ON_NON_EMPTY == false ]; then
	echo "$APP_NAME: failed to remove \`$ARGUMENT': Directory not empty" >&2
	exit 512
fi

# okay, let's delete this directory
DELETE_DIRECTORIES[${#DELETE_DIRECTORIES[*]}]="$DIRECTORY"

# delete parent directories, too
if [ $PARENTS == true ]; then
	DELETE_PARENT_DIRECTORY_PREFIX=""
	if [ "${ARGUMENT:0:1}" != "/" ]; then
		DELETE_PARENT_DIRECTORY_PREFIX="$PWD"
	fi

	# get parent arguments
	while IFS="" read -r -u 4 -d $'\0' PARENT_ARGUMENT; do
		PARENT_ARGUMENTS[${#PARENT_ARGUMENTS[*]}]="$PARENT_ARGUMENT"
	done 4< <(echo "$ARGUMENT" | tr "/" "\0")

	# delete parent directories
	let "PARENT_ARGUMENTS_MAIN_INDEX = ${#PARENT_ARGUMENTS[*]} - 1"
	PARENT_ARGUMENTS_MAIN_MIN=0
	while [ $PARENT_ARGUMENTS_MAIN_INDEX -ge $PARENT_ARGUMENTS_MAIN_MIN ]; do
		PARENT_ARGUMENTS_INDEX=0
		PARENT_ARGUMENTS_MAX=$PARENT_ARGUMENTS_MAIN_INDEX

		# add parent directories to the path of the directory which should be deleted
		DELETE_PARENT_DIRECTORYNAME=""
		while [ $PARENT_ARGUMENTS_INDEX -le $PARENT_ARGUMENTS_MAX ]; do
			if [ "$DELETE_PARENT_DIRECTORYNAME" == "" ]; then
				DELETE_PARENT_DIRECTORYNAME="${PARENT_ARGUMENTS[$PARENT_ARGUMENTS_INDEX]}"
			else
				DELETE_PARENT_DIRECTORYNAME="$DELETE_PARENT_DIRECTORYNAME/${PARENT_ARGUMENTS[$PARENT_ARGUMENTS_INDEX]}"
			fi
			let "PARENT_ARGUMENTS_INDEX++"
		done
		DELETE_PARENT_DIRECTORY="$DELETE_PARENT_DIRECTORY_PREFIX/$DELETE_PARENT_DIRECTORYNAME"

		# get the directory that was deleted lastly
		let "LAST_DELETE_DIRECTORY_INDEX = ${#DELETE_DIRECTORIES[*]} - 1"
		LAST_DELETE_DIRECTORY="${DELETE_DIRECTORIES[$LAST_DELETE_DIRECTORY_INDEX]}"

		# directory not empty
		while IFS="" read -r -u 4 -d $'\0' DELETE_PARENT_DIRECTORY_CONTENT; do
			if [ "$DELETE_PARENT_DIRECTORY_CONTENT" != "$LAST_DELETE_DIRECTORY" ]; then
				break 2
			fi
		done 4< <(find "$DELETE_PARENT_DIRECTORY" -mindepth 1 -maxdepth 1 -print0)
		
		# okay, let's delete this directory instead of the inferior one
		DELETE_DIRECTORIES[$LAST_DELETE_DIRECTORY_INDEX]="$DELETE_PARENT_DIRECTORY"
		let "PARENT_ARGUMENTS_MAIN_INDEX--"
	done

	# clear list of parent directories
	unset PARENT_ARGUMENTS
fi

# create command
if [ ${#DELETE_DIRECTORIES[@]} -gt 0 ]; then
	CMD="$TRASH_CMD"
	if [ $VERBOSE == true ]; then
		CMD+=" --verbose"
	fi
	
	# add directories to command
	INDEX=0
	MAX=${#DELETE_DIRECTORIES[@]}
	while [ $INDEX -lt $MAX ]; do
		CMD+=" \"${DELETE_DIRECTORIES[$INDEX]}\""
		let "INDEX++"
	done
	
	# execute command
	if [ $VERBOSE == true ]; then
		echo "$APP_NAME: executing \`$CMD'"
	fi
	STDOUT="$(eval "$CMD" 2>&1)"
	
	# remove traceback from stdout
	if [ -n "$STDOUT" ]; then
		STDOUT_OLD="$STDOUT"
		STDOUT=""
	
		IFS=$'\n'
		IS_TRACEBACK=false
		for STDOUT_LINE in $STDOUT_OLD; do
			if [ -n "$STDOUT" ]; then
				n=$'\n'
			fi
	
			if [ "$STDOUT_LINE" == "Traceback (most recent call last):" ]; then
				IS_TRACEBACK=true
				continue
			fi
		
			if [ $IS_TRACEBACK == true ]; then
				if [ "${STDOUT_LINE:0:2}" != "  " ]; then
					STDOUT+="$n$STDOUT_LINE"
					IS_TRACEBACK=false
				fi
			else
				STDOUT+="$n$STDOUT_LINE"
			fi
		done
	fi
	
	# catch some special errors
	if [ -n "$STDOUT" ]; then
		# unable to create trashcan (permission denied)
		while [ true ]; do
			# get insufficient trashcan
			INSUFFICIENT_TRASHCAN="$(echo "$STDOUT" | grep -m 1 -b -oP "(?<=^OSError: \[Errno 13\] Permission denied: ')([^\0]*?)/\.Trash-([0-9]+?)(?='$)")"
			if [ -n "$INSUFFICIENT_TRASHCAN" ]; then
				TEMP="$(echo "$INSUFFICIENT_TRASHCAN" | grep -m 1 -oP "^([0-9]+?)(?=:)")"
				
				# fix insufficient trashcan string
				let "INSUFFICIENT_TRASHCAN_INDEX = ${#TEMP} + 1"
				INSUFFICIENT_TRASHCAN="${INSUFFICIENT_TRASHCAN:$INSUFFICIENT_TRASHCAN_INDEX}"
		
				# remove that error from stdout
				let "STDOUT_PREFIX_LENGTH = $TEMP - 40"
				let "STDOUT_SUFFIX_INDEX = $TEMP + ${#INSUFFICIENT_TRASHCAN} + 1 + 1"
				STDOUT="${STDOUT:0:$STDOUT_PREFIX_LENGTH}${STDOUT:$STDOUT_SUFFIX_INDEX}"
				
				# check if insufficient trashcan is valid
				if [ "$(basename "$INSUFFICIENT_TRASHCAN")" != ".Trash-$(id -u)" ]; then
					continue
				fi
				
				# get delete argument
				DELETE_ARGUMENT="$(echo "$STDOUT" | grep -m 1 -b -oP "(?<=^trash: cannot trash \`)([^\0]+?)(?=': \[Errno 13\] Permission denied: '$INSUFFICIENT_TRASHCAN'$)")"
				if [ -n "$DELETE_ARGUMENT" ]; then
					TEMP="$(echo "$DELETE_ARGUMENT" | grep -m 1 -oP "^([0-9]+?)(?=:)")"
				
					# fix delete argument string
					let "DELETE_ARGUMENT_INDEX = ${#TEMP} + 1"
					DELETE_ARGUMENT="${DELETE_ARGUMENT:$DELETE_ARGUMENT_INDEX}"
		
					# remove that error from stdout
					let "STDOUT_PREFIX_LENGTH = $TEMP - 21"
					let "STDOUT_SUFFIX_INDEX = $TEMP + ${#DELETE_ARGUMENT} + 34 + ${#INSUFFICIENT_TRASHCAN} + 1 + 1"
					STDOUT="${STDOUT:0:$STDOUT_PREFIX_LENGTH}${STDOUT:$STDOUT_SUFFIX_INDEX}"
					
					# output what actually happened
					echo "$APP_NAME: cannot remove \`$DELETE_ARGUMENT': unable to create trashcan \`$INSUFFICIENT_TRASHCAN': Permission denied" >&2
					EXIT=$(( $EXIT | 1024 ))
					
					# recommend user to create a trashcan-base-directory
					INSUFFICIENT_TRASHCAN_BASE_DIRECTORY="$(echo "$INSUFFICIENT_TRASHCAN" | grep -m 1 -oP "^([^\0]+?).Trash(?=-$(id -u)$)")"
					
					echo "" >&2
					echo "According to the FreeDesktop.org Trash Specification, it's recommended to" >&2
					echo "create a directory where all users can create a trashcan on their own." >&2
					echo "You can catch up on that by typing:" >&2
					echo -e "\tsudo mkdir -p \"$INSUFFICIENT_TRASHCAN_BASE_DIRECTORY\"" >&2
					echo -e "\tsudo chmod 1777 \"$INSUFFICIENT_TRASHCAN_BASE_DIRECTORY\"" >&2
					echo "When you've done that, repeat your deletion-command. Alternatively" >&2
					echo "you can delete the argument instead of trashing it." >&2
					echo "" >&2
	
					# ask to pass argument to rm if shell is in interactive mode
					if [ $SHELL_IN_INTERACTIVE_MODE == true ]; then
						echo -n "pass argument to \`$RMDIR_CMD' (delete argument instead of trashing)? "
						read PASS_COMMAND_ANSWER

						if [ "$PASS_COMMAND_ANSWER" == "y" ] || [ "$PASS_COMMAND_ANSWER" == "yes" ]; then
							CMD="$RMDIR_CMD"
							CMD_OPTIONS="$(getOptionsAsCmdString)"
							CMD_ARGUMENTS="\"$DELETE_ARGUMENT\""
						
							if [ $VERBOSE == true ]; then
								echo "$APP_NAME: executing \`$CMD $CMD_OPTIONS $CMD_ARGUMENTS'"
							fi
							eval "$CMD $CMD_OPTIONS $CMD_ARGUMENTS"
							RMDIR_EXIT_STATUS=$?
							
							if [ "$RMDIR_EXIT_STATUS" -ne 0 ]; then
								EXIT=$(( $EXIT | 16 ))
								echo "$APP_NAME: execution of \`$RMDIR_CMD' failed (exit status $RMDIR_EXIT_STATUS)" >&2
							fi
						fi
					fi
				fi
				
			# no more errors
			else
				break
			fi
		done
		
		# you can't trash the trashcan
		while [ true ]; do
			# get delete argument
			DELETE_ARGUMENT="$(echo "$STDOUT" | grep -m 1 -b -oP "(?<=^shutil\.Error: Cannot move a directory ')([^\0]+?)(?=' into itself '([^\0]+?)'\.$)")"
			if [ -n "$DELETE_ARGUMENT" ]; then
				TEMP="$(echo "$DELETE_ARGUMENT" | grep -m 1 -oP "^([0-9]+?)(?=:)")"
				
				# fix delete argument string
				let "DELETE_ARGUMENT_INDEX = ${#TEMP} + 1"
				DELETE_ARGUMENT="${DELETE_ARGUMENT:$DELETE_ARGUMENT_INDEX}"
				
				# get delete argument target
				DELETE_ARGUMENT_TARGET="$(echo "$STDOUT" | grep -m 1 -oP "(?<=^shutil\.Error: Cannot move a directory '$DELETE_ARGUMENT' into itself ')([^\0]+?)(?='\.$)")"
		
				# remove that error from stdout
				let "STDOUT_PREFIX_LENGTH = $TEMP - 39"
				let "STDOUT_SUFFIX_INDEX = $TEMP + ${#DELETE_ARGUMENT} + 15 + ${#DELETE_ARGUMENT_TARGET} + 2 + 1"
				STDOUT="${STDOUT:0:$STDOUT_PREFIX_LENGTH}${STDOUT:$STDOUT_SUFFIX_INDEX}"
				
				# output what actually happened
				echo "$APP_NAME: cannot remove \`$DELETE_ARGUMENT': you can't trash the trashcan" >&2
				EXIT=$(( $EXIT | 2048 ))
			
				# ask to pass argument to rm if shell is in interactive mode
				if [ $SHELL_IN_INTERACTIVE_MODE == true ]; then
					echo -n "pass argument to \`$RMDIR_CMD' (delete argument instead of trashing)? "
					read PASS_COMMAND_ANSWER

					if [ "$PASS_COMMAND_ANSWER" == "y" ] || [ "$PASS_COMMAND_ANSWER" == "yes" ]; then
						CMD="$RMDIR_CMD"
						CMD_OPTIONS="$(getOptionsAsCmdString)"
						CMD_ARGUMENTS="\"$DELETE_ARGUMENT\""
						
						if [ $VERBOSE == true ]; then
							echo "$APP_NAME: executing \`$CMD $CMD_OPTIONS $CMD_ARGUMENTS'"
						fi
						eval "$CMD $CMD_OPTIONS $CMD_ARGUMENTS"
						RMDIR_EXIT_STATUS=$?
							
						if [ "$RMDIR_EXIT_STATUS" -ne 0 ]; then
							EXIT=$(( $EXIT | 16 ))
							echo "$APP_NAME: execution of \`$RMDIR_CMD' failed (exit status $RMDIR_EXIT_STATUS)" >&2
						fi
					fi
				fi
				
			# no more errors
			else
				break
			fi
		done
	fi
		
	# output stdout
	if [ -n "$STDOUT" ]; then
		echo "$STDOUT"
	fi
fi

exit $EXIT