Splux Linux

source-based Linux using SPS

SPS · log · files · refs · tags · releases · people · languages · GitHub · file

bin/sget


Shell

43501 bytes at 2909919 · GitHub

#!/bin/sh
# High-level package and dependency coordinator.

sps_prog=sget
sps_bin_dir=$(CDPATH= cd -P "$(dirname "$0")" 2>/dev/null && pwd) || exit 1
if [ -z "${SPS_LIBDIR+x}" ]; then
    if [ -r "$sps_bin_dir/../lib/common.sh" ]; then
        SPS_LIBDIR=$sps_bin_dir/../lib
    else
        SPS_LIBDIR=$sps_bin_dir/../lib/sps
    fi
fi
if ! . "$SPS_LIBDIR/common.sh"; then
    printf '%s\n' "sget: cannot load $SPS_LIBDIR/common.sh" >&2
    exit 1
fi
sps_load_config || exit $?

sps_index=$SPS_CACHE/indexes/packages.index
sps_tab=$(printf '\tX')
sps_tab=${sps_tab%X}

sget_begin_transaction()
{
    if [ -e "$SPS_DB" ] || [ -L "$SPS_DB" ]; then
        [ -d "$SPS_DB" ] && [ ! -L "$SPS_DB" ] ||
            sps_die "$SPS_EX_DATABASE" "package database is not a real directory: $SPS_DB"
    else
        mkdir -p "$SPS_DB" ||
            sps_die "$SPS_EX_PERMISSION" "cannot create package database '$SPS_DB'"
        [ -d "$SPS_DB" ] && [ ! -L "$SPS_DB" ] ||
            sps_die "$SPS_EX_DATABASE" "package database is not a real directory: $SPS_DB"
    fi
    sps_lock_acquire "$SPS_DB" .transaction ||
        sps_die "$SPS_EX_DATABASE" "another high-level SPS transaction is active: '$SPS_DB/.transaction'"
}

sget_usage()
{
    cat <<'EOF_USAGE'
usage: sget [-q|--quiet] [-v|--verbose] search PATTERN [--raw]
       sget [-q|--quiet] [-v|--verbose] info PACKAGE [--raw]
       sget [-q|--quiet] [-v|--verbose] depends PACKAGE [--raw] [--build]
       sget [-q|--quiet] [-v|--verbose] dependees PACKAGE [--build]
       sget [-q|--quiet] [-v|--verbose] why PACKAGE DEPENDENCY [--build]
       sget [-q|--quiet] [-v|--verbose] install [--plan] [--nodeps] [--reinstall] PACKAGE...
       sget [-q|--quiet] [-v|--verbose] upgrade [--plan] [--downgrade]
       sget [-q|--quiet] [-v|--verbose] remove [--plan] [--nodeps] PACKAGE...
       sget [-q|--quiet] [-v|--verbose] config-status [--raw] [PACKAGE]
       sget [-q|--quiet] [-v|--verbose] config-status --diff PATH
EOF_USAGE
}

sget_note()
{
    [ "${sget_quiet:-0}" -eq 1 ] && return 0
    printf '%s: %s\n' "$sps_prog" "$*" >&2
}

sget_need_index()
{
    [ -r "$sps_index" ] ||
        sps_die "$SPS_EX_NOTFOUND" "repository index not found; run 'src update'"
}

sget_query()
{
    sget_action=$1
    shift
    sps_need_arg "$sget_action" "${1-}"
    sget_value=$1
    shift
    sget_raw=0
    case ${1-} in
        --raw) sget_raw=1; shift ;;
    esac
    [ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    sget_need_index

    case $sget_action in
        search)
            sps_tab_safe "$sget_value" ||
                sps_die "$SPS_EX_USAGE" "search pattern contains a tab or newline"
            [ "$sget_raw" -eq 0 ] || sget_action=search_raw
            ;;
        info)
            sps_validate_package_name "$sget_value" ||
                sps_die "$SPS_EX_USAGE" "invalid package name '$sget_value'"
            [ "$sget_raw" -eq 0 ] && sget_action=show || sget_action=exact
            ;;
    esac

    awk -v action="$sget_action" -v query="$sget_value" \
        -v program=sget -f "$SPS_LIBDIR/index.awk" "$sps_index"
}

sget_depends()
{
    sps_need_arg depends "${1-}"
    sget_package=$1
    shift
    sps_validate_package_name "$sget_package" ||
        sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
    sget_dep_action=tree
    sget_include_build=0
    while [ "$#" -gt 0 ]; do
        case $1 in
            --raw) sget_dep_action=order ;;
            --build) sget_include_build=1 ;;
            *) sget_usage >&2; exit "$SPS_EX_USAGE" ;;
        esac
        shift
    done
    sget_need_index
    awk -v action="$sget_dep_action" -v roots="$sget_package" \
        -v include_build="$sget_include_build" -v program=sget \
        -f "$SPS_LIBDIR/version.awk" -f "$SPS_LIBDIR/deps.awk" \
        "$sps_index"
}

sget_dependees()
{
    sps_need_arg dependees "${1-}"
    sget_package=$1
    shift
    sps_validate_package_name "$sget_package" ||
        sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
    sget_include_build=0
    case ${1-} in --build) sget_include_build=1; shift ;; esac
    [ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    sget_need_index
    awk -v action=dependees -v query="$sget_package" \
        -v include_build="$sget_include_build" -v program=sget \
        -f "$SPS_LIBDIR/relations.awk" "$sps_index"
}

