#!/bin/sh
#
#  Copyright (c) 1999 Michael Merten <mikemerten@yahoo.com>
#  Copyright (c) 1999-2000 Gregory T. Norris <adric@debian.org>
#  Copyright (c) 2000 Herbert Xu <herbert@debian.org>
#
#  License:  This script is distributed under the terms of version 2
#            of the GNU GPL. See the LICENSE file included with the package.
#
#  $Id: apt-move,v 1.32.2.11 2002/04/14 07:04:23 herbert Exp $
#
#  The apt-move(8) manpage contains the most up-to-date documentation
#     for this script.  Also, see the README file.
#
#  Configuration for this script can be found in /etc/apt-move.conf
#
#  Requires GNU sed.
#
set -e

[ ${CDPATH+1} ] && unset CDPATH

# set some defaults for conffile items, just in case someone tries to
# "clean up" the file
ARCH=`dpkg --print-installation-architecture`
ARCHS="alpha arm hurd-i386 i386 m68k powerpc sparc"
LOCALDIR=/mirrors/debian
DIST=stable
PKGTYPE=binary
APTSITES=
FILECACHE=/var/cache/apt/archives
LISTSTATE=/var/lib/apt/lists
DELETE=no
MAXDELETE=20
STRICTMOVE=no

# Remember where we came from.
ORIGDIR=`pwd`

# Global variables - Do not change these.  Config is in /etc/apt-move.conf -
# these are the temporary files...
TMPDELI=`tempfile -p MOVE_`	# index files to delete
TMPDEL=`tempfile -p MOVE_`	# files to delete
TMPLOC=`tempfile -p MOVE_`	# local package list

TMPLIST="$LOCALDIR/.archive $TMPLOC $TMPDEL $TMPDELI"

# make sure that tempfile always get cleaned up
trap 'rm -rf $TMPLIST' EXIT

#
TEST=				# test run flag (-t)
FORCE=				# force delete flag (-f)
#
GET_BINARY=			# binary distribution flag
GET_SOURCE=			# source distribution flag
FETCH=/usr/lib/apt-move/fetch	# crappy replacement for apt-get
MOVE1=/usr/share/apt-move/move1	# used internally by movefiles
MOVE2=/usr/share/apt-move/move2
MOVE3=/usr/share/apt-move/move3
# End Globals ---------------------------------------------------------------

info() {
	echo "$@"
}

showusage() {
	revision='$Id: apt-move,v 1.32.2.11 2002/04/14 07:04:23 herbert Exp $'
	# display a 'usage' message.
	cat >&2 <<- EOF

	$revision

	Usage:    apt-move [-c conffile] [-ft] COMMAND
	
	Commands:
	       get         - update your master files from local apt.
	       getlocal    - alias of get.
	       fsck        - use this if your repository needs it.
	       move        - move cache files into mirror tree.
	       movefile    - move files specified on the command line.
	       delete      - delete obsolete packages and excluded files.
	       packages    - create new local Packages.gz files.
	       update      - alias for 'get move delete packages'.
	       local       - alias for 'move delete packages'.
	       localupdate - alias for 'getlocal move delete packages'.
	       mirror      - update your local mirror from remote rsync site.
	       sync        - same as mirror, but only gets packages that
	                     you currently have installed on your system.
	       exclude     - prints a list of all packages EXCLUDED from the
	                     mirror by the .exclude file (ignores -t).
	       listbin     - prints lists of packages which can serve as the
	                     input to mirrorbin.  Takes the arguments mirror,
	                     sync, or repo.
	       listsrc     - same as listbin, but lists source packages.
	       mirrorbin   - same as mirror, but gets the packages specified
	                     on stdin.
	       mirrorsrc   - same as mirrorbin, but gets source packages.

	Options:
	  -c  Specify an alternative configuration file.
	  -f  Override the MAXDELETE setting (use with caution).
	  -t  Show what apt-move would do, but do not actually do anything.

	See the apt-move(8) manpage for further details.

	EOF
	exit 1
}

checklist() {
	[ -d .apt-move ] || return 24
	touch .apt-move/$DIST.binary
	! [ $GET_BINARY ] || [ -s .apt-move/$DIST.binary ] || return 24
	touch .apt-move/$DIST.source
	! [ $GET_SOURCE ] || [ -s .apt-move/$DIST.source ] || return 24
}

