#!/bin/dash
set -e
. /usr/lib/cruft/common.sh

cruft_debug 2

# empty means stdout
REPORTFILE=""
MAILTO=""
CLEANUP=yes
FILES=yes
REPORT=yes
CHROOTS=""
IGNORES=""
STARTDIR="$(pwd)"
# first, read the paths from command line
DRIVES=""
exitcode=0

usage()
{
	echo "Usage:"
	echo "  cruft -h            Shows this message and exits."
	echo "  cruft -k            Only cleans the spool directory."
	echo "  cruft -n [OPTIONS]  Generates input files for reporting, but does not"
	echo "                      generate a report."
	echo "  cruft -N [OPTIONS]  Only generates a report."
	echo "  cruft [OPTIONS]     Performs a run and generates a report."
	echo
	echo "Options: [-r REPORTFILE] [-m ADDRESS]"
	echo "         [-d DRIVES] [--chroots CHROOTS] [--ignore IGNORES]"
}

while [ $# != 0 ]; do
	case "$1" in
		-h)	usage; exit 0;;
		-d)	DRIVES="$DRIVES $2"; shift;;
		-r)	REPORTFILE="$2"; shift
			if [ -z "$STARTDIR" -o ! -d "$STARTDIR" ]; then
				echo "Start directory \"$STARTDIR\" is not accessible." >&2
				exit 1
			fi
			;;
		-m)	MAILTO="$2"; shift;;
		--chroots) CHROOTS="$2"; shift ;;
		--ignore) IGNORES="$IGNORES $2"; shift ;;
		-k)	CLEANUP=yes; FILES=no; REPORT=no;;
		-n)	CLEANUP=no; FILES=yes; REPORT=no;;
		-N)	CLEANUP=no; FILES=no; REPORT=yes;;
		-*)	echo "$0: invalid option -- ${i#-}"; usage; exit 2;;
		*)	echo "$0: invalid argument: ${i#-}"; usage; exit 2;;
	esac
	shift
done

cd /var/spool/cruft || { echo "Are you root?" ; exit 1; }

# now set the default, if none were specified
if [ -z "$DRIVES" ] ; then
	default_drives=$(cruft_default_scan_fs)
	DRIVES=${default_drives:-/}
fi

