#!/bin/sh
# vim:ts=4:ft=sh
#-----------------------------------------------------------------------
# SuperPaco - installs Debian, RPM or Slackware binary packages without
#             dpkg, RPM or pkgtool; and optionally logs them with paco.
#
# To install RPM packages, cpio and (rpm2cpio or rpmunpack) are required.
#-----------------------------------------------------------------------
# AUTHOR
#--------
# SuperPaco is part of the package paco
# Copyright (C) 2005-2009 David Rosal
# For more information visit http://paco.sourceforge.net
#-----------------------------------------------------------------------

me="superpaco"

if [ -n "$ZSH_VERSION" ]; then
	emulate sh >/dev/null 2>&1
elif [ -n "$BASH_VERSION" ]; then
	set -o posix >/dev/null 2>&1
fi

#---------------------------------------#
# Initialize and parse the command line #
#---------------------------------------#

# Remove tmp stuff on exit
trap 'rm -rf $tmpdir' 0

# Defaults
ROOT=/
QUIET=no
STRIP=no
PACKAGES=
USE_PACO=yes

# Parse the command line
while [ -n "$1" ]; do
	case $1 in
	
		-n|--no-paco)
			USE_PACO=no;;
			
		-q|--quiet)
			QUIET=yes;;
		
		-s|--strip-release|--strip)
			STRIP=yes;;
		
		-v|--version)
			( paco --version | sed "s@^paco@$me@" ) || echo "$me (unknown version)"
			exit 0;;
			
		-h|--help)
cat << EOF
$me - installs Debian, RPM or Slackware binary packages with paco.

Usage:
  $me [OPTIONS] <packages>

Options:  [default values listed in brackets]
  -h, --help           Display this usage message and exit.
  -v, --version        Display version information and exit.
      --root=PATH      Use PATH as the root directory  [$ROOT].
  -n, --no-paco        Do not use paco to log the installation.
  -q, --quiet          Be silent.
  -s, --strip-release  Remove the release suffix from the name of the package
                        (e.g. 'foo-1.0.2-2.1' --> 'foo-1.0.2').

Note:
  To install rpm packages, cpio and ( rpm2cpio or rpmunpack ) are required.

EOF
			exit 0;;
		
		--root|--root=*)
			ROOT=`expr $1 : '[^=]*=\(.*\)'` || {
				echo "$me: Option '$1' requires an argument" >&2
				echo "Try '$me --help' for more information" >&2
				exit 1
			};;
			
		-*)
			echo "$me: Unrecognized option: '$1'" >&2
			echo "Try '$me --help' for more information" >&2
			exit 1;;
		
		*)	# Non option argument = Name of a package
			PACKAGES="$PACKAGES $1";;
	esac
	shift
done


[ $QUIET = yes ] && exec 6>/dev/null || exec 6>&1

# Absolutize and remove repeated package names
if [ -n "$PACKAGES" ]; then
	__PACKAGES=""
	for pkg in $PACKAGES; do
		expr $pkg : / >/dev/null || pkg=`pwd`/$pkg
		pkg=`expr $pkg : '\(.*[^/]\)' '|' '/' | tr -s '/'`
		__PACKAGES="$__PACKAGES $pkg"
	done
	PACKAGES=`for pkg in $__PACKAGES; do echo $pkg; done | sort -u`
else
	echo "$me: No input packages" >&2
	echo "Try '$me --help' for more information" >&2
	exit 1
fi


# "Absolutize" ROOT, and remove trailing and duplicate slashes
expr "$ROOT" : '/' >/dev/null || ROOT=`pwd`/$ROOT
ROOT=`expr "$ROOT" : '\(.*[^/]\)' '|' '/' | tr -s '/'`

if [ -f $ROOT ]; then
	echo "$me: $ROOT: Not a directory" >&2
	exit 1
fi

# Tmp dir to store intermediate files
tmpdir=`mktemp -dt ${me}XXXXXX 2>/dev/null || echo /tmp/$me.$$`
rm -rf $tmpdir
install -d $tmpdir

cpio --version >/dev/null 2>&1 && have_cpio=yes || have_cpio=no
type rpm2cpio >/dev/null 2>&1 && have_rpm2cpio=yes || have_rpm2cpio=no
type rpmunpack >/dev/null 2>&1 && have_rpmunpack=yes || have_rpmunpack=no

if [ "$USE_PACO" = yes ]; then
	type paco >/dev/null 2>&1 || {
		echo "$me: Cannot find the paco program" >&2
		exit 1
	}
fi
			