sget_why()
{
    [ "$#" -ge 2 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    sget_root=$1
    sget_dependency=$2
    shift 2
    sps_validate_package_name "$sget_root" ||
        sps_die "$SPS_EX_USAGE" "invalid package name '$sget_root'"
    sps_validate_package_name "$sget_dependency" ||
        sps_die "$SPS_EX_USAGE" "invalid package name '$sget_dependency'"
    sget_include_build=0
    case ${1-} in --build) sget_include_build=1; shift ;; esac
    [ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    sget_need_index
    awk -v action=why -v root="$sget_root" -v query="$sget_dependency" \
        -v include_build="$sget_include_build" -v program=sget \
        -f "$SPS_LIBDIR/relations.awk" "$sps_index"
}

sget_read_installed()
{
    sget_installed_name=$1
    sget_installed_record=$SPS_DB/installed/$sget_installed_name
    if [ -L "$sget_installed_record" ] ||
       { [ -f "$sget_installed_record" ] && [ ! -d "$sget_installed_record" ]; }; then
        sps_die "$SPS_EX_DATABASE" "installed record for '$sget_installed_name' is unsafe"
    fi
    [ -d "$sget_installed_record" ] || return 1
    [ -f "$sget_installed_record/meta" ] && [ ! -L "$sget_installed_record/meta" ] ||
        sps_die "$SPS_EX_DATABASE" "installed metadata for '$sget_installed_name' is missing or unsafe"
    sget_installed_id=$(awk -v sps_action=meta-id -v sps_program=sget \
        -f "$SPS_LIBDIR/package.awk" "$sget_installed_record/meta") ||
        sps_die "$SPS_EX_DATABASE" "installed metadata for '$sget_installed_name' is malformed"
    sget_saved_ifs=$IFS
    IFS=$sps_tab
    set -- $sget_installed_id
    IFS=$sget_saved_ifs
    [ "$#" -eq 4 ] ||
        sps_die "$SPS_EX_DATABASE" "installed identity for '$sget_installed_name' is malformed"
    [ "$1" = "$sget_installed_name" ] ||
        sps_die "$SPS_EX_DATABASE" "installed record '$sget_installed_name' identifies package '$1'"
    sget_installed_version=$2
    sget_installed_release=$3
    sget_installed_arch=$4
    return 0
}

# Compare the selected version/release with what is installed. Prints -1, 0, or 1.
sget_compare_selected()
{
    sget_cmp=$(awk -v version_action=compare -v version_a="$1" -v version_b="$3" \
        -f "$SPS_LIBDIR/version.awk" /dev/null) || return 1
    if [ "$sget_cmp" -eq 0 ]; then
        sget_cmp=$(awk -v version_action=compare -v version_a="$2" -v version_b="$4" \
            -f "$SPS_LIBDIR/version.awk" /dev/null) || return 1
    fi
    printf '%s\n' "$sget_cmp"
}

sget_installed_names()
{
    sget_list=
    [ -d "$SPS_DB/installed" ] || {
        printf ''
        return 0
    }
    for sget_d in "$SPS_DB/installed"/*; do
        [ -d "$sget_d" ] || continue
        sget_n=${sget_d##*/}
        [ -n "$sget_list" ] && sget_list=$sget_list,$sget_n || sget_list=$sget_n
    done
    printf '%s' "$sget_list"
}

sget_cleanup_install()
{
    if [ -n "${sget_work:-}" ]; then
        rm -rf "$sget_work"
        sget_work=
    fi
    sps_lock_release
}

sget_verify_artifact_identity()
{
    sget_verify_archive=$1
    sget_verify_name=$2
    sget_verify_version=$3
    sget_verify_release=$4
    sget_verify_arch=$5
    sget_verify_definition=${6-}

    case $sget_verify_archive in
        *.tar.gz|*.tgz)
            sps_require_cmd gzip
            sget_verify_meta=$(gzip -dc "$sget_verify_archive" 2>/dev/null |
                tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
            ;;
        *.tar.xz|*.txz)
            sps_require_cmd xz
            sget_verify_meta=$(xz -dc "$sget_verify_archive" 2>/dev/null |
                tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
            ;;
        *.tar.zst|*.tzst)
            sps_require_cmd zstd
            sget_verify_meta=$(zstd -q -dc "$sget_verify_archive" 2>/dev/null |
                tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
            ;;
        *)
            sget_verify_meta=$(tar -xOf "$sget_verify_archive" ./.SPS/meta 2>/dev/null) || return 1
            ;;
    esac
    sget_verify_id=$(printf '%s\n' "$sget_verify_meta" |
        awk -v sps_action=meta-id -v sps_program=sget \
            -f "$SPS_LIBDIR/package.awk") || return 1
    sget_verify_saved_ifs=$IFS
    IFS=$sps_tab
    set -- $sget_verify_id
    IFS=$sget_verify_saved_ifs
    [ "$#" -eq 4 ] || return 1
    [ "$1" = "$sget_verify_name" ] &&
    [ "$2" = "$sget_verify_version" ] &&
    [ "$3" = "$sget_verify_release" ] &&
    [ "$4" = "$sget_verify_arch" ] || return 1

    [ -n "$sget_verify_definition" ] || return 0
    sget_verify_cached_definition=$(printf '%s\n' "$sget_verify_meta" |
        awk -v sps_action=meta-get -v sps_key_name=definition_sha256 \
            -v sps_program=sget -f "$SPS_LIBDIR/package.awk" 2>/dev/null) ||
        return 2
    [ "$sget_verify_cached_definition" = "$sget_verify_definition" ] ||
        return 2
}

# Set by the upgrade path.
sget_upgrade_mode=${sget_upgrade_mode:-0}

# Build-only tools that a live disc already seeds. Skip installing the
# package when it is not a runtime requirement and the host command exists.
sget_host_build_tool()
{
    case $1 in
        cmake) command -v cmake >/dev/null 2>&1 ;;
        ninja) command -v ninja >/dev/null 2>&1 ;;
        meson) command -v meson >/dev/null 2>&1 ;;
        patch) command -v patch >/dev/null 2>&1 ;;
        pkgconf) command -v pkg-config >/dev/null 2>&1 ||
            command -v pkgconf >/dev/null 2>&1 ;;
        gperf) command -v gperf >/dev/null 2>&1 ;;
        python) command -v python3 >/dev/null 2>&1 ;;
        go) command -v go >/dev/null 2>&1 ;;
        rust) command -v rustc >/dev/null 2>&1 &&
            command -v cargo >/dev/null 2>&1 ;;
        unzip) command -v unzip >/dev/null 2>&1 ;;
        lzip) command -v lzip >/dev/null 2>&1 ;;
        *) return 1 ;;
    esac
}

sget_selected_index_field()
{
    awk -F "$sps_tab" -v pkg="$1" -v col="$2" '
        $1 == pkg {
            pri = $9 + 0
            if (!seen || pri > best) {
                best = pri
                val = $col
                seen = 1
            }
        }
        END { if (seen) print val }
    ' "$sps_index"
}