createbinary() {
	awk '
		{ line = tolower($0) }
		line ~ /^package:/ { source = package = $2; next }
		line ~ /^priority:/ { priority = $2; next }
		line ~ /^section:/ { section = $2; next }
		line ~ /^version:/ { sub(/^[0-9]*:/, "", $2); version = $2
				     next }
		line ~ /^source:/ { source = $2; next }
		/^$/ { print package "\t" priority "\t" section "\t" source \
			     "\t" version }
	' | sort -k 1,1
}

createbinarymaster() { (
	touch .apt-move/$DIST.binary .apt-move/$DIST.binary.local
	[ $GET_BINARY ] || return 0

	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	TMP3=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2 $TMP3' EXIT

	sort -k 1,1 .apt-move/$DIST.binary.local > $TMP1
	> .apt-move/$DIST.arch.$ARCH
	sort -um .apt-move/$DIST.arch.* | join - .apt-move/$DIST.binary > $TMP2
	for i in $APTSITES; do
		echo $LISTSTATE/${i}_*_Packages
	done | tr " " "\n" | xargs cat | createbinary |
		sort -k 1,1 -um $TMP1 - | tee $TMP3 | cut -f 1 \
		> .apt-move/$DIST.arch.$ARCH
	sort -k 1,1 -um $TMP3 $TMP2 | awk '
			{ printf("%-30s\t%-9s\t%-15s\t%-30s\t%-30s\n",
				 $1, $2, $3, $4, $5) }
		' > .apt-move/$DIST.binary
); }

createsource() {
	awk '
		{ line = tolower($0) }
		line ~ /^package:/ { package = $2; next }
		line ~ /^priority:/ { priority = $2; next }
		line ~ /^section:/ { section = $2; next }
		line ~ /^version:/ { sub(/^[0-9]*:/, "", $2); version = $2
				     next }
		/^$/ { print package "\t" priority "\t" section "\t" version }
	' | sort -k 1,1
}

createsourcemaster() { (
	touch .apt-move/$DIST.source .apt-move/$DIST.source.local
	[ $GET_SOURCE ] || return 0

	TMP1=`tempfile -p MOVE_`
	trap 'rm -f $TMP1' EXIT

	sort -k 1,1 .apt-move/$DIST.source.local > $TMP1
	for i in $APTSITES; do
		echo $LISTSTATE/${i}_*_Sources
	done | tr " " "\n" | xargs cat | createsource |
		sort -k 1,1 -um $TMP1 - | awk '
			{ printf("%-30s\t%-9s\t%-15s\t%-30s\n",
				 $1, $2, $3, $4) }
		' > .apt-move/$DIST.source
); }

pkgdir() {
	sed '
		s:^non-US :non-US/main :
		s:^\(non-US/.*\) :\1/ :
		s:^[^/]* :main/&:
		s:\(non-US/.*\|.*/\)\(.*\) \(.*\):dists/'$DIST'/\1\3/\2:
		s:/$::
	'
}

getfiles() { (
	info
	info "Updating from local Packages files..."

	[ $TEST ] && return 0

	# make sure target directory exists.
	[ -d .apt-move ] || install -d .apt-move || return 5

	createbinarymaster
	createsourcemaster

	TMP1=`tempfile -p MOVE_`
	trap 'rm -f $TMP1' EXIT

	cut -f 3 .apt-move/$DIST.binary | sed 's/[[:blank:]]*$/ binary-/' | {
			sort -u | pkgdir | tee .apt-move/$DIST.dirs |
				sed 's:/binary-\(/\|$\):/binary-all\1:'
			{
				for arch in $ARCHS; do
					sed \
's:/binary-\(/\|$\):/binary-'$arch'\1:' \
						.apt-move/$DIST.dirs
				done
				cut -f 3 .apt-move/$DIST.source |
					sed 's/[[:blank:]]*$/ source/' |
					sort -u | pkgdir |
					tee -a .apt-move/$DIST.dirs
			} | {
				tee $TMP1
				sed -n \
's:/\(binary-[^/]*\|source\)\(/.*\)\?$:/\1/.index:p' \
					$TMP1 | sort -u
			}
		} | xargs mkdir -p || return 5
); }

