#!/bin/bash
# I want to use -e switch but it doesn't always work and I'm not sure why!

# tpkg - transport package

# Copyright (c) Sam Watkins, 2005, 2009
#     license: GPL - http://www.gnu.org/licenses/gpl.txt

# This program recursively uncompresses and untars the named files /
# archives, and recompresses them with tar to produce a "transport archive",
# suitable for efficient transport using rsync.  After unpacking the tpkg using
# tar, run tunpkg.$PACKAGE.sh to recreate the original package more or less.

# TODO add support for .iso .nrg (fuseiso) .cab (cabextract) etc.  .odt etc?

PROG=`basename "$0"`

if [ "$#" = 0 ]; then
	echo "syntax: $PROG output-file input-file ..." >&2
	echo "        TPKG_ZIP=\"gzip -9\" $PROG out in ..."
	echo "        TPKG_ZIP=gzr $PROG out in ..."
	echo "        TPKG_ZIP=f7z $PROG out in ..."
	exit 1
fi

if [ ! "`id -u`" = 0 ]; then
	exec fakeroot -- "$0" "$@"
fi

f7z() {
	O=`temp -s .7z`
	rm "$O"
	7z a -si "$@" "$O" /dev/stdin
	cat "$O"
	rm "$O"
}

gzr() {
	gzip -9 --rsyncable -n
}

uniqname() {
	local name="$1" suffix="$2" N=1 name

	while [ -e "$name$suffix" ]; do
		name="$name.$N"
	done
	echo $name$suffix
}

dt() {
	date +%Y.%m.%d\"%T "$@"
}

temp() {
	local opt dir suffix name
	while getopts ds: opt
	do
		if [ "$opt" = d ]; then dir=1; fi
		if [ "$opt" = s ]; then suffix="$OPTARG"; fi
	done
	shift $(( $OPTIND - 1 ))

	while true; do
		name=/tmp/temp.`dt`,$$,$(($RANDOM + $RANDOM * 32768))$suffix
		if [ ! -e "$name" ]; then break; fi
	done
	umask 0077
	if [ -n "$dir" ]; then
		mkdir $name
	else
		> $name
	fi
	echo $name
}

p() {
	if [ -e "$1" ]; then
		readlink -f -- "$1"
	else
		echo $(p "`dirname -- "$1"`")/"`basename -- "$1"`"
	fi
}

TPKG="$1"
shift

if [ -e "$TPKG" ]; then
	echo >&2 "$PROG: tpkg $TPKG already exists"
	for F; do
		if [ "$TPKG" -ot "$F" ]; then
			echo >&2 "  tpkg is older than source file $F, overwriting"
			break
		fi
		echo >&2 "  tpkg is newer than source files "$@", leaving it alone"
		exit 0
	done
fi

z="" # "-9"
TPKG=`p "$TPKG"`
PKG="${TPKG##*/}" ; PKG="${PKG%.*}"

DIR=`temp -d`
TUNPKGSH="tunpkg.${PKG}.sh"
TUNPKGSH_TMP=`temp`
LIST=`temp`
LIST1=`temp`

for F; do
	cp -a "$F" "$DIR"
done
cd "$DIR"

add_to_list() {
	echo "$D" >>"$LIST1"
}

add_to_list_rec() {
	find "$D" \! -type d | sort >>"$LIST1"
}

map() {
	echo "extracting $F to ${F%$1}$2" >&2
	map1 "$@"
}

