#!/bin/bash readonly BE_VERBOSE=$( [[ "$1" == '-v' ]] && echo 1 || echo 0 ) # readonly REPOS=( nonprism nonsystemd{-testing,} libre{-testing,} readonly REPOS=( nonprism{-testing,} nonsystemd{-testing,} libre{-testing,} kernels{-testing,} testing core extra community{-testing,} pcr{-testing,} nonprism-multilib{-testing,} nonsystemd-multilib libre-multilib{-testing,} multilib{-testing,} pcr-multilib{-testing,} ) readonly DUMMY_PKGBUILD='pkgname=%s pkgver=0.0.0 pkgrel=42 pkgdesc="dummy PKGBUILD for experimentation" arch=(armv7h i686 x86_64) url=https://nowhere.man license=(GPL) source=(PKGBUILD) sha256sums=(SKIP) package() { echo "created '"'\${pkgname}'"' package" ; }' readonly USAGE='USAGE: parabola-dependents [-v] List all parabola packages which are dependents of a specified package. By default, only first-order dependents are listed, with counts of higher-order dependents. If does not exist, a dummy package will be created, in memory, for pactree to reflect upon. Note: this script can take several minutes to complete, if the dependency-tree for is large. Options: -v Itemize all higher-order dependents, displaying the dependency-chain.' readonly INVALID_ARG_MSG="no dependency package specified\n\n${USAGE}" readonly UNPRIVILEGED_MSG="this script requires super-user privileges" readonly CBLUE='\033[0;36m' readonly CRED='\033[0;31m' readonly CEND='\033[0m' readonly SEP_CHAR='=' DB_DIR='' # Init() CFG_FILE='' # Init() OPTS='' # Init() Ignored=() # CollectResults() Dependents=() # CollectResults() declare -A HiorderDependents # CollectResults() Log() # (log_fmt [args]*) { local log_fmt=$1 ; shift ; local args="$@" printf "${CBLUE}${log_fmt}${CEND}\n" ${args} >&2 } LogError() # (source_file func_name line_n) { local source_file="$1" local func_name=$2 local line_n=$3 printf "${CRED}ERROR: in ${func_name}\n${line_n}: %s${CEND}\n" \ "$(awk "(( NR == ${line_n} )) { print }" ${source_file})" >&2 } Init() # (dep_pkgname) { local dep_pkgname=$1 local dummy_pkg=${dep_pkgname}-0.0.0-42-$(uname -m).pkg.tar.xz readonly DB_DIR="$(su $(logname) -c 'mktemp -d -t parabola-dependents-XXXXXXXXXX')" readonly CFG_FILE=${DB_DIR}/pacman-all.conf readonly OPTS="--dbpath=${DB_DIR} --config=${CFG_FILE}" printf "[options]\nArchitecture = auto\n" > ${CFG_FILE} for repo in ${REPOS[@]} do printf "[${repo}]\nInclude = /etc/pacman.d/mirrorlist\n" >> ${CFG_FILE} done Log "updating database ...." pacman ${OPTS} -Sy &> /dev/null || true # create dummy for missing dependency package if ! pacman ${OPTS} -Ss ^${dep_pkgname}$ &> /dev/null then Log "package '${dep_pkgname}' not found - creating dummy" cd ${DB_DIR} printf "${DUMMY_PKGBUILD}\n" ${dep_pkgname} > ./PKGBUILD su $(logname) -c 'makepkg --force &> /dev/null' [[ -f ./${dummy_pkg} ]] repo-add --new parabola-dependents.db.tar ./${dummy_pkg} &> /dev/null Log "dummy package '${dep_pkgname}' created" printf "[parabola-dependents]\nServer = file://${DB_DIR}\n" >> ${CFG_FILE} fi pacman ${OPTS} -Sy &> /dev/null } IsArchRepo() # (repo) { local repo=$1 ; [[ "${repo}" =~ ^(community|core|extra|multilib|testing)$ ]] } CollectResults() # (dep_pkgname) { local dep_pkgname=$1 local dep_chains dep_chain dep_pkg via_pkg repos repo # TODO: --chain is a custom pactree feature export PATH="/code/pacman-contrib/src:${PATH}" # query database Log "searching ...." mapfile -t dep_chains < <(pactree ${OPTS} --sync --reverse --unique --chain \ ${dep_pkgname} | sort ) # compile results Log "compiling results for (${#dep_chains[@]}) dependencies ...." for dep_chain in "${dep_chains[@]}" do dep_pkg=$(sed 's|.*<- ||' <<<${dep_chain}) via_pkg=$(sed 's|.*\] <- \([^ ]*\).*|\1|' <<<${dep_chain}) repos=$(pacman ${OPTS} -Si ${dep_pkg} | grep Repository | cut -d ':' -f 2 | tr -d ' ') for repo in ${repos} do if IsArchRepo ${repo} then Ignored+=( "${repo}/${dep_pkg}" ) else Dependents+=( "${repo}/${dep_pkg} ${dep_chain}" ) HiorderDependents[${via_pkg}]=$(( ${HiorderDependents[${via_pkg}]} + 1 )) fi done done } PrintReport() { local dep_chain is_hiorder_dep via_pkg repo_pkg declare -i n_hiorder_deps echo for dep_chain in "${Dependents[@]}" do if (( BE_VERBOSE )) then echo "${dep_chain/ /${SEP_CHAR}}" else is_hiorder_dep=$( (( $(tr '<' '\n' <<<${dep_chain} | wc -l) > 2 )) && echo 1 || echo 0 ) if ! (( is_hiorder_dep )) then via_pkg=$(sed 's|.*\] <- \([^ ]*\).*|\1|' <<<${dep_chain}) repo_pkg=${dep_chain/ *} n_hiorder_deps=$(( ${HiorderDependents[${via_pkg}]} - 1 )) echo "${repo_pkg}${SEP_CHAR}(${n_hiorder_deps} higher-order deps)" fi fi done | column --table --separator="${SEP_CHAR}" --table-wrap=2 echo if (( BE_VERBOSE && ${#Ignored[@]} )) then printf "%s${SEP_CHAR}(ignored)\n" "${Ignored[@]}" | column --table --separator="${SEP_CHAR}" else echo "(${#Ignored[@]} ignored)" fi } Cleanup() { if [[ -d "${DB_DIR}" && "${DB_DIR}" =~ parabola-dependents ]] then rm --force --recursive ${DB_DIR} ; fi } set -o errexit -o errtrace trap 'Cleanup' EXIT INT TERM trap 'LogError "${BASH_SOURCE[0]}" "${FUNCNAME[0]}" "${LINENO}"' ERR (( $BE_VERBOSE )) && shift || true (( ! EUID )) || ! echo -e "${UNPRIVILEGED_MSG}" || exit 1 (( $# )) || ! echo -e "${INVALID_ARG_MSG}" || exit 1 LANG=C Init $1 CollectResults $1 PrintReport