checkversion() {
	if [ "$STRICTMOVE" != yes ]; then
		cut -d " " -f 3- > $1
		return 0
	fi
	> $1
	awk '
		$1 == $2 || $1 == "_" {
			for (i = 3; i < NF; i++) {
				printf("%s ", $i) > "'$1'"
			}
			print $NF > "'$1'"
			next
		}
		{
			print $7
		}
	'
}

movefiles() { (
	info
	info "Moving files..."

	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	TMP3=`tempfile -p MOVE_`
	TMP4=`tempfile -p MOVE_`
	TMP5=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2 $TMP3 $TMP4 $TMP5' EXIT

	sed -n '
		\:/_[^/]*$: {
			w '$TMP1'
			d
		}
		/\.dsc$/ {
			s@.*/\([^_]*\)_\([^_].*\(:\|%3a\)\)\?\(.*\)\.dsc$@\1	\4	\1_\4.dsc	&	source@
			w '$TMP4'
			d
		}
		/\.deb$/ {
			s@.*/\([^_]*\)_\([^_].*\(:\|%3a\)\)\?\([^_]*\)\(_[^.]*\)\?\.deb$@\1	\4	\1_\4\6.deb	&	binary-@
			p
			d
		}
		w '$TMP1'
		d
	' | sort -k 1,1 > $TMP3
	sort -k 1,1 $TMP4 -o $TMP4

	info "Skipping files:"
	cat $TMP1
	join -o 1.4 -v 1 $TMP3 .apt-move/$DIST.binary >&2
	join -o 1.4 -v 1 $TMP4 .apt-move/$DIST.source >&2

	join -e _ -o 1.5,2.2,2.3,1.1,1.2,1.3,2.4,2.5 \
	     .apt-move/$DIST.binary $TMP3 | checkversion $TMP2
	join -e _ -o 1.4,2.2,2.3,1.1,1.2,1.3,2.4,2.5 \
	     .apt-move/$DIST.source $TMP4 | checkversion $TMP3

	info "Moving files:"
	cut -d " " -f 5 $TMP2 $TMP3 >&2

	if [ $TEST ] || { [ ! -s $TMP2 ] && [ ! -s $TMP3 ]; }; then
		return 0
	fi

	rm -rf .archive
	mkdir .archive || return 5

	echo | cat $TMP2 - $TMP3 > $TMP1

	cut -d " " -f 4,6 $TMP1 | pkgdir | paste -d / - $TMP1 |
		cut -d " " -f 1,2,5 | $MOVE1 $TMP4 $DIST |
			{ cd .archive; $MOVE3; } | sort -u | xargs -r rm
	[ $(id -u) -eq 0 ] && chown root.root .archive 2> /dev/null &&
		chown -R root:root .archive

	< $TMP2 cut -d " " -f 2-4 | uniq > $TMP1
	< $TMP3 cut -d " " -f 2-4 | uniq > $TMP2
	{
		cd .archive
		< $TMP4 xargs -r -i dpkg-scanpackages {} $TMP1 2> $TMP5
		< $TMP4 xargs -r -i dpkg-scansources {} $TMP2 2>> $TMP5
		sed '/!!/ { N; d; }; /^ Wrote /d' $TMP5 >&2
	} | $MOVE2 "$ARCHS" | $MOVE3
); }

