view share/local-bsdtools/ports.subr @ 815:e2f262ec2bf4

Extend copyright year to 2025. Because a new version will be released.
author Franz Glasner <fzglas.hg@dom66.de>
date Sat, 18 Jan 2025 16:34:56 +0100
parents 2310764e5c4e
children
line wrap: on
line source

#!/bin/sh
# -*- mode: shell-script; indent-tabs-mode: nil; -*-
#:
#: Helper functions for :command:`fports` and friends.
#:
#: :Author:    Franz Glasner
#: :Copyright: (c) 2017-2025 Franz Glasner.
#:             All rights reserved.
#: :License:   BSD 3-Clause "New" or "Revised" License.
#:             See LICENSE for details.
#:             If you cannot find LICENSE see
#:             <https://opensource.org/licenses/BSD-3-Clause>
#: :ID:        @(#)@@SIMPLEVERSIONTAG@@
#:


: # Dummy separator for shellcheck: no module-wide settings below this line


# Need definitions from farray.subr
type falist_create 1>/dev/null 2>/dev/null || { echo "ERROR: source \`farray.sh' first"; exit 70; }
# Need definitions from common.subr
type fatal 1>/dev/null 2>/dev/null || { echo "ERROR: source \`common.subr' first"; exit 70; }


#:
#: Determine whether a package `_package` is essentially the same as
#: another package.
#:
#: Args:
#:   $1 (str): The package mapping database alist. This database must have
#:             been initialized by `init_package_mapping`.
#:
#:   $2 (str): The name of the installed package
#:
#: Returns:
#:   int: 0 (truthy) when a package mapping has been found,
#:        1 (falsy) otherwise (in this case the output to stdout is empty)
#:
#: Output (stdout):
#:   The name of the package on which `_package` is/was based on
#:
get_package_mapping() {
    local _mapping _installed_package

    local _pos _iname _mapped_package

    _mapping="${1-}"
    _installed_package="${2-}"
    [ -z "${_installed_package}" ] && fatal "${EX_USAGE}" "missing package name"

    if falist_tryget _mapped_package "${_mapping}" "${_installed_package}"; then
        printf '%s' "${_mapped_package}"
        return 0
    else
        return 1
    fi
}


#:
#: Slurp in the configured :file:`package-mapping.conf`.
#:
#: This command reads in the the mapping database in in file which defaults
#: to :file:`/usr/local/etc/local-bsdtools/package-mapping.conf`.
#: It is read into a freshly created alist. Its variable name is given as
#: `$1`.
#:
#: Args:
#:   $1 (str): The variable where to create the alist
#:   $2 (str, null): The jail from which to retrieve the package mapping
#:                   configuration
#:
#: Input (Globals):
#:   PACKAGE_MAPPING: See also :manpage:`package-mapping.conf(5)`.
#:
#: Return:
#:   int: 0 on success,
#:        65 (aka `EX_DATAERR`) if a duplicate package name was encountered.
#:
#:        Note that an unreadable `PACKAGE_MAPPING` database file does not
#:        error but yields an empty database.
#:
#: Example of a :file:`package-mapping.conf`::
#:
#:   #
#:   # _installed_package               mapped_package_name
#:   #
#:   fmg-nextcloud-php71                nextcloud-php71
#:   fmg-nextcloud-twofactor_totp-php71 nextcloud-twofactor_totp-php71
#:
init_package_mapping() {
    local _mapping _jail

    local _iname _mapped_package

    falist_create "$1" || return
    _mapping="$1"
    _jail="$2"

    if call_command "${_jail}" /bin/test -r "${PACKAGE_MAPPING}" ; then
        while IFS=$' \t' read -r _iname _mapped_package ; do
            case "${_iname}" in
                '')
                    # empty line
                    continue;;
                \#*)
                    # comment
                    continue;;
                *)
                    if ! falist_set_unique "${_mapping}" "${_iname}" "${_mapped_package}" ; then
                        fatal "${EX_DATAERR}" "duplicate installed package name \`${_iname}'"
                        return 1
                    fi
                    ;;
            esac
        done <<EOF_6969d915-b864-47bb-a0f9-70519b1be0e2
