#!/bin/bash
#
# File: /usr/local/bin/del
#
# delete files and directories into a trash and handle the trash
#
#    Copyright (C) 2013 Ingo Kaesmann
#
#    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; either version 2 of the License, or
#    (at your option) any later version.
#
#    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, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# Script template: 0.3.0
#
# Exit codes:
# 0 = OK
# 1 = Error in parameters
# 2 = Error in configfile, configfile old or not found
# 3 = Library old or not found
##############################################################################
# SOURCE FILES

. /usr/local/etc/main-inka-sh.conf			# main config
. $LLIBDIR/lib-inka-std.sh				# functions standard
test -e ~/.delrc && . ~/.delrc				# local config
##############################################################################
# DECLARATION OF VARIABLES

# STRINGS
pkgname=inka-utilities					# package name
lib_inka_std_sh_needed=1.3.6				# standard lib version
conf_file_needed=0.1.4					# conf file version
#----------------------- end of variables to configure -----------------------
version=$(grep "^VERSION" $PROGREG/$pkgname.inf | cut -f 2)	# version
scriptname=$(basename $0)				# name of script
tempfile=$TEMPDIR/$scriptname.$UID.$$.tmp		# temporary file
infolog="logger -p user.info -t $scriptname --"		# log info messages
errlog="logger -p user.err -t $scriptname -- ERROR:"	# log error messages
debuglog="logger -p user.debug -t $scriptname -- DEBUG:" # log debug messages
debug=no						# debug=yes/no
verbose="$Verbose"					# verbose=""/-q/-v
opt_found=no						# opt_found=yes/no
opt=""							# option
#------------------------- end of standard variables -------------------------
ls="/bin/ls $LS_OPTIONS"				# ls command
opt_D=""						# option -D
opt_U=""						# option -U
opt_r=""						# option -r
sorting=$Sorting				# sorting when show trash
deldir="$HOME/.deleted"				# dir contents deleted files
undeldir="$HOME/undeleted"			# dir for undelete files

# NUMBERS
err=0							# exit code

##############################################################################
# FUNCTIONS

