#!/bin/sh
#
# Spk-convert - Convert other distribution's packages to tazpkg form. Read the README before adding or
# modifying any code in spk!
#
# Copyright (C) SliTaz GNU/Linux - BSD License
# Author: See AUTHORS files
#
. /usr/lib/slitaz/libspk.sh

#
# Functions
#

# Help and usage
usage() {
	name=$(basename $0)
	cat << EOT

$(boldify $(gettext "Usage:")) $name [packages|--options]

$(gettext "Convert SliTaz Packages")

$(boldify $(gettext "Options:"))
  --install   $(gettext "Install package after conversion")
  --root=     $(gettext "Set the root file system path")
  --debug     $(gettext "Display some useful debug information")

$(boldify $(gettext "Examples:"))
  $name package1.rpm package2.tgz packageN.ipk

EOT
	exit 0
}

# Move tazpkg pack to shared lib?

# Backslash-escape the chars special inside a double-quoted shell string
# (\ " ` $). The generated receipt is sourced (`. receipt`) by spk-add as
# root, so an unescaped value from a foreign package's metadata — e.g. a .deb
# Description of  "; rm -rf / ; :  — would otherwise execute.
receipt_esc() { printf '%s' "$1" | sed 's/[\\"`$]/\\&/g'; }

# generate a receipt based on standard receipt vars
# Usage: generate_receipt $receipt $base_package
generate_receipt() {
	local receipt=$1
	local base_package=$2
	cat > $receipt <<EOT
# SliTaz package receipt.
# generated by tazpkg from package $(basename "$base_package")
PACKAGE="$(receipt_esc "$PACKAGE")"
VERSION="$(receipt_esc "$VERSION")"
CATEGORY="misc"
SHORT_DESC="$(receipt_esc "$SHORT_DESC")"
WEB_SITE="$(receipt_esc "$WEB_SITE")"
MAINTAINER="$(receipt_esc "$MAINTAINER")"
# NOTE: DEPENDS are NOT auto-detected on conversion — review and complete them.
DEPENDS="$(receipt_esc "$DEPENDS")"
CONFIG_FILES="$(receipt_esc "$CONFIG_FILES")"
EOT
		
}