domove() {
	tomove=$(echo $FILECACHE/*.deb)
	if [ "$tomove" = "$FILECACHE/*.deb" ]; then
		return 0
	fi
	echo $tomove | tr " " "\n" | movefiles
}

dofsck() { (
	info
	info "Rebuilding index files..."
	[ $TEST ] && return 0
	# make sure target directory exists.
	[ -d .apt-move ] || install -d .apt-move || return 5

	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	TMP3=`tempfile -p MOVE_`
	TMP4=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2 $TMP3 $TMP4' EXIT

	for dist in $(cd dists; find * -maxdepth 0 -type d); do
		rm -rf .archive
		mkdir .archive || return 5
		touch .apt-move/$dist.binary
		cut -f -3 .apt-move/$dist.binary > $TMP2
		if [ $GET_BINARY ] && [ ! -s $TMP2 ]; then
			> $TMP3
			> $TMP4
			for arch in $ARCHS; do
				find dists/$dist \
				     -path "*/binary-$arch/Packages" -o \
				     -path "*/binary-$arch/Packages.gz" |
					tee -a $TMP4 | xargs -r zcat |
					createbinary |
					sort -k 1,1 -um $TMP3 - -o $TMP3
			done
			< $TMP4 xargs -r rm -f
			cut -f -3 $TMP3 > $TMP2
		fi

		touch .apt-move/$dist.source
		cut -f -3 .apt-move/$dist.source > $TMP3
		if [ $GET_SOURCE ] && [ ! -s $TMP3 ]; then
			find dists/$dist -path "*/source/Sources" -o \
			     -path "*/source/Sources.gz" | tee $TMP4 |
				xargs zcat | createsource > $TMP3
			< $TMP4 xargs -r rm -f
		fi

		find dists/$dist -name .index | xargs -r rm -r
		find dists/$dist -type d -links 2 |
			sed 's:/binary-[^/]*\(/\|$\):/binary-\1:' | sort -u |
			tee .apt-move/$dist.dirs |
			awk 'BEGIN { split("all '"$ARCHS"'", archs); }
			     /\/source(\/|$)/ { print; next }
			     /\/binary-\// { for (i in archs) { dir = $0
					     sub(/\/binary-\//,
						 "/binary-" archs[i] "/", dir)
					     print dir } next }
			     { for (i in archs) { dir = $0;
			       sub(/\/binary-$/, "/binary-" archs[i], dir)
			       print dir } }
			    ' | {
				tee $TMP1
				sed '\:/binary-all\(/\|$\):d
				     s:/\(binary-[^/]*\|source\)\(/.*\)\?$:/\1/.index:
				    ' $TMP1 | sort -u
			} | xargs mkdir -p || return 5
		{
			[ $GET_BINARY ] &&
				find dists/$dist -name '*.deb' -type f |
				sed 's@\(.*/binary-\)[^/]*\(/.*\)\?/\([^_]*\)_\([^_]*\(:\|%3a\)\)\?\([^_]*\)\(_[^.]*\)\?\(.*\)@\1\2/\3_\6\8 \3 '$LOCALDIR'/&@' |
				sort -k 2,2
			echo
			[ $GET_SOURCE ] &&
				find dists/$dist -name '*.dsc' -type f |
				sed 's@\(.*/\([^_]*\)_\)\([^_]*\(:\|%3a\)\)\?\(.*\)@\1\5 \2 '$LOCALDIR'/&@' |
				sort -k 2,2
		} | $MOVE1 $TMP1 $dist | sed 's/^[mn]/h/' |
			{ cd .archive; $MOVE3; }
		{
			cd .archive
			< $TMP1 xargs -r -i dpkg-scanpackages {} $TMP2 \
				2> $TMP4
			< $TMP1 xargs -r -i dpkg-scansources {} $TMP3 \
				2>> $TMP4
			sed '/!!/ { N; d; }; /^ Wrote /d' $TMP4 >&2
		} | $MOVE2 "$ARCHS" | $MOVE3
	done
); }

dodeleteobsoleteind() { (
	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2' EXIT

	sed 's:.*/\(.*\):& \1:' $TMPLOC | sort -k 2 | {
			if [ -f $1 ]; then
				tee $TMP2 2> /dev/null |
					join -v 1 -j1 2 -o 1.1 - $1
				join -j1 2 -o 1.1,1.2 $TMP2 $1 |
					uniq -f 1 -D > $TMP1
			else
				uniq -f 1 -D > $TMP1
			fi
		}
	cut -d " " -f 1 $TMP1 | xargs cat | sed -n 's/^version: //pI' |
		paste $TMP1 - | awk '
			function newer(a, b) {
				return system("dpkg --compare-versions " a \
					      " le \"" b "\"")
			}
			pkg == $2 {
				if (newer(ver, $3)) {
					print $1
				} else {
					print file
					file = $1
					ver = $3
				}
				next
			}
			{
				file = $1
				pkg = $2
				ver = $3
			}
		'
); }