function show_help ()
{
# Show help
# Uses: cat
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_help: $# $@" >&2
	#$debuglog show_help: $# $@
fi

cat <<EOT

Aufruf: $scriptname [OPTION]... [DATEI... | DATUM-1 DATUM-2]
Loescht angegebene Dateien in einen Muelleimer und verwaltet diesen

        -c      entfernen von Dateien/Verzeichnissen aus dem Muelleimer
        -s      anzeigen des Muelleimerinhalts (nur oberste Ebene)
        -u      wiederherstellen von Dateien/Verzeichnissen in Verzeichnis
                  $undeldir
        -D      mit -c,-s,-u, anstatt Dateiname, wird Loeschdatum benutzt
        -n      mit -s, nach Dateiname sortieren
        -t      mit -s, nach Loeschzeit sortieren
        -r      mit -s, auch Inhalt geloeschter Verzeichnisse anzeigen,
                  Angabe von DATEI(en) erforderlich
-h, --help      Hilfe anzeigen und beenden
 --version      Versionsinformationen anzeigen und beenden
--show-config      Programmkonfiguration anzeigen und beenden
   --debug      Debug-Modus benutzen
        -Q      ohne Erklaerungen der durchgefuehrten Taetigkeiten
        -q      nur wenige Fehlermeldungen
        -v      mit Erklaerungen der durchgefuehrten Taetigkeiten

Es kann nur eine der Optionen -c, -s oder -u angegeben werden.
DATUM-1: Datum vor zu bearbeitenden Dateien, ab 01.01.1900
DATUM-2: Datum nach zu bearbeitenden Dateien, bis 31.12.2300
EOT
return 0
}
#-----------------------------------------------------------------------------
function show_config ()
{
# Show configuration of program
# Uses: cat, cut, grep
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_config: $# $@" >&2
	#$debuglog show_config: $# $@
fi

cat <<EOT

Voreingestellte Sortier-Reihenfolge bei der Anzeige des Muelleimer-Inhalts:
$sorting

Voreingestelltes Ausgabeverhalten bei Aktionen:
""=-Q, -q=-q, -v=-v
"$verbose"

EOT

return 0
}
#-----------------------------------------------------------------------------
function delete_by_name ()
{
# Move files and directoriess to trash
# Parameters: FILE...
# Uses: cp, echo, rm
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG delete_by_name: $# $@" >&2
	#$debuglog show_config: $# $@
fi

while [ $# -gt 0 ]; do
	if [ "$verbose" == "-v" ]; then
		$ls -AR1 $1				# show file
	fi
	cp -r --backup=numbered $1 $deldir		# copy to deldir
	if [ $? -eq  0 ]; then
		rm -r $1				# remove file
	fi
	shift
done

return  0
}
#-----------------------------------------------------------------------------
function show_by_name ()
{
# Show files recursive by name
# Parameters: files (only if opt_r is given)
# Uses: cd, echo, ls
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_by_date: $# $@" >&2
	#$debuglog show_config: $# $@
fi

if [ "$opt_r" == "-r" ]; then			# opt_r given
	cd $deldir
	if [ "$sorting" == "name" ]; then
		while [ $# -gt 0 ]; do
			$ls -AgGR $1		# show named files, name sorted
			shift
		done
	else
		while [ $# -gt 0 ]; do
			$ls -AgGtrR $1		# show named files, time sorted
			shift
		done
	fi
else						# only show trash main dir
	if [ "$sorting" == "name" ]; then
		$ls -AgG $deldir		# show trash dir, name sorted
	else
		$ls -AgGtr $deldir		# show trash dir, time sorted
	fi
fi

return 0
}
#-----------------------------------------------------------------------------
function show_by_date ()
{
# Show files by date
# Parameters: DATE-1 DATE-2 (format: 31.01.2001)
# Uses: cat, cut, cd, echo, ls, rm, wc, Read_Lines
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_by_date: $# $@" >&2
	#$debuglog show_config: $# $@
fi

local dirname filedate firstchar line day1 month1 year1 day2 month2 year2 lastline n
dirname=""		# directory-name
filedate=""		# m-time of file
firstchar=""		# 1. character of line
line=""			# line of tempfile
day1=${1:0:2}		# day befor date
month1=${1:3:2}		# month befor date
year1=${1:6:4}		# year befor date
day2=${2:0:2}		# day after date
month2=${2:3:2}		# month after date
year2=${2:6:4}		# year after date
lastline=0		# number of lines of tempfile
n=0			# counter

# show files and directories
cd $deldir
ls -AgGR --time-style=long-iso . > $tempfile	# make list of whole trash
lastline=$(cat $tempfile | wc -l)
while [ $n -lt $lastline ]; do
	let n++
	line=$(Read_Lines $tempfile $n $n)		# read line
	firstchar=${line:0:1}
	if [ "$firstchar" == "." ]; then		# line is directory
		dirname=$(echo $line | cut -d ":" -f 1)
		echo "$line"				# show directory
	elif [ "$firstchar" == "-" ]; then		# line is file
		filedate=$(echo $line | cut -d " " -f 4)
		if [ "$filedate" \> "$year1-$month1-$day1" ] && [ "$filedate" \< "$year2-$month2-$day2" ]; then
			# filedate after date-1 and befor date-2
			$ls -AgG $dirname/$(echo $line | cut -d " " -f 6)	# show file
		fi
	fi
done

rm -f $tempfile
return 0
}
#-----------------------------------------------------------------------------
function undelete_by_name ()
{
# Move files by name to actual directory
# Parameters: FILE...
# Uses: echo, mv, shift
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG undelete_by_name: $# $@" >&2
	#$debuglog show_config: $# $@
fi

# message where it will be undeleted
if [ "$verbose" == "-v" ]; then
	echo "Es wird wiederhergestellt:"
fi

# show files and move them to actual directory or undeldir
while [ $# -gt 0 ]; do
	if [ "$verbose" == "-v" ]; then
		$ls -AR1 $deldir/$1			# show file
	fi
	mv --backup=numbered $deldir/$1 $undeldir
	shift
done

return 0
}
#-----------------------------------------------------------------------------
function undelete_by_date ()
{
# Move files by date to actual directory
# Parameters: DATE-1 DATE-2 (format: 31.01.2001)
# Uses: cat, cut, echo, ls, mkdir, mv, rm, rmdir, wc, Read_Lines
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG undelete_by_date: $# $@" >&2
	#$debuglog show_config: $# $@
fi

local dir dirlong dirshort filedate filename firstchar line day1 month1 year1 day2 month2 year2 deldirnumber lastline n
dir=""			# undelete-directory (a or u)
dirlong=""		# dirname with deldir
dirshort=""		# dirname without deldir
filedate=""		# m-time of file
filename=""		# filename
firstchar=""		# 1. character of line
line=""			# line of tempfile
day1=${1:0:2}		# day befor date
month1=${1:3:2}		# month befor date
year1=${1:6:4}		# year befor date
day2=${2:0:2}		# day after date
month2=${2:3:2}		# month after date
year2=${2:6:4}		# year after date
deldirnumber=0		# number of chars of deldir
lastline=0		# number of lines of tempfile
n=0			# counter

# message where it will be undeleted
if [ "$verbose" == "-v" ]; then
	echo "Es wird wiederhergestellt:"
fi

# build deldirnumber for dirname
echo $deldir > $tempfile
deldirnumber=$(ls -l $tempfile | cut -d " " -f 5)
let deldirnumber++				# number of chars for output

# show files and move them to actual directory or undeldir
ls -AgGR --time-style=long-iso $deldir > $tempfile	# make list of whole trash
lastline=$(cat $tempfile | wc -l)
while [ $n -lt $lastline ]; do
	let n++
	line=$(Read_Lines $tempfile $n $n)		# read line
	firstchar=${line:0:1}
	if [ "$firstchar" == "/" ]; then		# line is directory
		dirlong=$(echo $line | cut -d ":" -f 1)
		dirshort=$(echo $line | cut -d ":" -f 1 | cut -c "$deldirnumber-")
	elif [ "$firstchar" == "-" ]; then		# line is file
		filedate=$(echo $line | cut -d " " -f 4)
		if [ "$filedate" \> "$year1-$month1-$day1" ] && [ "$filedate" \< "$year2-$month2-$day2" ]; then
			# filedate after date-1 and befor date-2
			filename=$(echo $line | cut -d " " -f 6)
			if [ "$verbose" == "-v" ]; then	# show filename
				echo "$(echo $dirlong/$filename | cut -c "$deldirnumber-")"
			fi
			mkdir -p $undeldir/$dirname
			mv --backup=numbered $deldir/$dirshort/$filename $undeldir/$dirshort/$filename
		fi
	fi
done

# message delete empty dirs
if [ "$verbose" == "-v" ]; then
	echo "Entferne leere Verzeichnisse..."
fi

# remove empty directories from trash
ls -AgGR --time-style=long-iso $deldir > $tempfile	# make list of whole trash
lastline=$(cat $tempfile | wc -l)
n=$lastline
while [ $n -gt 0 ]; do
	line=$(Read_Lines $tempfile $n $n)		# read line
	firstchar=${line:0:1}
	if [ "$firstchar" == "/" ]; then		# line is directory
		dirlong=$(echo $line | cut -d ":" -f 1)
		if [ "$dirlong" != "$deldir" ]; then	# remove dir
			rmdir --ignore-fail-on-non-empty $dirlong
		fi
	fi
	let n--
done

rm -f $tempfile
return 0
}
#-----------------------------------------------------------------------------
function clear_trash_by_name ()
{
# Clear files by name recursive from trash
# Parameters: FILE...
# Uses: cd, echo, rm
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG clear_trash_by_name: $# $@" >&2
	#$debuglog show_config: $# $@
fi

# message clear trash:
if [ "$verbose" == "-v" ]; then
	echo "Aus dem Muelleimer wird entfernt:"
fi

# show files and remove them from trash
cd $deldir
while [ $# -gt 0 ]; do
	if [ "$verbose" == "-v" ]; then
		$ls -AR1 $1				# show files
	fi
	rm -r $1					# remove files
	shift
done

return 0
}
#-----------------------------------------------------------------------------
function clear_trash_by_date ()
{
# Clear files by date and empty directorys from trash
# Parameters: DATE-1 DATE-2 (format: 31.01.2001)
# Uses: cat, cut, echo, ls, rm, rmdir, wc, Read_Lines
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG clear_trash_by_date: $# $@" >&2
	#$debuglog show_config: $# $@
fi

local dirname filedate filename firstchar line day1 month1 year1 day2 month2 year2 deldirnumber lastline n
dirname=""		# directory-name
filedate=""		# m-time of file
filename=""		# filename
firstchar=""		# 1. character of line
line=""			# line of tempfile
day1=${1:0:2}		# day befor date
month1=${1:3:2}		# month befor date
year1=${1:6:4}		# year befor date
day2=${2:0:2}		# day after date
month2=${2:3:2}		# month after date
year2=${2:6:4}		# year after date
deldirnumber=0		# number of chars of deldir
lastline=0		# number of lines of tempfile
n=0			# counter

# message clear files:
if [ "$verbose" == "-v" ]; then
	echo "Entferne Dateien aus dem Muelleimer:"
fi

# build deldirnumber for output of directoryname
echo $deldir > $tempfile
deldirnumber=$(ls -l $tempfile | cut -d " " -f 5)
let deldirnumber++				# number of chars for output

# show files and remove them from trash
ls -AgGR --time-style=long-iso $deldir > $tempfile	# make list of wohle trash
lastline=$(cat $tempfile | wc -l)
while [ $n -lt $lastline ]; do
	let n++
	line=$(Read_Lines $tempfile $n $n)		# read line
	firstchar=${line:0:1}
	if [ "$firstchar" == "/" ]; then		# line is directory
		dirname=$(echo $line | cut -d ":" -f 1)
	elif [ "$firstchar" == "-" ]; then		# line is file
		filedate=$(echo $line | cut -d " " -f 4)
		if [ "$filedate" \> "$year1-$month1-$day1" ] && [ "$filedate" \< "$year2-$month2-$day2" ]; then
			# filedate after date-1 and befor date-2
			filename=$(echo $line | cut -d " " -f 6)
			if [ "$verbose" == "-v" ]; then	# show file
				echo "$(echo $dirname/$filename | cut -c "$deldirnumber-")"
			fi
			rm $dirname/$filename		# remove file
		fi
	fi
done

# message clear directories:
if [ "$verbose" == "-v" ]; then
	echo "Entferne leere Verzeichnisse aus dem Muelleimer:"
fi

# show directories and remove them from trash
n=$lastline
while [ $n -gt 0 ]; do
	line=$(Read_Lines $tempfile $n $n)		# read line
	firstchar=${line:0:1}
	if [ "$firstchar" == "/" ]; then		# line is directory
		dirname=$(echo $line | cut -d ":" -f 1)
		if [ "$dirname" != "$deldir" ]; then
			if [ "$verbose" == "-v" ]; then	# show directory
				echo "$(echo $dirname | cut -c "$deldirnumber-")/"
			fi
			rmdir --ignore-fail-on-non-empty $dirname	# remove directory
		fi
	fi
	let n--
done

rm -f $tempfile
return 0
}

##############################################################################
# PARSE COMMANDLINE

# Read all options with args and all additional args into variables.
# Stop parsing after last parameter.
# Try to do a check on all parameters and version numbers.
# Handle options -h, -C and -V directly.
# Exit on error.

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Parse Commandline: $# $@" >&2
	#$debuglog Parse Commandline: $# $@
fi

# get options and options with arguments
while [ "${1:0:1}" == "-" ]; do				# get all options
	case "$1" in
	  -h|--help)	opt=-h;;			# option -h
	  --version)	opt=--v;; 			# option --version
	  --show-config) opt=--s;;			# option --show-config
	  --debug)	debug=yes;;			# option --debug
	  -q|--quiet)	verbose=-q;;			# option -q
	  -v|--verbose)	verbose=-v;;			# option -v