# Create SliTaz package archive using cpio and lzma.
# TODO: Cook also packs packages, we should share code in libpkg.sh
# Usage: pack $package
pack() {
	local package=$1
	local dir="$2"
	local fs="$dir/fs"
	local receipt="$dir/receipt"
	
	if [ ! -f "$receipt" ]; then
			gettext "Receipt is missing. Please read the documentation."; newline
			exit 1
	else
#		cd $dir
		newline
		echo $(boldify "Packing :") $package
		separator
		
		# Create files.list with redirecting find output.
		gettext "Creating the list of files..."
		cd $fs
		find . -type f -print > $dir/files.list
		find . -type l -print >> $dir/files.list
		sed -i s/'^.'/''/ $dir/files.list
		cd - >/dev/null
				
		status
		gettext "Creating $CHECKSUM of files..."
		local file
		cd $fs
		# Literal read — never glob-expand a files.list path (see spk-add).
		while IFS= read -r file; do
			[ -n "$file" ] || continue
			# files.list holds absolute paths (/usr/bin/foo); cwd is $fs, so
			# reference them as ".$file" (./usr/bin/foo) to read the PACKAGE's
			# file. Using $file straight tested/hashed the HOST's /usr/bin/foo
			# (or nothing), so the manifest held host checksums and the old
			# 's/  fs/' rewrite never matched. Strip the leading "." so the
			# manifest keeps the "/path" form spk-add verifies (fs$path).
			[ -L ".$file" ] && continue
			[ -f ".$file" ] || continue
			case "$file" in
				/lib/modules/*/modules.*|*.pyc) continue;;
			esac
			$CHECKSUM ".$file" | sed 's|  \./|  /|' >> $dir/$CHECKSUM
		done < $dir/files.list
		cd - >/dev/null
		
		status
		UNPACKED_SIZE=$(du -chs $fs $receipt $dir/files.list $dir/$CHECKSUM \
				$dir/description.txt 2> /dev/null | \
				awk '{ sz=$1 } END { print sz }')
		
		# Build cpio archives.
		gettext "Compressing the fs... "
		cd $dir
		find fs | cpio -o -H newc --quiet | lzma e $fs.cpio.lzma -si
		cd - >/dev/null
		rm -rf $fs
		status
		
		PACKED_SIZE=$(du -chs $fs.cpio.lzma $receipt $dir/files.list \
				$dir/$CHECKSUM $dir/description.txt 2> /dev/null | \
				awk '{ sz=$1 } END { print sz }')
		
		gettext "Updating receipt sizes..."
		sed -i s/^PACKED_SIZE.*$// $receipt
		sed -i s/^UNPACKED_SIZE.*$// $receipt
		sed -i "s/^PACKAGE=/PACKED_SIZE=\"$PACKED_SIZE\"\nUNPACKED_SIZE=\"$UNPACKED_SIZE\"\nPACKAGE=/" $receipt
		status
		
		gettext "Creating full cpio archive... "
		cd $dir
		find . -print | cpio -o -H newc --quiet > $dir.tazpkg
		cd - >/dev/null
		status
		# (The old "Restoring original package tree" step was a no-op: cwd is
		# back in the caller's dir and the cpio filter never matched, so it
		# only printed a misleading "Done". $tmpdir is removed by the caller.)
		separator
		eval_gettext "Package \$package compressed successfully."; newline
		echo $(gettext "Size") : $(du -sh $dir.tazpkg)
		newline
	fi
}



# DEPENDS auto-detection is intentionally disabled. The old ldd + files.list
# scan resolved library owners against a "files.list" in the current dir that
# doesn't exist in the conversion context, so it always produced empty/wrong
# DEPENDS (and spurious "unknown dependency" BUGS entries). Foreign-package
# conversion is best-effort: emit nothing here and let generate_receipt tell
# the user to complete DEPENDS by hand. Usage: find_depends $fs -> ""
find_depends() {
	:
}

show_unresolved_lib() {
	local receipt=$1
	if [ -s $tmpdir/unresolved ]; then
		echo -e "BUGS=\"$(gettext 'No dependency for')" >> $receipt
		for lib in $(cat $tmpdir/unresolved | sort | uniq); do
			eval_gettext "WARNING: unknown dependency for \$lib"; newline
			echo -n " $lib" >> $receipt
		done
		echo "\"" >> $receipt
	fi
}



# convert a .ipk package to .tazpkg
# Usage: convert_ipk $package_file
convert_ipk() {
	local package_file=$1
	
	mkdir -p $tmpdir
	tar xOzf $package_file ./control.tar.gz | tar xzf - -C $tmpdir

	local control="$tmpdir/control"
	
	# filter ipk control file for variable
	# Usage: filter_vars $search_var
	filter_vars() {
		local var=$1
		echo "$(grep ^$var $control | sed 's/.*: //')"
	}
	
	# Load up receipt template variables
	PACKAGE=$(filter_vars "Package")
	VERSION=$(filter_vars "Version")
	MAINTAINER=$(filter_vars "Maintainer")
	SHORT_DESC=$(filter_vars "Description")
	WEB_SITE="http://openwrt.org/"
	
	target=$(filter_vars "Architecture")
	case "$target" in
		i386|all)
			local file="$tmpdir/$PACKAGE-$VERSION"
			local receipt="$file/receipt"
			local fs="$file/fs"
			
			mkdir -p $fs
			tar xOzf $package_file ./data.tar.gz | tar xzf - -C $fs
			
			unset CONFIG_FILES
			[ -s $tmpdir/conffiles ] && CONFIG_FILES=$(cat $tmpdir/conffiles)



			DEPENDS=$(find_depends $fs)
			generate_receipt $receipt $package_file
			
			show_unresolved_lib $receipt
			
			# Writes a ipk script to receipt function
			# Usage: transpose_func $function_name $script
			transpose_func() {
				local func=$1
				local script=$2
				if [ -s $script ]; then
					cat >> $receipt << EOT
$func()
{
$(cat $script)
}
EOT
				fi
			}
			
			# read functions into receipt
			transpose_func pre_install $tmpdir/preinst
			transpose_func post_install $tmpdir/postinst
			transpose_func pre_remove $tmpdir/prerm
			transpose_func post_remove $tmpdir/postrm
			
			[ -z "$SHORT_DESC" ] || echo $SHORT_DESC > $file/description.txt

			pack $PACKAGE $file
			
			mv $file.tazpkg .
			;;
		*)
			gettext "Invalid target: $target (expected i386)"; newline
		;;
	esac
	unset_receipt
	rm -rf $tmpdir
}

# convert a .pkg.tar.gz/.apk package to .tazpkg
# Usage: convert_arch $package_file
convert_arch() {
	local package_file=$1
	
	# Extract
	local fs="$tmpdir/fs"
	mkdir -p $fs
	
	case $package_file in
		*.apk|*.pkg.tar.gz)
			tar xzf $package_file -C $fs;;
		*.pkg.tar.xz)
			tar xf $package_file -C $fs;;
	esac
	
	local PKGINFO="$fs/.PKGINFO"
	if [ -f $PKGINFO ]; then
	
		# filter .PKGINFO file for variable
		# Usage: filter_vars $search_var
		filter_vars() {
			local var=$1
			echo $(grep ^$var $PKGINFO | sed 's/.*= //')
		}
	
		PACKAGE=$(filter_vars "pkgname")
		VERSION=$(filter_vars "pkgver")
		SHORT_DESC=$(filter_vars "pkgdesc")
		WEB_SITE=$(filter_vars "url")
		MAINTAINER=$(filter_vars "packager")
		
		local file="$tmpdir/$PACKAGE-$VERSION"
		local receipt="$file/receipt"
		
		mkdir $file
		mv $fs $file
		
		DEPENDS=$(find_depends $fs)
		generate_receipt $receipt $package_file
		
		show_unresolved_lib $tmpdir/$file/receipt
		rm -f $file/fs/.[A-Z]*
		
		pack $PACKAGE $file
		
		mv $file.tazpkg .
	else
		eval_gettext "\$package_file does not look like an Archlinux/Alpine package !"; newline
	fi
	unset_receipt
	rm -rf $tmpdir
}

# convert a .deb package to .tazpkg
# Usage: convert_deb $package_file
convert_deb() {
	local package_file="$1"
	
	# Extract deb control
	mkdir -p $tmpdir
	dpkg-deb -e $package_file $tmpdir
	
	
	# filter control file for variable
	# Usage: filter_vars $search_var
	filter_vars() {
		local var=$1
		local result=$(grep "^ *$var:" $tmpdir/control)
		echo ${result##*:}
	}
	
	PACKAGE=$(filter_vars "Package")
	VERSION=$(filter_vars "Version")
	SHORT_DESC=$(filter_vars "Description")
	WEB_SITE="http://packages.debian.org/search?keywords=$PACKAGE"
	MAINTAINER="nobody@slitaz.org"
	target="$(grep ^Architecture $tmpdir/control | sed 's/.*: //')"
	
	case "$target" in
	i386|all)
		local file="$tmpdir/$PACKAGE-$VERSION"
		local receipt="$file/receipt"
		local fs="$file/fs"
		
		# Extract deb contents
		mkdir -p $fs
		dpkg-deb -x $package_file $fs
		
		DEPENDS=$(find_depends $fs)
		generate_receipt $receipt $package_file
				
		if [ -s $tmpdir/conffiles ]; then
		 	cat >> $receipt <<EOT
CONFIG_FILES="$(cat $tmpdir/conffiles)"
EOT
		fi
		
		show_unresolved_lib $file/receipt
		
		# Description.txt
		[ -z "$SHORT_DESC" ] || echo "$SHORT_DESC" > $file/description.txt
		
		pack $PACKAGE $file
		
		mv $file.tazpkg .
		;;
	*)
		gettext "Invalid target: $target (expected i386 or all)"; newline
		;;
	esac
	rm -rf $tmpdir
}

# convert a .rpm package to .tazpkg
# Usage: convert_rpm $package_file
convert_rpm() {
	local package_file=$1
	
	# Move package_file to tmp
	mkdir -p $tmpdir
	cp $package_file $tmpdir
	package_file=$tmpdir/$(basename $package_file)
	
	# filter package_file for variable
	# Usage: filter_vars $search_var
	filter_vars() {
		local var=$1
		echo $(rpm -qip $package_file | grep "^$var*" | awk '{print $3}')
	}
	
	PACKAGE=$(filter_vars "Name")
	VERSION=$(filter_vars "Version")
	SHORT_DESC=$(filter_vars "Summary")
	WEB_SITE=$(filter_vars "URL")
	if [ -z "$WEB_SITE" ]; then
		WEB_SITE="http://rpmfind.net/linux/rpm2html/search.php?query=$PACKAGE"
	fi
	CATEGORY="misc";
	MAINTAINER="nobody@slitaz.org";
	
	
	local file="$tmpdir/$PACKAGE-$VERSION"
	local receipt="$file/receipt"
	local fs="$file/fs"
	
	mkdir -p $fs
	
	rpm -qip $package_file | \
		grep "^Description*" -A 100 | \
		grep -v "^Description*" > \
		$file/description.txt
	
	cd $fs
	rpm2cpio $package_file | cpio -idm --quiet
	cd - > /dev/null
	
	
	DEPENDS=$(find_depends $fs)
	generate_receipt $receipt $package_file
	
	show_unresolved_lib $receipt
	
	pack $PACKAGE $file
	
	mv $file.tazpkg .
		
	unset_receipt
	rm -rf $tmpdir
}


#
# Commands and exit
#

case "$1" in
	""|*usage|*help) usage ;;
esac

#
# Handle packages: package package.tazpkg ... packageN packageN.tazpkg
#

check_root

# Track failures so `spk-convert x && spk-add …` works: the old blanket
# exit 0 hid a missing input or an unsupported format.
rc=0
for pkg in $@
do
	if [ ! -f "$pkg" ]; then
		gettext "No such file:"; colorize 31 " $pkg"; newline
		rc=1; continue
	fi
	if [ "$(dd if=$pkg bs=8 count=1 skip=1 2> /dev/null)" = "debian-b" ];
	then
		convert_deb $pkg
	else
		case "$pkg" in
			*.deb|*.udeb)
				convert_deb $pkg;;
			*.rpm)
				convert_rpm $pkg;;
			*.apk|*.pkg.tar.gz|*.pkg.tar.xz)
				convert_arch $pkg;;
			*.ipk|*.opk)
				convert_ipk $pkg;;
			*)
				gettext "Unsupported format:"; colorize 31 " $pkg"
				newline; rc=1;;
		esac
	fi
done

exit $rc