dodeleteobsoletepkg() { (
	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	TMP3=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2 $TMP3' EXIT

	sed 's:\(.*/'$1'\)\(/.*\)\?/\(.*\)_.*:\1/.index/\3 &:' $TMPLOC |
		sort -k 1,1 | tee $TMP1 2> /dev/null | join - $TMPDELI |
		cut -d " " -f 2
	join -v 1 $TMP1 $TMPDELI | tee $TMP2 | cut -d " " -f 1 | uniq |
		xargs grep -s -H "" |
		awk '/:[Vv][Ee][Rr][Ss][Ii][Oo][Nn]:/ {
			sub(/.*\//, "")
			sub(/:[Vv][Ee][Rr][Ss][Ii][Oo][Nn]: ([0-9]+:)?/, "_")
			print
		}' |
#		sed -n 's%.*/\(.*\):version: \([[:digit:]]\+:\)\?%\1_%pI' |
		sort > $TMP3
	sed 's:.* \(.*/\(.*\)\.\(deb\|dsc\)\)$:\2 \1:' $TMP2 | sort -k 1,1 | {
			if ! [ ${1##binary-*} ]; then
				tee $TMP1 | join -o 1.2 - $TMP3 >&3
				join -v 1 -o 1.2 $TMP1 $TMP3
			else
				join -v 1 -o 1.2 - $TMP3
			fi
		}
); }

expanddsc() {
	xargs awk '
		!start || FNR == 1 || !/^ / {
			start = tolower($0) ~ /^files:/
			if (start) {
				print FILENAME
				dir = FILENAME
				sub(/[^\/]*$/, "", dir)
			}
			next
		}

		start {
			print dir $3
		}
	'
}

dodeleteobsoletesrc() { (
	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2' EXIT

	dodeleteobsoletepkg source | sort > $TMP2
	sort $TMPLOC | join -v 1 - $TMP2 | expanddsc | sort > $TMP1
	< $TMP2 expanddsc | sort -u | comm -23 - $TMP1
); }

dodeleteall() { (
	TMP1=`tempfile -p MOVE_`
	trap 'rm -f $TMP1' EXIT

	< $TMPLOC xargs -r -n 1 readlink -f | paste - $TMPLOC | sort -k 1,1 \
		> $TMP1
	< $1 xargs -r -n 1 readlink -f | sort -u | join -v 2 -o 2.2 - $TMP1
); }

dodeletes() { (
	info
	if [ "$DELETE" != "yes" ]; then
		info "File deletes disabled, skipping."
		return 0
	fi
	info "Removing obsolete packages..."

	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2' EXIT

	makeexcl

	# list all .deb files in the mirror
	loccount=0
	if [ $GET_BINARY ]; then
		for dist in $(cd dists; find * -maxdepth 0 -type d); do
			> $TMP1
			for arch in $ARCHS; do
				dirlist=$(nice find dists/$dist/ \
					  -type d -name binary-$arch -print)
				[ -n "$dirlist" ] || continue
				nice find $dirlist -path "*/.index/*" -print \
					> $TMPLOC
				loccount=$(($loccount + $(wc -l < $TMPLOC)))
				if [ -s $TMPEXCL ]; then
					join \
						.apt-move/$DIST.arch.$arch \
						.apt-move/$DIST.binary |
						listbin | grep -v -f $TMPEXCL |
						sed 's:.*/\(.*\)_\*$:\1:' \
						> $TMP2
					dodeleteobsoleteind $TMP2
				else
					dodeleteobsoleteind \
						.apt-move/$DIST.arch.$arch
				fi | tee -a $TMPDEL | sort > $TMPDELI
				nice find $dirlist -name "*.deb" -print \
					> $TMPLOC
				loccount=$(($loccount + $(wc -l < $TMPLOC)))
				dodeleteobsoletepkg binary-$arch >> $TMPDEL \
					3>> $TMP1
			done
			dirlist=$(nice find dists/$dist/ \
				  -type d -name binary-all -print)
			[ -n "$dirlist" ] || continue
			nice find $dirlist -name "*.deb" -print | sort \
				> $TMPLOC
			dodeleteall $TMP1 >> $TMPDEL
		done
	fi
	if [ $GET_SOURCE ]; then
		for dist in $(cd dists; find * -maxdepth 0 -type d); do
			dirlist=$(nice find dists/$dist/ \
				  -type d -name source -print)
			[ -n "$dirlist" ] || continue
			nice find $dirlist -path "*/.index/*" -print > $TMPLOC
			loccount=$(($loccount + $(wc -l < $TMPLOC)))
			if [ -s $TMPEXCL ]; then
				listsrcmirror |
					sed 's:.*/\(.*\)_\*$:\1:' > $TMP2
				dodeleteobsoleteind $TMP2
			else
				dodeleteobsoleteind .apt-move/$dist.source
			fi | tee -a $TMPDEL | sort > $TMPDELI
			nice find $dirlist -name "*.dsc" -print > $TMPLOC
			loccount=$(($loccount+`wc -l < $TMPLOC`))
			dodeleteobsoletesrc >> $TMPDEL
		done
	fi
	# I do this just because a bug caused there to be a total of 0 files
	# in the distribution, and bc had a heart-attack.
	[ $loccount -eq 0 ] && return 0
	delcount=`wc -l < $TMPDEL`
	# check the results
	case `echo "scale=2; (($delcount/$loccount)*100) <= $MAXDELETE" | bc` in
	0)
		[ -z "$FORCE" ] && return 13
		info
		info "Too many files, but FORCE used... deleting anyway!"
		;;
	1) ;;
	*) return 12 ;;
	esac
	# we are either ok, or forced...
	for path in `cat $TMPDEL`; do
		info "removing:  $path"
		[ $TEST ] || rm -f $path
	done
); }