#-------------------------- end of standard options --------------------------
	  -Q)		verbose="";;			# option -Q
	  -c|-s|-u)	opt="$1";;			# option -c,-s,-u
	  -D)		opt_D=$1;;			# option -D
	  -n)		sorting=name;;			# option -n
	  -t)		sorting=time;;			# option -t
	  -r)		opt_r=$1;;			# option -r
	  *)	if [ "$verbose" != "-q" ]; then
			echo "Fehler - Option $1 falsch!" >&2	# error
		else
			$errlog unknown option $1
		fi
		exit 1;;
	esac
	shift
	opt_found=yes
done

# handle standard options -h, --v, --s
case "$opt" in
  -h)	show_help; exit 0;;					# show help
  --v)	echo "$scriptname (${pkgname}) $version"; exit 0;;	# show version
  --s)	show_config; exit 0;;					# show config
esac

# check library
Compare_Version $lib_inka_std_sh_needed $Lib_Inka_Std_Sh_Version
ret=$?						# (ret: 0,1,2,9,127)
if [ $ret -eq 1 ]; then				# needed lib-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Programm benoetigt neuere Version von lib-inka-std.sh!" >&2
	else
		$errlog newer version of lib-inka-std.sh needed
	fi
	exit 3
elif [ $ret -eq 9 ] || [ $ret -eq 127 ]; then	# other error (version or lib missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung von lib-inka-std.sh nicht moeglich!"
	else
		$errlog check of version of lib-inka-std.sh impossible
	fi
	exit 3
