#!/bin/sh
# shellcheck disable=SC3043
# in Debian, /bin/sh must support local (policy 10.4)
#
# (C) Copyright 2018-2026 Marc Haber <mh+debian-packages@zugschlus.de>
# License: GPL-2 or any later version

UCF="${UCF_TEST_BINDIR:-}ucf"
UCFTD="${UCF_TEST_BINDIR:-}ucf --three-way --debconf-ok"
UCFR="${UCF_TEST_BINDIR:-}ucfr"
UCFQ="${UCF_TEST_BINDIR:-}ucfq"

# rename ucf-conffile. This was mostly stolen from cacti.postinst after
# a short discussion on debian-mentors, see
# http://lists.debian.org/debian-mentors/2013/07/msg00027.html
# and the following thread. Thanks to Paul Gevers
rename_ucf_file() {
    local oldname newname
    oldname="$1"
    newname="$2"

    # override UCF_FORCE_CONFFNEW with an empty local variable
    # shellcheck disable=SC2034
    local UCF_FORCE_CONFFNEW
    if [ ! -e "${DPKG_ROOT:-}${newname}" ]; then
        if [ -e "${DPKG_ROOT:-}${oldname}" ]; then
            mv "${DPKG_ROOT:-}${oldname}" "${DPKG_ROOT:-}${newname}"
        fi

        # ucf doesn't offer a documented way to do this, so we need to
        # peddle around with undocumented ucf internals.
        sed -i "s|${oldname}|${newname}|" "${DPKG_ROOT:-}/var/lib/ucf/hashfile"

        ${UCFR} --purge "${PKGNAME}" "${oldname}"
        ${UCFR} "${PKGNAME}" "${newname}"
        # else: Don't do anything, leave old file in place
    fi

    ${UCFR} "${PKGNAME}" "${newname}"
}

generate_directory_structure() {
    local pkgdir locdir
    pkgdir="$1"
    locdir="$2"

    # generate empty directory structure

    (cd "${pkgdir}" && find . -type d -print0 ) | \
    (cd "${locdir}" && xargs -0 mkdir -p --)
}

# handle a single ucf_conffile by first checking whether the file might be
# associated with a different package. If so, we keep our hands off the file
# so that a different package can safely hijack our conffiles.
# to hijack a file, simply ucfr it to a package before the ucf processing
# code.
# If the file is either unassociated or already associated with us, call ucf
# proper and register the file as ours.
handle_single_ucf_file() {
    local pkgfile locfile PKG
    if [ -n "${UCF_HELPER_FUNCTIONS_DEBUG-}" ]; then
        set -x
    fi
    pkgfile="$1"
    locfile="$2"

    export DEBIAN_FRONTEND

    # Robust extraction of package name:
    # ucfq format assumed: locfile:pkg:...
    PKG=$(
        ${UCFQ} --with-colons "${locfile}" |
        sed -n '1s/^[^:]*:\([^:]*\).*/\1/p;q'
    )

    # skip conffile if it is associated with a different package.
    # This allows other packages to safely hijack our conffiles.
    if [ -z "${PKG}" ] || [ "${PKG}" = "${PKGNAME}" ]; then
        # try locdir/file instead
        ${UCFTD} "${pkgfile}" "${locfile}"
        ${UCFR} "${PKGNAME}" "${locfile}"
    fi

    if [ -n "${UCF_HELPER_FUNCTIONS_DEBUG-}" ]; then
        set +x
    fi
}

# checks whether a file was deleted in the package and handle it on the local
# system appropriately: If the local file differs from what we had previously,
# we just unregister it and leave it on the system (preserving local changes),
# otherwise we remove it.
# this also removes conffiles that are zero-size after the
# ucf run, which might happen if the local admin has
# deleted a conffile that has changed in the package.
handle_deleted_ucf_file() {
    local locfile locdir pkgdir

    if [ -n "${UCF_HELPER_FUNCTIONS_DEBUG-}" ]; then
        set -x
    fi
    locfile="$1"
    pkgdir="$2"
    locdir="$3"

    # compute the name of the reference file in $pkgdir
    reffile=$(printf '%s' "${locfile}" | sed "s|${locdir}|${pkgdir}|")

    if [ ! -e "${reffile}" ]; then
        # if the reference file does not exist, then it was removed in the package
        # do as if the file was replaced with an empty file
        ${UCFTD} /dev/null "${locfile}"

        if [ -s "${DPKG_ROOT:-}${locfile}" ]; then
            # the file has non-zero size after the ucf run. local admin must
            # have decided to keep the file with contents. Done here.
            :
        else
            for ext in '' '~' '%' .bak .dpkg-tmp .dpkg-new .dpkg-old .dpkg-dist .ucf-new .ucf-old .ucf-dist; do
                rm -f "${DPKG_ROOT:-}${locfile}${ext}"
            done
        fi

        ${UCF} --purge "${locfile}"
        ${UCFR} --purge "${PKGNAME}" "${locfile}"
    fi

    if [ -n "${UCF_HELPER_FUNCTIONS_DEBUG-}" ]; then
        set +x
    fi
}

handle_all_ucf_files() {
    local pkgdir locdir
    pkgdir="$1"
    locdir="$2"

    generate_directory_structure "${pkgdir}" "${DPKG_ROOT:-}${locdir}"

    # handle regular ucf-conffiles by iterating through all conffiles
    # that come with the package. using find | read uses a subshell which
    # might break downstream software. We better ignore files with
    # whitespace or shell metachars here.
    # shellcheck disable=SC2044
    for file in $(
        find "$pkgdir" -type f \
            ! -path '*[[:space:]]*' \
            ! -path '*[*?[]*' \
            -printf '%P\n'
        ); do
        handle_single_ucf_file "${pkgdir}/${file}" "${locdir}/${file}"
    done

    # handle ucf-conffiles that were deleted in our package by iterating
    # through all ucf-conffiles that are registered for the package
    # the for in $() is okay here since package names dont contain shell
    # metacharacters or whitespace
    for locfile in $(${UCFQ} --with-colons "$PKGNAME" | cut --delimiter=: --fields=1); do
        handle_deleted_ucf_file "${locfile}" "${pkgdir}" "${locdir}"
    done
}

# vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