listbin() { (
	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2' EXIT

	join -o 2.1,2.3 .apt-move/$DIST.arch.$ARCH - |
		tee $TMP1 | sed 's/^\([^[:blank:]]*\)[[:blank:]].*/\1_*/' \
		> $TMP2
	sed 's/.* \(.*\)$/\1 binary-'$ARCH'/' $TMP1 | pkgdir |
		paste -d / - $TMP2
); }

listsrc() { (
	TMP1=`tempfile -p MOVE_`
	TMP2=`tempfile -p MOVE_`
	trap 'rm -f $TMP1 $TMP2' EXIT

	tee $TMP1 | sed 's/^\([^[:blank:]]*\)[[:blank:]].*/\1_*/' > $TMP2
	sed 's/.*[[:blank:]]\(.*\)/\1 source/' $TMP1 | pkgdir |
		paste -d / - $TMP2
); }

excluded() { (
	TMP1=`tempfile -p MOVE_`
	trap 'rm -f $TMP1' EXIT

	# print list of excluded files
	info "The following files are EXCLUDED from the mirror:"
	# if a $LOCALDIR/.exclude file exists, also find the files that
	# match one of the exclude patterns in that file.
	if [ -f $LOCALDIR/.exclude ]; then
		makeexcl
		{
			if [ $GET_BINARY ]; then
				< .apt-move/$DIST.binary listbin
			fi
			if [ $GET_SOURCE ]; then
				< .apt-move/$DIST.source listsrc
			fi
		} | grep -f $TMPEXCL | sort
	fi
); }

dopackages() {
	info
	# build our own local Packages.gz files
	# paths stored in packages files are relative to debian/
	if [ $GET_BINARY ]; then
		info "Creating Packages.gz files..." 
		for dist in $(cd dists; find * -maxdepth 0 -type d); do
			for sect in $(nice find dists/$dist \
				      -type d -name binary-\* \
				      ! -name binary-all -print); do
				info "Building: $dist $sect Packages.gz"
				[ $TEST ] && continue
				echo $sect/.index/* | tr " " "\n" |
					xargs cat 2> /dev/null | gzip -f \
					> $sect/Packages.gz
			done
		done
	fi

	if [ $GET_SOURCE ]; then
		info "Creating Sources.gz files..."
		for dist in $(cd dists; find * -maxdepth 0 -type d); do
			for sect in $(nice find dists/$dist \
				      -type d -name source -print); do
				info "Building: $dist $sect Sources.gz"
				[ $TEST ] && continue;
				echo $sect/.index/* | tr " " "\n" |
					xargs cat 2> /dev/null | gzip -f \
					> $sect/Sources.gz
			done
		done
	fi
}

procbin() {
	sed 's:.*/\(.*\)_\*$:\1:' | {
		if [ $TEST ]; then
			xargs -r $FETCH -t
			return 0
		fi
		xargs -r $FETCH
	}
	domove
}

