#!/bin/sh

## Licensed under
## BSD License (2-clause)
## https://opensource.org/licenses/BSD-2-Clause

## Copyright (c) 2015, John D. "Trix" Farrar <trix@basement.net>

## pkgng module for CFEngine 3.7+


if [ -n "$CFENGINE_TEST_PKGNG_MOCK" ]; then
    alias pkg='./mock_pkgng'
fi

CMD="$1"

PKG_OPTIONS=''
REPO=''
INPUTLIST=''

mangle_arch() {
    awk 'BEGIN {
            arch["x86:32"] = "i386";
            arch["x86:64"] = "amd64";
            arch["aarch64:64"] = "aarch64";
            arch["armv6:32:el:eabi:softfp"] = "armv6";
            arch["armv7:32:el:eabi:softfp"] = "armv7";
            arch["mips:32:eb:o32"] = "mips";
            arch["mips:64:eb:n64"] = "mips64";
            arch["powerpc:64:eb"] = "powerpc64";
        }
        {
            for (a in arch) {
                re = ":" a "$";
                sub(re, ":" arch[a]);
            }
            sub(/[Ff]ree[Bb][Ss][Dd]:[0-9]*:/, "");
        }
        $0'
}

fatal() {
    echo "ErrorMessage=$*"
    exit 1
}

parse_line() {
    echo "$1" | awk -F= '{
        i=index($0,"=");
        print "KEYWORD=\"" $1 "\"" "; VALUE=\"" substr($0, i+1) "\"";
    }' 2>/dev/null
}

read_options() {
    while read -r INPUT ; do
        if [ -n "${INPUT}" ] ; then
            eval `parse_line "${INPUT}"`
            case "${KEYWORD}" in
                File|Name)
                    if [ "${KEYWORD}" = 'File' ] ; then
                        PKG="${VALUE}"
                    else
                        NAM="${VALUE}"
                    fi
                    ;;
                Version|Architecture)
                    # Do not store the value separately, as process_inputs()
                    # will get it from $INPUTLIST
                    ;;
                options)
                    eval `parse_line "${VALUE}"`
                    case "${KEYWORD}" in
                        repo|repository) REPO="-r ${VALUE}";;
                        option) PKG_OPTIONS="${PKG_OPTIONS} -o ${VALUE}";;
                    esac
                    continue
                    ;;
                PackageType*)
                    TYP="${VALUE}"
                    ;;
                *)
                    fatal "Invalid input: '${KEYWORD}'."
                    ;;
            esac
            if [ -n "${KEYWORD}" ] && [ -n "${VALUE}" ] ; then
                if [ -n "${INPUTLIST}" ] ; then
                    INPUTLIST="${INPUTLIST} ${KEYWORD}=${VALUE}"
                else
                    INPUTLIST="${KEYWORD}=${VALUE}"
                fi
            fi
        fi
    done
}

process_inputs() {
    CMD="$1" ; shift
    OPTIONS="$*"

    for OPT in ${OPTIONS} ; do
        eval `parse_line ${OPT}`
        case "${KEYWORD}" in
            File*)
                if [ "${CMD}" = 'file-install' ] ; then
                    PKG="${PKG} ${VALUE}"
                else
                    fatal "Bad Input: '${OPT}' (should be Name=...)."
                fi
                ;;
            Name*)
                if [ "${CMD}" != 'file-install' ] ; then
                    PKG="${PKG} ${VALUE}"
                else
                    fatal "Bad Input: '${OPT}' (should be File=...)"
                fi
                ;;
            Version*)
                PKG="${PKG}-${VALUE}"
                ;;
            Architecture*)
                if [ -n "${VALUE}" ] &&
                       [ "${VALUE}" != "${ARCH}" ] ; then
                    fatal "Bad Input: Architecture ${VALUE} does not match ${ARCH}."
                fi
                ;;
            PackageType*)
                TYP="${VALUE}"
                ;;
            *)
                fatal "Invalid input: '${INPUT}'."
                ;;
        esac
    done
}

_pkg() {
    pkg ${PKG_OPTIONS} "$@"
}

_pkg_query() {
    _pkg query "$@" "Name=%n\nVersion=%v\nArchitecture=${ARCH}"
}

get_package_data() {
    if [ -n "${NAM}" ] && [ -z "${PKG}" ] ; then
        PKG="${NAM}"
    fi
    if [ -n "${PKG}" ] ; then
        case "${PKG}" in
            /*) # File Path.  It's a file
                TYP='file'
                if [ ! -e "${PKG}" ] ; then
                    fatal "No such file: '${PKG}'."
                fi
                ;;
            http://*|ftp://|file://) # URL, it's a file.
                TYP='file'
                ;;
            *)
                TYP='repo'
                ;;
        esac
        echo "PackageType=${TYP}"
        if [ "${TYP}" = 'repo' ] ; then
            PKGNAM=`basename "${PKG}" | sed -r -e 's/^([A-Za-z0-9_-]+)-[0-9a-z.,_]+/\1/'`
            PKGVER=`basename "${PKG}" | sed -r -e 's/^[A-Za-z0-9_-]+-([0-9a-z.,_]+)/\1/' -e 's/\.(tgz|(tar\.)?gz|txz|zip)$//'`
            if [ -z "`echo ${PKGVER} | grep -E '^[0-9]'`" ] ; then
                if [ "${PKGNAM}" != "${PKGVER}" ] ;  then
                    PKGNAM="${PKGNAM}-${PKGVER}"
                fi
                PKGVER=''
            fi
            echo "Name=${PKGNAM}"
            # Returning version for PackageType=repo is not advised
            # https://docs.cfengine.com/latest/reference-standard-library-package_modules.html
            # if [ -n "${PKGVER}" ] ; then
            #     echo "Version=${PKGVER}"
            # fi
        else
            _pkg_query --file "${PKG}"
        fi
    fi
}

if [ "${CMD}" = 'supports-api-version' ] ; then
    echo '1'
    exit 0
else
    read_options
fi

ARCH=$(_pkg config abi | mangle_arch)
case "${CMD}" in
    get-package-data)
        get_package_data
        ;;
    list-installed)
        _pkg_query -a
        ;;
    list-updates-local|list-updates)
        if [ "${CMD}" = 'list-updates-local' ] ; then
            LOCALUPDATE='-U'
        else
            LOCALUPDATE=''
        fi
        _pkg version ${LOCALUPDATE} -vl'<' | \
            awk -v arch="${ARCH}" \
                '/ repositor(y|ies) / { next; }
                {
                  pkgname = $1;
                  pkgver  = $7;
                  sub(/-[0-9_.,]*$/, "", pkgname);
                  sub(/\)/, "", pkgver);
                  print "Name=" pkgname;
                  print "Version=" pkgver;
                  print "Architecture=" arch;
                }'
        ;;
    repo-install|file-install|remove)
        process_inputs "${CMD}" "${INPUTLIST}"
        case "${CMD}" in
            repo-install)
                _pkg install -qy ${REPO} ${PKG} 2>/dev/null >/dev/null
                ;;
            file-install)
                _pkg install -qy ${PKG} 2>/dev/null >/dev/null
                ;;
            remove)
                _pkg delete -Rqy ${PKG} 2>/dev/null >/dev/null
                ;;
        esac
        RC=$?
        if [ ${RC} -ne 0 ] ; then
            fatal "Package manager error"
            exit $RC
        fi
        ;;
    *)
        fatal "Command '${CMD}' is either not valid or not implemented."
        ;;
esac