# Process the packages
for pkg in $PACKAGES; do

	# Check that the package exists and it's a regular file
	[ -f $pkg ] || {
		echo "$me: $pkg: No such regular file" >&2
		continue
	}
	
	printf "Processing $pkg: " >&6
	
	# Get the type of the package and the basename. Strip suffixes.
	# In Debian packages, also strip the architecture suffix and convert
	# underscores ('_') to hyphens ('-').
	
	case "`file $pkg`" in
	
		*\.deb:\ Debian\ binary\ package*)
			pkg_type=Debian
			name=`basename $pkg .deb | sed 's@_[a-zA-Z].*@@; y@_@-@'`
			;;
			
		*\.rpm:\ RPM*\ src\ *)
			echo
			echo "$me can't install source RPM packages (only binary)" >&2
			exit 1
			;;
			
		*\.rpm:\ RPM*\ bin\ *)
			# RPM packages require cpio and rpm2cpio or rpmunpack.
			if [ $have_cpio = no ]; then
				echo
				echo "*** Can't find cpio program" >&2
				echo "*** cpio is required to install RPM packages" >&2
				continue
			elif [ $have_rpm2cpio = no -a $have_rpmunpack = no ]; then
				echo
				echo "*** Can't find rpm2cpio or rpmunpack program" >&2
				echo "*** rpm2cpio or rpmunpack is required to install RPM packages" >&2
				continue
			fi
			pkg_type=RPM
			name=`basename $pkg .rpm`
			;;
			
		*\.tgz:\ gzip\ compressed\ data*)
			pkg_type=Slackware
			name=`basename $pkg .tgz`
			;;
			
		*)
			echo "Unsupported format" >&2
			continue
			;;
	esac
	
	echo "$pkg_type package" >&6
	
	# Strip the release suffix, if needed.
	if [ $STRIP = yes ]; then
		name=`echo $name | awk '{ 						\
			out = $0;									\
			cnt = gsub(/-[0-9][^-]*/, "&");				\
			if (cnt > 1)  								\
				out = gensub(/-[0-9][^-]*/, "", cnt);	\
			print out;									\
		}'`
	fi

	echo "  Name: $name" >&6
	
	
	#----------------#
	# Debian package #
	#----------------#

	if [ $pkg_type = Debian ]; then
	
		printf "  Converting .deb package to .tar.gz... " >&6
		
		# Extract the control portion
		( ar p $pkg control.tar.gz > $tmpdir/control.tar.gz ) || continue
		( cd $tmpdir && gzip -cd control.tar.gz | tar xf - ) || continue
		
		# Extract the data portion
		( ar p $pkg data.tar.gz > $tmpdir/$name.tar.gz ) || continue
		( gzip -d $tmpdir/$name.tar.gz ) || continue
		
		(
		cd $tmpdir
		
		# Take into account the preinst script
		[ -x preinst ] && cmd="./preinst &&" || cmd=""

		# Install destdir
		[ -d $ROOT ] || install -d $ROOT
		
		if [ $USE_PACO = yes ]; then
			printf "ok\n  Installing the package with paco... " >&6
			# Create an artificial .spec file to be read by paco
			sed -ne "{ \
				/^[Dd]escription:/,/^$/h;		\
				s@^[Pp]ackage:@Name:@p;			\
				/^[Vv]ersion:/p;				\
				s@^[Dd]escription:@Summary:@p;	\
				s@^[Mm]aintainer:@Vendor:@p;	\
				g; {s,^ *,,; s,^.$,,; s,^[Dd]escription:.*,%description,; p} \
			}" control > $name.spec
			paco -lp $name "$cmd tar -C $ROOT -xf $tmpdir/$name.tar"
		else
			printf "ok\n  Installing the package... " >&6
			$cmd tar -C $ROOT -xf $tmpdir/$name.tar
		fi
		
		)
	
	
	#-------------#
	# RPM package #
	#-------------#

	elif [ $pkg_type = RPM ]; then
	
		printf "  Converting the RPM package to cpio... " >&6
		
		(
		cd $tmpdir &&
		
		if [ $have_rpmunpack = yes ]; then
			( rpmunpack < $pkg > $name.cpio.gz ) || continue
			gunzip $name.cpio.gz || continue
		else
			( rpm2cpio $pkg > $name.cpio ) || continue
		fi
		
		printf " ok\n  Extracting files from the cpio package... " >&6
		cpio --extract --make-directories --quiet < $name.cpio
		rm -f $name.cpio
		find . -type d -perm 700 -exec chmod 755 '{}' \;
		
		[ -d $ROOT ] || install -d $ROOT
		
		if [ $USE_PACO = yes ]; then
			printf "ok\n  Installing the package with paco... " >&6
			paco -lp $name "tar -cf - . | tar -C $ROOT/ -xf -"
		else
			printf "ok\n  Installing the package... " >&6
			tar -cf - . | tar -C $ROOT/ -xf -
		fi

		)
	
	#-------------------#
	# Slackware package #
	#-------------------#

	else
		printf "  Converting the .tgz package to .tar... " >&6
		
		base=`basename $pkg`
		cp $pkg $tmpdir/$base || continue
		
		(
		cd $tmpdir &&
		
		( gzip -fcd $base | tar xf - ) || continue
		( tar cf pkg.tar `ls | grep -wv install` ) || continue
		
		aux_cmd=""
		if [ -f install/doinst.sh ]; then
			aux_cmd="&& /bin/sh $tmpdir/install/doinst.sh"
		fi
		if [ -f install/slack-desc ]; then
			echo "%description" > $name.spec
			sed '1,/----/d;s@^[^:]*: *@@' install/slack-desc >> $name.spec
		fi
		[ -d $ROOT ] || install -d $ROOT

		if [ $USE_PACO = yes ]; then
			printf "ok\n  Installing the package with paco... " >&6
			paco -lp $name "tar -C $ROOT -xf $tmpdir/pkg.tar $aux_cmd"
		else
			printf "ok\n  Installing the package... " >&6
			tar -C $ROOT -xf $tmpdir/pkg.tar $aux_cmd
		fi
		
		)
	fi
	
	if [ $? -eq 0 ]; then
		echo ok >&6
	else
		exit 1
	fi

	# Remove tmp files
	rm -rf $tmpdir/*
	
done

# Nice job SuperPaco!
exit 0

