#!/usr/bin/env bash name="Libredbdiff" # Copyright (C) 2014 Esteban Carnevale # Copyright (C) 2014 Luke Shumaker # # License: GNU GPLv3+ # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . baseconfpath="/etc/libredbdiff" basedbpath="/var/lib/libredbdiff" conffile="$baseconfpath/pacman.conf.parabola" conffilearch="$baseconfpath/pacman.conf.archlinux" dbpath="$basedbpath/pacman.parabola" dbpatharch="$basedbpath/pacman.archlinux" mirrorlist="$baseconfpath/mirrorlist.parabola" mirrorlistarch="$baseconfpath/mirrorlist.archlinux" mirror='http://repo.parabola.nu/$repo/os/$arch' mirrorarch='http://mirrors.kernel.org/archlinux/$repo/os/$arch' repos="libre pcr libre-multilib nonprism" field_pkgname_parabola=30 field_pkgname_arch=30 . libremessages cmd="${0##*/}" arch_packages_tmp="/tmp/${cmd}.arch-packages" parabola_packages_tmp="/tmp/${cmd}.parabola-packages" field_pkgname_total="$((${field_pkgname_parabola} + ${field_pkgname_arch}))" printf_format="%s %-${field_pkgname_parabola}s%-${field_pkgname_arch}s %s | %s\n" printf_format_noarch="%s %-${field_pkgname_total}s %s\n" downloadfile() { local outfile=$1 local url=$2 local mesg=("${@:3}") if [[ ! -e $outfile ]] ; then msg "${mesg[@]}" if wget -q "$url" -O "$outfile"; then return 255 else die "Failed to download %q. Exiting." "$outfile" fi elif [[ $init ]]; then warning "%q already exists. Skipping." "$outfile" fi } enablerepo() { repo="$1" conffile_arg="$2" msg2 "Enabling repo %q in %q" "$repo" "${conffile_arg}" sed -i "s/\#\[$repo\]/[$repo]/" "${conffile_arg}" sed -i "\/\[$repo\]/,+1 s/#Include/Include/" "${conffile_arg}" } createdir() { local dir=$1 if [[ ! -e $dir ]] ; then msg "Creating directory %q" "$dir" mkdir -- "$1" || die "Failed to create directory %q. Exiting." "$dir" elif [[ $init ]]; then warning "%q already exists. Skipping." "$dir" fi } setmirror() { local distro="$1" local mirror="$2" local mirrorlist="$3" if [[ $init ]] && [[ $mirror ]]; then mirrorescaped="${mirror//./\\.}" mirrorescaped="${mirrorescaped//\$/\\$}" msg2 "Setting %s as the only enabled %s mirror." "${mirror}" "${distro}" sed -i 's|^#\(Server = '"${mirrorescaped}"'\)$|\1|' "${mirrorlist}" fi } filenotfound() { local file=$1 if [[ ! -r $1 ]]; then error "Could not read %q." "$file" die "Nothing done. It may be necessary to run %q without \ arguments as root to initialize %s." "$cmd" "$name" fi } initialize() { createdir "$baseconfpath" createdir "$basedbpath" downloadfile \ "${conffile}" \ "https://projects.parabola.nu/abslibre.git/plain/libre/pacman/pacman.conf.x86_64" \ "Downloading Parabola %q" \ pacman.conf if [[ $? == 255 ]] ; then msg2 "Setting DBPath in %q" "${conffile}" sed -i "s|^#DBPath .*|DBPath = ${dbpath}|" "${conffile}" enablerepo nonprism "${conffile}" enablerepo pcr "${conffile}" enablerepo libre-multilib "${conffile}" enablerepo multilib "${conffile}" fi downloadfile \ "${conffilearch}" \ "https://projects.archlinux.org/svntogit/packages.git/plain/pacman/trunk/pacman.conf.x86_64" \ "Downloading Arch %q" \ pacman.conf if [[ $? == 255 ]] ; then msg2 "Setting DBPath in %q" "${conffilearch}" sed -i "s|^#DBPath .*|DBPath = ${dbpatharch}|" "${conffilearch}" msg2 "Setting Arch mirrorlist file in %q" "${conffilearch}" sed -i "s|/etc/pacman\.d/mirrorlist$|$baseconfpath/mirrorlist.archlinux|" \ "${conffilearch}" enablerepo multilib "${conffilearch}" fi downloadfile \ "${mirrorlist}" \ "https://repo.parabola.nu/mirrorlist.txt" \ "Downloading Parabola %q" \ mirrorlist if [[ $? == 255 ]] ; then sed -i 's|^Server|#Server|' "${mirrorlist}" setmirror "Parabola" "$mirror" "$mirrorlist" fi downloadfile \ "${mirrorlistarch}" \ "https://projects.archlinux.org/svntogit/packages.git/plain/pacman-mirrorlist/trunk/mirrorlist" \ "Downloading Arch %q" \ mirrorlist if [[ $? == 255 ]] ; then setmirror "Arch" "$mirrorarch" "$mirrorlistarch" fi } repo_test() { for repo in ${repos} ; do if [[ $repo == $1 ]] ; then found=1 return 0 fi done if [[ $found != 1 ]] ; then die "The specified Parabola repo \"%s\" cannot be compared. It's \ not in the list of repos in the configuration variable \"repos\"." "$1" fi } expac_version_test() { if ! pkgname="$(pacman -Qoq expac 2> /dev/null)" ; then die "The command expac could not be found installed. The package \ pcr/expac-relative 4-2.parabola1 (or later) must be installed." elif [[ $pkgname == expac-git ]] ; then true elif [[ $pkgname == expac-relative ]] ; then expac_ver="$(expac %v expac-relative)" if [[ $(vercmp "${expac_ver}" 4-2.parabola1) == -1 ]] ; then die "The version of expac-relative installed on the system is \ lower than needed." fi else die "expac command must be provided by pcr/expac-relative version \ 4-2.parabola1 (or later). This package must be installed." fi } compare_pkgs() { local cmp if [[ ${verarch[$pkgname]} ]] ; then cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$pkgname]}") if [[ $cmp -lt 0 ]]; then printf "${printf_format}" \ '=' \ "${pkgname}" \ "" \ "${ver[$pkgname]}" \ "${verarch[$pkgname]}" fi elif [[ ${provides[$pkgname]} ]]; then for provide in "${provides[$pkgname]}"; do if [[ ${verarch["$provide"]} ]]; then cmp=$(vercmp "${ver[$pkgname]}" "${verarch[$provide]}") if [[ $cmp -lt 0 ]]; then printf "${printf_format}" \ 'p' \ "${pkgname}" \ "${provide}" \ "${ver[$pkgname]}" \ "${verarch[$provide]}" fi fi done else printf "${printf_format_noarch}" \ 'o' \ "${pkgname}" \ "${ver[$pkgname]}" fi } print_cmp() { local repo="$1" awk -F/ -v repo="$repo" \ '$1 == repo {print $2}' \ ${parabola_packages_tmp} | \ while read -a line ; do ver["${line[0]}"]="${line[1]}" provides[${line[0]}]="${line[@]:2}" pkgname=${line[0]} compare_pkgs done } usage() { print "Usage: %q [-n|-h]" "$cmd" print 'Show packages that need to be updated from Arch repositories.' echo prose "Compares packages in Parabola repositories. Packages from all configured Parabola repositories are compared. A Parabola repository name can be specified as argument to compare only packages in that repository." prose "The default mode of operation is to download/update all necessary files for comparison, but not compare them. Specify the \`-n\` flag to not download anything, but to compare already downloaded files." echo print 'Options:' flag '-n' "Don't update anything, just compare already downloaded files." flag '-h' 'Show this message' echo print "Output format: type_character parabola_pkgname (arch_pkgname) parabola_pkgver - (arch_pkgver)" echo print "Type characters: = Arch package with the same pkgname and greater pkgver was found p Arch package with pkgname equal to a provide and greater pkgver was found In this case arch_pkgname is a provide of parabola_pkgname o No Arch package with the same pkgname or with pkgname equal to a provide was found" } main() { local UPDATE=1 local arg local repo_arg for arg in "$@"; do case "$arg" in -n|--noupdate) UPDATE=0 ;; -h|--help) usage return 0 ;; *) repo_test "$arg" repo_arg="$arg" break ;; esac done if (( $UPDATE )) ; then if [[ $EUID != 0 ]]; then die "To initialize %s or update %s pacman \ databases, %s must be run as root (without arguments). Nothing done." \ "$name" \ "$name" \ "$cmd" fi if ! [[ -e "${conffile}" && \ -e "${conffilearch}" && \ -e "${mirrorlist}" && \ -e "${mirrorlist}" ]]; then warning "At least one %s configuration file is \ missing." \ "${name}" msg "Downloading and preparing missing \ configuration files." init=1 fi createdir "$baseconfpath" createdir "$basedbpath" initialize if ! [[ -d "$dbpath" && \ -d "$dbpatharch" ]]; then warning "At least one %s pacman DB directory \ is missing. Synchronizing %s DB files." "${name}" "${name}" fi createdir "$dbpath" msg "Synchronizing %s pacman databases for Parabola" "$name" pacman --config "${conffile}" -Sy || \ die "Failed to synchronize pacman database for Parabola. Exiting." createdir "$dbpatharch" msg "Synchronizing %s pacman databases for Arch" "$name" pacman --config "${conffilearch}" -b "${dbpatharch}" -Sy || \ die "Failed to synchronize pacman database for Arch. Exiting." msg "%s pacman databases are updated. %s is ready. Run %q -n to \ print results." "$name" "$name" "$cmd" return 0 else filenotfound "${dbpath}" filenotfound "${dbpatharch}" filenotfound "${conffile}" filenotfound "${conffilearch}" filenotfound "${mirrorlist}" filenotfound "${mirrorlistarch}" unset provides ver verarch declare -gA provides ver verarch if ! [[ -d "$dbpath" && \ -d "$dbpatharch" ]]; then die "At least one %s pacman DB directory \ is missing. To update %s pacman databases, %s must be run as root. \ Nothing done." \ "$name" \ "$name" \ "$cmd" fi expac_version_test pacman --config "${conffilearch}" -Ss | \ grep -v '^ ' | \ awk -F/ '{print $2}' \ > ${arch_packages_tmp} || \ die "pacman command to get Arch \ package data has failed. Exiting." chmod 777 ${arch_packages_tmp} while read -a line; do verarch["${line[0]}"]="${line[1]}" done < ${arch_packages_tmp} expac --config "${conffile}" -S '%r/%n %v %S' \ > ${parabola_packages_tmp} || \ die "expac command to get Parabola \ package data has failed. Exiting." chmod 777 ${parabola_packages_tmp} if [[ ${repo_arg} ]] ; then print_cmp "${repo_arg}" else for repo in ${repos} ; do print "[$repo]" print_cmp "$repo" done fi fi } main "$@"