sget_cache_usable()
{
    sget_cu_name=$1
    sget_cu_version=$2
    sget_cu_release=$3
    sget_cu_arch=$4
    sget_cu_hash=$5
    sget_cu_base=$SPS_CACHE/packages/$sget_cu_name-$sget_cu_version-$sget_cu_release-$sget_cu_arch.pkg.tar
    for sget_cu_candidate in "$sget_cu_base.zst" "$sget_cu_base.xz" \
                             "$sget_cu_base.gz" "$sget_cu_base"
    do
        [ -f "$sget_cu_candidate" ] && [ ! -L "$sget_cu_candidate" ] || continue
        sget_verify_artifact_identity "$sget_cu_candidate" "$sget_cu_name" \
            "$sget_cu_version" "$sget_cu_release" "$sget_cu_arch" \
            "$sget_cu_hash" >/dev/null 2>&1 || continue
        return 0
    done
    return 1
}

# True when mkpkg will run for this selected record.
sget_will_compile()
{
    sget_wc_name=$1
    sget_wc_version=$2
    sget_wc_release=$3
    sget_wc_arch=$4
    sget_wc_hash=$5
    sget_wc_cache=$sget_work/will-compile/$sget_wc_name
    if [ -f "$sget_wc_cache" ]; then
        read -r sget_wc_cached <"$sget_wc_cache"
        return "$sget_wc_cached"
    fi
    if sget_read_installed "$sget_wc_name"; then
        if ! sget_wc_cmp=$(sget_compare_selected "$sget_wc_version" \
            "$sget_wc_release" "$sget_installed_version" \
            "$sget_installed_release")
        then
            printf '%s\n' 0 >"$sget_wc_cache"
            return 0
        fi
        if [ "$sget_wc_cmp" -eq 0 ] &&
           [ "$sget_installed_arch" = "$sget_wc_arch" ] &&
           [ "$sget_reinstall" -eq 0 ]; then
            printf '%s\n' 1 >"$sget_wc_cache"
            return 1
        fi
    fi
    if sget_cache_usable "$sget_wc_name" "$sget_wc_version" \
        "$sget_wc_release" "$sget_wc_arch" "$sget_wc_hash"
    then
        printf '%s\n' 1 >"$sget_wc_cache"
        return 1
    fi
    printf '%s\n' 0 >"$sget_wc_cache"
    return 0
}

# Runtime closure first. Add builddeps only for packages that will be
# compiled, and skip well-known tools already on PATH.
sget_plan_with_builddeps()
{
    sget_all_roots=$sget_roots
    sget_round=0
    mkdir -p "$sget_work/will-compile"
    while :
    do
        sget_round=$((sget_round + 1))
        [ "$sget_round" -lt 32 ] ||
            sps_die "$SPS_EX_DEPENDENCY" "build-dependency expansion did not settle"
        if ! awk -v action=plan -v roots="$sget_all_roots" \
            -v include_build=0 -v program=sget \
            -v installed="$(sget_installed_names)" \
            -f "$SPS_LIBDIR/version.awk" -f "$SPS_LIBDIR/deps.awk" \
            "$sps_index" >"$sget_work/resolution"
        then
            sps_die "$SPS_EX_DEPENDENCY" "cannot resolve requested packages"
        fi
        : >"$sget_work/new-build-roots"
        while IFS="$sps_tab" read -r sget_pl_name sget_pl_recipe sget_pl_hash \
              sget_pl_version sget_pl_release sget_pl_arch sget_pl_bdeps || \
              [ -n "$sget_pl_name" ]
        do
            [ -n "$sget_pl_name" ] || continue
            sget_will_compile "$sget_pl_name" "$sget_pl_version" \
                "$sget_pl_release" "$sget_pl_arch" "$sget_pl_hash" || continue
            [ -n "$sget_pl_bdeps" ] || continue
            sget_pl_saved_ifs=$IFS
            IFS=,
            set -- $sget_pl_bdeps
            IFS=$sget_pl_saved_ifs
            for sget_pl_bdep
            do
                sget_pl_bdep=$(printf '%s' "$sget_pl_bdep" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
                [ -n "$sget_pl_bdep" ] || continue
                case $sget_pl_bdep in
                    *'>='*) sget_pl_bdep=${sget_pl_bdep%%>=*} ;;
                    *'<='*) sget_pl_bdep=${sget_pl_bdep%%<=*} ;;
                    *'='*) sget_pl_bdep=${sget_pl_bdep%%=*} ;;
                    *'>'*) sget_pl_bdep=${sget_pl_bdep%%>*} ;;
                    *'<'*) sget_pl_bdep=${sget_pl_bdep%%<*} ;;
                esac
                [ -n "$sget_pl_bdep" ] || continue
                case ,$sget_all_roots, in
                    *,"$sget_pl_bdep",*) continue ;;
                esac
                if sget_host_build_tool "$sget_pl_bdep"; then
                    sget_note "using host $sget_pl_bdep to build $sget_pl_name"
                    continue
                fi
                printf '%s\n' "$sget_pl_bdep" >>"$sget_work/new-build-roots"
            done
        done <"$sget_work/resolution"
        [ -s "$sget_work/new-build-roots" ] || return 0
        sget_new_roots=
        while IFS= read -r sget_pl_bdep || [ -n "$sget_pl_bdep" ]
        do
            [ -n "$sget_pl_bdep" ] || continue
            case ,$sget_all_roots, in
                *,"$sget_pl_bdep",*) continue ;;
            esac
            case ,$sget_new_roots, in
                *,"$sget_pl_bdep",*) continue ;;
            esac
            [ -n "$sget_new_roots" ] &&
                sget_new_roots=$sget_new_roots,$sget_pl_bdep ||
                sget_new_roots=$sget_pl_bdep
        done <"$sget_work/new-build-roots"
        [ -n "$sget_new_roots" ] || return 0
        sget_all_roots=$sget_new_roots,$sget_all_roots
    done
}