procsrc() {
	sed 's:.*/\(.*\)_\*$:\1:' | {
		if [ $TEST ]; then
			xargs -r apt-get --print-uris source
			return 0
		fi
		xargs -r apt-get -d source
	}
	tomove=$(find `pwd` -maxdepth 1 -name "*.dsc" -type f)
	if [ -n "$tomove" ]; then
		echo $tomove | tr " " "\n" | movefiles
	fi
}

runmirror() {
	# grab copy of all non-excluded files for the configured distribution.
	info
	info "Updating mirror..."

	makeexcl
	listbinmirror | procbin
	listsrcmirror | procsrc
}

runsync() {
	# grab copy of all the packages you currently have installed
	info
	info "Syncing mirror..."

	makeexcl
	makeselect
	listbinsync | procbin
	listsrcsync | procsrc
}

listbinmirror() {
	[ $GET_BINARY ] || return
	< .apt-move/$DIST.binary listbin | grep -v -f $TMPEXCL
}

listsrcmirror() {
	[ $GET_SOURCE ] || return
	< .apt-move/$DIST.source listsrc | grep -v -f $TMPEXCL
}

listbinsync() {
	[ $GET_BINARY ] || return
	join $TMPSELECT .apt-move/$DIST.binary | listbin | grep -v -f $TMPEXCL
}

listsrcsync() {
	[ $GET_SOURCE ] || return
	join -o 2.4 $TMPSELECT .apt-move/$DIST.binary | sort -u |
		join - .apt-move/$DIST.source | listsrc | grep -v -f $TMPEXCL
}

listbinrepo() {
	[ $GET_BINARY ] || return
	find dists/$DIST -path "*/binary-$ARCH/.index/*" -printf '%f\n' |
		sort -u | join - .apt-move/$DIST.binary | listbin |
		grep -v -f $TMPEXCL
}

listsrcrepo() {
	[ $GET_SOURCE ] || return
	find dists/$DIST -path '*/source/.index/*' -printf '%f\n' | sort -u |
		join - .apt-move/$DIST.source | listsrc | grep -v -f $TMPEXCL
}

makeexcl() {
	[ $TMPEXCL ] && return
	TMPEXCL=`tempfile -p MOVE_`
	TMPLIST="$TMPLIST $TMPEXCL"
	if [ -f $LOCALDIR/.exclude ]; then
		sed '/^[#;]/d; /^$/d; s/\*//g' $LOCALDIR/.exclude
	fi > $TMPEXCL
}

makeselect() {
	[ $TMPSELECT ] && return
	TMPSELECT=`tempfile -p MOVE_`
	TMPLIST="$TMPLIST $TMPSELECT"
	(
		TMP1=`tempfile -p MOVE_`
		trap 'rm -f $TMP1' EXIT
		dpkg --get-selections | sort -b -k 2,2 > $TMP1
		echo install | join -j2 2 -o 2.1 - $TMP1 > $TMPSELECT
	)
}

printerr() {
	# print error message and exit with appropriate status
	case "$2" in
	1)
		# this is used by the 'usage' function... should get none of
		# these here.
		;;
	4)
		info "Error: $1: You must specify at least one APTSITES in"
		info "           /etc/apt-move.conf."
		;;
	5)
		info "Error: $1: Could not create directory.  Aborting script."
		;;
	6)
		info "Error: $1: You failed to select a distribution.  Check"
		info "       the DIST setting in /etc/apt-move.conf. Aborting"
		info "       script."
		;;
	12)
		info "Error: $1: bc calculation returned invalid result"
		;;
	13)
		info "Error: $1: too many files to delete!  Your current limit"
		info "       is set to $MAXDELETE%.  To change that, see the"
		info "       MAXDELETE setting in /etc/apt-move.conf.  You can"
		info "       override this safety using the 'force' parameter,"
		info "       but be careful!  Aborting script."
		;;
	18)
		info "Error: $1: You specified an invalid package type."
		info "       See the PKGTYPE setting in /etc/apt-move.conf."
		info "       Your current setting is:"
		info "           PKGTYPE=$PKGTYPE"
		info "       Aborting script."
		;;
	20)
		info "Terminated by SIGINT (^C)..."
		;;
	23)
		info "Error: $1: Your current mirror directory is incompatible"
		info "       with this version of apt-move.  Please read"
		info "       /usr/share/doc/apt-move/README[.gz] for"
		info "       instructions on updating your mirror directory."
		;;
	24)
		info "Error: $1: No master files exist!"
		info "       You need to run apt-move get."
		;;
	*)
		info "Unknown error: $1: $2."
		;;
	esac
	exit $2
}

