#!/bin/bash # Show which SBo packages require a given package, marking installed ones. SBO_REPO="${SBO_REPO:-/var/lib/sbopkg/SBo-git}" PKG_LOG="${PKG_LOG:-/var/log/packages}" # Colors (disabled when stdout is not a terminal) if [[ -t 1 ]]; then C_RESET='\033[0m' C_INSTALLED='\033[1;32m' # bold green — installed C_MISSING='\033[0;33m' # yellow — not installed C_TARGET='\033[1;36m' # bold cyan — the queried package C_CATEGORY='\033[2;37m' # dim white — category label C_HEADER='\033[1;37m' # bold white — section headers C_ERROR='\033[1;31m' # bold red — errors C_SEP='\033[0;90m' # dark grey — separators else C_RESET='' C_INSTALLED='' C_MISSING='' C_TARGET='' C_CATEGORY='' C_HEADER='' C_ERROR='' C_SEP='' fi usage() { cat < Show SBo packages that list in their REQUIRES field. Options: -i show only installed dependents -u show only uninstalled dependents -r recursive: also find what requires those packages (one level) -h this help Symbols: [I] installed on this system [ ] not installed EOF exit 0 } die() { printf "${C_ERROR}error:${C_RESET} %s\n" "$*" >&2; exit 1; } OPT_INSTALLED=0 OPT_UNINSTALLED=0 OPT_RECURSIVE=0 while getopts "iurh" opt; do case "$opt" in i) OPT_INSTALLED=1 ;; u) OPT_UNINSTALLED=1 ;; r) OPT_RECURSIVE=1 ;; h) usage ;; *) usage ;; esac done shift $((OPTIND - 1)) [[ $# -lt 1 ]] && usage TARGET="$1" [[ -d "$SBO_REPO" ]] || die "SBo repo not found at $SBO_REPO (set \$SBO_REPO)" [[ -d "$PKG_LOG" ]] || die "package log not found at $PKG_LOG (set \$PKG_LOG)" # Build installed-package name set (strip version-arch-build suffix) declare -A INSTALLED while IFS= read -r entry; do name="${entry##*/}" # basename name="${name%-*-*-*}" # strip last three dash-fields INSTALLED["$name"]=1 done < <(find "$PKG_LOG" -maxdepth 1 -follow -type f) is_installed() { [[ -n "${INSTALLED[$1]+set}" ]]; } # Search .info files for packages that require TARGET (whole-word match) find_dependents() { local pkg="$1" grep -rl "REQUIRES=.*\b${pkg}\b" "$SBO_REPO" --include="*.info" 2>/dev/null } print_result() { local info_file="$1" local prgnam prgnam=$(grep -m1 '^PRGNAM=' "$info_file" | cut -d'"' -f2) local category category=$(awk -F'/' '{print $(NF-2)}' <<< "$info_file") if is_installed "$prgnam"; then [[ $OPT_UNINSTALLED -eq 1 ]] && return printf "${C_INSTALLED}[I] %-40s${C_RESET} ${C_CATEGORY}(%s)${C_RESET}\n" \ "$prgnam" "$category" else [[ $OPT_INSTALLED -eq 1 ]] && return printf "${C_MISSING}[ ] %-40s${C_RESET} ${C_CATEGORY}(%s)${C_RESET}\n" \ "$prgnam" "$category" fi } printf "${C_HEADER}Packages requiring ${C_TARGET}'%s'${C_RESET}${C_HEADER}:${C_RESET}\n" "$TARGET" printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────" mapfile -t DIRECT < <(find_dependents "$TARGET") if [[ ${#DIRECT[@]} -eq 0 ]]; then printf "${C_SEP}(none found)${C_RESET}\n" exit 0 fi for f in "${DIRECT[@]}"; do print_result "$f" done | sort if [[ $OPT_RECURSIVE -eq 1 ]]; then declare -A SEEN for f in "${DIRECT[@]}"; do prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2) SEEN["$prgnam"]=1 done printf "\n${C_HEADER}Packages requiring those (one level up):${C_RESET}\n" printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────" declare -A SECOND_LEVEL for f in "${DIRECT[@]}"; do prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2) while IFS= read -r f2; do p2=$(grep -m1 '^PRGNAM=' "$f2" | cut -d'"' -f2) [[ -z "${SEEN[$p2]+set}" && -z "${SECOND_LEVEL[$p2]+set}" ]] || continue SECOND_LEVEL["$p2"]="$f2" done < <(find_dependents "$prgnam") done if [[ ${#SECOND_LEVEL[@]} -eq 0 ]]; then printf "${C_SEP}(none found)${C_RESET}\n" else for p2 in "${!SECOND_LEVEL[@]}"; do print_result "${SECOND_LEVEL[$p2]}" done | sort fi fi