sget_install()
{
    sget_plan=0
    sget_nodeps=0
    sget_reinstall=0
    sget_allow_downgrade=0
    while [ "$#" -gt 0 ]; do
        case $1 in
            --plan) sget_plan=1; shift ;;
            --nodeps) sget_nodeps=1; shift ;;
            --reinstall) sget_reinstall=1; shift ;;
            --downgrade) sget_allow_downgrade=1; shift ;;
            -q|--quiet) sget_quiet=1; shift ;;
            -v|--verbose) sget_verbose=1; shift ;;
            --) shift; break ;;
            -*) sps_die "$SPS_EX_USAGE" "unknown option '$1'" ;;
            *) break ;;
        esac
    done
    [ "$#" -gt 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    sget_need_index

    sget_roots=
    for sget_package
    do
        sps_validate_package_name "$sget_package" ||
            sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
        case ,$sget_roots, in
            *,"$sget_package",*)
                sps_die "$SPS_EX_USAGE" "package '$sget_package' was requested more than once" ;;
        esac
        [ -n "$sget_roots" ] && sget_roots=$sget_roots,$sget_package || sget_roots=$sget_package
    done

    sget_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" sget)
    trap sget_cleanup_install 0
    trap 'exit 129' 1
    trap 'exit 130' 2
    trap 'exit 131' 3
    trap 'exit 143' 15
    : >"$sget_work/resolution"
    : >"$sget_work/todo"
    : >"$sget_work/mark-explicit"
    sps_mkdir "$SPS_CACHE/packages"

    if [ "$sget_nodeps" -eq 1 ]; then
        for sget_package
        do
            awk -v action=install_record -v query="$sget_package" \
                -v program=sget -f "$SPS_LIBDIR/index.awk" "$sps_index" \
                >>"$sget_work/resolution" || exit $?
        done
    else
        sget_note "resolving $sget_roots"
        sget_plan_with_builddeps
    fi

    sget_count=0
    while IFS="$sps_tab" read -r sget_package sget_recipe sget_recipe_hash \
          sget_version sget_release sget_arch sget_unused_bdeps || \
          [ -n "$sget_package" ]
    do
        [ -n "$sget_package" ] || continue
        case $sget_arch in
            any|noarch|"$SPS_ARCH") ;;
            *) sps_die "$SPS_EX_PACKAGE" "'$sget_package' targets '$sget_arch', not '$SPS_ARCH'" ;;
        esac

        sget_action=install
        if sget_read_installed "$sget_package"; then
            sget_cmp=$(sget_compare_selected "$sget_version" "$sget_release" \
                       "$sget_installed_version" "$sget_installed_release") ||
                sps_die "$SPS_EX_PACKAGE" "cannot compare package versions"
            if [ "$sget_cmp" -eq 0 ] && [ "$sget_installed_arch" = "$sget_arch" ]; then
                case ,$sget_roots, in
                    *,"$sget_package",*)
                        if [ ! -r "$SPS_DB/world" ] ||
                           ! grep -F -x -e "$sget_package" "$SPS_DB/world" >/dev/null 2>&1; then
                            printf '%s\n' "$sget_package" >>"$sget_work/mark-explicit"
                        fi
                        ;;
                esac
                if [ "$sget_reinstall" -eq 1 ] && [ "$sget_upgrade_mode" -eq 0 ]; then
                    sget_action=reinstall
                else
                    sget_note "$sget_package satisfied by $sget_installed_version-$sget_installed_release"
                    continue
                fi
            elif [ "$sget_cmp" -gt 0 ]; then
                sget_action=upgrade
            else
                sget_action=downgrade
                if [ "$sget_upgrade_mode" -eq 1 ] && [ "$sget_allow_downgrade" -eq 0 ]; then
                    printf "%s: keeping newer installed %s %s-%s (repository has %s-%s)\n" \
                        "$sps_prog" "$sget_package" "$sget_installed_version" \
                        "$sget_installed_release" "$sget_version" "$sget_release" >&2
                    continue
                fi
            fi
        fi
        sget_old_version=
        sget_old_release=
        case $sget_action in
            upgrade|downgrade|reinstall)
                sget_old_version=$sget_installed_version
                sget_old_release=$sget_installed_release
                ;;
        esac
        printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
            "$sget_action" "$sget_package" "$sget_recipe" "$sget_recipe_hash" \
            "$sget_version" "$sget_release" "$sget_arch" \
            "$sget_old_version" "$sget_old_release" >>"$sget_work/todo"
        sget_count=$((sget_count + 1))
    done <"$sget_work/resolution"

    sget_mark_count=$(awk 'END { print NR + 0 }' "$sget_work/mark-explicit")
    sget_note "$sget_count package action(s), $sget_mark_count reason change(s)"
    if [ "$sget_plan" -eq 1 ]; then
        if [ "$sget_upgrade_mode" -eq 1 ]; then
            printf 'Upgrade plan\n\n'
        else
            printf 'Install plan\n\n'
        fi
        if [ "$sget_count" -eq 0 ] && [ "$sget_mark_count" -eq 0 ]; then
            printf '%s\n' 'No changes are needed.'
        else
            while IFS="$sps_tab" read -r sget_action sget_package sget_recipe \
                  sget_recipe_hash sget_version sget_release sget_arch \
                  sget_old_version sget_old_release || [ -n "$sget_action" ]
            do
                [ -n "$sget_action" ] || continue
                if [ "$sget_action" = upgrade ] || [ "$sget_action" = downgrade ]; then
                    printf '  %-10s %s %s-%s -> %s-%s\n' \
                        "$sget_action" "$sget_package" \
                        "$sget_old_version" "$sget_old_release" \
                        "$sget_version" "$sget_release"
                else
                    printf '  %-10s %s %s-%s\n' \
                        "$sget_action" "$sget_package" "$sget_version" "$sget_release"
                fi
            done <"$sget_work/todo"
            while IFS= read -r sget_package || [ -n "$sget_package" ]; do
                [ -n "$sget_package" ] && printf '  %-10s %s\n' explicit "$sget_package"
            done <"$sget_work/mark-explicit"
            if [ "$sget_mark_count" -gt 0 ]; then
                printf '\n%d packages, %d reason changes\n' "$sget_count" "$sget_mark_count"
            else
                printf '\n%d packages\n' "$sget_count"
            fi
        fi
        sget_cleanup_install
        trap - 0 1 2 3 15
        return 0
    fi

    if [ "$sget_count" -gt 0 ]; then
        sps_require_cmd mkpkg
        sps_require_cmd pkin
        sget_seen=0
        while IFS="$sps_tab" read -r sget_action sget_package sget_recipe \
              sget_recipe_hash sget_version sget_release sget_arch \
              sget_old_version sget_old_release || [ -n "$sget_action" ]
        do
            [ -n "$sget_action" ] || continue
            [ -f "$sget_recipe" ] ||
                sps_die "$SPS_EX_NOTFOUND" "recipe for '$sget_package' is missing: $sget_recipe"
			sget_current_hash=$(sps_definition_sha256 "$sget_recipe") ||
				sps_die "$SPS_EX_CHECKSUM" "cannot hash package definition for '$sget_package'"
			[ "$sget_current_hash" = "$sget_recipe_hash" ] ||
				sps_die "$SPS_EX_CHECKSUM" "package definition for '$sget_package' changed since indexing; run 'src update'"
            sget_recipe_dir=${sget_recipe%/*}
            sget_recipe_name=${sget_recipe##*/}
            sget_note "selected $sget_recipe $sget_version-$sget_release"

            if [ "$sget_seen" -eq 1 ]; then
                printf '\n'
            fi
            sget_seen=1
            case $sget_action in
                upgrade|downgrade)
                    printf '%s %s %s-%s -> %s-%s\n' "$sget_action" "$sget_package" \
                        "$sget_old_version" "$sget_old_release" \
                        "$sget_version" "$sget_release"
                    ;;
                *)
                    printf '%s %s %s-%s\n' "$sget_action" "$sget_package" \
                        "$sget_version" "$sget_release"
                    ;;
            esac
            sget_note "checking binary cache for $sget_package"
            sget_cache_base=$SPS_CACHE/packages/$sget_package-$sget_version-$sget_release-$sget_arch.pkg.tar
            sget_artifact=
            for sget_candidate in "$sget_cache_base.zst" "$sget_cache_base.xz" \
                                  "$sget_cache_base.gz" "$sget_cache_base"; do
                if [ -f "$sget_candidate" ] && [ ! -L "$sget_candidate" ]; then
                    sget_artifact=$sget_candidate
                    break
                fi
            done
            if [ -n "$sget_artifact" ]; then
                sget_cache_status=0
                sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
                    "$sget_version" "$sget_release" "$sget_arch" \
                    "$sget_recipe_hash" || sget_cache_status=$?
                case $sget_cache_status in
                    0)
                        sget_note "binary cache hit"
                        printf '%s: using cached %s\n' \
                            "$sps_prog" "${sget_artifact##*/}"
                        ;;
                    2)
                        printf '%s: discarding stale cached %s\n' \
                            "$sps_prog" "${sget_artifact##*/}"
                        rm -f "$sget_artifact" ||
                            sps_die "$SPS_EX_PERMISSION" \
                                "cannot replace stale cache artifact: $sget_artifact"
                        sget_artifact=
                        ;;
                    *)
                        sps_die "$SPS_EX_PACKAGE" \
                            "cached artifact identity is invalid for '$sget_package': $sget_artifact"
                        ;;
                esac
            fi
            if [ -z "$sget_artifact" ]; then
                sget_note "binary cache miss; building $sget_package"
                sget_note "building $sget_recipe_dir"
                sget_artifact_file=$sget_work/artifact-$sget_package
                sget_build_log=$sget_work/build-$sget_package.log
                if [ "${sget_quiet:-0}" -eq 1 ]; then
                    if (CDPATH= cd "$sget_recipe_dir" &&
                        mkpkg --artifact-file "$sget_artifact_file" \
                            --output "$SPS_CACHE/packages" "./$sget_recipe_name" \
                            >"$sget_build_log" 2>&1)
                    then
                        :
                    else
                        [ ! -s "$sget_build_log" ] || cat "$sget_build_log" >&2
                        sps_die "$SPS_EX_BUILD" \
                            "failed to build $sget_package $sget_version-$sget_release"
                    fi
                else
                    (CDPATH= cd "$sget_recipe_dir" &&
                     mkpkg --artifact-file "$sget_artifact_file" \
                         --output "$SPS_CACHE/packages" "./$sget_recipe_name") ||
                        sps_die "$SPS_EX_BUILD" \
                            "failed to build $sget_package $sget_version-$sget_release"
                fi
                sget_artifact=$(sed -n '1p' "$sget_artifact_file" 2>/dev/null) || sget_artifact=
                [ -n "$sget_artifact" ] ||
                    sps_die "$SPS_EX_BUILD" "mkpkg did not report an artifact for '$sget_package'"
                [ -f "$sget_artifact" ] ||
                    sps_die "$SPS_EX_BUILD" "mkpkg artifact for '$sget_package' is missing: $sget_artifact"
                sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
                    "$sget_version" "$sget_release" "$sget_arch" \
                    "$sget_recipe_hash" ||
                    sps_die "$SPS_EX_PACKAGE" "mkpkg produced the wrong package identity for '$sget_package'"
            fi

            case ,$sget_roots, in *,"$sget_package",*) sget_reason=explicit ;; *) sget_reason=dependency ;; esac
            sget_pkin_flags=
            if [ "${sget_verbose:-0}" -eq 1 ]; then
                sget_pkin_flags=-v
            elif [ "${sget_quiet:-0}" -eq 1 ]; then
                sget_pkin_flags=-q
            fi
            # shellcheck disable=SC2086
            SPS_INSTALL_REASON=$sget_reason pkin $sget_pkin_flags "$sget_artifact" ||
                sps_die $? "installation failed for '$sget_package'"
        done <"$sget_work/todo"
    fi

    if [ "$sget_mark_count" -gt 0 ]; then
        sps_require_cmd pkmark
        # Package names cannot contain spaces, so splitting this list is safe.
        set -- $(cat "$sget_work/mark-explicit")
        [ "$#" -eq 0 ] || pkmark explicit "$@" || exit $?
    fi

    if [ "$sget_count" -eq 0 ] && [ "$sget_mark_count" -eq 0 ]; then
        [ "$sget_upgrade_mode" -eq 1 ] && printf '%s\n' 'System packages are up to date.' ||
            printf '%s\n' 'All requested packages are already installed.'
    fi
    if [ "${sget_plan:-0}" -eq 0 ] && sps_config_review_pending; then
        sget_note "run 'sget config-status' to review protected configuration changes from this update, or 'pkconf' to reconcile them"
    fi
    sget_cleanup_install
    trap - 0 1 2 3 15
}