else						# check ok, reset $ret
	ret=0
fi

# check version of config files			# set config var!
#Compare_Version $conf_file_needed $Program_Conf
Compare_Version $conf_file_needed $Del_Rc
ret=$?						# (ret: 0,1,2,9)
if [ $ret -eq 1 ]; then				# needed config-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - $scriptname benoetigt neuere Version der Konfig-datei!" >&2
	else
		$errlog newer version of conf file needed
	fi
	exit 2
elif [ $ret -eq 9 ]; then			# other error (version of conffile missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung der Konfig-datei von $scriptname nicht moeglich!"
	else
		$errlog check of version of conf file impossible
	fi
	exit 2
else						# check ok, reset $ret
	ret=0
fi

# check arguments after options
case "$opt" in
  -c|-s|-u)	if [ "$opt_D" == "-D" ]; then				# parameters must be dates
			if [ "${1:0:2}" \< "01" ] || [ "${2:0:2}" \< "01" ] ||
			[ "${1:0:2}" \> "31" ] || [ "${2:0:2}" \> "31" ] ||
			[ "${1:3:2}" \< "01" ] || [ "${2:3:2}" \< "01" ] ||
			[ "${1:3:2}" \> "12" ] || [ "${2:3:2}" \> "12" ] ||
			[ "${1:6:4}" \< "1900" ] || [ "${2:6:4}" \< "1900" ] ||
			[ "${1:6:4}" \> "2300" ] || [ "${2:6:4}" \> "2300" ] ||
			[ "${1:2:1}" != "." ] || [ "${2:2:1}" != "." ] ||
			[ "${1:5:1}" != "." ] || [ "${2:5:1}" != "." ] ||
			[ "${1:10:1}" != "" ] || [ "${2:10:1}" != "" ]; then
				if [ "$verbose" != "-q" ]; then		# date wrong
					echo "Fehler - Datum fehlt oder falsch angegeben!" >&2
				else
					$errlog date missing or wrong
				fi
				exit 1
			else				# dates are ok, read it
				arg1="$1"
				arg2="$2"
			fi
		else					# no dates
			if [ "$opt" == "-c" ] || [ "$opt" == "-u" ] ||
			( [ "$opt" == "-s" ] && [ "$opt_r" == "-r" ] ); then # files must given
				if [ $# -ne 0 ]; then	# pars given, read it
					arg3=$@
				else			# parameter missing
					if [ "$verbose" != "-q" ]; then
						echo "Fehler - Datei fehlt!" >&2
					else
						$errlog parameter count
					fi
					exit 1
				fi
			else
				if [ $# -ne 0 ]; then	# parameter missing
					if [ "$verbose" != "-q" ]; then
						echo "Fehler - nicht verarbeitungsfaehige Datei(en) angegeben!" >&2
					else
						$errlog parameter count
					fi
					exit 1
				fi
			fi
		fi;;
  "")			# no opt is given, so parameters are files, must given
	if [ $# -ne 0 ]; then				# pars given, read it
		arg3=$@
	else						# parameter missing
		if [ "$verbose" != "-q" ]; then
			echo "Fehler - Datei fehlt!" >&2
		else
			$errlog parameter count
		fi
		exit 1
	fi;;
  *)	if [ $# -ne 0 ]; then				# error in param count
		if [ "$verbose" != "-q" ]; then
			echo "Fehler - Parameteranzahl falsch!" >&2
		else
			$errlog parameter count
		fi
		exit 1
	fi;;
esac

# check undeldir exists
test -d $undeldir
if [ $? -eq 1 ]; then
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - $undeldir nicht gefunden!" >&2
	else
		$errlog directory missing
	fi
	exit 2
fi
##############################################################################
# MAINPROGRAM

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Main: $# $@" >&2
	#$debuglog Main: $# $@
fi

# Check what to do (initial options and args are removed from cmdline)
case "$opt" in
  -c)	if [ "$opt_D" == "-D" ]; then			# opt -c and opt_D
		clear_trash_by_date $arg1 $arg2
		err=$?
	else						# opt -c
		clear_trash_by_name $arg3
		err=$?
	fi;;
  -s)	if [ "$opt_D" == "-D" ]; then			# opt -s and opt_D
		show_by_date $arg1 $arg2
		err=$?
	else						# opt -s
		show_by_name $arg3
		err=$?
	fi;;
  -u)	if [ "$opt_D" == "-D" ]; then			# opt -u and opt_D
		undelete_by_date $arg1 $arg2
		err=$?
	else						# opt -u
		undelete_by_name $arg3
		err=$?
	fi;;
  "")	delete_by_name $arg3				# no opt given
	err=$?;;
esac

exit $err