shesc() {
	perl -e "`cat <<'End'
$arg = shift;
$arg =~ s/'/'\\''/sg;
print "'$arg'";
End
`" "$@"
}

map1() {
	D=`uniqname "${F%$1}" "$2"`
	FE="`shesc "$F"`"
	DE="`shesc "$D"`"
	RFE='"$ROOT"'/"$FE"
}

move() {
	echo "moving $1 to $2" >&2
	map1 $1 $2
	mv -- "$F" "$D"
	# inverse process
	echo mv -- "$DE" "$FE"
	add_to_list
}

move_away() {
	if [ -e "$1" ]; then
		echo "moving $1 away" >&2
		move -v "$1" "$1.$$.moved"
	fi
}

fixperms() {
	# Cope with fakeroot not wrapping open(2) or creat(2) and making unreadable files.
	# Apparently "find" can't find specifically unreadable files.
	chmod -R o=o "$@"
}

zimage_extract() {
	perl -e "`cat <<'End'
$zimage = join "", <>;
# this is what you might call DODGY!
($boot, $kernelgz) = $zimage =~ /^(.*?)(\x1f\x8b......\x02.*)\z/s;
defined $kernelgz or
	die "can't grok - is this a [b]zImage?\n";
open OUT, ">boot"; print OUT $boot; close OUT;
open OUT, ">kernel.gz"; print OUT $kernelgz; close OUT;
End
`"
	gunzip -N kernel.gz
}

FIXPERMS="chmod -R o=o"

{
	move_away "$TUNPKGSH"
	
	D=. add_to_list_rec

	while [ -s "$LIST1" ]; do
		mv "$LIST1" "$LIST"
		>"$LIST1"
		while read F; do
			case "$F" in
			# equivalents
			*.jar)	move .jar .zip ;;
			*.udeb)	move .udeb .deb ;;
			*.tgz)	move .tgz .tar.gz ;;
			*.tbz2)	move .tbz2 .tar.bz2 ;;
			*.gz)
				map .gz ""
				gunzip -c -- "$F" >"$D"
				rm -f "$F"
				{
				echo gzip -c $z -- "$DE" ">$FE"
				echo rm -f "$DE"
				} | tac
				add_to_list
				;;
			*.bz2)
				map .bz2 ""
				bunzip2 -c -- "$F" >"$D"
				rm -f "$F"
				{
				echo bzip2 -c $z -- "$DE" ">$FE"
				echo rm -f "$DE"
				} | tac
				add_to_list
				;;
			*.lzma)
				map .lzma ""
				lzma -d -c -- "$F" >"$D"
				rm -f "$F"
				{
				echo lzma -z -c $z -- "$DE" ">$FE"
				echo rm -f "$DE"
				} | tac
				add_to_list
				;;
			*.tar)
				map .tar ""
				mkdir "$D"
				tar -x -f "$F" -C "$D"
				fixperms "$D"
				rm -f "$F"
				{
				echo $FIXPERMS "$DE"
				echo tar -c -f "$FE" -C "$DE" .
				echo rm -rf "$DE"
				} | tac
				add_to_list_rec
				;;
			*.zip)
				map .zip ""
				unzip -qq -d "$D" -- "$F"
				fixperms "$D"
				rm -f "$F"
				{
				echo $FIXPERMS "$DE"
				echo cd "$DE"
				echo zip -q -r $z "$RFE" .
#				echo 7z a -tzip -r -mx=9 "$RFE" .
				echo cd '"$ROOT"'
				echo rm -rf "$DE"
				} | tac
				add_to_list_rec
				;;
			*.deb)
				map .deb ""
				FILES=$(ar t "$F")
				mkdir "$D"
				cd "$D"
				ar x "$DIR"/"$F"
				cd "$DIR"
				rm -f "$F"
				{
				echo cd "$DE"
				echo ar qcS "$RFE" $FILES
				echo cd '"$ROOT"'
				echo rm -rf "$DE"
				} | tac
				add_to_list_rec
				;;
			*/vmlinuz*)
				map "" .d
				mkdir "$D"
				cd "$D"
				zimage_extract < "$DIR"/"$F"
				cd "$DIR"
				rm -f "$F"
				{
				echo cd "$DE"
				echo mv boot "$RFE"
				echo \< kernel gzip -9 -N \>\> "$RFE"
				echo cd '"$ROOT"'
				echo rm -rf "$DE"
				} | tac
				;;
			*.dz)
				map .dz ""
				dictunzip -c -- "$F" >"$D"
				rm -f "$F"
				{
				# this is a dodgy: can't specify the output filename, could clobber something
				echo dictzip "$DE"
				if [ "$D".dz != "$F" ]; then
					echo mv "$DE".dz "$FE"
				fi
				} | tac
				;;
			esac
		done < "$LIST"
	done

	rm "$LIST" "$LIST1"
	{
	echo "#!/bin/sh -e"
	echo 'ROOT=`pwd`'
	echo "rm $TUNPKGSH"
	} | tac

} | tac > "$TUNPKGSH_TMP"

mv "$TUNPKGSH_TMP" "$TUNPKGSH"
chmod +x "$TUNPKGSH"

fdupes -q -r . | while read A; do while read B; do if [ -z "$B" ]; then break; fi; ln -f "$A" "$B"; done; done

if [ -n "$TPKG_ZIP" ]; then
	find . \! -type d | LANG=C sort | tar -T- -c | $TPKG_ZIP >"$TPKG"
else
	find . \! -type d | LANG=C sort | tar -T- -c >"$TPKG"
fi

cd /
rm -rf "$DIR"