sget_upgrade()
{
    sget_upgrade_plan=0
    sget_upgrade_downgrade=0
    while [ "$#" -gt 0 ]; do
        case $1 in
            --plan) sget_upgrade_plan=1 ;;
            --downgrade) sget_upgrade_downgrade=1 ;;
            -q|--quiet) sget_quiet=1 ;;
            -v|--verbose) sget_verbose=1 ;;
            *) sget_usage >&2; exit "$SPS_EX_USAGE" ;;
        esac
        shift
    done
    [ -f "$SPS_DB/world" ] && [ ! -L "$SPS_DB/world" ] ||
        sps_die "$SPS_EX_DATABASE" "world database is missing or unsafe"
    awk -v sps_action=world-validate -v sps_program=sget \
        -f "$SPS_LIBDIR/db.awk" "$SPS_DB/world" ||
        sps_die "$SPS_EX_DATABASE" "world database is malformed"
    set -- $(cat "$SPS_DB/world")
    if [ "$#" -eq 0 ]; then
        printf '%s\n' 'No explicit packages are installed.'
        return 0
    fi
    sget_upgrade_mode=1
    if [ "$sget_upgrade_plan" -eq 1 ]; then set -- --plan "$@"; fi
    if [ "$sget_upgrade_downgrade" -eq 1 ]; then set -- --downgrade "$@"; fi
    sget_install "$@"
}