getout() {
    # called when one of the main functions returns an error.
    # display the appropriate message
    # $1 should be the function name, and $2 should be the error code
    printerr $1 $2 >&2
}

# START main program logic -----------------------------------------------

umask 022

CONFFILE=/etc/apt-move.conf

# read the options
while getopts "c:ft" flag; do
	case $flag in
	c)
		CONFFILE="$OPTARG"
		[ -n "${CONFFILE##/*}" ] && CONFFILE="$ORIGDIR/$CONFFILE"
		;;
	t)
		TEST=yes
		;;
	f)
		FORCE=yes
		;;
	*)
		showusage
		;;
	esac
done
shift $(($OPTIND - 1))

# source the conffile
if [ ! -f "$CONFFILE" ]; then
	info "Could not read configuration. Aborting." >&2
	exit 1
fi

. "$CONFFILE"

# check $DIST
[ -z "$DIST" ] && getout apt-move 6
# check $PKGTYPE
case "$PKGTYPE" in
binary)
	GET_BINARY=yes
	;;
source)
	GET_SOURCE=yes
	;;
both)
	GET_BINARY=yes
	GET_SOURCE=yes
	;;
*)
	getout apt-move 18
esac
# check the APTSITES (it's not optional)
[ -z "$APTSITES" ] && getout apt-move 4
# if nothing on command line, 
[ -z "$1" ] && showusage

# set the working directory - create it if required
[ -d $LOCALDIR ] || install -d $LOCALDIR || getout apt-move 5
cd $LOCALDIR

# setup the ^C handler
trap "getout apt-move 20" SIGINT

[ -f .apt-move/ancient ] && [ "$1" != fsck ] && getout apt-move 23

# Put together the basic functions for each option
case "$1" in
get) FUNCT="getfiles";;
getlocal) FUNCT="getfiles";;
move) FUNCT="checklist domove";;
delete) FUNCT="checklist dodeletes";;
packages) FUNCT="dopackages";;
update) FUNCT="getfiles domove dodeletes dopackages" ;;
local) FUNCT="checklist domove dodeletes dopackages";;
localupdate) FUNCT="getfiles domove dodeletes dopackages";;
mirror) FUNCT="getfiles runmirror dodeletes dopackages";;
sync) FUNCT="getfiles runsync dodeletes dopackages";;
exclude) FUNCT="checklist excluded";;
fsck) FUNCT="dofsck";;
mirrorbin) FUNCT="getfiles procbin dodeletes dopackages";;
mirrorsrc) FUNCT="getfiles procsrc dodeletes dopackages";;

movefile)
	shift
	if [ -z "$*" ]; then
		showusage
	fi
	checklist || getout "movefile" "$?"
	echo $* | tr " " "\n" | sed "/^[^/]/s:^:$ORIGDIR/:" | movefiles ||
		getout "movefile" "$?"

	;;

listbin | listsrc)
	case "$2" in
	mirror | repo)
		;;
	sync)
		makeselect
		;;
	*)
		showusage
		;;
	esac
	makeexcl
	$1$2 | sed 's:.*/\(.*\)_\*$:\1:' 
	;;

*)
	showusage
	;;
esac
if [ -n "$FUNCT" ]; then
	if [ $# -gt 1 ]; then
		showusage
	fi
	for funct in $FUNCT; do
		$funct || getout $funct $?
	done
fi
#
# normal exit
# done (I think)
info
info "All done, exiting." 
exit 0
