#!/bin/sh
#
# pkgdel - Package removal utility
# POSIX-compliant shell script for removing installed packages
#

trap "interrupted" 1 2 3 15

export LC_ALL=C

interrupted() {
	printf '\n'
	ret 1
}

msg() { printf "==> %s\n" "$1"; }
msgerr() { printf "ERROR: %s\n" "$1" >&2; }

help() {
	cat << 'EOF'
Usage:
  pkgdel [ <options> <package name> ]

Options:
  -h, --help            show this help message
  -v, --verbose         print removed files
      --root=<path>     remove package from custom root directory

EOF
}

ret() {
	rm -f "$ROOT_DIR/$LOCK_FILE"
	exit "$1"
}

# Dependency check first — fail fast before any processing
command -v pkgadd >/dev/null 2>&1 || {
	msgerr "'pkgadd' not found in \$PATH!"
	exit 1
}

# Parse options directly; combined flags like -vh are not supported
# (keeping it simple avoids a subshell and an extra pass over arguments)
if [ -z "$1" ]; then
	help
	exit 0
fi

while [ -n "$1" ]; do
	case $1 in
		-h | --help)   help; exit 0 ;;
		-v | --verbose) VERBOSE_REMOVE=yes ;;
		--root=*)      ROOT_DIR="${1#*=}" ;;
		-*)            msg "Invalid option: ($1)"; exit 1 ;;
		*)             RMNAME=$1 ;;
	esac
	shift
done

if [ -z "$RMNAME" ]; then
	help
	exit 0
fi

PKGDB_DIR="$(pkgadd --print-dbdir)"
PKGDB_DIR="${PKGDB_DIR##/}"

LOCK_FILE="var/lib/scratchpkg/spkg.lock"

[ "$(id -u)" = "0" ] || {
	printf "Removing package need root access!\n"
	help
	exit 1
}

[ -f "$ROOT_DIR/$LOCK_FILE" ] && {
	msgerr "Cannot install/remove package simultaneously."
	msgerr "remove '$ROOT_DIR/$LOCK_FILE' if no install/remove package process running."
	exit 1
}

touch "$ROOT_DIR/$LOCK_FILE" 2>/dev/null || {
	msgerr "Cannot create lock file in '$ROOT_DIR/$LOCK_FILE'"
	exit 1
}

# Inline the isinstalled check — only called once, no need for a function
[ -s "$ROOT_DIR/$PKGDB_DIR/$RMNAME" ] || {
	msgerr "Package '$RMNAME' not installed."
	ret 1
}

name=$RMNAME
read -r version release < "$ROOT_DIR/$PKGDB_DIR/$name"

if [ -z "$version" ] && [ -z "$release" ]; then
	msgerr "Package '$RMNAME' is not installed but exists in database."
	ret 1
fi

# Build a merged DB from all packages except the one being removed.
# Used later to detect directories still owned by other packages.
tmpdb=$(mktemp)
find "$ROOT_DIR/$PKGDB_DIR" -type f ! -name "$name" -exec cat {} + 2>/dev/null >"$tmpdb" || {
	msgerr "Failed to build temporary DB file."
	rm -f "$tmpdb"
	ret 1
}

# Split the package's file list (skipping the version/release header on line 1)
# into plain files and directories using a single awk pass.
_rm_files=$(mktemp)
_rm_dirs=$(mktemp)

awk -v rm_files="$_rm_files" -v rm_dirs="$_rm_dirs" '
NR == 1 { next }
{ if (substr($0, length($0), 1) == "/") print > rm_dirs; else print > rm_files }
' "$ROOT_DIR/$PKGDB_DIR/$name"

# Remove regular files in one xargs batch
if [ -s "$_rm_files" ]; then
	[ "$VERBOSE_REMOVE" ] && sed "s|^|removing '${ROOT_DIR}/|;s|\$|'|" "$_rm_files"
	sed "s|^|${ROOT_DIR}/|" "$_rm_files" | xargs -r rm -f
fi

# Remove directories deepest-first; skip any still referenced by another package
if [ -s "$_rm_dirs" ]; then
	sort -r "$_rm_dirs" | while IFS= read -r d; do
		if ! grep -Fxq "$d" "$tmpdb"; then
			[ "$VERBOSE_REMOVE" ] && printf "removing directory '%s'\n" "${ROOT_DIR}/${d}"
			rmdir "${ROOT_DIR}/${d}" 2>/dev/null
		fi
	done
fi

# Remove files, dirs, tmpdb and the package entry from the database
rm -f "$_rm_files" "$_rm_dirs" "$tmpdb" "$ROOT_DIR/$PKGDB_DIR/$name"

# Refresh the dynamic linker cache if available
[ -x "$ROOT_DIR/sbin/ldconfig" ] && "$ROOT_DIR/sbin/ldconfig" -r "$ROOT_DIR/"

ret 0