sget_remove()
{
    sget_remove_plan=0
    sget_remove_nodeps=0
    while [ "$#" -gt 0 ]; do
        case $1 in
            --plan) sget_remove_plan=1; shift ;;
            --nodeps) sget_remove_nodeps=1; shift ;;
            -q|--quiet) sget_quiet=1; shift ;;
            -v|--verbose) sget_verbose=1; shift ;;
            --) shift; break ;;
            -*) sps_die "$SPS_EX_USAGE" "unknown option '$1'" ;;
            *) break ;;
        esac
    done
    [ "$#" -gt 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
    [ -d "$SPS_DB/installed" ] && [ ! -L "$SPS_DB/installed" ] ||
        sps_die "$SPS_EX_DATABASE" "installed database is missing or unsafe"

    sget_remove_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" remove-plan)
    trap 'status=$?; rm -rf "$sget_remove_work"; sps_lock_release; exit "$status"' 0
    trap 'exit 130' 1 2 3 15
    : >"$sget_remove_work/requested"
    for sget_package
    do
        sps_validate_package_name "$sget_package" ||
            sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
        [ -d "$SPS_DB/installed/$sget_package" ] && [ ! -L "$SPS_DB/installed/$sget_package" ] ||
            sps_die "$SPS_EX_NOTFOUND" "package '$sget_package' is not installed"
        if grep -F -x -e "$sget_package" "$sget_remove_work/requested" >/dev/null 2>&1; then
            sps_die "$SPS_EX_USAGE" "package '$sget_package' was requested more than once"
        fi
        printf '%s\n' "$sget_package" >>"$sget_remove_work/requested"
    done

    : >"$sget_remove_work/edges"
    set -- "$SPS_DB"/installed/*/meta
    if [ -f "$1" ]; then
        awk -v out="$sget_remove_work/edges" '
        FNR == 1 {
            pkg=FILENAME; sub(/\/meta$/, "", pkg); sub(/^.*\//, "", pkg)
        }
        {
            p=match($0, /[[:space:]]/); if (!p) next
            key=substr($0,1,p-1); if (key != "depend") next
            value=substr($0,p); sub(/^[[:space:]]+/,"",value)
            gsub(/,/," ",value); n=split(value,a,/[[:space:]]+/)
            for (i=1;i<=n;i++) {
                d=a[i]; sub(/(>=|<=|=|>|<).*/,"",d)
                if (d != "") print pkg "\t" d > out
            }
        }' "$@"
    fi

    if [ "$sget_remove_nodeps" -eq 0 ]; then
        if ! awk -F '\t' '
            NR==FNR { req[$1]=1; next }
            !($1 in req) && ($2 in req) {
                print $1 " requires " $2 > "/dev/stderr"; bad=1
            }
            END { exit bad }
        ' "$sget_remove_work/requested" "$sget_remove_work/edges"; then
            sps_die "$SPS_EX_DEPENDENCY" "removal would break installed dependencies (use --nodeps to override)"
        fi
    fi

    # Remove requested dependents before their dependencies. Cycles fall back to
    # lexical order.
    awk -F '\t' '
        NR==FNR { req[$1]=1; names[++n]=$1; next }
        ($1 in req) && ($2 in req) { dep[$1 SUBSEP $2]=1 }
        END {
            left=n
            while (left>0) {
                pick=""
                for (i=1;i<=n;i++) if (!(names[i] in done)) {
                    p=names[i]; incoming=0
                    for (j=1;j<=n;j++) if (!(names[j] in done) && dep[names[j] SUBSEP p]) incoming=1
                    if (!incoming && (pick=="" || p<pick)) pick=p
                }
                if (pick=="") for (i=1;i<=n;i++) if (!(names[i] in done) && (pick=="" || names[i]<pick)) pick=names[i]
                print pick; done[pick]=1; left--
            }
        }
    ' "$sget_remove_work/requested" "$sget_remove_work/edges" >"$sget_remove_work/order"

    if [ "$sget_remove_plan" -eq 1 ]; then
        printf 'Remove plan\n\n'
        while IFS= read -r sget_package || [ -n "$sget_package" ]; do
            [ -n "$sget_package" ] && printf '  remove     %s\n' "$sget_package"
        done <"$sget_remove_work/order"
        printf '\n%s packages\n' "$(awk 'END { print NR + 0 }' "$sget_remove_work/order")"
        rm -rf "$sget_remove_work"
        sps_lock_release
        trap - 0 1 2 3 15
        return 0
    fi

    sps_require_cmd pkdel
    while IFS= read -r sget_package || [ -n "$sget_package" ]; do
        [ -n "$sget_package" ] || continue
        pkdel "$sget_package" || exit $?
    done <"$sget_remove_work/order"
    rm -rf "$sget_remove_work"
    if [ "$sget_remove_plan" -eq 0 ] && sps_config_review_pending; then
        sget_note "run 'sget config-status' to review protected configuration changes, or 'pkconf' to reconcile them"
    fi
    sps_lock_release
    trap - 0 1 2 3 15
}

