diff options
| author | Danilo M. <danix@danix.xyz> | 2026-05-18 20:51:25 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-05-18 20:51:25 +0200 |
| commit | 2456315640a9ec33f9f61a13c44a23a69d85d931 (patch) | |
| tree | 1011bb99ded70dc17ebc52778293387e58579931 /is_required | |
| download | is-required-2456315640a9ec33f9f61a13c44a23a69d85d931.tar.gz is-required-2456315640a9ec33f9f61a13c44a23a69d85d931.zip | |
Initial commit: is_required script and bash completion
Bash script to find SBo packages that depend on a given package,
with installed/uninstalled marking and color output.
Includes bash-completion for package name suggestions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'is_required')
| -rwxr-xr-x | is_required | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/is_required b/is_required new file mode 100755 index 0000000..f64c95f --- /dev/null +++ b/is_required @@ -0,0 +1,139 @@ +#!/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 <<EOF +Usage: $(basename "$0") [OPTIONS] <package> + +Show SBo packages that list <package> 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 |
