#! /bin/bash
#
# pkgsync - Automated package synchronization tool
# 2004-2007 Steinar H. Gunderson <sgunderson@bigfoot.com>.
#
# Licensed under the GNU GPL version 2, as publicshed by the FSF;
# see /usr/share/common-licenses/GPL-2 on Debian systems or visit
# www.fsf.org.
#

set -e
set -o noglob
export DEBIAN_FRONTEND=noninteractive
export VERSION=1.26

if [ ! -r /etc/pkgsync/musthave -o \
     ! -r /etc/pkgsync/mayhave -o \
     ! -r /etc/pkgsync/maynothave ]; then
	echo Error: Missing files in /etc/pkgsync. Aborting.
	echo 
	echo Please see /usr/share/doc/pkgsync/README.Debian for information on 
	echo configuring pkgsync.
	
	exit 1
fi

print_help () {
	echo "pkgsync $VERSION"
	echo "Automated package synchronization tool"
	echo ""
	echo "Usage: pkgsync [OPTIONS]"
	echo "Recognized options:"
	echo "  -h, --help           display this help and exit"
	echo "  -k, --keep-unused    don't remove unused packages"
	echo "  -s, --simulate       don't do anything, just print out what would have happened"
	echo ""
	echo "Complete documentation can be found in /usr/share/doc/pkgsync/README.Debian."
}

# Largely adapted from /usr/lib/getopt/parse.bash
parse_options () {
	TEMP=`getopt -o hksad --long help,keep-unused,simulate -n 'pkgsync' -- "$@"`
	eval set -- "$TEMP"

	APTITUDE_ARGS="-y -q -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold"

	while :; do
		case "$1" in
			-s|--simulate)
				APTITUDE_ARGS="$APTITUDE_ARGS -s"
				shift
				;;
			-k|--keep-unused)
				APTITUDE_ARGS="$APTITUDE_ARGS -o Aptitude::Delete-Unused=false"
				shift
				;;
			-h|--help)
				print_help
				exit 0
				;;
			--)
				shift
				break
				;;
			*)
				echo "Internal error: doesn't recognize argument '$1'"
				exit 1
				;;
		esac
	done
}
readpkgs () {
	grep -vE '^#' "$1" | grep -vE '^\s*$' | tr -d " \t"
}
getpkgs () {
	IFS="
"
	for pkg in $( readpkgs $1 ); do
		# if the line starts with "debtags:", it's a debtags expression,
		# so run it through debtags.
		if echo "$pkg" | grep -Eq '^debtags:'; then
			if ! [ "$USE_DEBTAGS" ]; then
				echo Error: "debtags:" line encountered, but debtags is not installed. Stopping.
				exit 1
			fi
			PATTERN=$( echo "$pkg" | cut -d: -f2- )
			debtags grep "$PATTERN" | tagcoll copy | cut -d: -f1
		else
			# if the line is "meta:current-kernel", use the kernel package
			# for the currently running kernel, if it exists
			if [ "$pkg" = "meta:current-kernel" ]; then
				KVERS=$( uname -r )
				aptitude -F '%p' search ".*-image-$KVERS$" | sed "s/ \+$//" 2>/dev/null || true
			else
				# if there's a wildcard in this, push it through aptitude
				# to glob. if not, just print it out.
				if echo "$pkg" | grep -Eq '[][*?()|~]'; then
					aptitude -F '%p' search "$pkg" | sed "s/ \+$//" 2>/dev/null || true
				else
					echo "$pkg"
				fi
			fi
		fi
	done
}
normalize_arch () {
	# Remove the main arch (e.g. :amd64) from each line that contains it.
	# All other architectures are left alone.
	# We do this both on output from dpkg and when reading the package lists,
	# so that filtering works as expected. On explicit installation and removal,
	# aptitude will treat a bare package as implicitly on the main architecture,
	# so we don't need to re-add it later.
	main_arch="$1"
	sed "s/:$main_arch\$//"
}
run_aptitude () {
	echo RUNNING: aptitude $APTITUDE_ARGS "$@"
	aptitude $APTITUDE_ARGS "$@" 
}
run_debtags () {
	if [ "$USE_DEBTAGS" ]; then
		echo RUNNING: debtags "$@"
		debtags "$@" 
	fi
}

# The beautiful look of hacks in the morning...
filter () { 
	echo "$@" | tr " " "\n" | sort | uniq -c | grep "     2" | cut -c9-
}

parse_options "$@"

# Check if we've got debtags installed
[ -x /usr/bin/debtags ] && USE_DEBTAGS=yes

# Update the package lists
#aptitude update
#run_debtags update

# Find out what parameters to give to aptitude.
main_arch=$( dpkg --print-architecture )
installed=$( dpkg -l | grep '^ii' | cut -c5- | cut '-d ' -f1 | normalize_arch $main_arch )
musthave_install=$( getpkgs /etc/pkgsync/musthave | normalize_arch $main_arch | sort -u | sed "s/$/+/" )
maynothave_remove=$( getpkgs /etc/pkgsync/maynothave | normalize_arch $main_arch | sort -u | sed "s/$/-/" )
mayhave_marknonauto=$( getpkgs /etc/pkgsync/mayhave | normalize_arch $main_arch | sort -u | sed "s/$/\&m/" )
mustormayhave=$( ( getpkgs /etc/pkgsync/musthave $main_arch ; getpkgs /etc/pkgsync/mayhave $main_arch ) | normalize_arch $main_arch | sort -u )
rest_markauto=$( filter $installed $installed $mustormayhave | sed "s/$/\&M/" )

run_aptitude full-upgrade '?upgradable' $musthave_install $maynothave_remove $mayhave_marknonauto $rest_markauto
run_aptitude autoclean