# Keep a whole sget install/upgrade/remove from interleaving with another one.
# Low-level tools still use their shorter database lock. Queries and plans do not.

# Read-only. Report protected configuration files that differ from what their
# installed package recorded, and which of those have a packaged default that
# an earlier upgrade wrote beside them (".sps-new*"). The admin reviews and
# merges those by hand; nothing here writes to the filesystem.
sget_config_status()
{
	sget_cs_diff=0
	sget_cs_raw=0
	sget_cs_scope=
	sget_cs_nargs=0
	while [ "$#" -gt 0 ]; do
		case $1 in
			--diff) sget_cs_diff=1; shift ;;
			--raw) sget_cs_raw=1; shift ;;
			--)
				shift
				while [ "$#" -gt 0 ]; do
					sget_cs_scope=$1; sget_cs_nargs=$((sget_cs_nargs + 1)); shift
				done
				break
				;;
			-*)
				sget_usage >&2
				exit "$SPS_EX_USAGE"
				;;
			*)
				sget_cs_scope=$1; sget_cs_nargs=$((sget_cs_nargs + 1)); shift
				;;
		esac
	done
	if [ "$sget_cs_diff" -eq 1 ]; then
		[ "$sget_cs_nargs" -eq 1 ] ||
			sps_die "$SPS_EX_USAGE" "config --diff takes exactly one path"
		sps_require_cmd diff ||
			sps_die "$SPS_EX_NOTFOUND" "diff is required for config --diff"
	else
		[ "$sget_cs_nargs" -le 1 ] ||
			sps_die "$SPS_EX_USAGE" "config takes at most one package name"
		if [ -n "$sget_cs_scope" ]; then
			sps_validate_package_name "$sget_cs_scope" ||
				sps_die "$SPS_EX_USAGE" "invalid package name '$sget_cs_scope'"
		fi
	fi

	[ -d "$SPS_DB/installed" ] && [ ! -L "$SPS_DB/installed" ] ||
		sps_die "$SPS_EX_DATABASE" "installed database is missing or is not a real directory"
	[ -f "$SPS_DB/owners" ] && [ ! -L "$SPS_DB/owners" ] ||
		sps_die "$SPS_EX_DATABASE" "ownership database is missing or is not a real file"

	sget_cs_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" config) ||
		sps_die "$SPS_EX_PERMISSION" "cannot create config review workspace"
	sget_cs_cleanup()
	{
		rm -rf "$sget_cs_work"
	}
	trap 'sget_cs_cleanup' 0

	# --diff accepts the plain path, the ".sps-new" sibling, or the
	# ".sps-new.VERSION-RELEASE" spelling. Normalize to the package-relative
	# base path before matching hash records.
	sget_cs_want=
	if [ "$sget_cs_diff" -eq 1 ]; then
		case "$SPS_ROOT" in
			/) case $sget_cs_scope in /*) sget_cs_want=${sget_cs_scope#/} ;; *) sget_cs_want=$sget_cs_scope ;; esac ;;
			*)
				case $sget_cs_scope in
					"$SPS_ROOT"/*) sget_cs_want=${sget_cs_scope#"$SPS_ROOT"/} ;;
					/*) sget_cs_want=${sget_cs_scope#/} ;;
					*) sget_cs_want=$sget_cs_scope ;;
				esac
				;;
		esac
		sget_cs_want=${sget_cs_want%/}
		case $sget_cs_want in
			*.sps-new) sget_cs_want=${sget_cs_want%.sps-new} ;;
			*.sps-new.*) sget_cs_want=${sget_cs_want%.sps-new.*} ;;
		esac
		[ -n "$sget_cs_want" ] ||
			sps_die "$SPS_EX_USAGE" "invalid config path '$sget_cs_scope'"
		if ! printf '%s\n' "$sget_cs_want" | awk -v sps_action=validate-paths \
			-v sps_program=sget -f "$SPS_LIBDIR/package.awk"; then
			sps_die "$SPS_EX_USAGE" "invalid config path '$sget_cs_scope'"
		fi
		sget_cs_owner=$(awk -v sps_action=owners-query -v sps_path="$sget_cs_want" \
			-v sps_program=sget -f "$SPS_LIBDIR/db.awk" "$SPS_DB/owners" 2>/dev/null) || :
		[ -n "$sget_cs_owner" ] ||
			sps_die "$SPS_EX_NOTFOUND" "no installed package owns /$sget_cs_want"
	fi

	# Walk installed hash records for preserved paths. Config files that live
	# under SPS_PRESERVE and changed locally are the ones an upgrade refused to
	# overwrite, so those are the interesting cases for review.
	: >"$sget_cs_work/rows"
	for sget_cs_record in "$SPS_DB/installed"/*; do
		[ -d "$sget_cs_record" ] && [ ! -L "$sget_cs_record" ] || continue
		sget_cs_name=${sget_cs_record##*/}
		case $sget_cs_name in .|..) continue ;; esac
		[ -f "$sget_cs_record/hashes" ] && [ ! -L "$sget_cs_record/hashes" ] || continue
		[ -f "$sget_cs_record/meta" ] && [ ! -L "$sget_cs_record/meta" ] || continue
		if [ "$sget_cs_diff" -eq 0 ] && [ -n "$sget_cs_scope" ] &&
		   [ "$sget_cs_name" != "$sget_cs_scope" ]; then
			continue
		fi
		while IFS="$sps_tab" read -r sget_cs_algo sget_cs_expected sget_cs_path \
		      || [ -n "$sget_cs_path" ]; do
			[ -n "$sget_cs_path" ] || continue
			if [ "$sget_cs_diff" -eq 1 ]; then
				[ "$sget_cs_path" = "$sget_cs_want" ] || continue
			fi
			sps_is_preserved_path "$sget_cs_path" || continue
			sget_cs_target=$(sps_root_path "/$sget_cs_path")
			sget_cs_state=clean
			if [ -f "$sget_cs_target" ] && [ ! -L "$sget_cs_target" ]; then
				sget_cs_live=$(sps_sha256 "$sget_cs_target") || sget_cs_live=
				set -- $sget_cs_live
				sget_cs_live=$1
				if [ -n "$sget_cs_live" ] && [ "$sget_cs_live" != "$sget_cs_expected" ]; then
					sget_cs_state=modified
				fi
			elif [ "$sget_cs_diff" -eq 1 ]; then
				sget_cs_state=missing
			else
				continue
			fi
			sget_cs_defaults=$(sps_config_new_defaults "$sget_cs_path")
			sget_cs_has_default=0
			for sget_cs_def in $sget_cs_defaults; do
				[ -n "$sget_cs_def" ] && sget_cs_has_default=1
			done
			printf '%s\t%s\t%s\t%s\n' \
				"$sget_cs_name" "$sget_cs_path" "$sget_cs_state" \
				"$sget_cs_has_default" >>"$sget_cs_work/rows"
		done <"$sget_cs_record/hashes"
	done
	LC_ALL=C sort "$sget_cs_work/rows" | LC_ALL=C uniq >"$sget_cs_work/rows.sorted"
	mv -f "$sget_cs_work/rows.sorted" "$sget_cs_work/rows"

	if [ "$sget_cs_diff" -eq 1 ]; then
		sget_cs_found=0
		sget_cs_state=clean
		while IFS="$sps_tab" read -r sget_cs_name sget_cs_path sget_cs_state \
		     sget_cs_has_default; do
			[ -n "$sget_cs_name" ] || continue
			sget_cs_found=1
		done <"$sget_cs_work/rows"
		[ "$sget_cs_found" -eq 1 ] ||
			sps_die "$SPS_EX_NOTFOUND" "/$sget_cs_want is not a managed protected config file"
		sget_cs_live_path=$(sps_root_path "/$sget_cs_want")
		sget_cs_defaults=$(sps_config_new_defaults "$sget_cs_want")
		sget_cs_first=0
		if [ "$sget_cs_state" = missing ]; then
			sget_cs_first=1
			printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
			printf '%s\n' 'The local file is missing.'
			for sget_cs_default in $sget_cs_defaults; do
				[ -f "$sget_cs_default" ] && [ ! -L "$sget_cs_default" ] || continue
				diff -u /dev/null "$sget_cs_default"
			done
			[ -n "$sget_cs_defaults" ] ||
				printf '%s\n' 'No packaged default (.sps-new) is present for this file.'
			exit 0
		fi
		for sget_cs_default in $sget_cs_defaults; do
			[ -f "$sget_cs_default" ] && [ ! -L "$sget_cs_default" ] || continue
			if [ "$sget_cs_first" -eq 0 ]; then
				printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
				printf '%s\n' 'Local file versus packaged default (written as .sps-new during an upgrade):'
				sget_cs_first=1
			fi
			diff -u "$sget_cs_live_path" "$sget_cs_default"
		done
		if [ "$sget_cs_first" -eq 0 ]; then
			printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
			printf '%s\n' 'No packaged default (.sps-new) is present for this file.'
		fi
		exit 0
	fi

	sget_cs_any=0
	sget_cs_pending=0
	if [ -s "$sget_cs_work/rows" ]; then
		while IFS="$sps_tab" read -r sget_cs_name sget_cs_path sget_cs_state \
		     sget_cs_has_default; do
			[ -n "$sget_cs_name" ] || continue
			case $sget_cs_state in
				modified)
					sget_cs_pending=$((sget_cs_pending + 1))
					if [ "$sget_cs_has_default" -eq 1 ]; then
						sget_cs_label='new packaged default available'
					else
						sget_cs_label='modified locally'
					fi
					;;
				*)
					[ "$sget_cs_has_default" -eq 1 ] || continue
					sget_cs_label='matches package; .sps-new left over'
					;;
			esac
			sget_cs_any=1
			if [ "$sget_cs_raw" -eq 1 ]; then
				printf '%s\t%s\t%s\t%s\n' \
					"$sget_cs_name" "$sget_cs_path" "$sget_cs_state" \
					"$sget_cs_has_default"
			else
				printf '%-18s  /%-32s  %s\n' \
					"$sget_cs_name" "$sget_cs_path" "$sget_cs_label"
			fi
		done <"$sget_cs_work/rows"
	fi
	if [ "$sget_cs_any" -eq 0 ] && [ "$sget_cs_raw" -eq 0 ]; then
		printf '%s\n' 'no protected configuration changes found'
	fi
	printf '%s\n' "summary: $sget_cs_pending config file(s) differ from their packaged default" >&2
	exit 0
}

