depends: use flat dictionary for RequiredBy
[aurutils.git] / lib / aur-repo-filter
blob7fd11936ef7082d4072cd063878facb246220a14
1 #!/bin/bash
2 # aur-repo-filter - filter packages in the Arch Linux repositories
3 [[ -v AUR_DEBUG ]] && set -o xtrace
4 argv0='repo-filter'
5 PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
6 arch_repo=(core extra testing community{,-testing} multilib{,-testing})
8 # default options
9 sync_repo=0
11 args_csv() {
12 # shellcheck disable=SC2155
13 local str=$(printf '%s,' "$@")
14 printf '%s' "${str%,}"
17 # The command line will hit ARG_MAX on approximately 70k packages;
18 # spread arguments with xargs (and printf, as a shell built-in).
19 satisfies() {
20 #global sift_args
21 # Support `unbuffer` if redirecting from `/dev/tty` is insufficient (#1051)
22 if command -v unbuffer >/dev/null; then
23 unbuffer xargs -a <(printf -- "--satisfies=%s\n" "$@") pacsift "${sift_args[@]}"
24 else
25 </dev/tty xargs -a <(printf -- "--satisfies=%s\n" "$@") pacsift "${sift_args[@]}"
29 provides() {
30 #global info_args
31 pacinfo "${info_args[@]}" | awk -F'[:<=>]' '
32 /Name:/ { pkgname=$2; }
33 /Repository:/ { repo=$2
34 print repo, pkgname, pkgname; }
35 /Provides:/ { print repo, pkgname, $2; }
36 /Replaces:/ { print repo, pkgname, $2; }
40 usage() {
41 plain >&2 'usage: %s [-d repo] [-S]' "$argv0"
42 exit 1
45 opt_short='d:a'
46 opt_long=('all' 'sync' 'config:' 'database:' 'sysroot:')
47 opt_hidden=('dump-options' 'repo:')
49 if opts=$(getopt -o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
50 eval set -- "$opts"
51 else
52 usage
55 unset argv_repo sift_args info_args
56 while true; do
57 case "$1" in
58 -a|--all|--sync)
59 sync_repo=1 ;;
60 -d|--database|--repo)
61 shift; argv_repo+=("$1") ;;
62 --config)
63 shift; sift_args+=(--config="$1")
64 info_args+=(--config="$1") ;;
65 --sysroot)
66 shift; sift_args+=(--sysroot="$1")
67 info_args+=(--sysroot="$1") ;;
68 --dump-options)
69 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
70 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
71 exit ;;
72 --) shift; break;;
73 esac
74 shift
75 done
77 if (( sync_repo )); then
78 sift_args+=(--sync)
79 elif [[ -v argv_repo ]]; then
80 sift_args+=(--exact "${argv_repo[@]/#/--repo=}")
81 else
82 sift_args+=(--exact "${arch_repo[@]/#/--repo=}")
85 # check for interactive terminal
86 if [[ -t 0 ]]; then
87 cat >&2 <<EOF
88 Warning: Input is read from the terminal. You either know what you
89 Warning: are doing, or you forgot to pipe data into $argv0.
90 Warning: Press CTRL-D to exit.
91 EOF
94 # associative array for input package names
95 declare -A pkgset
96 strset=()
98 while IFS= read -r str; do
99 # store unversioned string as key
100 name=${str%%[<>=]*}
102 # store version + operator as value (1 if unavailable)
103 if (( ${#str} - ${#name} )); then
104 pkgset[$name]=${str:${#name}}
105 else
106 pkgset[$name]=1
109 # store original versioned string
110 strset+=("$str")
111 done
113 # exit on empty stdin
114 if ! (( ${#pkgset[@]} )); then
115 exit 0
118 # Standard input is taken as "provides[<cmp><version>]", where pacsift
119 # checks for a package satisfying this condition. Results are printed
120 # as "repository/package", and piped to pacinfo to relate the names of
121 # the original provides to its corresponding package(s).
122 satisfies "${strset[@]}" | provides | while read -r repo package virtual; do
123 if [[ ${pkgset[$package]} ]]; then
124 printf '%s\n' "$package"
127 if [[ $virtual != "$package" ]]; then
128 version=${pkgset[$virtual]:-1}
129 else
130 continue
133 case $version in
134 1) printf >&2 'dependency %s satisfied by %s/%s\n' "$virtual" "$repo" "$package"
135 printf '%s\n' "$virtual" ;;
136 *) printf >&2 'dependency %s%s satisfied by %s/%s\n' "$virtual" "$version" "$repo" "$package"
137 printf '%s\n' "$virtual" ;;
138 esac
139 done | sort | uniq
141 # vim: set et sw=4 sts=4 ft=sh: