#!/usr/bin/env bash
#
# pkgexport 
#
# Copyright (c) 2002--2004 by Andrew Green <crux at dorkatronix dot org>
# Revised 2024--2025 by John McQuah <jmcquah at disroot dot org>
# License: GNU General Public License (GPL)
#
# Script to build a package from installed components. 
#
# Should be useful if upgraded package fails 
#
# 
COMMAND=$(basename "$0")
VERSION=0.9
TARARGS=""
TARGET="$(pwd)"
ROOT=""
PKGDB="/var/lib/pkg/db"
force_creation=0

#########
# Usage
#########
usage() {
cat <<EOF
$COMMAND $VERSION
options: [-h] [-v] [-r <mountpt>] <packagename> [outputfile/dir] 

-r Directory of alternate package root.  See prt-get(8), --install-root option
-f Force $COMMAND to overwrite an existing package
-h Usage/Help
-v Verbose output

Examples:

Export a current package to 'package#1.2.4-1.pkg.tar.gz' in the current dir:
$COMMAND currentpackage

Export mozilla to '/tmp/mozilla#1.2.1-1.pkg.tar.gz': 
$COMMAND mozilla /tmp

Export pkgutils to '/root/pkgutils.tar.gz':
$COMMAND pkgutils /root/pkgutils.tar.gz

Export samba from a different CRUX installation:
$COMMAND -r /mnt samba
EOF
exit 1
}

#########
# pkginfo_i queries to see if a package is installed,
# simultaneously populating an array with the file list
#########
pkginfo_i() {

[ -f "$ROOT$PKGDB" ] || { echo "CRUX package database not found, exiting."; exit 1; }
mapfile -t MANIFEST < <(awk -v RS="" -v FS="\n" -v port="$1" '($1 == port) {print $0}' "$ROOT$PKGDB")

[ ${#MANIFEST[@]} -ge 2 ] || { echo "Package $1 is not installed."; exit 1; }

}

#########
# exportpkg will build a compressed package
#########
exportpkg() {

pkgname="${MANIFEST[0]}"
version="${MANIFEST[1]}"

# after extracting the pkgname and the version, let's keep only the list of files
unset "MANIFEST[0]"
unset "MANIFEST[1]"

# apparently the pkgdb still keeps an entry for files matching an INSTALL NO rule
mapfile -t INSTALLED < <(grep -v -f <(awk '($1 == "INSTALL") && ($3 == "NO") { print $2 }' "$ROOT/etc/pkgadd.conf") <(printf '%s\n' "${MANIFEST[@]}"))

# get compression mode from /etc/pkgmk.conf
declare -A ZFLAGS
ZFLAGS=([gz]="-z" [bz2]="-j" [xz]="-J" [lz]="--lzip" [zst]="--zstd")
compress=$([ -f /etc/pkgmk.conf ] && . /etc/pkgmk.conf && echo "$PKGMK_COMPRESSION_MODE")
[ "$compress" ] || compress="gz"
suffix=".pkg.tar.$compress"

# respect absolute paths, but treat anything else as relative to $PWD
if [ "${TARGET:0:1}" != "/" ]; then
	newpkg="$(pwd)/$TARGET"
else
	newpkg="$TARGET"
fi

# determine whether the user passed a complete file name, or just the basedir
if [ -d "$newpkg" ]; then
	BIGFILENAME="${newpkg}/${pkgname}#${version}${suffix}"
else
	BIGFILENAME="${newpkg}"
fi

[ ! -f "$BIGFILENAME" ] || [ $force_creation = 1 ] || \
	{ echo "$BIGFILENAME exists. Use -f to overwrite."; exit 1; }

if ! bsdtar -C "/$ROOT" -c ${ZFLAGS[$compress]} $TARARGS -f "$BIGFILENAME" -n "${INSTALLED[@]}"; then
	echo "Tar: error in creating $BIGFILENAME."
	rm -f "$BIGFILENAME"
	exit 1
fi

if [ -n "$TARARGS" ]; then cat <<EOF
===============================================

Package export information for current package:

Name: $pkgname
Version: $version

Package saved as: $BIGFILENAME

===============================================
EOF
	else echo "Package saved as: $BIGFILENAME"
fi

}

#########
# Main !
#########
main() {

while getopts :r:hv arg
do
	case $arg in
	r)  altRoot="$OPTARG"
		if [ "${altRoot:0:1}" = "/" ]; then
			ROOT="$altRoot"
		else
			ROOT="$(pwd)/$altRoot"
		fi
	;;
	f)  force_creation=1;;
	h)  usage;;
	v)  TARARGS="-v";;
	\?)  usage;;
	:)   usage;;
	esac
done
shift $((OPTIND - 1))

[ -n "$1" ] || usage
[ -n "$2" ] && TARGET="$2"

pkginfo_i "$1"
exportpkg

}

main "$@"