sget_quiet=0
sget_verbose=0
while [ "$#" -gt 0 ]; do
    case $1 in
        -q|--quiet) sget_quiet=1; shift ;;
        -v|--verbose) sget_verbose=1; shift ;;
        -h|--help) sget_usage; exit 0 ;;
        --) shift; break ;;
        -*)
            sget_usage >&2
            exit "$SPS_EX_USAGE"
            ;;
        *) break ;;
    esac
done

sget_top_command=${1-}
case $sget_top_command in
    install|upgrade|remove)
        sget_top_plan=0
        shift_index=0
        for sget_top_arg in "$@"; do
            [ "$shift_index" -eq 0 ] && { shift_index=1; continue; }
            [ "$sget_top_arg" = --plan ] && sget_top_plan=1
        done
        if [ "$sget_top_plan" -eq 0 ]; then
            sget_begin_transaction
            trap 'status=$?; sps_lock_release; exit "$status"' 0
            trap 'exit 130' 1 2 3 15
        fi
        ;;
esac

case ${1-} in
    search|info)
        sget_command=$1; shift; sget_query "$sget_command" "$@" ;;
    depends)
        shift; sget_depends "$@" ;;
    dependees)
        shift; sget_dependees "$@" ;;
    why)
        shift; sget_why "$@" ;;
    install)
        shift; sget_upgrade_mode=0; sget_install "$@" ;;
    upgrade)
        shift; sget_upgrade "$@" ;;
    remove)
        shift; sget_remove "$@" ;;
    config-status)
        shift; sget_config_status "$@" ;;
    -h|--help)
        sget_usage ;;
    *)
        sget_usage >&2; exit "$SPS_EX_USAGE" ;;
esac