#!/usr/bin/env bash
# pacrename -- edit installed package versions in the pacman database
#
# Used in situations where a big AUR package gets an utterly insignificant
# pkgrel bump and I'm just like "yeah, no, I'm not wasting my CPU rebuilding
# all that just because the source URL changed".

. lib.bash || exit

lockpath='/var/lib/pacman/db.lck'
dbpath='/var/lib/pacman/local'

usage() {
	echo "Usage: $progname <oldpkg-oldver> <newpkg-newver>"
}

db_lock() {
	debug "locking DB"
	touch "$lockpath.$$"
	ln "$lockpath.$$" "$lockpath"
}

db_unlock() {
	debug "unlocking DB"
	rm -f "$lockpath.$$" "$lockpath"
	trap - EXIT
}

_die() {
	db_unlock
	die "$@"
}

fix_desc_version() {
	local new_name=$1 new_version=$2
	local line last_line
	while read -r line; do
		if [[ $last_line == "%NAME%" ]]; then
			line=$new_name
		elif [[ $last_line == "%VERSION%" ]]; then
			line=$new_version
		fi
		printf '%s\n' "$line"
		last_line=$line
	done
}

unset {old,new}{pkg,name,ver}

if (( $# == 2 )); then
	oldpkg=$1
	newpkg=$2
	pkgre='^(.+)-([^-]+-[^-]+)$'
	if [[ $oldpkg =~ $pkgre ]]; then
		oldname=${BASH_REMATCH[1]}
		oldver=${BASH_REMATCH[2]}
	fi
	if [[ $newpkg =~ $pkgre ]]; then
		newname=${BASH_REMATCH[1]}
		newver=${BASH_REMATCH[2]}
	fi
elif (( $# > 2 )); then
	oldname=$1
	oldver=$2
	if (( $# == 3 )); then
		newname=$1
		newver=$3
	elif (( $# == 4 )); then
		newname=$3
		newver=$4
	fi
	oldpkg="$oldname-$oldver"
	newpkg="$newname-$newver"
fi

if ! [[ $oldname && $oldver && $newname && $newver ]]; then
	usage >&2
	die "missing parameters"
fi

if [[ $newver == *-*-* ]]; then
	die "package version cannot contain more than one dash"
fi

olddir="$dbpath/$oldpkg"
newdir="$dbpath/$newpkg"

debug "old dir: $olddir"
debug "new dir: $newdir"

(( UID == 0 )) ||
	die "must be run as root"

trap 'die "interrupted - database remains locked"' EXIT

db_lock ||
	die "database is locked"

test -e "$olddir/desc" ||
	_die "package '$oldpkg' not found"

mkdir "$newdir" ||
	_die "package '$newpkg' already exists"

lib:echo "Renaming package '$oldpkg' to '$newpkg'"

debug "rsyncing DB entry to '$newpkg'"

rsync -a "$olddir/" "$newdir/" || {
	rm -rf "$newdir"
	_die "could not copy '$oldpkg' to '$newpkg'"
}

debug "updating version in desc"

fix_desc_version "$newname" "$newver" < "$olddir/desc" > "$newdir/desc" || {
	rm -rf "$newdir"
	_die "could not update name/version of '$newpkg'"
}

debug "removing old DB entry '$oldpkg'"

rm -rf "$olddir" ||
	_die "could not remove old package '$oldpkg'"

db_unlock