$(call_command "${_jail}" /bin/cat "${PACKAGE_MAPPING}")
EOF_6969d915-b864-47bb-a0f9-70519b1be0e2
    fi
    return 0
}


#:
#: Read in the list of configured and enabled package repositories
#: from :manpage:`pkg(8)`.
#:
#: The output of :command:`pkg -vv` is parsed to get all the configured
#: and enabled repositories.
#:
#: Args:
#:   $1 (str, null, optional): The jail from where to read the repositoriy
#:                             configuration
#:
#: Output (stdout):
#:   Each configured and enabled repository name is printed on a single
#:   line.
#:
get_configured_pkg_repository_names() {
    # $1

    local _line _status _repository_name _repository_enabled _key _value _rest

    _status=''
    while IFS=$' \t' read -r _line; do
        case "${_status}" in
            '')
                if [ "${_line}" = "Repositories:" ]; then
                    _status="repositories"
                fi
                ;;
            repositories)
                case "${_line}" in
                    *': {')
                        _status=repository
                        _repository_name="${_line%:*}"
                        _repository_enabled=no
                        ;;
                    *)
                        ;;
                esac
                ;;
            repository)
                case "${_line}" in
                    *\})
                        if [ "${_repository_enabled}" = 'yes' ] ; then
                            printf '%s\n' "${_repository_name}"
                        fi
                        _repository_name=''
                        _repository_enabled=no
                        _status=repositories
                        ;;
                    *)
                        _key=''
                        _value=''
                        IFS=$' \t:' read -r _key _value _rest || true <<EOF_73d43dccec1a496f98cb519be160f549
${_line}
EOF_73d43dccec1a496f98cb519be160f549
                        if [ "${_key}" = "enabled" ]; then
                            case "${_value}" in
                                'yes,'|yes)
                                    _repository_enabled=yes;;
                                *)
                                    _repository_enabled=no;;
                            esac
                        fi
                        ;;
                esac
                ;;
            *)
                fatal "${EX_SOFTWARE}" "unhandled format of \`pkg -vv'"
        esac
    done <<EOF_7c6ea1b0ce544021a7813757c7003392
$(call_pkg "${1-}" -vv)
EOF_7c6ea1b0ce544021a7813757c7003392
}


#:
#: Create an array with all configured and active repository names.
#:
#: Args:
#:   $1 (str): The variable name where to store the array with the
#:             repository names. It must be released by the caller.
#:   $2 (str, null): The jail from where to read the repository
#:                   configurations
#:
get_active_repositories() {
    # $1 $2

    local repo

    farray_create "${1}" || return
    while IFS='' read -r repo; do
        farray_append "${1}" "${repo}"
    done <<EOF_b4e5385c-4c37-413b-b6f2-7909ac9eaa86
$(get_configured_pkg_repository_names "${2}")
EOF_b4e5385c-4c37-413b-b6f2-7909ac9eaa86
}


_cleanup_init_repositories() {
    [ -n "${__repodb}" ] && falist_release "${_repodb}"
    [ -n "${__allrepos}" ] && falist_release "${_allrepos}"
}


init_repositories() {
    local _allrepos

    local _reponame _repodb _pkgname _pkgversion
    local _idx
    local _test

    falist_create "$1" || return
    _allrepos="$1"

    while IFS='' read -r _reponame; do
        if ! falist_create _repodb; then
            _cleanup_init_repositories
            return 1
        fi
        farray_create _test
        _idx=0
        while IFS=$'\t' read -r _pkgname _pkgversion; do
            _idx=$((_idx + 1))
            echo "$_pkgname $_pkgversion"
            #_test="$(/sbin/skein256 -q "${_pkgname}")"
            falist_add _repodb "${_pkgname}" "${_pkgversion}"
            #farray_append _test "${_pkgversion}"
            #[ $_idx -gt 5000 ] && break
        done <<EOF_pkg_9b5d20d4-805e-484e-9afb-ecc62e75f7cc
$(LC_ALL=C.UTF-8 "${PKG}" rquery -U -r "${_reponame}" '%n\t%v')
EOF_pkg_9b5d20d4-805e-484e-9afb-ecc62e75f7cc
        falist_set "${_allrepos}" "${_reponame}" "${_repodb}"
        falist_release _repodb
    done <<EOF_repos_d6177ceb-b027-4fe2-bc71-e6b5017c0663
$(get_configured_pkg_repository_names)
EOF_repos_d6177ceb-b027-4fe2-bc71-e6b5017c0663
    falist_debug "${_allrepos}"
}