# clean up from previous runs
if [ "$CLEANUP" = "yes" ]; then
	rm -f /var/spool/cruft/*
fi

set_cruft_scan_fs $DRIVES
set_ignores $IGNORES

# set collation order to POSIX, to avoid differences between own programs' and
# sort output
export LC_COLLATE="C"

if [ "$FILES" = yes ]; then
	# delete old explain output files
	rm -f /var/spool/cruft/must_*
	rm -f /var/spool/cruft/mayx_*
	rm -f /var/spool/cruft/msnt_*

	debug "*** Running explain scripts"
	# run explain scripts
	(
		cd /usr/lib/cruft/explain && ls -1 2>/dev/null
		cd /etc/cruft/explain     && ls -1 2>/dev/null
	) | sort -u |
	while read f ; do
		process_package "${f}" || continue
		# File in /etc overrides the one in /usr
		if [ -x "/etc/cruft/explain/${f}" -a ! -d "/etc/cruft/explain/${f}" ] ; then
			script="/etc/cruft/explain/${f}"
		elif [ -x "/usr/lib/cruft/explain/${f}" -a ! -d "/usr/lib/cruft/explain/${f}" ] ; then
			script="/usr/lib/cruft/explain/${f}"
		else
			debug " - Skipping [${f}] - probably a directory"
			continue
		fi
		debug " - Running ${script}"
		# FD1 -> must exist
		# FD3 -> may exist
		# FD4 -> must not exist
		# FD2 -> stderr
		( ( ${script} |
			fixup_slashes |
			/usr/lib/cruft/filter_ignores |
			/usr/lib/cruft/canonical |
			sort -u > /var/spool/cruft/must_${f}
		) 3>&1 | fixup_slashes |
			/usr/lib/cruft/filter_ignores |
			/usr/lib/cruft/canonical |
			sort -u > /var/spool/cruft/mayx_${f}
		) 4>&1 | fixup_slashes |
			/usr/lib/cruft/filter_ignores |
			/usr/lib/cruft/canonical |
			sort -u > /var/spool/cruft/msnt_${f}
	done

	# delete old find output

	rm -f /var/spool/cruft/file_*
	rm -f /var/spool/cruft/type_*

	# run find on the filesystems

	debug "*** Running find commands"
	for DRIVE in $DRIVES; do 
		if [ "$DRIVE" = "/" ]; then
			FILENAME=file_root
		else
			FILENAME=file_in_`echo "$DRIVE" | sed 's:^/::;s:/:-:g'`
		fi

		PRUNE=$(get_prunes_for "$DRIVE")
		if [ "$PRUNE" = skip ]; then
			debug " - Skipping [${DRIVE}]"
			: > "${FILENAME}"
			: > "type_symlink${FILENAME#file}"
		else
			debug " - Running find on [${DRIVE}]"
			find "$DRIVE" -xdev $PRUNE -print \
				-type l -fprint "type_symlink${FILENAME#file}" \
				| sort -u > "$FILENAME"
		fi
	done

	# objects of particular type
	# - delete old output lists
	rm -f /var/spool/cruft/broken_symlinks_*
	# - generate new ones
	debug "*** Checking symlinks"
	/usr/lib/cruft/check_type_symlink $CHROOTS
fi

if [ "$REPORT" = yes ]; then
	# delete old target files
	rm -f /var/spool/cruft/frbn_*
	rm -f /var/spool/cruft/miss_*
	rm -f /var/spool/cruft/unex_*
	rm -f /var/spool/cruft/whte_*
	rm -f /var/spool/cruft/filter_list_*
		
	# generate differences
	debug "*** Calculating differences"
	/usr/lib/cruft/merge_diff

	cd /var/spool/cruft

	debug "*** Filtering results"
	# filter the results
	for type in frbn miss unex broken_symlinks
	do
		debug " - Filtering type [${type}]"
		/usr/lib/cruft/filters_list "${type}" > "filter_list_${type}"
		find . -maxdepth 1 -mindepth 1 -type f -name "${type}_*" | while read f ; do
			/usr/lib/cruft/filter "filter_list_${type}" < "${f}" > "${f}.tmp"
			mv "${f}.tmp" "${f}"
		done
	done

	# remove empty files
	find . -maxdepth 1 -mindepth 1 -type f -size 0 | xargs rm -r

	debug "*** Generating report"
	# generate report
	TEMPFILE="`tempfile`"
	( echo -n 'cruft report: '; date
	  echo
	  for a in miss_*; do
		if [ -e $a ]; then
			echo ---- $a ---- | sed 's/miss_/missing: /'
	 		cat $a | sed 's/^/        /'
	  	fi
	  done
	  for a in frbn_*; do
		if [ -e $a ]; then
			echo ---- $a ---- | sed 's/want_/missing: /'
	 		cat $a | sed 's/^/        /'
	  	fi
	  done
	  for a in unex_*; do
		if [ -e $a ]; then
	 	    echo ---- $a ---- | 
		     sed 's=unex_root=unexplained: /=;s=unex_in_=unexplained: ='
		    cat $a | sed 's/^/        /'
		fi
  	  done
	  for a in broken_symlinks_*; do
		if [ -e $a ]; then
	 	    echo ---- $a ---- | 
		     sed 's=broken_symlinks_root=broken symlinks: /=;s=broken_symlinks_in_=broken symlinks: ='
		    cat $a | sed 's/^/        /'
		fi
  	  done
	  echo
	  echo end.
	) > "$TEMPFILE"

	if [ -z "$REPORTFILE" ]; then
		cat "$TEMPFILE"
	else
		cd "$STARTDIR"
		cat "$TEMPFILE" > "$REPORTFILE"
	fi
	if [ "$MAILTO" != "" ]; then
		if which mail >/dev/null 2>&1 ; then
			cat "$TEMPFILE" | mail -s "[`hostname`] Cruft Report" $MAILTO
		else
			echo "mail program unavailable, not sending the email" >&2
			exitcode=1
		fi
	fi
	rm -f "$TEMPFILE"
fi
exit $exitcode
