summaryrefslogtreecommitdiff
path: root/pacpur
diff options
context:
space:
mode:
authorbill-auger <mr.j.spam.me@gmail.com>2020-10-26 00:59:34 -0400
committerbill-auger <mr.j.spam.me@gmail.com>2020-10-26 02:08:06 -0400
commite5d02b363472083090c6e40df46629028407cc3d (patch)
tree3b72de94e3b7ce67b145259645432ace6f2d14f7 /pacpur
parent63d5ee70ab5d0daeff0e5455a462bcab16c9d95f (diff)
rename script pacaur->pacpur
Diffstat (limited to 'pacpur')
-rwxr-xr-xpacpur1888
1 files changed, 1888 insertions, 0 deletions
diff --git a/pacpur b/pacpur
new file mode 100755
index 0000000..684ca98
--- /dev/null
+++ b/pacpur
@@ -0,0 +1,1888 @@
+#!/bin/bash
+# shellcheck source=/dev/null
+
+#
+# pacpur: a PUR helper that minimizes user interaction
+#
+
+shopt -s extglob nullglob
+
+export LC_COLLATE='C'
+# gettext initialization
+export TEXTDOMAIN='pacpur' TEXTDOMAINDIR='/usr/share/locale'
+
+declare -r version='4.8.6'
+
+#
+# Config
+#
+
+# Options
+aur=0 asdeps=0 asexplicit=0 checkdeps=0 clean=0 color='' devel=0 downloadonly=0 help=0
+info=0 installpkg=0 needed=0 noconfirm=0 noedit=0 operation='' pace=0 pacQ=0 pacS=0
+rebuild=0 refresh=0 repo=0 search=0 upgrade=0 ver=0
+declare -i ccount=0 dcount=0 pac=0
+pacmanarg=() pacopts=() auropts=() makeopts+=() pkgs=()
+export QUIET="${QUIET:-0}"
+
+# determine whether we have gettext and replace with noop if not
+command -v gettext >/dev/null || gettext() { printf '%s\n' "$@";}
+
+# check if we have sudo, if not use su instead, but this isn't a good time
+command -v sudo >/dev/null ||
+ sudo() {
+ local params
+ printf -v params '%q ' "$@"
+ su root -c "${params}"
+ }
+
+# import libmakepkg
+for lib in config option parseopts; do
+ source "${LIBRARY:=/usr/share/makepkg}/util/${lib}.sh"
+done
+load_makepkg_config
+: "${PKGDEST:=}" "${CARCH:=}"
+check_buildenv check n || checkdeps=1
+
+## determine config location
+XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS:-/etc/xdg}" XDG_CONFIG_DIRS="${XDG_CONFIG_DIRS//://pacpur:}/pacpur:"
+while read -rd: i; do [[ ! -d "${i}" ]] || { configdir="${i}"; break;} done <<<"${XDG_CONFIG_DIRS}"
+declare -r tmpdir="${XDG_RUNTIME_DIR:-/tmp}" configdir="${configdir:-/etc/xdg/pacpur}" \
+ userconfigdir="${XDG_CONFIG_HOME:-${HOME}/.config}/pacpur" \
+ usercachedir="${XDG_CACHE_HOME:-${HOME}/.cache}/pacpur"
+
+# set default config variables
+editor="${VISUAL:-${EDITOR:-vi}}" # build files editor
+displaybuildfiles='diff' # display build files (none|diff|full)
+silent=0 # silence output
+sortby='name' # sort method (name|votes|popularity)
+sortorder='ascending' # sort order (ascending|descending)
+sudoloop=1 # prevent sudo timeout
+
+# set variables
+declare -r pacman="${PACMAN:-pacman}" # pacman binary
+declare -r clonedir="${PURDEST:-${usercachedir}}" # clone directory
+pacman_conf=('pacman-conf')
+
+# source xdg config
+[[ ! -r "${configdir}/config" ]] || source "${configdir}/config"
+[[ ! -r "${userconfigdir}/config" ]] || source "${userconfigdir}/config"
+
+case "${silent}" in true) silent=1 ;; false) silent=0 ;; esac
+case "${sudoloop}" in true) sudoloop=1;; false) sudoloop=0;; esac
+
+declare -r vcs='-@(cvs|svn|git|hg|bzr|darcs|daily*|nightly*)' # vcs package glob
+
+[[ -d "${clonedir}" && -w "${clonedir}" ]] || mkdir -p "${clonedir}" # setup clonedir
+
+#
+# Functions
+#
+
+ClassifyPkgs() {
+ local i noaurpkgs=()
+ # global aurpkgs repopkgs
+ ((! repo)) || repopkgs=("${pkgs[@]}") aurpkgs=()
+ ((! aur)) || aurpkgs=("${pkgs[@]#aur/}") repopkgs=() # search aur/pkgs in PUR
+ if ((! repo && ! aur)); then
+ for i in "${pkgs[@]}"; do
+ [[ "${i::4}" = 'aur/' ]] && aurpkgs+=("${i:4}") || noaurpkgs+=("${i}") # search aur/pkgs in PUR
+ done
+ if [[ "${noaurpkgs[*]}" ]]; then
+ while read -r i; do
+ [[ "${i}" != 'error: database not found: '* ]] || continue
+ i="${i#error: target not found: }"
+ [[ "${i}" != 'error: '* ]] || continue
+ [[ " ${noaurpkgs[*]} " = *' '+([a-zA-Z0-9.+-])"/${i} "* ]] || aurpkgs+=("${i}")
+ done < <(LC_ALL='C' "${pacman}" -Sp "${noaurpkgs[@]}" 2>&1 >/dev/null)
+ fi
+ mapfile -t repopkgs < <(CommArr -13 aurpkgs noaurpkgs)
+ fi
+}
+
+Core() {
+ GetIgnoredPkgs
+ GetIgnoredGrps
+ ((! upgrade)) || UpgradeAur
+ IgnoreChecks
+ DepsSolver
+ IgnoreDepsCheck
+ ProviderChecks
+ ConflictChecks
+ ReinstallChecks
+ OutOfDateChecks
+ OrphanChecks
+ Prompt
+ MakePkgs
+}
+
+UpgradeAur() {
+ local foreignpkgs allaurpkgs i
+ # global aurpkgs
+ info $"%sStarting PUR upgrade...%s" "${colorW}" "${reset}"
+ mapfile -t aurpkgs < <(auracle sync -q) # use auracle to find out of date PUR packages
+
+ mapfile -t foreignpkgs < <("${pacman}" -Qmq)
+ SetInfo "${foreignpkgs[@]}"
+ mapfile -t allaurpkgs < <(GetInfo Name)
+
+ # foreign packages check
+ while read -r i; do
+ warn $"%s is %snot present%s in PUR -- skipping" "${colorW}${i}${reset}" "${colorY}" "${reset}"
+ done < <(CommArr -13 allaurpkgs foreignpkgs)
+
+ # add devel packages
+ if ((devel)); then
+ needed=1
+ for i in "${allaurpkgs[@]}"; do
+ [[ "${i}" != *${vcs} || " ${aurpkgs[*]} " = *" ${i} "* ]] || aurpkgs+=("${i}")
+ done
+ fi
+
+ aurpkgs+=("${pkgs[@]}")
+
+ SortArrAssign aurpkgs # avoid possible duplicate
+
+ NothingToDo "${aurpkgs[@]}"
+}
+
+IgnoreChecks() {
+ local checkaurpkgs checkaurpkgsver checkaurgrps i j
+ # global aurpkgs rmaurpkgs
+ [[ "${ignoredpkgs[*]:-}${ignoredgrps[*]:-}" ]] || return
+
+ # remove PUR pkgs versioning
+ aurpkgsnover=("${aurpkgs[@]%%[><=]*}")
+
+ # check targets
+ SetInfo "${aurpkgsnover[@]}"
+ mapfile -t checkaurpkgs < <(GetInfo Name)
+ mapfile -t errdeps < <(CommArr -3 aurpkgsnover checkaurpkgs)
+ aurpkgsnover=()
+
+ mapfile -t checkaurpkgsver < <(GetInfo Version)
+ for i in "${!checkaurpkgs[@]}"; do
+ [[ "${checkaurpkgs[i]}" != *${vcs} ]] || checkaurpkgsver[i]=$"latest"
+ isignored=0
+ if [[ "${ignoredpkgs[*]}" && " ${ignoredpkgs[*]} " = *" ${checkaurpkgs[i]} "* ]]; then
+ isignored=1
+ elif [[ "${ignoredgrps[*]}" ]]; then
+ mapfile -t checkaurgrps < <(GetInfo Groups "${checkaurpkgs[i]}")
+ MapfileAdd checkaurgrps < <(expac -Ql'\n' '%G' "${checkaurpkgs[i]}")
+ for j in "${checkaurgrps[@]}"; do
+ [[ " ${ignoredgrps[*]} " != *" ${j} "* ]] || isignored=1
+ done
+ fi
+
+ if ((isignored)); then
+ if ((! upgrade)); then
+ if ((! noconfirm)); then
+ if ! Proceed y $"%s is in IgnorePkg/IgnoreGroup. Install anyway?" "${checkaurpkgs[i]}"; then
+ warn $"skipping target: %s" "${colorW}${checkaurpkgs[i]}${reset}"
+ rmaurpkgs+=("${checkaurpkgs[i]}")
+ continue
+ fi
+ else
+ warn $"skipping target: %s" "${colorW}${checkaurpkgs[i]}${reset}"
+ rmaurpkgs+=("${checkaurpkgs[i]}")
+ continue
+ fi
+ else
+ warn $"%s: ignoring package upgrade (%s => %s)" "${colorW}${checkaurpkgs[i]}${reset}" \
+ "${colorR}$(expac -Q '%v' "${checkaurpkgs[i]}")${reset}" \
+ "${colorG}${checkaurpkgsver[i]}${reset}"
+ rmaurpkgs+=("${checkaurpkgs[i]}")
+ continue
+ fi
+ fi
+ aurpkgsnover+=("${checkaurpkgs[i]}")
+ done
+
+ aurpkgs=("${aurpkgsnover[@]}")
+ NothingToDo "${aurpkgs[@]}"
+}
+
+DepsSolver() {
+ local i j aurpkgsconflicts repodeps=()
+ # global aurpkgs aurpkgsnover aurpkgsproviders aurdeps deps errdeps
+ # global errdepsnover foreignpkgs depsAname depsAver depsAood depsQver
+ printf '%s\n' $"resolving dependencies..."
+
+ # remove PUR pkgs versioning
+ aurpkgsnover=("${aurpkgs[@]%%[><=]*}")
+
+ # set unversioned info
+ SetInfo "${aurpkgsnover[@]}"
+
+ # set targets providers
+ aurpkgsproviders=("${aurpkgsnover[@]}")
+ MapfileAdd aurpkgsproviders < <(GetInfo Provides)
+ aurpkgsproviders=("${aurpkgsproviders[@]%%[><=]*}")
+
+ # check targets conflicts
+ mapfile -t aurpkgsconflicts < <(GetInfo Conflicts)
+ if [[ "${aurpkgsconflicts[*]}" ]]; then
+ aurpkgsconflicts=("${aurpkgsconflicts[@]%%[><=]*}")
+ mapfile -t aurpkgsconflicts < <(CommArr -12 aurpkgsproviders aurpkgsconflicts)
+ for i in "${aurpkgsconflicts[@]}"; do
+ [[ " ${aurpkgsnover[*]} " = *" ${i} "* ]] || continue
+ [[ " $(GetInfo Conflicts "${i}") " != *" ${i} "* ]] || continue
+ fail $"unresolvable package conflicts detected"
+ error $"failed to prepare transaction (conflicting dependencies: %s)" "${i}" \
+ "${E_INSTALL_DEPS_FAILED}"
+ done
+ fi
+
+ deps=("${aurpkgsnover[@]}")
+
+ [[ "${foreignpkgs[*]}" ]] || mapfile -t foreignpkgs < <("${pacman}" -Qmq)
+ FindDepsAur "${aurpkgsnover[@]}"
+
+ # avoid possible duplicate
+ mapfile -t deps < <(CommArr -13 aurdepspkgs deps)
+ deps+=("${aurdepspkgs[@]}")
+
+ # ensure correct dependency order
+ SetInfo "${deps[@]}"
+ SortDepsAur "${aurpkgs[@]}"
+ mapfile -t deps < <(tsort <<< "${tsortdeps[@]}" 2>/dev/null)
+ wait "$!" || error $"dependency cycle detected" "${E_INSTALL_DEPS_FAILED}"
+
+ # get PUR packages info
+ mapfile -t depsAname < <(GetInfo Name)
+ mapfile -t depsAver < <(GetInfo Version)
+ mapfile -t depsAood < <(GetInfo OutOfDate)
+ mapfile -t depsAmain < <(GetInfo Maintainer)
+ for i in "${!depsAname[@]}"; do
+ read -r depsQver[i] < <(expac -Qs '%v' "^${depsAname[i]}$")
+ : "${depsQver[i]:=%}" # avoid empty elements shift
+ [[ "${depsAname[i]}" != *${vcs} ]] || depsAver[i]=$"latest"
+ done
+
+ # no results check
+ if [[ "${errdeps[*]}" ]]; then
+ for i in "${!errdepsnover[@]}"; do
+ if [[ " ${aurpkgsnover[*]} " = *" ${errdepsnover[i]} "* ]]; then
+ fail $"no results found for %s" "${errdeps[i]}"
+ else
+ unset -v currenterrdep
+ # find relevant tsorted deps chain
+ for j in "${deps[@]}"; do
+ tsorterrdeps+=("${j}")
+ [[ "${j}" != "${errdepsnover[i]}" ]] || break
+ done
+ for j in "${!tsorterrdeps[@]}"; do tsorterrdeps+=("${deps[j]}")
+ [[ "${deps[j]}" != "${errdepsnover[i]}" ]] || break; done
+ # reverse deps order
+ mapfile -t tsorterrdeps < \
+ <(for ((j=0; j<${#tsorterrdeps[@]}; j++)) { printf '%s\n' "${tsorterrdeps[-j-1]}";})
+ errdepslist+=("${tsorterrdeps[0]}")
+ FindDepsAurError "${tsorterrdeps[@]}"
+ mapfile -t errdepslist < \
+ <(for ((j=0; j<${#errdepslist[@]}; j++)) { printf '%s\n' "${errdepslist[-j-1]}";})
+ fail $"no results found for %s (dependency tree: %s)" "${errdeps[i]}" "${errdepslist[*]}"
+ fi
+ done
+ exit "${E_INSTALL_DEPS_FAILED}"
+ fi
+
+ # return all binary deps
+ FindDepsRepo "${repodeps[@]}"
+
+ # avoid possible duplicate
+ SortArrAssign repodepspkgs
+}
+
+FindDepsAur() {
+ local depAname depAver depspkgs depspkgsaurtmp builtpkg assumedepspkgs vcsdepspkgs=() aurversionpkgs=()
+ local aurversionpkgsname aurversionpkgsver aurversionpkgsaurver aurversionpkgsverdiff i j
+ # global aurpkgsnover depspkgsaur errdeps repodeps aurdepspkgs prevdepspkgsaur foreignpkgs
+ ((dcount < 2)) || return
+
+ # set info
+ if [[ "${depspkgsaur[*]}" ]]; then
+ SetInfo "${depspkgsaur[@]}"
+ aurversionpkgs=("${prevdepspkgsaur[@]}")
+ else
+ SetInfo "${aurpkgsnover[@]}"
+ aurversionpkgs=("${aurpkgs[@]}")
+ fi
+
+ # versioning check
+ if [[ "${aurversionpkgs[*]}" ]]; then
+ for i in "${!aurversionpkgs[@]}"; do
+ aurversionpkgsname="${aurversionpkgs[i]%%[><=]*}" aurversionpkgsver="${aurversionpkgs[i]##*[><=]}"
+ aurversionpkgsaurver="$(GetInfo Version "${aurversionpkgsname}")"
+ aurversionpkgsverdiff="$(RunVercmp "${aurversionpkgsaurver}" "${aurversionpkgsver}")"
+
+ # not found in PUR nor repo
+ [[ "${aurversionpkgsaurver}" || " ${errdeps[*]} " = *" ${aurversionpkgs[i]} "* ]] ||
+ { errdeps+=("${aurversionpkgs[i]}"); continue;}
+
+ case "${aurversionpkgs[i]}" in
+ *">"*|*"<"*|*"="*) # found in PUR but version not correct
+ case "${aurversionpkgs[i]}" in
+ *">="*) ((aurversionpkgsverdiff < 0)) || continue;;
+ *"<="*) ((aurversionpkgsverdiff > 0)) || continue;;
+ *">"*) ((aurversionpkgsverdiff <= 0)) || continue;;
+ *"<"*) ((aurversionpkgsverdiff >= 0)) || continue;;
+ *"="*) ((aurversionpkgsverdiff)) || continue;;
+ esac
+ [[ " ${errdeps[*]} " = *" ${aurversionpkgs[i]} "* ]] || errdeps+=("${aurversionpkgs[i]}");;
+ *) continue;;
+ esac
+ done
+ fi
+
+ mapfile -t depspkgs < <(GetInfo Depends)
+
+ # cached packages makedeps check
+ if [[ ! "${PKGDEST}" ]] || ((rebuild)); then
+ MapfileAdd depspkgs < <(GetInfo MakeDepends)
+ ((! checkdeps)) || MapfileAdd depspkgs < <(GetInfo CheckDepends)
+ else
+ depspkgsaurtmp=("${depspkgsaur[@]:-${aurpkgs[@]}}")
+ for i in "${!depspkgsaurtmp[@]}"; do
+ depAname="$(GetInfo Name "${depspkgsaurtmp[i]}")"
+ depAver="$(GetInfo Version "${depspkgsaurtmp[i]}")"
+ GetBuiltPkg "${depAname}-${depAver}" "${PKGDEST}"
+ if [[ ! "${builtpkg}" ]]; then
+ MapfileAdd depspkgs < <(GetInfo MakeDepends "${depspkgsaurtmp[i]}")
+ ((! checkdeps)) || MapfileAdd depspkgs < <(GetInfo CheckDepends "${depspkgsaurtmp[i]}")
+ fi
+ builtpkg=''
+ done
+ fi
+
+ # remove deps provided by targets
+ [[ ! "${aurpkgsproviders[*]}" ]] || mapfile -t depspkgs < <(CommArr -13 aurpkgsproviders depspkgs)
+
+ # workaround for limited RPC support of architecture dependent fields
+ if [[ "${CARCH}" = 'i686' ]]; then
+ for i in "${!depspkgs[@]}"; do
+ case "${depspkgs[i]}" in lib32-*|gcc-multilib*) unset -v 'depspkgs[i]';; esac
+ done
+ fi
+
+ if ((devel)); then
+ # remove versioning and check providers
+ depspkgs=("${depspkgs[@]%%[><=]*}")
+ for i in "${!depspkgs[@]}"; do
+ read -r j < <(expac -Qs '%n' "^${depspkgs[i]}$")
+ if [[ "${j}" ]]; then
+ depspkgs[i]="${j}"
+ [[ " ${ignoredpkgs[*]} " = *" ${j} "* || "${j}" != *${vcs} ]] || vcsdepspkgs+=("${j}")
+ else
+ foreignpkgs+=("${depspkgs[i]}")
+ fi
+ done
+ # reorder devel
+ mapfile -t depspkgs < <("${pacman}" -T "${depspkgs[@]}")
+ mapfile -t depspkgs < <(CommArr -3 depspkgs vcsdepspkgs)
+ else
+ mapfile -t depspkgs < <("${pacman}" -T "${depspkgs[@]}")
+ fi
+
+ # split repo and PUR depends pkgs
+ unset -v depspkgsaur
+ if [[ "${depspkgs[*]}" ]]; then
+ # remove all pkgs versioning
+ if ((dcount == 1)); then
+ depspkgs=("${depspkgs[@]%%[><=]*}")
+ # assume installed deps
+ elif [[ "${assumeinstalled[*]}" ]]; then
+ # remove versioning
+ assumeinstalled=("${assumeinstalled[@]%%[><=]*}")
+ for i in "${!assumeinstalled[@]}"; do
+ unset -v assumedepspkgs
+ for j in "${!depspkgs[@]}"; do
+ assumedepspkgs[j]="${depspkgs[j]%%[><=]*}"
+ [[ " ${assumedepspkgs[*]} " = *" ${assumeinstalled[i]} "* ]] &&
+ depspkgs[j]="${assumeinstalled[i]}"
+ done
+ done
+ mapfile -t depspkgs < <(CommArr -13 assumeinstalled depspkgs)
+ fi
+ if [[ "${depspkgs[*]}" ]]; then
+ mapfile -t depspkgsaur < <(LC_ALL='C' "${pacman}" -Sp "${depspkgs[@]}" 2>&1 >/dev/null)
+ depspkgsaur=("${depspkgsaur[@]#error: target not found: }")
+ MapfileAdd repodeps < <(CommArr -13 depspkgsaur depspkgs)
+ fi
+ fi
+ unset -v depspkgs
+
+ # remove duplicates
+ [[ ! "${depspkgsaur[*]}" ]] || mapfile -t depspkgsaur < <(CommArr -13 aurdepspkgs depspkgsaur)
+
+ # dependency cycle check
+ [[ ! "${prevdepspkgsaur[*]}" || "${prevdepspkgsaur[*]}" != "${depspkgsaur[*]}" ]] ||
+ error $"dependency cycle detected (%s)" "${depspkgsaur[*]}" "${E_INSTALL_DEPS_FAILED}"
+
+ if [[ "${depspkgsaur[*]}" ]]; then
+ ((dcount)) || prevdepspkgsaur=("${depspkgsaur[@]}") # store for PUR version check
+ depspkgsaur=("${depspkgsaur[@]%%[><=]*}") # remove versioning
+ fi
+
+ [[ ! "${depspkgsaur[*]}" ]] || { aurdepspkgs+=("${depspkgsaur[@]}"); FindDepsAur "${depspkgsaur[@]}";}
+}
+
+SortDepsAur() {
+ local i j sortdepspkgs sortdepspkgsaur sortaurpkgs=("${checkedsortdepspkgsaur[@]:-${aurpkgs[@]}}")
+ # global checkedsortdepspkgsaur allcheckedsortdepspkgsaur errdepsnover
+
+ checkedsortdepspkgsaur=()
+ for i in "${!sortaurpkgs[@]}"; do
+ sortdepspkgsaur=()
+
+ mapfile -t sortdepspkgs < <(GetInfo Depends "${sortaurpkgs[i]}")
+ MapfileAdd sortdepspkgs < <(GetInfo MakeDepends "${sortaurpkgs[i]}")
+ ((! checkdeps)) || MapfileAdd sortdepspkgs < <(GetInfo CheckDepends "${sortaurpkgs[i]}")
+
+ # remove versioning
+ errdepsnover=("${errdeps[@]%%[><=]*}")
+
+ # check PUR deps only
+ for j in "${!sortdepspkgs[@]}"; do
+ sortdepspkgs[j]="${sortdepspkgs[j]%%[><=]*}"
+ MapfileAdd sortdepspkgsaur < <(GetInfo Name "${sortdepspkgs[j]}")
+ # add erroneous PUR deps
+ [[ " ${errdepsnover[*]} " != *" ${sortdepspkgs[j]} "* ]] || sortdepspkgsaur+=("${sortdepspkgs[j]}")
+ done
+
+ # prepare tsort list
+ if [[ "${sortdepspkgsaur[*]}" ]]; then
+ for j in "${!sortdepspkgsaur[@]}"; do
+ tsortdeps+=("${sortaurpkgs[i]} ${sortdepspkgsaur[j]}")
+ done
+ else
+ tsortdeps+=("${sortaurpkgs[i]} ${sortaurpkgs[i]}")
+ fi
+
+ # filter non checked deps
+ mapfile -t sortdepspkgsaur < <(CommArr -13 allcheckedsortdepspkgsaur sortdepspkgsaur)
+ if [[ "${sortdepspkgsaur[*]}" ]]; then
+ checkedsortdepspkgsaur+=("${sortdepspkgsaur[@]}")
+ allcheckedsortdepspkgsaur+=("${sortdepspkgsaur[@]}")
+ SortArrAssign allcheckedsortdepspkgsaur
+ fi
+ done
+ if [[ "${checkedsortdepspkgsaur[*]}" ]]; then
+ SortArrAssign checkedsortdepspkgsaur
+ SortDepsAur "${checkedsortdepspkgsaur[@]}"
+ fi
+}
+
+FindDepsAurError() {
+ local i nexterrdep="${nexterrdep-}" nextallerrdeps
+ # global errdepsnover errdepslist tsorterrdeps currenterrdep
+
+ for i in "${tsorterrdeps[@]}"; do
+ [[ " ${errdepsnover[*]} ${errdepslist[*]} " = *" ${i} "* ]] && break || nexterrdep="${i}"
+ done
+
+ currenterrdep="${currenterrdep:-${tsorterrdeps[0]}}"
+ if [[ " ${aurpkgs[*]} " != *" ${nexterrdep} "* ]]; then
+ mapfile -t nextallerrdeps < <(GetInfo Depends "${nexterrdep}")
+ MapfileAdd nextallerrdeps < <(GetInfo MakeDepends "${nexterrdep}")
+ ((! checkdeps)) || MapfileAdd nextallerrdeps < <(GetInfo CheckDepends "${nexterrdep}")
+
+ # remove versioning
+ nextallerrdeps=("${nextallerrdeps[@]%%[><=]*}")
+
+ [[ " ${nextallerrdeps[*]} " = *" ${currenterrdep} "* ]] &&
+ errdepslist+=("${nexterrdep}") currenterrdep="${tsorterrdeps[0]}"
+ tsorterrdeps=("${tsorterrdeps[@]:1}")
+ FindDepsAurError "${tsorterrdeps[@]}"
+ else
+ for i in "${!aurpkgs[@]}"; do
+ mapfile -t nextallerrdeps < <(GetInfo Depends "${aurpkgs[i]}")
+ MapfileAdd nextallerrdeps < <(GetInfo MakeDepends "${aurpkgs[i]}")
+ ((! checkdeps)) || MapfileAdd nextallerrdeps < <(GetInfo CheckDepends "${aurpkgs[i]}")
+
+ # remove versioning
+ nextallerrdeps=("${nextallerrdeps[@]%%[><=]*}")
+
+ [[ " ${nextallerrdeps[*]} " != *" ${currenterrdep} "* ]] || errdepslist+=("${aurpkgs[i]}")
+ done
+ fi
+}
+
+FindDepsRepo() {
+ local allrepodepspkgs=() repodepspkgstmp=()
+ # global repodeps repodepspkgs
+ [[ "${repodeps[*]}" ]] || return
+
+ # reduce root binary deps
+ SortArrAssign repodeps
+
+ # add initial repodeps
+ [[ "${repodepspkgs[*]}" ]] || repodepspkgs=("${repodeps[@]}")
+ repodepspkgs=("${repodepspkgs[@]:-${repodeps[@]}}")
+
+ # get non installed binary deps, no version check needed as all deps are repo deps
+ [[ ! "${repodeps[*]}" ]] || mapfile -t allrepodepspkgs < <(expac -S1l'\n' '%E' "${repodeps[@]}")
+ [[ ! "${allrepodepspkgs[*]}" ]] ||
+ mapfile -t repodepspkgstmp < <("${pacman}" -T "${allrepodepspkgs[@]}" | sort -u)
+
+ # remove duplicate
+ [[ ! "${repodepspkgstmp[*]}" ]] || mapfile -t repodepspkgstmp < <(CommArr -13 repodepspkgs{,tmp})
+ [[ ! "${repodepspkgstmp[*]}" ]] || { repodepspkgs+=("${repodepspkgstmp[@]}")
+ repodeps=("${repodepspkgstmp[@]}"); FindDepsRepo "${repodeps[@]}";}
+}
+
+FindDepsRepoProvider() {
+ local -a allproviderrepodepspkgs=() providerrepodepspkgstmp=()
+ # global repodepspkgs
+ [[ "${providerspkgs[*]}" ]] || return
+
+ # reduce root binary deps
+ SortArrAssign providerspkgs
+
+ # get non installed repo deps
+ [[ ! "${providerspkgs[*]}" ]] ||
+ mapfile -t allproviderrepodepspkgs < <(expac -S1l'\n' '%E' "${providerspkgs[@]}")
+ # no version check needed as all deps are binary
+ [[ ! "${allproviderrepodepspkgs[*]}" ]] ||
+ mapfile -t providerrepodepspkgstmp < <("${pacman}" -T "${allproviderrepodepspkgs[@]}" | sort -u)
+
+ # remove duplicate
+ [[ ! "${providerrepodepspkgstmp[*]}" ]] ||
+ mapfile -t providerrepodepspkgstmp < <(CommArr -13 repodepspkgs providerrepodepspkgstmp)
+
+ [[ ! "${providerrepodepspkgstmp[*]}" ]] || { repodepspkgs+=("${providerrepodepspkgstmp[@]}")
+ providerspkgs=("${providerrepodepspkgstmp[@]}"); FindDepsRepoProvider "${providerspkgs[@]}";}
+}
+
+IgnoreDepsCheck() {
+ local i repodepspkgsgrp=()
+ # global ignoredpkgs ignoredgrps aurpkgs aurdepspkgs aurdepspkgsgrp rmaurpkgs deps repodepspkgs
+ [[ "${ignoredpkgs[*]}" || "${ignoredgrps[*]}" ]] || return
+
+ # add checked targets and preserve tsorted order
+ deps=("${deps[@]::${#aurpkgs[@]}}")
+
+ # check dependencies
+ for i in "${repodepspkgs[@]}"; do
+ isignored=0
+ if [[ " ${ignoredpkgs[*]} " = *" ${i} "* ]]; then
+ isignored=1
+ elif [[ "${ignoredgrps[*]}" ]]; then
+ mapfile -t repodepspkgsgrp < <(expac -S1l'\n' '%G' "${i}")
+ MapfileAdd repodepspkgsgrp < <(expac -Ql'\n' '%G' "${i}")
+ for j in "${repodepspkgsgrp[@]}"; do
+ [[ " ${ignoredgrps[*]} " != *" ${j} "* ]] || isignored=1
+ done
+ fi
+
+ if ((isignored)); then
+ { ((! upgrade)) && warn $"skipping target: %s" "${colorW}${i}${reset}";} ||
+ warn $"%s: ignoring package upgrade" "${colorW}${i}${reset}"
+ error $"Unresolved dependency '%s'" "${colorW}${i}${reset}" "${E_INSTALL_DEPS_FAILED}"
+ fi
+ done
+ for i in "${aurdepspkgs[@]}"; do
+ # skip already checked dependencies
+ [[ " ${aurpkgs[*]} " != *" ${i} "* ]] || continue
+ [[ " ${rmaurpkgs[*]} " != *" ${i} "* ]] ||
+ error $"Unresolved dependency '%s'" "${colorW}${i}${reset}" "${E_INSTALL_DEPS_FAILED}"
+
+ isignored=0
+ if [[ " ${ignoredpkgs[*]} " = *" ${i} "* ]]; then
+ isignored=1
+ elif [[ "${ignoredgrps[*]}" ]]; then
+ mapfile -t aurdepspkgsgrp < <(GetInfo Groups "${i}")
+ MapfileAdd aurdepspkgsgrp < <(expac -Ql'\n' '%G' "${i}")
+ for j in "${aurdepspkgsgrp[@]}"; do
+ [[ " ${ignoredgrps[*]} " != *" ${j} "* ]] || isignored=1
+ done
+ fi
+
+ if ((isignored)); then
+ if ((! noconfirm)); then
+ if ! Proceed y $"%s dependency is in IgnorePkg/IgnoreGroup. Install anyway?" "${i}"; then
+ warn $"skipping target: %s" "${colorW}${i}${reset}"
+ error $"Unresolved dependency '%s'" "${colorW}${i}${reset}" "${E_INSTALL_DEPS_FAILED}"
+ fi
+ else
+ { ((upgrade)) && warn $"%s: ignoring package upgrade" "${colorW}${i}${reset}";} ||
+ warn $"skipping target: %s" "${colorW}${i}${reset}"
+ error $"Unresolved dependency '%s'" "${colorW}${i}${reset}" "${E_INSTALL_DEPS_FAILED}"
+ fi
+ fi
+ deps+=("${i}")
+ done
+}
+
+ProviderChecks() {
+ local providersdeps{,nover} repodepspkgsprovided providerspkgs {,providers}{,nb} provided=()
+ # global repodepspkgs repoprovidersconflictingpkgs repodepsSver repodepsSrepo repodepsQver
+ [[ "${repodepspkgs[*]}" ]] || return
+
+ # filter directly provided deps
+ # shellcheck disable=SC2034
+ mapfile -t noprovidersdeps < <(expac -S1 '%n' "${repodepspkgs[@]}")
+ mapfile -t providersdeps < <(CommArr -13 noprovidersdeps repodepspkgs)
+
+ # remove installed providers
+ mapfile -t providersdeps < <("${pacman}" -T "${providersdeps[@]}" | sort -u)
+
+ for i in "${!providersdeps[@]}"; do
+ # check versioning
+ providersdepsname="${providersdeps[i]%%[><=]*}" providersdepsver="${providersdeps[i]##*[><=]}"
+ mapfile -t providersdepsSname < <(expac -Ss '%n' "^${providersdepsname}$")
+ mapfile -t providersdepsSver < <(expac -Ss '%v' "^${providersdepsname}$")
+
+ case "${providersdeps[i]}" in
+ *">"*|*"<"*|*"="*)
+ for j in "${!providersdepsSname[@]}"; do
+ providersdepsverdiff="$(RunVercmp "${providersdepsver}" "${providersdepsSver[j]}")"
+ # found in repo but version not correct
+ case "${providersdeps[i]}" in
+ *">="*) ((providersdepsverdiff < 0)) || continue;;
+ *"<="*) ((providersdepsverdiff > 0)) || continue;;
+ *">"*) ((providersdepsverdiff <= 0)) || continue;;
+ *"<"*) ((providersdepsverdiff >= 0)) || continue;;
+ *"="*) ((providersdepsverdiff)) || continue;;
+ esac
+ providersdepsnover+=("${providersdepsSname[j]}")
+ done
+ ;;
+ esac
+
+ # remove versioning
+ providersdeps[i]="${providersdeps[i]%%[><=]*}"
+
+ # list providers
+ mapfile -t providers < <(expac -Ss '%n' "^${providersdeps[i]}$" | sort -u)
+
+ # filter out non matching versioned providers
+ [[ ! "${providersdepsnover[*]}" ]] || mapfile -t providers < <(CommArr -12 providers{depsnover,})
+
+ # skip if provided in dependency chain
+ repodepspkgsprovided=0
+ for j in "${providers[@]}"; do
+ [[ " ${repodepspkgs[*]} " != *" ${j} "* ]] || repodepspkgsprovided=1
+ done
+ ((! repodepspkgsprovided)) || continue
+
+ # skip if already provided
+ if [[ "${providerspkgs[*]}" ]]; then
+ MapfileAdd provided < <(IFS='|'; expac -Ssl'\n' '%S' "^(${providerspkgs[*]})$")
+ [[ " ${provided[*]} " != *" ${providersdeps[i]} "* ]] || continue
+ fi
+
+ if ((! noconfirm && ${#providers[*]} > 1)); then
+ info $"%sThere are %s providers available for %s:%s" "${colorW}" "${#providers[@]}" \
+ "${providersdeps[i]}" "${reset}"
+ expac -S1 ' %!) %n (%r) ' "${providers[@]}"
+ nb='-1'
+ providersnb="$((${#providers[@]} - 1))" # count from 0
+ while ((nb < 0 || nb >= ${#providers})); do
+ printf '\n%s ' $"Enter a number (default=0):"
+ case "${TERM:-}" in
+ dumb) read -r nb;;
+ *) read -rn "${#providersnb}" nb; printf '\n';;
+ esac
+
+ case "${nb}" in
+ +([0-9])) if ((nb < 0 || nb >= ${#providers[@]})); then
+ printf '\n'
+ fail $"invalid value: %s is not between 0 and %s" "${nb}" "${providersnb}"; ((--i))
+ else
+ break
+ fi;;
+ '') nb=0;;
+ *) fail $"invalid number: %s" "${nb}";;
+ esac
+ done
+ else
+ nb=0
+ fi
+ [[ ! "${providers[*]}" ]] || providerspkgs+=("${providers[nb]}")
+ done
+
+ # add selected providers to repo deps and store for installation
+ repodepspkgs+=("${providerspkgs[@]}") repoprovidersconflictingpkgs+=("${providerspkgs[@]}")
+
+ FindDepsRepoProvider "${providerspkgs[@]}"
+
+ # get binary packages info
+ if [[ "${repodepspkgs[*]}" ]]; then
+ mapfile -t repodepspkgs < <(expac -S1 '%n' "${repodepspkgs[@]}" | sort -u)
+ mapfile -t repodepsSver < <(expac -S1 '%v' "${repodepspkgs[@]}")
+ if [[ "$("${pacman_conf[@]}" VerbosePkgLists)" ]]; then
+ for i in "${!repodepspkgs[@]}"; do
+ repodepsQver[i]="$(expac -Q '%v' "${repodepspkgs[i]}")"
+ done
+ fi
+ mapfile -t repodepsSrepo < <(expac -S1 '%r/%n' "${repodepspkgs[@]}")
+ fi
+}
+
+ConflictChecks() {
+ local allQprovides allQconflicts Aprovides Aconflicts aurconflicts aurAconflicts Qrequires i j
+ local k l repodepsprovides repodepsconflicts repodepsconflictsname checkedrepodepsconflicts=()
+ local repodepsconflictsver localver repoconflictingpkgs repoconflicts=()
+ # global deps depsAname aurdepspkgs aurconflictingpkgs aurconflictingpkgskeep aurconflictingpkgsrm
+ # global depsQver repodepspkgs repoconflictingpkgskeep repoconflictingpkgsrm repoprovidersconflictingpkgs
+ printf '%s\n' $"looking for inter-conflicts..."
+
+ # shellcheck disable=SC2034
+ mapfile -t allQprovides < <(expac -Q '%n')
+ MapfileAdd allQprovides < <(expac -Ql'\n' '%S') # no versioning
+ # shellcheck disable=SC2034
+ mapfile -t allQconflicts < <(expac -Ql'\n' '%C')
+
+ # PUR conflicts
+ Aprovides=("${depsAname[@]}")
+ MapfileAdd Aprovides < <(GetInfo Provides)
+ mapfile -t Aconflicts < <(GetInfo Conflicts)
+ # remove PUR versioning
+ Aprovides=("${Aprovides[@]%%[><=]*}") Aconflicts=("${Aconflicts[@]%%[><=]*}")
+ mapfile -t aurconflicts < <(CommArr -12 Aprovides allQconflicts; CommArr -12 Aconflicts allQprovides)
+ SortArrAssign aurconflicts
+
+ for i in "${aurconflicts[@]}"; do
+ aurAconflicts=()
+ [[ " ${depsAname[*]} " != *" ${i} "* ]] || aurAconflicts=("${i}")
+ for j in "${depsAname[@]}"; do
+ [[ " $(GetInfo Conflicts "${j}") " != *" ${i} "* ]] || aurAconflicts+=("${j}")
+ done
+
+ for j in "${aurAconflicts[@]}"; do
+ Aprovides=()
+ read -r k < <(expac -Qs '%n' "^${i}$")
+ ((installpkg)) || [[ " ${aurdepspkgs[*]} " = *" ${j} "* ]] || continue # download only
+ [[ "${j}" != "${k}" && "${k}" ]] || continue # skip if reinstalling or if no conflict exists
+
+ Aprovides=("${j}")
+ if ((! noconfirm)) && [[ " ${aurconflictingpkgs[*]} " != *" ${k} "* ]]; then
+ if ! Proceed n $"%s and %s are in conflict (%s). Remove %s?" "${j}" "${k}" "${i}" "${k}"; then
+ aurconflictingpkgs+=("${j}" "${k}")
+ aurconflictingpkgskeep+=("${j}")
+ aurconflictingpkgsrm+=("${k}")
+ for l in "${!depsAname[@]}"; do
+ [[ " ${depsAname[l]} " != *"${k}"* ]] || read -r depsQver[l] < <(expac -Qs '%v' "^${k}$")
+ done
+ MapfileAdd Aprovides < <(GetInfo Provides "${j}")
+ Aprovides=("${Aprovides[@]%%[><=]*}") # remove PUR versioning
+ [[ " ${Aprovides[*]} ${aurconflictingpkgsrm[*]} " = *" ${k} "* ]] || CheckRequires "${k}"
+ break
+ else
+ fail $"unresolvable package conflicts detected"
+ fail $"failed to prepare transaction (conflicting dependencies)"
+ if ((upgrade)); then
+ mapfile -t Qrequires < <(expac -Ql'\n' '%N' "${i}")
+ error $"%s and %s are in conflict (required by %s)" "${j}" "${k}" "${Qrequires[*]}" \
+ "${E_INSTALL_DEPS_FAILED}"
+ else
+ error $"%s and %s are in conflict" "${j}" "${k}" "${E_INSTALL_DEPS_FAILED}"
+ fi
+ fi
+ fi
+ MapfileAdd Aprovides < <(GetInfo Provides "${j}")
+ Aprovides=("${Aprovides[@]%%[><=]*}") # remove PUR versioning
+ [[ " ${Aprovides[*]} ${aurconflictingpkgsrm[*]} " = *" ${k} "* ]] || CheckRequires "${k}"
+ done
+ done
+
+ NothingToDo "${deps[@]}"
+
+ # repo conflicts
+ if [[ "${repodepspkgs[*]}" ]]; then
+ # shellcheck disable=SC2034
+ repodepsprovides=("${repodepspkgs[@]}")
+ # no versioning
+ MapfileAdd repodepsprovides < <(expac -S1l'\n' '%S' "${repodepspkgs[@]}")
+ mapfile -t repodepsconflicts < <(expac -S1l'\n' '%H' "${repodepspkgs[@]}")
+
+ # versioning check
+ for i in "${!repodepsconflicts[@]}"; do
+ repodepsconflictsname="${repodepsconflicts[i]%%[><=]*}"
+ repodepsconflictsver="${repodepsconflicts[i]##*[><=]}"
+ localver="$(expac -Q '%v' "${repodepsconflictsname}")"
+ repodepsconflictsverdiff="$(RunVercmp "${repodepsconflictsver}" "${localver}")"
+
+ if [[ "${localver}" ]]; then
+ case "${repodepsconflicts[i]}" in
+ *">="*) ((repodepsconflictsverdiff < 0)) || continue;;
+ *"<="*) ((repodepsconflictsverdiff > 0)) || continue;;
+ *">"*) ((repodepsconflictsverdiff <= 0)) || continue;;
+ *"<"*) ((repodepsconflictsverdiff >= 0)) || continue;;
+ *"="*) ((repodepsconflictsverdiff)) || continue;;
+ esac
+ checkedrepodepsconflicts+=("${repodepsconflictsname}")
+ fi
+ done
+
+ MapfileAdd repoconflicts < <(CommArr -12 repodepsprovides allQconflicts)
+ MapfileAdd repoconflicts < <(CommArr -12 checkedrepodepsconflicts allQprovides)
+ SortArrAssign repoconflicts
+ fi
+
+ for i in "${repoconflicts[@]}"; do
+ read -r j < <(expac -Ss '%n' "^${i}$")
+ read -r k < <(expac -Qs '%n' "^${i}$")
+ [[ "${j}" != "${k}" && "${k}" ]] || continue # skip when no conflict with repopkgs
+ if ((! noconfirm)) && [[ " ${repoconflictingpkgs[*]} " != *" ${k} "* ]]; then
+ if ! Proceed n $"%s and %s are in conflict (%s). Remove %s?" "${j}" "${k}" "${i}" "${k}"; then
+ repoconflictingpkgs+=("${j}" "${k}") repoconflictingpkgskeep+=("${j}")
+ repoconflictingpkgsrm+=("${k}") repoprovidersconflictingpkgs+=("${j}")
+ mapfile -t Qprovides < <(expac -Ssl'\n' '%S' "^${k}$")
+ [[ " ${Qprovides[*]} ${repoconflictingpkgsrm[*]} " = *" ${k} "* ]] || CheckRequires "${k}"
+ break
+ else
+ fail $"unresolvable package conflicts detected"
+ fail $"failed to prepare transaction (conflicting dependencies)"
+ if ((upgrade)); then
+ mapfile -t Qrequires < <(expac -Ql'\n' '%N' "${i}")
+ error $"%s and %s are in conflict (required by %s)" "${j}" "${k}" "${Qrequires[*]}" \
+ "${E_INSTALL_DEPS_FAILED}"
+ else
+ error $"%s and %s are in conflict" "${j}" "${k}" "${E_INSTALL_DEPS_FAILED}"
+ fi
+ fi
+ fi
+ mapfile -t Qprovides < <(expac -Ssl'\n' '%S' "^${k}$")
+ [[ " ${Qprovides[*]} " = *" ${k} "* ]] || CheckRequires "${k}"
+ done
+}
+
+ReinstallChecks() {
+ local i j depsAtmp=("${depsAname[@]}")
+ # global aurpkgs aurdepspkgs deps aurconflictingpkgs depsAname depsQver depsAver depsAood depsAmain
+ for i in "${!depsAtmp[@]}"; do
+ [[ " ${aurpkgs[*]} " = *" ${depsAname[i]} "* &&
+ " ${aurconflictingpkgs[*]} " != *" ${depsAname[i]} "* ]] || continue
+ [[ "${depsQver[i]}" != ?('%') && "$(RunVercmp "${depsAver[i]}" "${depsQver[i]}")" -le 0 ]] || continue
+ ((installpkg)) || [[ " ${aurdepspkgs[*]} " = *" ${depsAname[i]} "* ]] || continue
+ if [[ "${depsAname[i]}" = *${vcs} ]]; then
+ warn $"%s latest revision -- fetching" "${colorW}${depsAname[i]}${reset}"
+ elif ((needed)); then
+ warn $"%s-%s is up to date -- skipping" "${colorW}${depsAname[i]}" "${depsQver[i]}${reset}"
+ for ((j=0; j < ${#deps[@]}; j++)) { [[ "${deps[j]}" != "${depsAname[i]}" ]] || unset -v 'deps[j]';}
+ deps=("${deps[@]}")
+ unset -v 'depsAname[i]' 'depsQver[i]' 'depsAver[i]' 'depsAood[i]' 'depsAmain[i]'
+ else
+ warn $"%s-%s is up to date -- reinstalling" "${colorW}${depsAname[i]}" "${depsQver[i]}${reset}"
+ fi
+ done
+ depsAname=("${depsAname[@]}") depsQver=("${depsQver[@]}") depsAver=("${depsAver[@]}")
+ depsAood=("${depsAood[@]}") depsAmain=("${depsAmain[@]}")
+ NothingToDo "${deps[@]}"
+}
+
+OutOfDateChecks() {
+ local i
+ # global depsAname depsAver depsAood
+ for i in "${!depsAname[@]}"; do
+ ((depsAood[i] <= 0)) ||
+ warn $"%s-%s has been flagged %sout of date%s on %s" "${colorW}${depsAname[i]}" "${depsAver[i]}${reset}" "${colorR}" "${reset}" "${colorY}$(printf '%(%c)T' "${depsAood[i]}")${reset}"
+ done
+}
+
+OrphanChecks() {
+ local i
+ # global depsAname depsAver depsAmain
+ for i in "${!depsAname[@]}"; do
+ [[ "${depsAmain[i]}" != '%' ]] || warn $"%s-%s is %sorphaned%s in PUR" \
+ "${colorW}${depsAname[i]}" "${depsAver[i]}${reset}" "${colorR}" "${reset}"
+ done
+}
+
+Prompt() {
+ local i binaryksize sumk summ builtpkg cachedpkgs stroldver strnewver strsize depsver
+ local repodepspkgsver strrepodlsize strrepoinsize strsumk strsumm lreposizelabel lreposize
+ # global repodepspkgs repodepsSver depsAname depsAver depsArepo depsAcached lname lver lsize
+ # global deps depsQver repodepspkgs repodepsSrepo repodepsQver repodepsSver
+ # compute binary size
+ if [[ "${repodepspkgs[*]}" ]]; then
+ mapfile -t binaryksize < <(expac -S1 '%k' "${repodepspkgs[@]}")
+ mapfile -t binarymsize < <(expac -S1 '%m' "${repodepspkgs[@]}")
+ sumk=0 summ=0
+ for i in "${!repodepspkgs[@]}"; do
+ GetBuiltPkg "${repodepspkgs[i]}-${repodepsSver[i]}" "$("${pacman_conf[@]}" CacheDir)"
+ [[ ! "${builtpkg}" ]] || binaryksize[i]=0
+ ((sumk += binaryksize[i]))
+ ((summ += binarymsize[i]))
+ done
+ sumk="$((sumk / 1048576)).$((sumk / 1024 % 1024 * 100 / 1024))"
+ summ="$((summ / 1048576)).$((summ / 1024 % 1024 * 100 / 1024))"
+ fi
+
+ # cached packages check
+ for i in "${!depsAname[@]}"; do
+ [[ "${PKGDEST}" && rebuild -eq 0 ]] || break
+ GetBuiltPkg "${depsAname[i]}-${depsAver[i]}" "${PKGDEST}"
+ [[ "${builtpkg}" ]] &&
+ cachedpkgs+=("${depsAname[i]}") depsAcached[i]='('$"cached"')' || depsAcached[i]=''
+ builtpkg=''
+ done
+
+ if [[ "$("${pacman_conf[@]}" VerbosePkgLists)" ]]; then
+ straurname=$"PUR Packages ""(${#deps[@]})" strreponame=$"Repo Packages ""(${#repodepspkgs[@]})"
+ stroldver=$"Old Version" strnewver=$"New Version" strsize=$"Download Size"
+ depsArepo=("${depsAname[@]/#/aur/}")
+ lname="$(GetLength "${depsArepo[@]}" "${repodepsSrepo[@]}" "${straurname}" "${strreponame}")"
+ lver="$(GetLength "${depsQver[@]}" "${depsAver[@]}" "${repodepsQver[@]}" "${repodepsSver[@]}" \
+ "${stroldver}" "${strnewver}")"
+ lsize="$(GetLength "${strsize}")"
+
+ # local version column cleanup
+ for ((i=0; i<${#deps[@]}; i++)) { [[ "${depsQver[i]}" != '%' ]] || depsQver[i]='';}
+ # show detailed output
+ printf "\n${colorW}%-${lname}s %-${lver}s %-${lver}s${reset}\n\n" \
+ "${straurname}" "${stroldver}" "${strnewver}"
+ for i in "${!deps[@]}"; do
+ printf "%-${lname}s ${colorR}%-${lver}s${reset} ${colorG}%-${lver}s${reset} %${lsize}s\n" \
+ "${depsArepo[i]}" "${depsQver[i]}" "${depsAver[i]}" "${depsAcached[i]-}"
+ done
+
+ if [[ "${repodepspkgs[*]}" ]]; then
+ for i in "${!repodepspkgs[@]}"; do
+ binarysize[i]="$((binaryksize[i] / 1048576)).$((binaryksize[i] / 1024 % 1024 * 100 / 1024))"
+ done
+ printf "\n${colorW}%-${lname}s %-${lver}s %-${lver}s %s${reset}\n\n" \
+ "${strreponame}" "${stroldver}" "${strnewver}" "${strsize}"
+ for i in "${!repodepspkgs[@]}"; do
+ printf "%-${lname}s ${colorR}%-${lver}s${reset} ${colorG}%-${lver}s${reset} %${lsize}s\n" \
+ "${repodepsSrepo[i]}" "${repodepsQver[i]}" "${repodepsSver[i]}" "${binarysize[i]}"$" MiB"
+ done
+ fi
+ else
+ # show version
+ for i in "${!deps[@]}"; do
+ depsver+="${depsAname[i]}-${depsAver[i]} "
+ done
+ for i in "${!repodepspkgs[@]}"; do
+ repodepspkgsver+="${repodepspkgs[i]}-${repodepsSver[i]} "
+ done
+ printf "\n${colorW}%-16s${reset} %s\n" $"PUR Packages ""(${#deps[@]})" "${depsver}"
+ [[ ! "${repodepspkgs[*]}" ]] ||
+ printf "${colorW}%-16s${reset} %s\n" $"Repo Packages ""(${#repodepspkgs[@]})" "${repodepspkgsver}"
+ fi
+
+ if [[ "${repodepspkgs[*]}" ]]; then
+ strrepodlsize=$"Repo Download Size:" strrepoinsize=$"Repo Installed Size:" strsumk="${sumk}"$"MiB"
+ strsumm="${summ}"$"MiB" lreposizelabel="$(GetLength "${strrepodlsize}" "${strrepoinsize}")"
+ lreposize="$(GetLength "${strsumk}" "${strsumm}")"
+ printf "\n${colorW}%-${lreposizelabel}s${reset} %${lreposize}s\n" "${strrepodlsize}" "${strsumk}"
+ printf "${colorW}%-${lreposizelabel}s${reset} %${lreposize}s\n" "${strrepoinsize}" "${strsumm}"
+ fi
+
+ printf '\n'
+ if ((installpkg)); then
+ Proceed y $"Proceed with installation?" || exit "${E_FAIL}"
+ else
+ Proceed y $"Proceed with download?" || exit "${E_FAIL}"
+ fi
+}
+
+DownloadPkgs() {
+ local i
+ # global basepkgs
+ info $"%sRetrieving package(s)...%s" "${colorW}" "${reset}"
+ GetPkgbase "$@"
+
+ # no results check
+ [[ "${basepkgs[*]}" ]] || error $"no results found" "${E_INSTALL_DEPS_FAILED}"
+
+ # record previous HEAD for diff viewing
+ if [[ "${displaybuildfiles}" = 'diff' ]]; then
+ for i in "${basepkgs[@]/#/${clonedir}/}"; do
+ [[ ! -d "${i}" ]] || git -C "${i}" update-ref AUR_SEEN HEAD
+ done
+ fi
+
+ auracle -C "${clonedir}" clone "$@" >/dev/null || error $"failed to retrieve packages" 1 # clone
+}
+
+EditPkgs() {
+ local viewed=0 i j erreditpkg prev
+ # global cachedpkgs installscripts editor
+ ((! noedit)) || return
+ for i in "$@"; do
+ [[ " ${cachedpkgs[*]} " != *" ${i} "* ]] || continue
+ GetInstallScripts "${i}"
+ if ((! pace)); then
+ prev="$(git -C "${clonedir}/${i}" rev-parse AUR_SEEN 2>/dev/null)"
+ if [[ "${displaybuildfiles}" = 'diff' && "${prev}" != 'AUR_SEEN' ]]; then
+ # show diff
+ if git -C "${clonedir}/${i}" diff --quiet --no-ext-diff "${prev}" -- . ':!\.SRCINFO'; then
+ warn $"%s build files are up-to-date -- skipping" "${colorW}${i}${reset}"
+ else
+ if Proceed y $"View %s build files diff?" "${i}"; then
+ git -C "${clonedir}/${i}" diff --no-ext-diff "${prev}" -- . ':!\.SRCINFO' ||
+ erreditpkg+=("${i}")
+ info $"%s build files diff viewed" "${colorW}${i}${reset}"; viewed=1
+ fi
+ fi
+ elif [[ "${displaybuildfiles}" != 'none' ]]; then
+ # show pkgbuild
+ if Proceed y $"View %s PKGBUILD?" "${i}"; then
+ if [[ -e "${clonedir}/${i}/PKGBUILD" ]]; then
+ "${editor}" "${clonedir}/${i}/PKGBUILD" &&
+ info $"%s PKGBUILD viewed" "${colorW}${i}${reset}" || erreditpkg+=("${i}")
+ else
+ error $"Could not open %s PKGBUILD" "${colorW}${i}${reset}" "${E_MISSING_FILE}"
+ fi
+ fi
+ # show install script
+ if [[ "${installscripts[*]}" ]]; then
+ for j in "${installscripts[@]}"; do
+ if Proceed y $"View %s script?" "${j}"; then
+ if [[ -e "${clonedir}/${i}/${j}" ]]; then
+ "${editor}" "${clonedir}/${i}/${j}" &&
+ info $"%s script viewed" "${colorW}${j}${reset}" ||
+ erreditpkg+=("${i}")
+ else
+ error $"Could not open %s script" "${colorW}${j}${reset}" "${E_MISSING_FILE}"
+ fi
+ fi
+ done
+ fi
+ fi
+ else
+ # show pkgbuild and install script
+ if [[ -e "${clonedir}/${i}/PKGBUILD" ]]; then
+ "${editor}" "${clonedir}/${i}/PKGBUILD" &&
+ info $"%s PKGBUILD viewed" "${colorW}${i}${reset}" || erreditpkg+=("${i}")
+ else
+ error $"Could not open %s PKGBUILD" "${colorW}${i}${reset}" "${E_MISSING_FILE}"
+ fi
+ if [[ "${installscripts[*]}" ]]; then
+ for j in "${installscripts[@]}"; do
+ if [[ -e "${clonedir}/${i}/${j}" ]]; then
+ "${editor}" "${clonedir}/${i}/${j}" &&
+ info $"%s script viewed" "${colorW}${j}${reset}" || erreditpkg+=("${i}")
+ else
+ error $"Could not open %s script" "${colorW}${j}${reset}" "${E_MISSING_FILE}"
+ fi
+ done
+ fi
+ fi
+ done
+
+ if [[ "${erreditpkg[*]}" ]]; then
+ erreditpkg=("${erreditpkg[@]/#/${colorW}}")
+ fail $"%s errored on exit" "${erreditpkg[@]/%/${reset}}"
+ exit "${E_FAIL}"
+ fi
+
+ if [[ "${displaybuildfiles}" = 'diff' ]] && ((viewed)); then
+ if ((installpkg)); then
+ Proceed y $"Proceed with installation?" || exit
+ else
+ Proceed y $"Proceed with download?" || exit
+ fi
+ fi
+}
+
+MakePkgs() {
+ local oldorphanpkgs orphanpkgs oldoptionalpkgs optionalpkgs pkgsdepslist
+ local vcsclients vcschecked aurpkgsAver aurpkgsQver built{,deps}pkgs
+ local checkpkgsdepslist isaurdeps makedeps errmakepkgs i j k l
+ # global deps basepkgs sudoloop pkgsbase pkgsdeps aurpkgs aurdepspkgs builtpkg
+ # global repoprovidersconflictingpkgs
+
+ # download
+ DownloadPkgs "${deps[@]}"
+ EditPkgs "${basepkgs[@]}"
+
+ # current orphan and optional packages
+ # shellcheck disable=SC2034
+ mapfile -t oldorphanpkgs < <("${pacman}" -Qtdq)
+ mapfile -t oldoptionalpkgs < <("${pacman}" -Qttdq)
+ # shellcheck disable=SC2034
+ mapfile -t oldoptionalpkgs < <(CommArr -13 oldorphanpkgs oldoptionalpkgs)
+
+ # initialize sudo
+ if [[ "$(sudo -V 2>/dev/null)" = 'Sudo'* ]]; then
+ if sudo -n "${pacman}" -V &>/dev/null || sudo -v; then
+ ((! sudoloop)) || SudoV &
+ fi
+ fi
+
+ # split packages support
+ for i in "${!pkgsbase[@]}"; do
+ for j in "${!deps[@]}"; do
+ [[ "${pkgsbase[i]}" != "${pkgsbase[j]}" || "${pkgsdeps[*]}" = *",${deps[j]}"?(,*) ]] ||
+ pkgsdeps[i]+=",${deps[j]}"
+ done
+ done
+ pkgsdeps=("${pkgsdeps[@]#,}") # remove empty array indices and leading ','
+
+ # reverse deps order
+ mapfile -t basepkgs < <(for ((i=0; i<${#basepkgs[@]}; i++)) { printf '%s\n' "${basepkgs[-i-1]}";})
+ mapfile -t basepkgs < <(for ((i=0; i<${#pkgsdeps[@]}; i++)) { printf '%s\n' "${pkgsdeps[-i-1]}";})
+
+ # integrity check
+ for i in "${!basepkgs[@]}"; do
+ mapfile -td',' pkgsdepslist < <(printf '%s' "${pkgsdeps[i]}") # get split packages list
+
+ # cache check
+ builtpkg=''
+ if [[ "${basepkgs[i]}" != *${vcs} ]]; then
+ for j in "${pkgsdepslist[@]}"; do
+ [[ ! "${PKGDEST}" ]] || ((rebuild)) ||
+ GetBuiltPkg "${j}-$(GetInfo Version "${j}")" "${PKGDEST}"
+ done
+ fi
+
+ # install vcs clients (checking pkgbase extension only does not take fetching specific
+ # commit into account)
+ vcsclients=()
+ mapfile -t makedeps < <(GetInfo MakeDepends "${basepkgs[i]}")
+
+ for j in git subversion mercurial bzr cvs darcsl; do
+ [[ " ${makedeps[*]} " != *" ${j} "* ]] || vcsclients+=("${j}")
+ done
+ for j in "${vcsclients[@]}"; do
+ if [[ " ${vcschecked[*]} " != *" ${j} "* ]]; then
+ expac -Qs '' "^${j}$" || sudo "${pacman}" -S --asdeps --noconfirm "${j}"
+ vcschecked+=("${j}")
+ fi
+ done
+
+ if [[ ! "${builtpkg-}" ]] || ((rebuild)); then
+ cd_safe "${clonedir}/${basepkgs[i]}"
+ info $"Checking %s integrity..." "${colorW}${pkgsdeps[i]}${reset}"
+ MakePkg -f --verifysource >/dev/null || errmakepkgs+=("${pkgsdeps[i]}")
+ # extraction, prepare and pkgver update
+ info $"Preparing %s..." "${colorW}${pkgsdeps[i]}${reset}"
+ MakePkg -Cod --skipinteg || errmakepkgs+=("${pkgsdeps[i]}")
+ fi
+ done
+ if [[ "${errmakepkgs[*]}" ]]; then
+ errmakepkgs=("${errmakepkgs[@]/#/${colorW}}")
+ fail $"failed to verify integrity or prepare %s package" "${errmakepkgs[@]/%/${reset}}"
+ # remove sudo lock
+ rm -f "${tmpdir}/pacpur.sudov.lck"
+ exit "${E_FAIL}"
+ fi
+
+ # check database lock
+ [[ ! -e "/var/lib/pacman/db.lck" ]] || error $"db.lck exists in /var/lib/pacman" "${E_FAIL}"
+
+ # set build lock
+ [[ ! -e "${tmpdir}/pacpur.build.lck" ]] || error $"pacpur.build.lck exists in %s" "${tmpdir}" "${E_FAIL}"
+ :>"${tmpdir}/pacpur.build.lck"
+
+ # install provider packages and repo conflicting packages that makepkg --noconfirm cannot handle
+ if [[ "${repoprovidersconflictingpkgs[*]}" ]]; then
+ info $"Installing %s dependencies..." "${colorW}${repoprovidersconflictingpkgs[*]}${reset}"
+ sudo "${pacman}" -S "${repoprovidersconflictingpkgs[@]}" --ask 36 --asdeps --noconfirm
+ fi
+
+ # main
+ for i in "${!basepkgs[@]}"; do
+ mapfile -td',' pkgsdepslist < <(printf '%s' "${pkgsdeps[i]}") # get split packages list
+
+ cd_safe "${clonedir}/${basepkgs[i]}"
+ # retrieve updated version
+ mapfile -td'-' k < <(makepkg --packagelist); aurpkgsAver="${k[-3]}-${k[-2]}"; unset -v k
+ # build devel if necessary only (supported protocols only)
+ if [[ "${basepkgs[i]}" = *${vcs} ]]; then
+ # check split packages update
+ checkpkgsdepslist=()
+ for j in "${pkgsdepslist[@]}"; do
+ aurpkgsQver="$(expac -Qs '%v' "^${j}$")"
+ if ((needed && ! rebuild)) &&
+ [[ "${aurpkgsQver}" && "$(RunVercmp "${aurpkgsQver}" "${aurpkgsAver}")" -ge 0 ]]; then
+ warn $"%s is up-to-date -- skipping" "${colorW}${j}${reset}"; continue
+ else
+ checkpkgsdepslist+=("${j}")
+ fi
+ done
+ if [[ "${checkpkgsdepslist[*]}" ]]; then
+ pkgsdepslist=("${checkpkgsdepslist[@]}")
+ else
+ continue
+ fi
+ fi
+
+ # check package cache
+ for j in "${pkgsdepslist[@]}"; do
+ builtpkg=''
+ [[ ! "${PKGDEST}" ]] || ((rebuild)) || GetBuiltPkg "${j}-${aurpkgsAver}" "${PKGDEST}"
+ if [[ "${builtpkg}" ]]; then
+ if [[ " ${aurdepspkgs[*]} " = *" ${j} "* ]] || ((installpkg)); then
+ info $"Installing %s cached package..." "${colorW}${j}${reset}"
+ sudo "${pacman}" -U --ask 36 --noconfirm "${pacopts[@]}" "${builtpkg}"
+ [[ " ${aurpkgs[*]} " = *" ${j} "* ]] ||
+ sudo "${pacman}" -D "${j}" --asdeps "${pacopts[@]}" &>/dev/null
+ else
+ warn $"Package %s already available in cache" "${colorW}${j}${reset}"
+ fi
+ pkgsdeps=("${pkgsdeps[@]/#${j},}"); pkgsdeps=("${pkgsdeps[@]/%,${j}}")
+ pkgsdeps=("${pkgsdeps[@]//,${j},/,}")
+ for k in "${!pkgsdeps[@]}"; do
+ [[ "${pkgsdeps[k]}" != "${j}" ]] || pkgsdeps[k]='%'
+ done
+ continue
+ fi
+ done
+ [[ "${pkgsdeps[i]}" != '%' ]] || continue
+
+ # build
+ info $"Building %s package(s)..." "${colorW}${pkgsdeps[i]}${reset}"
+
+ # install then remove binary deps
+ makeopts=("${makeopts[@]/-r/}")
+
+ if ((! installpkg)); then
+ isaurdeps=0
+ for j in "${pkgsdepslist[@]}"; do
+ [[ " ${aurdepspkgs[*]} " != *" ${j} "* ]] || isaurdeps=1
+ done
+ ((! isaurdeps)) || makeopts+=('-r')
+ fi
+
+ # skip install for packages that fail to build
+ MakePkg -sefc --noconfirm || { errmakepkgs+=("${pkgsdeps[i]}"); continue;}
+
+ # retrieve filename
+ builtpkgs=() builtdepspkgs=()
+ for j in "${pkgsdepslist[@]}"; do
+ builtpkg=''
+ GetBuiltPkg "${j}-${aurpkgsAver}" "${PKGDEST:-${clonedir}/${basepkgs[i]}}"
+ [[ " ${aurdepspkgs[*]} " = *" ${j} "* ]] && builtdepspkgs+=("${builtpkg}") ||
+ builtpkgs+=("${builtpkg}")
+ done
+
+ # install
+ if ((installpkg)) || [[ ! "${builtpkgs[*]}" ]]; then
+ info $"Installing %s package(s)..." "${colorW}${pkgsdeps[i]}${reset}"
+ sudo "${pacman}" -U "${builtdepspkgs[@]}" "${builtpkgs[@]}" \
+ --ask 36 "${pacopts[@]}" --noconfirm
+ fi
+
+ # set dep status
+ if ((installpkg)); then
+ for j in "${pkgsdepslist[@]}"; do
+ [[ " ${aurpkgs[*]} " = *" ${j} "* ]] || sudo "${pacman}" -D "${j}" --asdeps &>/dev/null
+ ((! asdeps)) || sudo "${pacman}" -D "${j}" --asdeps &>/dev/null
+ ((! asexplicit)) || sudo "${pacman}" -D "${j}" --asexplicit &>/dev/null
+ done
+ fi
+ done
+
+ # remove PUR deps
+ if ((! installpkg)); then
+ [[ ! "${aurdepspkgs[*]}" ]] || mapfile -t aurdepspkgs < <(expac -Q '%n' "${aurdepspkgs[@]}")
+ [[ ! "${aurdepspkgs[*]}" ]] || { info $"Removing installed PUR dependencies..."
+ sudo "${pacman}" -Rsn "${aurdepspkgs[@]}" --noconfirm;}
+ # re-add removed conflicting packages
+ [[ ! "${aurconflictingpkgsrm[*]}${repoconflictingpkgsrm[*]}" ]] || sudo "${pacman}" -S \
+ "${aurconflictingpkgsrm[@]}" "${repoconflictingpkgsrm[@]}" --ask 36 --asdeps --needed --noconfirm
+ fi
+
+ rm -f "${tmpdir}/pacpur."{'build','sudov'}'.lck' # remove locks
+
+ # new orphan and optional packages check
+ # shellcheck disable=SC2034
+ mapfile -t orphanpkgs < <("${pacman}" -Qdtq)
+ while read -r i; do
+ warn $"%s is now an %sorphan%s package" "${colorW}${i}${reset}" "${colorY}" "${reset}"
+ done < <(CommArr -13 oldorphanpkgs orphanpkg)
+ mapfile -t optionalpkgs < <("${pacman}" -Qdttq)
+ # shellcheck disable=SC2034
+ mapfile -t optionalpkgs < <(CommArr -13 orphanpkgs optionalpkgs)
+ while read -r i; do
+ warn $"%s is now an %soptional%s package" "${colorW}${i}${reset}" "${colorY}" "${reset}"
+ done < <(CommArr -13 oldoptionalpkgs optionalpkgs)
+
+ # makepkg and install failure check
+ if [[ "${errmakepkgs[*]}" ]]; then
+ errmakepkgs=("${errmakepkgs[@]/#/${colorW}}")
+ fail $"failed to build %s package(s)" "${errmakepkgs[@]/%/${reset}}"
+ exit "${E_PACKAGE_FAILED}"
+ fi
+}
+
+CleanCache() {
+ local cachepkgs foreignpkgs foreignpkgsbase
+ mapfile -t cachedir < <("${pacman_conf[@]}" CacheDir)
+ [[ ! "${cachedir[*]}" ]] || cachedir=("${cachedir[@]%/}") PKGDEST="${PKGDEST%/}"
+ if [[ "${PKGDEST}" && " ${cachedir[*]} " != *" ${PKGDEST} "* ]]; then
+ ((ccount != 1)) || printf '\n%s\n %s\n' $"Packages to keep:" $"All locally installed packages"
+ printf '\n%s %s\n' $"PUR cache directory:" "${PKGDEST}"
+ if ((ccount == 1)); then
+ if Proceed y $"Do you want to remove all other packages from PUR cache?"; then
+ printf '%s\n' $"removing old packages from cache..."
+ cachepkgs=("${PKGDEST}/"*) cachepkgs=("${cachepkgs[@]##*/}")
+ for i in "${cachepkgs[@]%-*}"; do
+ [[ "${i}" = "$(expac -Q '%n-%v' "${i%-*-*}")" ]] || rm "${PKGDEST}/${i}-"*
+ done
+ fi
+ else
+ Proceed n $"Do you want to remove ALL files from PUR cache?" ||
+ { printf '%s\n' $"removing all files from PUR cache..."
+ rm "${PKGDEST}/"* &>/dev/null;}
+ fi
+ fi
+
+ if [[ -d "${SRCDEST}" ]]; then
+ ((ccount != 1)) || printf '\n%s\n %s\n' $"Sources to keep:" $"All development packages sources"
+ printf '\n%s %s\n' $"PUR source cache directory:" "${SRCDEST}"
+ if ((ccount == 1)) &&
+ Proceed y $"Do you want to remove all non development files from PUR source cache?"; then
+ printf '%s\n' $"removing non development files from source cache..."
+ rm -f "${SRCDEST}/"* &>/dev/null
+ elif ! Proceed n $"Do you want to remove ALL files from PUR source cache?"; then
+ printf '%s\n' $"removing all files from PUR source cache..."
+ rm -rf "${SRCDEST:?}/"*
+ fi
+ fi
+ if [[ -d "${clonedir}" ]]; then
+ if ((ccount == 1)); then
+ if [[ ! "${pkgs[*]}" ]]; then
+ printf '\n%s\n %s\n' $"Clones to keep:" $"All locally installed clones"
+ else
+ printf '\n%s\n %s\n' $"Clones to keep:" $"All other locally installed clones"
+ fi
+ fi
+ printf '\n%s %s\n' $"PUR clone directory:" "${clonedir}"
+ if ((ccount == 1)); then
+ mapfile -t foreignpkgs < <("${pacman}" -Qmq)
+ mapfile -t foreignpkgsbase < <(expac -Q '%e' "${foreignpkgs[@]}")
+ # get target
+ if [[ "${pkgs[*]}" ]]; then
+ mapfile -t pkgsbase < <(expac -Q '%e' "${pkgs[@]}")
+ mapfile -t aurpkgsbase < <(CommArr -13 pkgsbase foreignpkgsbase)
+ if Proceed y $"Do you want to remove %s clones from PUR clone directory?" \
+ "${aurpkgsbase[*]}"; then
+ printf '%s\n\n' $"removing uninstalled clones from PUR clone cache..."
+ for i in "${aurpkgsbase[@]}"; do
+ [[ ! -d "${clonedir}/${i}" ]] || rm -rf "${clonedir:?}/${i}"
+ done
+ fi
+ else
+ if Proceed y $"Do you want to remove all uninstalled clones from PUR clone directory?"; then
+ printf '%s\n\n' $"removing uninstalled clones from PUR clone cache..."
+ for i in "${clonedir}/"*; do
+ [[ ! -d "${i}" || " ${foreignpkgsbase[*]} " = *" ${i} "* ]] ||
+ rm -rf "${clonedir:?}/${i}"
+ done
+ fi
+ if [[ ! "${PKGDEST}" || ! "${SRCDEST}" ]]; then
+ if Proceed y $"Do you want to remove all untracked files from PUR clone directory?"; then
+ printf '%s\n' $"removing untracked files from PUR clone cache..."
+ for i in "${clonedir}/"*; do
+ [[ ! -d "${i}" ]] ||
+ git --git-dir="${i}/.git" --work-tree="${i}" clean -ffdx &>/dev/null
+ done
+ fi
+ fi
+ fi
+ else
+ if ! Proceed n $"Do you want to remove ALL clones from PUR clone directory?"; then
+ printf '%s\n' $"removing all clones from PUR clone cache..."
+ for i in "${clonedir}/"*; do [[ ! -d "${i}" ]] || rm -rf "${i}"; done
+ fi
+ fi
+ fi
+ exit "${E_OK}"
+}
+
+GetIgnoredPkgs() {
+ # global ignoredpkgs
+ MapfileAdd ignoredpkgs < <("${pacman_conf[@]}" IgnorePkg)
+}
+
+GetIgnoredGrps() {
+ # global ignoredgrps
+ MapfileAdd ignoredgrps < <("${pacman_conf[@]}" IgnoreGroup)
+}
+
+GetInstallScripts() {
+ # global installscripts
+ [[ ! -d "${clonedir}/$1" ]] ||
+ installscripts=("${clonedir}/$1/"*'.install') installscripts=("${installscripts[@]##*/}")
+}
+
+GetBuiltPkg() {
+ local ext
+ # global builtpkg
+ # check PKGEXT suffix first, then default .xz suffix for repository packages in pacman cache
+ # and lastly all remaining suffixes in case PKGEXT is locally overridden
+ for ext in "${PKGEXT}" .pkg.tar{.xz,,.gz,.bz2,.lzo,.lrz,.Z}; do
+ builtpkg="$2/$1-${CARCH}${ext}"
+ [[ -f "${builtpkg}" ]] || builtpkg="$2/$1-any${ext}"
+ [[ ! -f "${builtpkg}" ]] || break
+ done
+ [[ -f "${builtpkg}" ]] || { builtpkg=''; return 1;}
+}
+
+GetPkgbase() {
+ local i
+ # global pkgsbase basepkgs
+ [[ "${pkgsbase[*]:-}" ]] || pkgsbase=()
+ SetInfo "$@"
+ for i in "$@"; do
+ MapfileAdd pkgsbase < <(GetInfo PackageBase "${i}")
+ done
+ for i in "${pkgsbase[@]}"; do
+ [[ " ${basepkgs[*]} " = *" ${i} "* ]] || basepkgs+=("${i}")
+ done
+}
+
+# shellcheck disable=SC2034,SC2154
+SetInfo() {
+ # Use auracle formatted info output for all aur packages passed to SetInfo,
+ # and sort it into associated arrays
+ local i apkgs=() ainfo=()
+ local -n field
+
+ unset -v Name PackageBase Version Maintainer OutOfDate Groups {,Make,Check}Depends Provides Conflicts
+ [[ "$*" ]] || return
+ declare -Ag \
+ {,c}{Name,PackageBase,Version,Maintainer,OutOfDate,Groups,{,Make,Check}Depends,Provides,Conflicts}
+
+ for i in "$@"; do
+ if [[ " ${cName[*]} " = *" ${i} "* ]]; then
+ Name[${i}]="${cName[${i}]}" PackageBase[${i}]="${cPackageBase[${i}]}"
+ Version[${i}]="${cVersion[${i}]}" Maintainer[${i}]="${cMaintainer[${i}]}"
+ OutOfDate[${i}]="${cOutOfDate[${i}]}" Groups[${i}]="${cGroups[${i}]}"
+ Depends[${i}]="${cDepends[${i}]}" MakeDepends[${i}]="${cMakeDepends[${i}]}"
+ CheckDepends[${i}]="${cCheckDepends[${i}]}" Provides[${i}]="${cProvides[${i}]}"
+ Conflicts[${i}]="${cConflicts[${i}]}"
+ else
+ apkgs+=("${i}")
+ fi
+ done
+
+ if [[ "${apkgs[*]}" ]]; then
+ mapfile -t ainfo < <(auracle info "${apkgs[@]}" -F \
+ $'Name[{name}]={name}\nPackageBase[{name}]={pkgbase}
+ Version[{name}]={version}\nMaintainer[{name}]={maintainer}
+ OutOfDate[{name}]={outofdate:%s}\nGroups[{name}]={groups}
+ Depends[{name}]={depends}\nMakeDepends[{name}]={makedepends}
+ CheckDepends[{name}]={checkdepends}\nProvides[{name}]={provides}
+ Conflicts[{name}]={conflicts}'); ainfo=("${ainfo[@]##* }")
+ [[ ! "${ainfo[*]}" ]] || declare -g "${ainfo[@]}" "${ainfo[@]/#/c}"
+ fi
+ for i in "${!Maintainer[@]}"; do : "${Maintainer[${i}]:=%}"; done
+ for field in Groups Depends MakeDepends CheckDepends Provides Conflicts; do
+ for i in "${!field[@]}"; do
+ [[ "${field[${i}]}" ]] || unset -v "field[${i}]"
+ done
+ done
+}
+
+GetInfo() {
+ local i field
+ field="$(declare -p "$1")"
+ local -A "${field/#declare -A $1/field}"; shift
+
+ if (($#)); then
+ for i in "$@"; do
+ if [[ "${i}" && "${field[${i}]:-}" ]]; then
+ printf '%s\n' "${field[${i}]// /$'\n'}"
+ fi
+ done
+ elif [[ "${field[*]}" ]]; then
+ printf '%s\n' "${field[@]// /$'\n'}"
+ fi
+}
+
+CheckRequires() {
+ local -a Qrequires
+ if mapfile -t Qrequires < <(expac -Ql'\n' '%N' "$@"); wait "$!"; then
+ fail $"failed to prepare transaction (could not satisfy dependencies)"
+ error $"%s: requires %s" "${Qrequires[*]}" "$*" "${E_INSTALL_DEPS_FAILED}"
+ fi
+}
+
+Proceed() {
+ local answer ret readfullline=0
+
+ if [[ "${TERM:-}" = 'dumb' ]] || ((clean)); then
+ readfullline=1
+ fi
+ case "$1" in
+ y) printf "%s::%s %s$2 [Y/n] %s" "${colorB}" "${reset}" "${colorW}" "${@:3}" "${reset}"
+ ((! noconfirm)) || { printf '\n'; return 0;}
+ for ((;;)); do
+ if ((readfullline)); then
+ read -r answer
+ else
+ read -rsn 1 answer
+ fi
+ case "${answer}" in
+ [Yy]|'') ret=0; break;;
+ [Nn]) ret=1; break;;
+ *) ((! readfullline)) || { ret=1; break;};;
+ esac
+ done;;
+ n) printf "%s::%s %s$2 [y/N] %s" "${colorB}" "${reset}" "${colorW}" "${@:3}" "${reset}"
+ ((! noconfirm)) || { printf '\n'; return 0;}
+ for ((;;)); do
+ if ((readfullline)); then
+ read -r answer
+ else
+ read -rsn1 answer
+ fi
+ case "${answer}" in
+ [Nn]|'') ret=0; break;;
+ [Yy]) ret=1; break;;
+ *) ((! readfullline)) || { ret=0; break;};;
+ esac
+ done;;
+ esac
+ ((readfullline)) || printf '%s\n' "${answer}"
+ return "${ret}"
+}
+
+msg() { # shellcheck disable=SC2059
+ printf "$1 $2\n" "${@:3}"
+}
+
+info() {
+ msg "${colorB}::${reset}" "$@"
+}
+
+warn() {
+ msg "${colorY}warning:${reset}" "$@" >&2
+}
+
+fail() {
+ msg "${colorR}error:${reset}" "$@" >&2
+}
+
+error() {
+ fail "${@:1:$#-1}"; exit "${!#}"
+}
+
+GetLength() {
+ local length=0 i
+ for i in "$@"; do
+ ((length=${#i} > length ? ${#i} : length))
+ done
+ printf '%s\n' "${length}"
+}
+
+NothingToDo() {
+ (($#)) || { printf '%s\n' $" there is nothing to do"; exit "${E_OK}";}
+}
+
+SudoV() {
+ local sleep
+ :>"${tmpdir}/pacpur.sudov.lck"
+ exec {sleep}<> <(:)
+ while [[ -e "${tmpdir}/pacpur.sudov.lck" ]]; do
+ sudo "${pacman}" -V >/dev/null
+ read -rt 298 -u "${sleep}" || :
+ done
+}
+
+CommArr() {
+ # args are names of 2 array names and arguments to comm
+ local arr='' arr1="$2[@]" arr2="$3[@]"
+ [[ ! "${arr:=$(comm "$1" <(SortArr "${!arr1-}") <(SortArr "${!arr2-}"))}" ]] ||
+ printf '%s\n' "${arr}"
+}
+
+SortArr() {
+ local IFS=$'\n' # \n as only IFS character so $* expands newline-delimited
+ ((! $#)) || sort -u <<<"$*" # pass all arguments to sort -u
+}
+
+SortArrAssign() {
+ local -n arr="$1"
+ mapfile -t "$1" < <(SortArr "${arr[@]}")
+}
+
+RunVercmp() {
+ case "$1" in
+ "$2") printf '%d' 0;;
+ *) vercmp "$1" "$2";;
+ esac
+}
+
+MapfileAdd() {
+ [[ -v "$1" ]] || declare -ga "$1=()"
+ local -n arr="$1"
+ mapfile -tO "${#arr[@]}" "$1"
+}
+
+MakePkg() {
+ if ((silent)); then
+ makepkg "$@" "${makeopts[@]}" &>/dev/null
+ else
+ makepkg "$@" "${makeopts[@]}"
+ fi
+}
+
+Cancel() {
+ printf '\n'
+ rm -f "${tmpdir}"/pacpur.{build,sudov}.lck
+ exit 130
+}
+
+Usage() {
+ printf '%s\n' \
+ $"usage: pacpur <operation> [options] [target(s)] -- See also pacpur(8)" \
+ $"operations:" \
+ $" pacman extension" \
+ $" -S, -Ss, -Si, -Sw, -Su, -Qu, -Sc, -Scc" \
+ $" extend pacman operations to the PUR" \
+ $" general" \
+ $" -v, --version display version information" \
+ $" -h, --help display help information" \
+ '' \
+ $"options:" \
+ $" pacman extension - can be used with the -S, -Ss, -Si, -Sw, -Su, -Sc, -Scc operations" \
+ $" -p, --pur only search, build, install or clean target(s) from the PUR" \
+ $" -r, --repo only search, build, install or clean target(s) from the repositories" \
+ $" general" \
+ $" -e, --edit edit target(s) PKGBUILD and view install script" \
+ $" -q, --quiet show less information for query and search" \
+ $" --devel consider PUR development packages upgrade" \
+ $" --foreign consider already installed foreign dependencies" \
+ $" --ignore ignore a package upgrade (can be used more than once)" \
+ $" --needed do not reinstall already up-to-date target(s)" \
+ $" --noconfirm do not prompt for any confirmation" \
+ $" --noedit do not prompt to edit files" \
+ $" --rebuild always rebuild package(s)" \
+ $" --silent silence output"
+ exit "${E_OK}"
+}
+
+#
+# Main
+#
+
+trap Cancel INT
+
+# options
+shortopts='DFGQRSTUVacdeghiklmnopqrstuvwxy'
+longopts=('database' 'files' 'query' 'remove' 'sync' 'deptest' 'upgrade')
+printf -v case_ops '%s|' "${longopts[@]}"
+longopts+=('asdeps' 'asexplicit' 'cascade' 'changelog' 'check' 'clean' 'confirm' 'dbonly' 'debug'
+ 'deps' 'disable-download-timeout' 'downloadonly' 'explicit' 'file' 'foreign' 'groups'
+ 'help' 'info' 'list' 'machinereadable' 'native' 'needed' 'noconfirm' 'nodeps'
+ 'noprogressbar' 'nosave' 'noscriptlet' 'owns' 'print' 'recursive' 'refresh' 'regex'
+ 'quiet' 'search' 'sysupgrade' 'unneeded' 'unrequired' 'upgrades' 'verbose')
+printf -v case_lo '%s|' "${longopts[@]}"
+longopts_arg=('arch:' 'assume-installed:' 'cachedir:' 'color:' 'config:' 'dbpath:' 'gpgdir:' 'hookdir:'
+ 'ignore:' 'ignoregroup:' 'logfile:' 'overwrite:' 'print-format:' 'root:' 'sysroot:')
+printf -v case_lo_arg '%s|' "${longopts_arg[@]%:}"
+longopts_aur=('aur' 'by:' 'devel' {,'no'}'edit' 'literal' 'mflags:' 'rebuild' 'repo' 'rsort:'
+ 'searchby:' 'silent' 'sort:' {,'no'}'sudoloop' 'version')
+printf -v case_lo_aur_arg '%s' "${longopts_aur[@]%%*[^:]}"; case_lo_aur_arg="${case_lo_aur_arg//:/|}"
+parseopts "${shortopts}" "${longopts[@]}" "${longopts_arg[@]}" "${longopts_aur[@]}" -- "$@" ||
+ exit "${E_INVALID_OPTION}"
+set -- "${OPTRET[@]}"
+
+for ((;;)) {
+ case "$1" in
+ -S|--sync) pacS=1 installpkg=1 operation='sync' ;;&
+ -Q|--query) pacQ=1 ;;&
+ -c|--clean) clean=1 ccount+=1 ;;&
+ -d|--nodeps) makeopts+=("$1") dcount+=1 ;;&
+ -h|--help) help=1 ;;&
+ -i|--info) info=1 ;;&
+ -n|--native) repo=1 ;;&
+ -q|--quiet) QUIET=1 pacmanarg+=('-q') auropts+=("$1") ;;&
+ -s|--search) search=1 ;;&
+ -u|--upgrades|--sysupgrade) upgrade=1 installpkg=1 ;;&
+ -v) ver=1 ;;&
+ -w|--downloadonly) downloadonly=1 ;;&
+ -y|--refresh) refresh=1 ;;&
+ --as@(deps|explicit)) declare "${1#--}"=1 ;;&
+ --@(needed|noconfirm)) declare "${1#--}"=1 ;;&
+ --assume-installed) assumeinstalled+=("$2") ;;&
+ --color) color="$2" ;;&
+ --config) pacman_conf+=("$1" "$2") ;;&
+ --ignore) MapfileAdd ignoredpkgs <<<"${2//,/$'\n'}" ;;&
+ --ignoregroup) MapfileAdd ignoredgrps <<<"${2//,/$'\n'}" ;;&
+ --version) operation='version' ;;&
+ -e|--edit) pace=1 ;;&
+ -p|--pur) aur=1 ;;&
+ -r|--repo) repo=1 ;;&
+ --@(devel|noedit|rebuild)) declare "${1#--}"=1 ;;&
+ --literal) auropts+=("$1") ;;&
+ --mflags) MapfileAdd makeopts <<<"${2//[[:space:]]/$'\n'}" ;;&
+ --silent) silent=1 pacmanarg+=('-q') auropts+=('-q') makeopts+=('-L') ;;&
+ --@(search|sort)by) auropts+=("--searchby=$2") ;;&
+ --sort) sortorder='ascending' sortby="$2" ;;&
+ --rsort) sortorder='descending' sortby="$2" ;;&
+ --sudoloop) sudoloop=1 ;;&
+ --nosudoloop) sudoloop=0 ;;&
+ -[DFQRSTUV]) pacmanarg+=("$1") pac+=1 ;;&
+ -[cg-ik-qs-y]) pacmanarg+=("$1") ;;&
+ --@(${case_ops%|})) pacmanarg+=("${1:1:2}") pacmanarg[-1]="${pacmanarg[-1]^^}" pac+=1 ;;&
+ --@(${case_lo%|})|-d) pacopts+=("$1") ;;&
+ --@(${case_lo_arg%|})) pacopts+=("$1=$2") ;;&
+ --@(${case_lo_aur_arg%|})) shift ;;&
+ *) shift ;;&
+ --) break
+ esac
+}
+
+# help or version
+{ [[ "${operation}" != 'version' ]] && ((pac || ! ver));} ||
+ { printf 'pacpur %s\n' "${version}"; exit "${E_OK}";}
+((pac || ! help)) || Usage
+
+# sorting
+case "${sortorder}" in
+ ascending) auropts+=("--sort=${sortby}");;
+ descending) auropts+=("--rsort=${sortby}");;
+esac
+
+# packages
+for arg in "$@"; do
+ case "${arg}" in
+ -) MapfileAdd pkgs;;
+ *) pkgs+=("${arg}");;
+ esac
+done
+
+# color
+[[ ! "${color}" && (! "$("${pacman_conf[@]}" Color)" || "${operation}" = 'upgrades' ||
+ QUIET -ne 0 && search -ne 0) ]] && color='never' || color='auto'
+
+case "${color}" in
+ never) declare -r reset='' colorR='' colorG='' colorY='' colorB='' colorM='' colorW=''
+ pacopts+=('--color=never') auropts+=('--color=never') makeopts+=('-m');;
+ *) declare -r reset=$'\e[0m' colorR=$'\e[1;31m' colorG=$'\e[1;32m' colorY=$'\e[1;33m' \
+ colorB=$'\e[1;34m' colorM=$'\e[1;35m' colorW=$'\e[1;39m'
+ pacopts+=('--color=auto') auropts+=('--color=auto');;
+esac
+
+# sanity check
+((! aur)) || refresh=0
+((! pace || pac)) || operation='edit'
+((! pacQ || ! pace)) || pacmanarg+=('-e')
+((! pacQ || ! upgrade)) || operation='upgrades'
+((! pacS || ! downloadonly)) || installpkg=0
+((! pacS )) || [[ " ${pacmanarg[*]} ${pacopts[*]} " != *' -'@([glp]|-group|-list|-print)' '* ]] || operation=''
+((! pacS || ! clean)) || search=0 info=0 upgrade=0
+((pac <= 1)) || error $"only one operation may be used at a time" "${E_FAIL}"
+[[ "${operation}" != 'sync' ]] || ((search || info || clean || EUID)) ||
+ error $"you cannot perform this operation as root" "${E_ROOT}"
+((! pacS || ! search || ! info)) ||
+ error $"invalid option: '--info' and '--search' may not be used together" "${E_INVALID_OPTION}"
+command -v "${editor%% *}" >/dev/null ||
+ error $"%s\$VISUAL%s and %s\$EDITOR%s environment variables not set or defined %seditor%s not found" \
+ "${colorW}" "${reset}" "${colorW}" "${reset}" "${colorW}" "${reset}" "${E_MISSING_FILE}"
+[[ "${PACMAN-}" != "${0##*/}" ]] ||
+ error $"you cannot use %spacpur%s as PACMAN environment variable" "${colorW}" "${reset}" "${E_FAIL}"
+[[ -w "${clonedir}" ]] ||
+ error $"%s does not have write permission" "${colorW}${clonedir}${reset}" "${E_FS_PERMISSIONS}"
+[[ "${pkgs[*]}" || ("${operation}" != @(sync|edit) && " ${pacmanarg[*]} " != *' -'[RU]' '*) ]] ||
+ ((help + refresh + upgrade + clean + info)) || error $"no targets specified (use -h for help)" "${E_FAIL}"
+((! repo || ! aur)) ||
+ error $"invalid option: '--repo' and '--pur' may not be used together" "${E_INVALID_OPTION}"
+
+# operations
+case "${operation}" in
+ edit) # edit (-e) handling
+ GetPkgbase "${pkgs[@]}"
+ EditPkgs "${pkgsbase[@]}"
+ exit;;
+ sync)
+ # search (-Ss, -s) handling
+ if ((search)); then
+ if ((! aur)); then
+ if ((refresh)); then
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ else
+ "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ fi
+ fi
+ exitrepo="$?"
+ if ((! repo)) && [[ "${pkgs[*]}" ]]; then
+ auracle search "${auropts[@]}" -- "${pkgs[@]#aur/}"
+ fi
+ exitaur="$?"
+ ((! exitrepo || ! exitaur)) && exit "${E_OK}" || exit "${E_FAIL}" # exit code
+ # info (-Si, -i) handling
+ elif ((info)); then
+ if [[ ! "${pkgs[*]}" ]] && ((refresh)); then
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}"
+ elif [[ ! "${pkgs[*]}" ]]; then
+ "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}"
+ else
+ ClassifyPkgs "${pkgs[@]}"
+ fi
+ if [[ "${repopkgs[*]}" ]]; then
+ if ((refresh)); then
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" "${repopkgs[@]}"
+ else
+ "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" "${repopkgs[@]}"
+ fi
+ fi
+ if [[ "${aurpkgs[*]}" ]]; then
+ if ((refresh)) && [[ ! "${repopkgs[*]}" ]]; then
+ sudo "${pacman}" -Sy "${pacopts[@]}"
+ fi
+ ((aur)) || info $"Package(s) %s not found in repositories, trying %sPUR%s..." \
+ "${colorW}${aurpkgs[*]}${reset}" "${colorM}" "${reset}"
+ auracle info "${auropts[@]}" -- "${aurpkgs[@]}"
+ fi
+ # clean (-Sc) handling
+ elif ((clean)); then
+ ((aur)) || sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" "${repopkgs[@]}"
+ ((repo)) || CleanCache "${pkgs[@]}"
+ # sysupgrade (-Su, -u) handling
+ elif ((upgrade)); then
+ [[ ! "${pkgs[*]}" ]] || ClassifyPkgs "${pkgs[@]}"
+ if ((! aur)); then
+ if ! sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" "${repopkgs[@]}" && ((repo)); then
+ exit "${E_FAIL}"
+ fi
+ fi
+ if [[ "${aurpkgs[*]}" ]] && ((! aur)); then
+ info $"Package(s) %s not found in repositories, trying %sPUR%s..." \
+ "${colorW}${aurpkgs[*]}${reset}" "${colorM}" "${reset}"
+ fi
+ ((repo)) || Core
+ elif ((help)); then
+ "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ # sync (-S, -y), downloadonly (-Sw, -m), refresh (-Sy)
+ else
+ if [[ ! "${pkgs[*]}" ]]; then
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}"
+ else
+ ClassifyPkgs "${pkgs[@]}"
+ fi
+ [[ ! "${repopkgs[*]}" ]] || sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" "${repopkgs[@]}"
+ if [[ "${aurpkgs[*]}" ]]; then
+ if ((refresh)) && [[ ! "${repopkgs[*]}" ]]; then
+ sudo "${pacman}" -Sy "${pacopts[@]}"
+ fi
+ ((aur)) || info $"Package(s) %s not found in repositories, trying %sPUR%s..." \
+ "${colorW}${aurpkgs[*]}${reset}" "${colorM}" "${reset}"
+ Core
+ fi
+ fi
+ exit;;
+ upgrades) # upgrades (-Qu) handling
+ ((aur)) || "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ exitrepo="$?"
+ if ((! repo)); then
+ [[ "${pkgs[*]}" ]] ||
+ mapfile -t pkgs < <("${pacman}" -q "${pacmanarg[@]/%-u/-m}" \
+ "${pacopts[@]/%--upgrades/--foreign}")
+ auracle sync "${auropts[@]}" -- "${pkgs[@]}"
+ fi
+ exitaur="$?"
+ if ((! exitrepo || ! exitaur)); then
+ exit "${E_OK}"
+ else
+ exit "${E_FAIL}"
+ fi;;
+ *) # other operations handling
+ if [[ " ${pacmanarg[*]} " = *' -F '* ]] && ((refresh)); then
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ elif [[ ! "${pkgs[*]}" || " ${pacmanarg[*]} " = *' -'[DFQTglp]' '* ]] &&
+ ((! asdeps && ! asexplicit && ! refresh)); then
+ "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ else
+ sudo "${pacman}" "${pacmanarg[@]}" "${pacopts[@]}" -- "${pkgs[@]}"
+ fi
+ exit;;
+esac