#:
#: Check whether a local index is available.
#:
#: The check follows largely the conventions of :manpage:`pkg-version(8)`
#: where to check for a local index.
#:
#: Args:
#:   $1 (str, null): The jail in which to determine a local index file
#:
#: Returns:
#:   int: 0 if the index is available, 1 otherwise
#:
is_local_index_file_available() {
    # $1

    local versrc indexdir indexfile

    versrc="$(call_pkg "${1}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
    indexfile="$(call_pkg "${1}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
    case "${versrc}" in
        ''|R)
            # XXX FIXME: handle R ("remote")  really like default?
            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" && return 0
            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
            ;;
        I)
            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
            ;;
        P)
            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
            call_command "${1}" /bin/test -r "${indexdir}/${indexfile}"
            ;;
        *)
            fatal "${EX_SOFTWARE}" "unhandled value in VERSION_SOURCE: ${versrc}";;
    esac
}


#:
#: Like `is_local_index_file_available` but also print the found index file.
#:
#: Args:
#:   $1 (str, null): The jail in which to determine a local index file
#:
#: Output (stdout):
#:   The found index file
#:
get_local_index_file() {
    # $1

    local versrc indexdir indexfile

    versrc="$(call_pkg "${1}" config VERSION_SOURCE)" || fatal "${EX_UNAVAILABLE}" "cannot get VERSION_SOURCE configuration value"
    indexfile="$(call_pkg "${1}" config INDEXFILE)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXFILE configuration value"
    case "${versrc}" in
        ''|R)
            # XXX FIXME: handle R ("remote")  really like default?
            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                printf '%s' "${indexdir}/${indexfile}"
                return 0
            fi
            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                printf '%s' "${indexdir}/${indexfile}"
                return 0
            fi
            ;;
        I)
            indexdir="$(call_pkg "${1}" config INDEXDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get INDEXDIR configuration value"
            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                printf '%s' "${indexdir}/${indexfile}"
                return 0
            fi
            ;;
        P)
            indexdir="$(call_pkg "${1}" config PORTSDIR)" || fatal "${EX_UNAVAILABLE}" "cannot get PORTSDIR configuration value"
            if call_command "${1}" /bin/test -r "${indexdir}/${indexfile}" ; then
                printf '%s' "${indexdir}/${indexfile}"
                return 0
            fi
            ;;
        *)
            fatal "${EX_SOFTWARE}" "unhandled value in VERSION_SOURCE: ${versrc}"
            ;;
    esac
    return 1
}


#:
#: Parse a given index file for a package and yield its version.
#:
#: Args:
#:   $1 (str): An existing index file
#:   $2 (str): The package to search for
#:   $3 (str, null): The jail where `$1` lives
#:
#: Output (stdout):
#:   The package version if found, or a null string
#:
#: Returns:
#:   int: 0 if a matching package is found, 1 otherwise
#:
parse_index_file_for_package_version() {
    # $1 $2 $3

    local pkgspec _rest _pathprefix

    _pathprefix=''
    if [ -n "${3}" ] ; then
        _pathprefix="$(LC_ALL=C.UTF-8 /usr/sbin/jls -j "${3}" path)"
    fi
    [ -r "${_pathprefix}${1}" ] || fatal "${EX_SOFTWARE}" "given index file \`${1}' is not readable (or does not exist)"

    # Using grep to pre-select with BREs is at least one magnitude slower
    while IFS='|' read -r pkgspec _rest; do
        if [ "${pkgspec%-*}" = "${2}" ]; then
            printf '%s' "${pkgspec##*-}"
            return 0
        fi
    done <"${_pathprefix}${1}"
    return 1
}