summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile7
-rw-r--r--src/abslibre-tools/Makefile4
-rwxr-xr-xsrc/abslibre-tools/createworkdir54
-rw-r--r--src/abslibre-tools/createworkdir.md36
-rwxr-xr-xsrc/abslibre-tools/diff-unfree89
-rwxr-xr-xsrc/abslibre-tools/libreaddiff98
-rwxr-xr-xsrc/abslibre-tools/libredbdiff360
-rwxr-xr-xsrc/abslibre-tools/librerelease257
-rwxr-xr-xsrc/abslibre-tools/librestage154
-rwxr-xr-xsrc/aur146
-rw-r--r--src/chroot-tools/.gitignore7
-rw-r--r--src/chroot-tools/HACKING.md3
-rw-r--r--src/chroot-tools/Makefile27
-rw-r--r--src/chroot-tools/arch-nspawn.patch71
-rw-r--r--src/chroot-tools/chcleanup.in102
-rw-r--r--src/chroot-tools/chroot.conf13
-rwxr-xr-xsrc/chroot-tools/distcc-tool247
-rw-r--r--src/chroot-tools/hooks-chcleanup.sh32
-rw-r--r--src/chroot-tools/hooks-check.sh37
-rw-r--r--src/chroot-tools/hooks-distcc.sh106
-rwxr-xr-xsrc/chroot-tools/indent57
-rwxr-xr-xsrc/chroot-tools/librechroot509
-rwxr-xr-xsrc/chroot-tools/libremakepkg289
-rw-r--r--src/chroot-tools/makechrootpkg.sh.patch342
-rw-r--r--src/chroot-tools/mkarchroot.patch67
-rwxr-xr-xsrc/dagpkg222
-rw-r--r--src/devtools/.gitignore4
-rw-r--r--src/devtools/Makefile12
-rw-r--r--src/devtools/checkpkg.patch45
-rw-r--r--src/devtools/find-libdeps.patch47
-rw-r--r--src/devtools/finddeps.patch35
-rw-r--r--src/devtools/lddd.patch29
-rw-r--r--src/gitget/Makefile4
-rwxr-xr-xsrc/gitget/gitget221
-rwxr-xr-xsrc/gitget/libregit43
-rwxr-xr-xsrc/is_built69
-rw-r--r--src/lib/.gitignore3
-rw-r--r--src/lib/HACKING.md15
-rw-r--r--src/lib/Makefile45
-rw-r--r--src/lib/blacklist.sh96
l---------src/lib/blacklist.sh.3.ronn1
-rw-r--r--src/lib/common.sh.3.ronn16
-rw-r--r--src/lib/common.sh.head25
-rw-r--r--src/lib/common.sh.tail1
-rw-r--r--src/lib/conf.sh.3.ronn126
-rw-r--r--src/lib/conf.sh.in243
-rwxr-xr-xsrc/lib/libreblacklist83
-rw-r--r--src/lib/libreblacklist.1.ronn23
-rwxr-xr-xsrc/lib/librelib98
-rw-r--r--src/lib/librelib.1.ronn55
-rw-r--r--src/lib/librelib.7.ronn49
-rwxr-xr-xsrc/lib/libremessages25
-rw-r--r--src/lib/libremessages.1.ronn241
-rwxr-xr-xsrc/lib/librexgettext226
-rw-r--r--src/lib/librexgettext.1.ronn18
-rw-r--r--src/lib/messages.sh212
l---------src/lib/messages.sh.3.ronn1
-rw-r--r--src/librefetch/.gitignore2
-rw-r--r--src/librefetch/Makefile13
-rwxr-xr-xsrc/librefetch/librefetch400
-rw-r--r--src/librefetch/librefetch-install.in114
-rw-r--r--src/librefetch/librefetch-makepkg.conf.in16
-rw-r--r--src/librefetch/librefetch.8.ronn214
-rw-r--r--src/librefetch/librefetch.conf7
-rw-r--r--src/librefetch/librefetch.conf.5.ronn42
-rw-r--r--src/librefetch/librefetchdir/Makefile56
-rwxr-xr-xsrc/librefetch/librefetchdir/libmakepkg/source.sh.gen25
-rwxr-xr-xsrc/librefetch/librefetchdir/libmakepkg/tidy/purge.sh58
-rwxr-xr-xsrc/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh38
-rwxr-xr-xsrc/librefetch/librefetchdir/makepkg.gen42
-rw-r--r--src/libretools.conf72
-rwxr-xr-xsrc/pkgbuild-check-nonfree309
-rwxr-xr-xsrc/pkgbuild-summarize-nonfree106
-rwxr-xr-xsrc/repo-diff75
-rw-r--r--src/toru/Makefile4
-rwxr-xr-xsrc/toru/toru-info43
-rwxr-xr-xsrc/toru/toru-path92
-rwxr-xr-xsrc/toru/toru-where25
-rw-r--r--src/workflows.md64
-rw-r--r--src/xbs-abs/.gitignore3
-rw-r--r--src/xbs-abs/Makefile27
-rw-r--r--src/xbs-abs/archrelease.patch8
-rwxr-xr-xsrc/xbs-abs/helper-abs201
-rw-r--r--src/xbs-abs/xbs-abs.conf13
-rw-r--r--src/xbs-abslibre/Makefile8
-rwxr-xr-xsrc/xbs-abslibre/helper-abslibre202
-rw-r--r--src/xbs/Makefile5
-rwxr-xr-xsrc/xbs/xbs186
-rw-r--r--src/xbs/xbs.conf1
89 files changed, 7918 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..e1e5cf4
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,7 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../config.mk
+include $(topsrcdir)/automake.head.mk
+pkgconfdir = $(sysconfdir)
+
+srcfiles-ignore/$(srcdir) = $(addsuffix /%,abslibre-tools chroot-tools devtools gitget lib librefetch toru xbs xbs-abs xbs-abslibre)
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/abslibre-tools/Makefile b/src/abslibre-tools/Makefile
new file mode 100644
index 0000000..2903f4a
--- /dev/null
+++ b/src/abslibre-tools/Makefile
@@ -0,0 +1,4 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/abslibre-tools/createworkdir b/src/abslibre-tools/createworkdir
new file mode 100755
index 0000000..5599de9
--- /dev/null
+++ b/src/abslibre-tools/createworkdir
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# CreateWorkDir
+# Creates a dir structure for working with Parabola packages
+
+# Copyright (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+set -euE
+
+. libremessages
+. "$(librelib conf.sh)"
+load_files libretools
+check_vars libretools WORKDIR ABSLIBRERECV ABSLIBRESEND # these are asked for by `xbs download`
+
+trap 'error "Aborting..."' EXIT
+
+msg "Creating WORKDIR at %s..." "$WORKDIR"
+mkdir -p "$WORKDIR"
+
+msg "Creating staging directory in WORKDIR..."
+mkdir -p "$WORKDIR/staging"
+
+cmd=(xbs -b abslibre download)
+if ! "${cmd[@]}"; then
+ error "Could not clone ABSLibre"
+ plain "Try running this command:"
+ echo
+ printf '%q ' "${cmd[@]}"
+ echo
+ exit 1
+fi
+
+msg "Finished, your packaging directory tree looks like this now:"
+ls --color=auto "${WORKDIR}"/*
+
+trap -- EXIT
diff --git a/src/abslibre-tools/createworkdir.md b/src/abslibre-tools/createworkdir.md
new file mode 100644
index 0000000..e50b00f
--- /dev/null
+++ b/src/abslibre-tools/createworkdir.md
@@ -0,0 +1,36 @@
+# CreateWorkDir
+
+This script recreates a proper directory tree for packaging. Its aim is to help
+you be organized with the work you do as a packager, and establish a certain
+standard for packages' publication, so you don't have to lose much time with
+them. Just package and upload!
+
+It will create a directory tree like this:
+
+ $WORKDIR/
+ ├── abslibre/
+ │ ├── .git/
+ │ ├── libre/<PKGBUILDS>
+ │ └── libre-testing/<PKGBUILDS>
+ └── staging/
+ ├── libre/
+ └── libre-testing/
+
+*Related Variables*
+ - WORKDIR
+
+## staging/
+
+This directory contains one directory for each repository, where the resulting
+packages are in moved for syncing against the main repository on Parabola's
+server. This directory is architecture independent.
+
+## abslibre/
+
+This is the git repo for Parabola's PKGBUILDs. Here you can find the ABS tree
+for our packages, and also where you'll have to put new ones for commit.
+
+(You'll need push access to Parabola's main server, but pulling is public.)
+
+*Related Variables*
+ - ABSLIBREGIT
diff --git a/src/abslibre-tools/diff-unfree b/src/abslibre-tools/diff-unfree
new file mode 100755
index 0000000..12c919f
--- /dev/null
+++ b/src/abslibre-tools/diff-unfree
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+# This script will help you diff a *-libre PKGBUILD against the unfree one
+# to check for updates.
+
+# Copyright (C) 2010-2011 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. libremessages
+. "$(librelib conf.sh)"
+load_files libretools
+check_vars libretools DIFFPROG || exit 1
+
+usage() {
+ print "Usage: %s [community|packages] [unfree-package] [repo]" "${0##*/}"
+ print "Usage: %s --help" "${0##*/}"
+ prose "Helps you diff build scripts from ABSLibre against (Unfree) ABS."
+ echo
+ prose "Package name and repo will we guessed if you don't specify them."
+}
+
+main() {
+ if [[ "$1" == "--help" ]]; then
+ usage
+ exit 0
+ fi
+
+ local package_guess=${PWD##*/}
+ local repo=${1:-$(basename ${PWD%/*})}
+ local package=${2:-${package_guess%-libre}}
+ local trunk=${3:-trunk}
+
+ svnrepo="packages"
+ case $repo in
+ community*) svnrepo="community";;
+ multilib*) svnrepo="community";;
+ *) :;;
+ esac
+
+ if [[ ! -r PKGBUILD ]]; then
+ error "This is not a build dir."
+ exit 1
+ fi
+
+
+ tmp_dir="$(mktemp --tmpdir -d ${package}.XXXXXX)"
+ if [[ ! -d "${tmp_dir}" ]]; then
+ error "Can't create temp dir"
+ exit 1
+ fi
+ unfree_dir="${tmp_dir}/${svnrepo}/${package}/${trunk}"
+
+ pushd "${tmp_dir}" &>/dev/null
+
+ msg "Getting diff from %s..." "$repo/$package"
+
+ svn checkout --depth=empty svn://svn.archlinux.org/$svnrepo &>/dev/null
+
+ cd ${svnrepo}
+ svn update ${package}
+
+ # Back to start dir
+ popd &>/dev/null
+
+ msg "Diffing files"
+
+ for _file in ${unfree_dir}/*; do
+ msg2 "%s" "$(basename "${_file}")"
+ ${DIFFPROG} "$PWD/$(basename "${_file}")" "${_file}"
+ done
+}
+
+main "$@"
diff --git a/src/abslibre-tools/libreaddiff b/src/abslibre-tools/libreaddiff
new file mode 100755
index 0000000..8698920
--- /dev/null
+++ b/src/abslibre-tools/libreaddiff
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2011-2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012 Daniel Molina (lluvia)
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+set -e
+
+. libremessages
+. "$(librelib conf.sh)"
+load_files libretools
+check_vars libretools WORKDIR
+
+for arg in "$@" ; do
+ case "$arg" in
+ -h|--h|--he|--hel|--help|-\?)
+ {
+ print 'Usage: %s repo [arch]' "${0##*/}"
+ echo
+ prose "This script outputs a diff of package names and versions
+ in repo between pacman's sync db and abslibre checkout."
+ } >&2
+ exit 0
+ ;;
+ esac
+done
+
+# The repo to find missing packages in.
+repo=$1
+# The arch to check in Arch repos, other will have all arches checked.
+arch=${2:-mips64el}
+# A Python tuple of repos which don't have arch=any packages.
+archrepos='("core", "extra", "community")'
+
+diff -U0 \
+ <( (
+ cd /var/lib/pacman/sync
+ for f in $repo.db ; do
+ tar xOf $f | python -c 'import sys
+arch = None
+name = None
+version = None
+it = iter(sys.stdin)
+try:
+ while True:
+ line = next(it)
+ if line == "%ARCH%\n":
+ arch = next(it)
+ if arch == "'"$arch"'\n" or "'$repo'" not in '"$archrepos"':
+ print("%s-%s" % (name.strip(), version.strip()))
+ if line == "%NAME%\n":
+ name = next(it)
+ if line == "%VERSION%\n":
+ version = next(it)
+except StopIteration:
+ pass
+'
+ done
+ ) | sort ) \
+ <( (
+ cd "${WORKDIR}/abslibre"
+ # Needed to not include pkgnames specific to other arches.
+ CARCH=$arch
+ for f in $repo/* ; do
+ load_PKGBUILD "$f/PKGBUILD" || continue
+ is_here=false
+ for arc in ${arch[@]} ; do
+ if [[ "$arc" == "any" ]] || [[ "$arc" == "$CARCH" ]] ; then
+ is_here=true
+ break
+ fi
+ done
+ if [[ "$is_here" == "true" ]] ; then
+ for name in "${pkgname[@]}" ; do
+ if [[ -z "$epoch" ]] ; then
+ echo $name-$pkgver-$pkgrel
+ else
+ echo $name-$epoch:$pkgver-$pkgrel
+ fi
+ done
+ fi
+ done
+ ) | sort ) | sed -rn 's/^[+-][^+-].+$/&/p'
diff --git a/src/abslibre-tools/libredbdiff b/src/abslibre-tools/libredbdiff
new file mode 100755
index 0000000..09c7ad1
--- /dev/null
+++ b/src/abslibre-tools/libredbdiff
@@ -0,0 +1,360 @@
+#!/usr/bin/env bash
+name="Libredbdiff"
+
+# Copyright (C) 2014 Esteban Carnevale <alfplayer@mailoo.org>
+# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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
+}
+
+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."
+ echo
+ 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:"
+ print "type_character parabola_pkgname (arch_pkgname) parabola_pkgver - (arch_pkgver)"
+ echo
+ print "Type characters:"
+ flag '=' "Arch package with the same pkgname and greater pkgver was found"
+ flag '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"
+ flag '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
+
+ 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
+ echo "[$repo]"
+ print_cmp "$repo"
+ done
+ fi
+ fi
+}
+
+main "$@"
diff --git a/src/abslibre-tools/librerelease b/src/abslibre-tools/librerelease
new file mode 100755
index 0000000..b7a77d3
--- /dev/null
+++ b/src/abslibre-tools/librerelease
@@ -0,0 +1,257 @@
+#!/usr/bin/env bash
+# Librerelease
+# Uploads packages and releases them
+
+# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2010-2013 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2013 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# For just the create_signature() function:
+# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org>
+# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org>
+# Copyright (C) 2005 Aurelien Foret <orelien@chez.com>
+# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org>
+# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu>
+# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk>
+# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# create_signature() is taken from pacman:makepkg, which is GPLv2+,
+# so we take the '+' to combine it with our GPLv3+.
+
+. libremessages
+. "$(librelib conf.sh)"
+
+dryrun=""
+upload_only=false
+readonly rsync_flags=(
+ --no-group
+ --no-perms
+ --copy-links
+ --hard-links
+ --partial
+ --human-readable
+ --progress
+ -e ssh
+)
+
+# Functions ####################################################################
+
+list0_files() {
+ find -L "${WORKDIR}/staging" -type f -not -name '*.lock' \
+ -exec realpath -z --relative-to="${WORKDIR}/staging" {} +
+}
+
+# This function is taken almost verbatim from makepkg
+create_signature() {
+ local ret=0
+ local filename="$1"
+ msg "Signing package..."
+
+ local SIGNWITHKEY=()
+ if [[ -n $GPGKEY ]]; then
+ SIGNWITHKEY=(-u "${GPGKEY}")
+ fi
+ # The signature will be generated directly in ascii-friendly format
+ gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" "$filename" || ret=$?
+
+
+ if (( ! ret )); then
+ msg2 "Created signature file %s." "$filename.sig"
+ else
+ error "Failed to sign package file."
+ return $ret
+ fi
+}
+
+sign_packages() {
+ IFS=$'\n'
+ local files=($(find "${WORKDIR}/staging/" \
+ \( -type d -name "${ABSLIBREDEST##*/}" \) -prune \
+ -o \( -type f -not -iname '*.sig' \) -print))
+ local file
+ for file in "${files[@]}"; do
+ if [[ -f "${file}.sig" ]]; then
+ msg2 "File signature found, verifying..."
+
+ # Verify that the signature is correct, else remove for re-signing
+ if ! gpg --quiet --verify "${file}.sig" >/dev/null 2>&1; then
+ error "Failed! Re-signing..."
+ rm -f "${file}.sig"
+ fi
+ fi
+
+ if ! [[ -f "${file}.sig" ]]; then
+ create_signature "$file" || return 2
+ fi
+ done
+}
+
+# Clean everything if not in dry-run mode
+clean_files() {
+ local file_list=$1
+
+ local rmcmd=(rm -fv)
+ if [[ -n "${dryrun}" ]]; then
+ rmcmd=(printf "$(_ "removed '%s' (dry-run)")\n")
+ fi
+
+ msg "Removing files from local staging directory"
+ cd "${WORKDIR}/staging" && xargs -0r -a "$file_list" "${rmcmd[@]}"
+ cd "${WORKDIR}/staging" && find . -mindepth 1 -type d -empty \
+ -exec rmdir -p {} + 2>/dev/null
+}
+
+################################################################################
+
+usage() {
+ print "Usage: %s [OPTIONS]" "${0##*/}"
+ echo
+ prose 'This script uploads packages on $WORKDIR/staging
+ to the Parabola server.'
+ echo
+ print "Options:"
+ flag '-c' 'Clean; delete packages in $WORKDIR/staging'
+ flag '-l' "List; list packages but not upload them"
+ flag '-u' "Upload-only; do not run db-update on the server"
+
+ flag '-n' "Dry-run; don't actually do anything"
+ flag '-h' "Show this message"
+}
+
+main() {
+ if [[ -w / ]]; then
+ error "This program should be run as regular user"
+ return 1
+ fi
+
+ # Parse options
+ local mode="release_packages"
+ while getopts 'clunh' arg; do
+ case $arg in
+ c) mode=clean ;;
+ l) mode=pretty_print_packages ;;
+ u) upload_only=true ;;
+ n) dryrun="--dry-run" ;;
+ h) mode=usage ;;
+ *) usage >&2; return 1 ;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# != 0 ]]; then
+ usage >&2
+ return 1
+ fi
+
+ if [[ $mode == usage ]]; then
+ usage
+ return 0
+ fi
+
+ load_files makepkg
+ check_vars makepkg GPGKEY
+ load_files libretools
+ check_vars libretools WORKDIR REPODEST ABSLIBREDEST || return 1
+ REPODEST+='/staging/'
+ # The following settings are actually optional
+ #check_vars libretools HOOKPRERELEASE HOOKPOSTRELEASE || return 1
+
+ "$mode"
+}
+
+# The different modes (sans 'usage') ###########################################
+
+pretty_print_packages() {
+ find "$WORKDIR/staging/" -mindepth 1 -maxdepth 1 -type d -not -empty | sort |
+ while read -r path; do
+ msg2 "${path##*/}"
+ cd "$path"
+ find -L . -type f -not -name '*.lock' | sed 's|^\./| |' | sort
+ done
+}
+
+clean() {
+ lock 8 "${WORKDIR}/staging.lock" \
+ 'Waiting for an exclusive lock on the staging directory'
+
+ local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)"
+ trap "$(printf 'rm -f -- %q' "$file_list")" EXIT
+ list0_files > "$file_list"
+
+ lock_close 8
+
+ clean_files "$file_list"
+}
+
+release_packages() {
+ if [[ -n $HOOKPRERELEASE ]]; then
+ msg "Running HOOKPRERELEASE..."
+ plain '%s' "${HOOKPRERELEASE}"
+ bash -c "${HOOKPRERELEASE}"
+ fi
+
+ lock 8 "${WORKDIR}/staging.lock" \
+ 'Waiting for an exclusive lock on the staging directory'
+
+ sign_packages || return 1
+
+ # Make the permissions of the packages 644 otherwise the user will get access
+ # denied error when they try to download (rsync --no-perms doesn't seem to
+ # work).
+ find "${WORKDIR}/staging" -type f -exec chmod 644 {} +
+ find "${WORKDIR}/staging" -type d -exec chmod 755 {} +
+
+ local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)"
+ trap "$(printf 'rm -f -- %q' "$file_list")" EXIT
+ list0_files > "$file_list"
+
+ lock_close 8
+
+ msg "%s to upload" "$(cd "${WORKDIR}/staging" && du -hc --files0-from="$file_list" | sed -n '$s/\t.*//p')"
+ msg "Uploading packages..."
+ xargs -0r -a "$file_list" dirname -z | ssh "${REPODEST%%:*}" "$(printf 'mkdir -p -- %q && cd %q && xargs -0r mkdir -pv --' "${REPODEST#*:}"{,})"
+ if ! rsync ${dryrun} "${rsync_flags[@]}" \
+ -0 --files-from="$file_list" \
+ "${WORKDIR}/staging" \
+ "${REPODEST}/"
+ then
+ error "Sync failed, try again"
+ return 1
+ fi
+
+ clean_files "$file_list"
+
+ if $upload_only; then
+ return 0
+ fi
+
+ msg "Running db-update on repos"
+ ssh "${REPODEST%%:*}" "$(printf 'STAGING=%q dbscripts/db-update' "${REPODEST#*:}")"
+
+ if [[ -n $HOOKPOSTRELEASE ]]; then
+ msg "Running HOOKPOSTRELEASE..."
+ plain '%s' "${HOOKPOSTRELEASE}"
+ bash -c "${HOOKPOSTRELEASE}"
+ fi
+
+ return 0
+}
+
+main "$@"
diff --git a/src/abslibre-tools/librestage b/src/abslibre-tools/librestage
new file mode 100755
index 0000000..39523ac
--- /dev/null
+++ b/src/abslibre-tools/librestage
@@ -0,0 +1,154 @@
+#!/usr/bin/env bash
+# LibreStage
+# Prepares packages for upload
+
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. libremessages
+. "$(librelib conf.sh)"
+
+usage() {
+ print "Usage: %s [REPO]" "${0##*/}"
+ print "Stages the package(s) build by ./PKGBUILD for upload."
+ echo
+ prose "The package(s) are staged for the named repository, or the name
+ of the parent directory if a repository is not named."
+}
+
+main() {
+ if [[ -w / ]]; then
+ error "This program should be run as a regular user"
+ return 1
+ fi
+
+ # Parse options, set up
+ while getopts 'h' arg; do
+ case $arg in
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ local repo=
+ case $# in
+ 0) repo="$(basename "$(dirname "$PWD")")";;
+ 1) repo=$1;;
+ *) usage >&2; return 1;;
+ esac
+
+ if ! [[ -e ./PKGBUILD ]]; then
+ error "PKGBUILD not found"
+ return 1
+ fi
+
+ if ! xbs -b abslibre status; then
+ error "There are uncommitted changes in the current directory"
+ return 1
+ fi
+
+ # Load configuration
+ load_files libretools
+ # ABSLIBREDEST is used by xbs release-client
+ check_vars libretools WORKDIR ARCHES ABSLIBREDEST || return 1
+ load_files makepkg # for PKGDEST and SRCDEST, which are optional
+ load_files librefetch # for MIRRORS, which is optional
+
+ # Load the PKGBUILD
+ load_PKGBUILD
+
+ # Now for the main routine.
+ local staged=false
+ slock 8 "${WORKDIR}/staging.lock" \
+ 'Waiting for a shared lock on the staging directory'
+
+ # Look for makepkg output
+ local CARCH _pkgname pkgfile
+ for CARCH in "${ARCHES[@]}" any; do
+ for _pkgname in "${pkgname[@]}" "${pkgname[@]/%/-debug}"; do
+ if ! pkgfile=$(find_cached_package "$_pkgname" "$(get_full_version "$_pkgname")" "$CARCH"); then
+ continue
+ fi
+
+ msg 'Found package: %s' "${pkgfile##*/}"
+
+ # This little check is from devtools:commitpkg
+ if grep -q "packager = Unknown Packager" <(bsdtar -xOqf "$pkgfile" .PKGINFO); then
+ die "PACKAGER wes not set when building package"
+ fi
+
+ xbs -b abslibre release-client "$repo" "$CARCH"
+ mkdir -p "${WORKDIR}/staging/${repo}"
+ if cp "$pkgfile" "${WORKDIR}/staging/${repo}/${pkgfile##*/}"; then
+ msg2 "%s staged on [%s]" "$_pkgname" "$repo"
+ staged=true
+ else
+ error "Can't put %s on [%s]" "$_pkgname" "$repo"
+ return 1
+ fi
+ done
+ done
+
+ # Look for librefetch output
+ local netfile mirror path
+ local srcurl srcname srcpath
+ for netfile in "${source[@]}"; do
+ for mirror in "${MIRRORS[@]}"; do
+ srcurl=${netfile#*::}
+ if [[ "$srcurl" == "$mirror"* ]]; then
+ if [[ $netfile = *::* ]]; then
+ srcname=${netfile%%::*}
+ else
+ srcname=${netfile##*/}
+ fi
+
+ srcpath=''
+ for path in "./$srcname" "${SRCDEST:-.}/$srcname"; do
+ if [[ -f "$path" ]]; then
+ srcpath="$path"
+ break
+ fi
+ done
+ if [[ -n "$srcpath" ]]; then
+ msg "Found generated source file: %s" "$srcname"
+ local dest="${WORKDIR}/staging/other/${srcurl##"$mirror"}"
+ mkdir -p -- "${dest%/*}"
+ if cp "$srcpath" "$dest"; then
+ msg2 "%s staged on [%s]" "$srcname" other
+ staged=true
+ else
+ error "Can't put %s on [%s]" "$srcname" other
+ return 1
+ fi
+ fi
+ break
+ fi
+ done
+ done
+
+ if $staged ; then
+ return 0
+ else
+ error "Nothing was staged"
+ return 1
+ fi
+}
+
+main "$@"
diff --git a/src/aur b/src/aur
new file mode 100755
index 0000000..49c79b9
--- /dev/null
+++ b/src/aur
@@ -0,0 +1,146 @@
+#!/usr/bin/env bash
+# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. libremessages
+
+usage() {
+ print "Usage: %s [-h] PKGNAME [PKGNAME2 PKGNAME3...]" "${0##*/}"
+ print "Downloads packages from the AUR, and does basic freedom checks."
+ echo
+ prose "This script will download packages from AUR to the current
+ directory and check their license for nonfree issues. This does
+ not mean that they are free; they may be incorrectly labeled, or
+ have other freedom issues. It's a tool to help Parabola
+ packagers, not to help users install things directly from AUR."
+}
+
+main() {
+ while getopts 'h' arg; do
+ case $arg in
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ if [[ $# -lt 1 ]]; then
+ usage >&2
+ return 1
+ fi
+
+ . "$(librelib conf.sh)"
+ load_files libretools
+ check_vars libretools DIFFPROG || exit 1
+
+ local startdir="$(pwd)"
+ local missing_deps=()
+ local ret=0
+ local pkg
+ local copy_new
+ local copy_old
+ for pkg in "$@"; do
+ pkg="${pkg%%[<>=]*}" # remove the version
+ msg "Processing package: %s" "$pkg"
+ copy_new="$startdir/$pkg"
+ copy_old=
+
+ if [[ -f "${copy_new}/PKGBUILD" ]]; then
+ warning "%s already exists, will compare with new version." "$pkg"
+
+ # Store our copy of the PKGBUILD dir
+ copy_old=$copy_new
+ copy_new="$(mktemp --tmpdir -d aur-${pkg}.new.XXXX)/$pkg"
+ cd "${copy_new%/*}"
+ fi
+
+ msg2 "Downloading"
+ local url="https://aur.archlinux.org/packages/${pkg:0:2}/$pkg/$pkg.tar.gz"
+ set -o pipefail
+ if ! wget -O- -q "$url" | bsdtar xf -; then
+ ret=$(($ret|2))
+ error "Couldn't get %s" "$pkg"
+ continue
+ fi
+ set +o pipefail
+
+ if [[ -n $copy_old ]]; then
+ msg2 "Diffing files"
+ cd "$copy_new"
+
+ # Diff all files with our difftool
+ local diffed=false
+ for file in *; do
+ if ! cmp -s "${copy_old}/${file}" "${copy_new}/${file}" ; then
+ warning "%s != %s" "${copy_old}/${file}" "${copy_new}/${file}"
+ diffed=true
+ "${DIFFPROG}" "${copy_old}/${file}" "${copy_new}/${file}"
+ fi
+ done
+ if $diffed; then
+ read -p "Press enter to continue."
+ fi
+
+ # Go back to our copy to continue working
+ cd "$copy_old"
+ rm -rf -- "${copy_new%/*}"
+ else
+ cd "$copy_new"
+ fi
+
+ load_PKGBUILD
+
+ ################################################################
+
+ pkgbuild-check-nonfree -c
+ pkgbuild-summarize-nonfree $?
+
+ ################################################################
+
+ local _deps=(
+ # depends
+ "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}"
+ # mksource depends
+ "${mkdepends[@]}"
+ )
+ local _dep
+ msg2 "Checking dependencies"
+ for _dep in "${_deps[@]}"; do
+ _dep=${_dep/[<>=]*/}
+ if ! is_built $_dep; then
+ if ! pacman -Sddp "$_dep" &>/dev/null ; then
+ plain "%s: will be downloaded from AUR" "$_dep"
+ missing_deps+=($_dep)
+ fi
+ else
+ plain "%s: is on repos" "$_dep"
+ fi
+ done
+ cd "$startdir"
+ done
+
+ if [[ ${#missing_deps[*]} -gt 0 ]]; then
+ msg "Retrieving missing deps: %s" "${missing_deps[*]}"
+ "$0" "${missing_deps[@]}"
+ ret=$(($ret|$?))
+ fi
+ return $ret;
+}
+
+main "$@"
diff --git a/src/chroot-tools/.gitignore b/src/chroot-tools/.gitignore
new file mode 100644
index 0000000..f0969c7
--- /dev/null
+++ b/src/chroot-tools/.gitignore
@@ -0,0 +1,7 @@
+makechrootpkg.sh*
+arch-nspawn*
+mkarchroot*
+!*.patch
+
+chcleanup
+chcleanup.lib
diff --git a/src/chroot-tools/HACKING.md b/src/chroot-tools/HACKING.md
new file mode 100644
index 0000000..e50aa11
--- /dev/null
+++ b/src/chroot-tools/HACKING.md
@@ -0,0 +1,3 @@
+Unfortunately, `makechrootpkg.sh` is GPLv2 ONLY. This means that
+everything that uses it must be held to GPLv2+ instead of GPLv3+. I'm
+calling this anything that gets loaded into the same process as it.
diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile
new file mode 100644
index 0000000..48a1631
--- /dev/null
+++ b/src/chroot-tools/Makefile
@@ -0,0 +1,27 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+pkglibexecdir = $(libexecdir)/libretools/chroot
+
+libretools-bins = chcleanup librechroot libremakepkg
+libretools-libexecs += arch-nspawn chcleanup distcc-tool indent mkarchroot
+libretools-libs += makechrootpkg.sh
+devtools-files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in
+am_clean_files += chcleanup.lib
+
+$(srcdir)/makechrootpkg.sh.in: $(srcdir)/%.sh.in: $(devtoolsdir)/%.in
+ cp $< $@
+
+$(outdir)/chcleanup: $(srcdir)/chcleanup.in $(outdir)/chcleanup.lib
+ m4 -I$(@D) -P $< | $(edit) | install -m755 /dev/stdin $@
+$(outdir)/chcleanup.lib: $(call _am_path,$(topoutdir)/src/lib/common.sh) $(outdir)/Makefile
+ bash -c '. $<; declare -f _l plain msg msg2 error' > $@
+
+$(outdir)/distcc-tool.pot: LIBREXGETTEXT_FLAGS+=--simple=errusage
+
+$(DESTDIR)$(bindir)/chcleanup: $(var)bindir $(var)libexecdir
+ mkdir -p $(@D)
+ ln -srfT $(DESTDIR)$(libexecdir)/libretools/chroot/chcleanup $@
+
+am_depdirs = ../lib
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/chroot-tools/arch-nspawn.patch b/src/chroot-tools/arch-nspawn.patch
new file mode 100644
index 0000000..dc4cb25
--- /dev/null
+++ b/src/chroot-tools/arch-nspawn.patch
@@ -0,0 +1,71 @@
+--- arch-nspawn.in 2016-05-10 13:48:14.303797115 -0400
++++ arch-nspawn.ugly 2016-05-10 13:48:23.296957958 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -14,6 +16,8 @@
+
+ working_dir=''
+
++files=()
++
+ usage() {
+ echo "Usage: ${0##*/} [options] working-dir [systemd-nspawn arguments]"
+ echo "A wrapper around systemd-nspawn. Provides support for pacman."
+@@ -22,17 +26,21 @@
+ echo ' -C <file> Location of a pacman config file'
+ echo ' -M <file> Location of a makepkg config file'
+ echo ' -c <dir> Set pacman cache'
++ echo ' -f <file> Copy file from the host to the chroot'
++ echo ' -s Do not run setarch'
+ echo ' -h This message'
+ exit 1
+ }
+
+ orig_argv=("$@")
+
+-while getopts 'hC:M:c:' arg; do
++while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ C) pac_conf="$OPTARG" ;;
+ M) makepkg_conf="$OPTARG" ;;
+ c) cache_dir="$OPTARG" ;;
++ f) files+=("$OPTARG") ;;
++ s) nosetarch=1 ;;
+ h|?) usage ;;
+ *) error "invalid argument '%s'" "$arg"; usage ;;
+ esac
+@@ -78,6 +86,12 @@
+ [[ -n $pac_conf ]] && cp $pac_conf "$working_dir/etc/pacman.conf"
+ [[ -n $makepkg_conf ]] && cp $makepkg_conf "$working_dir/etc/makepkg.conf"
+
++ local file
++ for file in "${files[@]}"; do
++ mkdir -p "$(dirname "$working_dir$file")"
++ cp -T "$file" "$working_dir$file"
++ done
++
+ sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${cache_dirs[@]})|g" -i "$working_dir/etc/pacman.conf"
+ }
+ # }}}
+@@ -92,6 +106,7 @@
+ fi
+
+ build_mount_args
++cache_dirs+=('/repo/')
+ copy_hostconf
+
+ eval $(grep '^CARCH=' "$working_dir/etc/makepkg.conf")
+@@ -99,6 +114,8 @@
+ armv7h) CARCH=armv7l;;
+ esac
+
++[[ -z $nosetarch ]] || unset CARCH
++
+ exec ${CARCH:+setarch "$CARCH"} systemd-nspawn -q \
+ -D "$working_dir" \
+ --register=no \
diff --git a/src/chroot-tools/chcleanup.in b/src/chroot-tools/chcleanup.in
new file mode 100644
index 0000000..04cf8ad
--- /dev/null
+++ b/src/chroot-tools/chcleanup.in
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012-2013, 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# If you don't see m4_include(...) below, but see function definitions
+# for msg() et al., then this is a generated file, and contains some
+# code from librelib. The the source distribution for full copyright
+# information.
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+# Performs chroot cleanup smartly, it only removes the unneeded packages or
+# leaves you with a cleansystem
+#
+# See: HOOKPREBUILD
+
+set -eE
+
+DRYRUN=${DRYRUN:-false}
+
+# Statically include various library routines to avoid having
+# dependencies on outside files.
+[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools'
+[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale'
+
+if type gettext &>/dev/null; then
+ _() { gettext "$@"; }
+else
+ _() { echo "$@"; }
+fi
+# Begin chcleanup.lib ##########################################################
+m4_include(chcleanup.lib)
+# End chcleanup.lib ############################################################
+
+if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then
+ error "(chcleanup): Must be run inside of a chroot"
+ exit 1
+fi
+
+# Note: the in-chroot pkgconfdir is non-configurable, this is
+# intentionally hard-coded.
+source /etc/libretools.d/chroot.conf
+# If we're running makepkg
+if [[ -f PKGBUILD ]]; then
+ export CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")"
+ source ./PKGBUILD
+ CHROOTEXTRAPKG+=("${depends[@]}"
+ "${makedepends[@]}"
+ "${checkdepends[@]}")
+fi
+
+msg "Cleaning chroot..."
+
+# Sync the local repo with pacman
+cp /repo/repo.db /var/lib/pacman/sync/repo.db
+
+# Setup the temporary directory
+TEMPDIR="$(mktemp --tmpdir -d ${0##*/}.XXXXXXXXXX)"
+trap "rm -rf -- $(printf '%q' "$TEMPDIR")" EXIT
+
+cp -a /var/lib/pacman/sync "${TEMPDIR}/"
+pkglist="${TEMPDIR}"/pkglist.txt
+
+# Get the full list of packages needed by dependencies, including the base system
+msg2 "Creating a full list of packages..."
+pacman -b "${TEMPDIR}" \
+ -Sp --print-format "%n" base-devel "${CHROOTEXTRAPKG[@]}" >"$pkglist" || {
+ ret=$?
+ error "Could not create a full list of packages, exiting."
+ plain "This is likely caused by a dependency that could not be found."
+ exit $ret
+}
+
+# Diff installed packages against a clean chroot then remove leftovers
+packages=($(comm -23 <(pacman -Qq | sort -u) \
+ <(sort -u "${pkglist}")))
+
+if [[ ${#packages[@]} = 0 ]]; then
+ msg2 "No packages to remove"
+else
+ msg2 "Removing %d packages" ${#packages[@]}
+
+ if ${DRYRUN}; then
+ echo "${packages[*]}"
+ else
+ # Only remove leftovers, -Rcs removes too much
+ pacman --noconfirm -Rn "${packages[@]}"
+ fi
+fi
diff --git a/src/chroot-tools/chroot.conf b/src/chroot-tools/chroot.conf
new file mode 100644
index 0000000..3b3c445
--- /dev/null
+++ b/src/chroot-tools/chroot.conf
@@ -0,0 +1,13 @@
+# The full path to the chroot is
+# $CHROOTDIR/$CHROOT/$COPY
+# where $COPY is set at runtime.
+# See `librechroot help` for details.
+CHROOTDIR=/var/lib/archbuild
+CHROOT=default
+
+# Extra packages to have installed on the chroot.
+# This is in addition to CHROOTPKG=(base-devel)
+CHROOTEXTRAPKG=()
+#CHROOTEXTRAPKG+=(distcc-nozeroconf socat) # for BUILDENV+=(distcc)
+#CHROOTEXTRAPKG+=(ccache) # for BUILDENV+=(ccache)
+#CHROOTEXTRAPKG+=(libretools) # for running libremakepkg inside the chroot
diff --git a/src/chroot-tools/distcc-tool b/src/chroot-tools/distcc-tool
new file mode 100755
index 0000000..d181109
--- /dev/null
+++ b/src/chroot-tools/distcc-tool
@@ -0,0 +1,247 @@
+#!/usr/bin/env bash
+# -*- tab-width: 4; sh-basic-offset: 4 -*-
+# distcc-tool
+
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# This program has very few dependencies:
+# - bash: I don't know what version, I use fairly modern features
+# - socat
+# - cat: any version
+# - rm: any version
+# - sed: any version
+# On Parabola, this means the packages:
+# bash, coreutils, sed, socat
+
+if ! type gettext &>/dev/null; then
+ gettext() { echo "$@"; }
+fi
+
+q0="$(printf '%q' "$0")" # quoted $0
+
+panic() {
+ echo "$(gettext 'panic: malformed call to internal function')" >&2
+ exit 1
+}
+
+error() {
+ mesg="$(gettext "$1")"; shift
+ printf "$(gettext 'ERROR:') $mesg\n" "$@" >&2
+ exit 1
+}
+
+print() {
+ local mesg=$1
+ shift
+ printf -- "$(gettext "$mesg")\n" "$@"
+}
+
+usage() {
+ print "Usage: %s COMMAND [COMMAND-ARGS]" "$q0"
+ print "Tool for using distcc within a networkless chroot"
+ echo
+ print "Commands:"
+ print ' help print this message'
+ print ' odaemon CHROOTPATH daemon to run outside of the chroot'
+ print ' idaemon DISTCC_HOSTS daemon to run inside of the chroot'
+ print ' rewrite DISTCC_HOSTS prints a rewritten version of DISTCC_HOSTS'
+ print ' client HOST PORT connects stdio to TCP:$HOST:$PORT'
+ print 'Commands: for internal use'
+ print ' server counterpart to client; spawned by odaemon'
+}
+
+errusage() {
+ if [[ $# -gt 0 ]]; then
+ fmt="$(gettext "$1")"; shift
+ printf "$(gettext 'ERROR:') $fmt\n" "$@" >&2
+ fi
+ usage >&2
+ exit 1
+}
+
+main() {
+ local cmd=$1
+ shift
+ case "$cmd" in
+ help)
+ [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ usage;;
+ odaemon|idaemon|rewrite)
+ [[ $# -eq 1 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ client)
+ [[ $# -eq 2 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ server)
+ [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ *) errusage 'unknown subcommand: %s' "$cmd";;
+ esac
+}
+
+################################################################################
+# DISTCC_HOSTS parser #
+################################################################################
+
+# usage: parse_DISTCC_HOSTS true|false DISTCC_HOSTS
+# parses DISTCC_HOSTS and:
+# $1==true : It sets up port forwarding for inside the choot, sleep forever
+# $1==false: Prints a modified version of DISTCC_HOSTS that uses the forwarded
+# ports that were set up when $1==true.
+parse_DISTCC_HOSTS() {
+ { [[ $# -eq 2 ]] && { [[ $1 == true ]] || [[ $1 == false ]]; }; } || panic
+ local forward_ports=$1
+ local DISTCC_HOSTS=$2
+
+ local pids=() # child pids
+ local newhosts=()
+ local newport=8000 # next port to be used for port forwarding
+
+ # This is based on the grammar specified in distcc(1)
+ local HOSTSPEC
+ for HOSTSPEC in $(sed 's/#.*//' <<<"$DISTCC_HOSTS"); do
+ case "$HOSTSPEC" in
+ # LOCAL_HOST
+ localhost|localhost/*|--localslots=*|--localslots_cpp=*)
+ # "localhost" runs commands directly, not talking to distccd at
+ # localhost, use an IP or real hostname for that.
+ # So, just pass these through.
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # SSH_HOST
+ *@*)
+ # SSH_HOST doesn't allow custom port numbers, and even if it
+ # did, ssh would complain about MITM. Instead, we'll count on
+ # ssh ProxyCommand being configured to use `client`.
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # GLOBAL_OPTION
+ --*)
+ # pass these through
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # ZEROCONF
+ +zeroconf)
+ error "%s does not support the +zeroconf option" "$q0"
+ exit 1
+ ;;
+ # TCP_HOST or OLDSTYLE_TCP_HOST
+ *)
+ declare HOSTID= PORT= LIMIT= OPTIONS=
+ if [[ $HOSTSPEC =~ ^([^:/]+)(:([0-9]+))?(/([0-9]+))?(,.*)?$ ]]; then
+ # TCP_HOST
+ HOSTID=${BASH_REMATCH[1]}
+ PORT=${BASH_REMATCH[3]}
+ LIMIT=${BASH_REMATCH[5]}
+ OPTIONS=${BASH_REMATCH[6]}
+ elif [[ $HOSTSPEC =~ ^([^:/]+)(/([0-9]+))?(:([0-9]+))?(,.*)?$ ]]; then
+ # OLDSTYLE_TCP_HOST
+ HOSTID=${BASH_REMATCH[1]}
+ LIMIT=${BASH_REMATCH[3]}
+ PORT=${BASH_REMATCH[5]}
+ OPTIONS=${BASH_REMATCH[6]}
+ else
+ error "Could not parse HOSTSPEC: %s" "$HOSTSPEC"
+ fi
+
+ # set up port forwaring
+ if $forward_ports; then
+ socat TCP-LISTEN:${newport},fork SYSTEM:"$q0 client $HOSTID ${PORT:-3632}" &
+ pids+=($!)
+ fi
+
+ # add the forwarded port
+ local newhost="127.0.0.1:$newport"
+ [[ -z $LIMIT ]] || newhost+="/$LIMIT"
+ [[ -z $OPTIONS ]] || newhost+="$OPTIONS"
+ newhosts+=("$newhost")
+ : $((newport++))
+ ;;
+ esac
+ done
+ if $forward_ports; then
+ if [[ -z "${pids[*]}" ]]; then
+ # listen on port 8000, but immediatly close, just so that we are
+ # listening on something
+ socat TCP-LISTEN:${newport},fork SYSTEM:true &
+ pids+=($!)
+ fi
+ trap "kill -- ${pids[*]}" EXIT
+ wait "${pids[@]}"
+ else
+ printf '%s\n' "${newhosts[*]}"
+ fi
+}
+
+################################################################################
+# Port forwarding primitives #
+################################################################################
+
+# Usage: server
+# Reads "host port" from the first line of stdin, then connects stdio to the
+# specified TCP socket.
+server() {
+ [[ $# -eq 0 ]] || panic
+ local host port
+ read -r host port
+ socat STDIO TCP:"$host:$port"
+}
+
+# Usage: client HOST PORT
+# For usage inside of a chroot. It talks through the UNIX-domain socket to an
+# instance of `server` outside, in order to connect stdio to the specified TCP
+# socket.
+client() {
+ [[ $# -eq 2 ]] || panic
+ local file=/socket
+ { printf '%s\n' "$*"; cat; } | socat UNIX-CONNECT:"$file" STDIO
+}
+
+################################################################################
+# High-level routines #
+################################################################################
+
+# Usage: odaemon CHROOTPATH
+# Listens on "$CHROOTPATH/socket" and spawns a `server` for each new connection.
+odaemon() {
+ [[ $# -eq 1 ]] || panic
+ local chrootpath=$1
+
+ umask 111
+ socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$q0 server" &
+ trap "kill -- $!; rm -f -- $(printf '%q' "$chrootpath/socket")" EXIT
+ wait
+}
+
+# Usage: idaemon DISTCC_HOSTS
+# Sets things up inside of the chroot to forward distcc hosts out.
+idaemon() {
+ [[ $# -eq 1 ]] || panic
+ parse_DISTCC_HOSTS true "$1"
+}
+
+# Usage: rewrite DISTCC_HOSTS
+# Prints a modified version of DISTCC_HOSTS for inside the chroot.
+rewrite() {
+ [[ $# -eq 1 ]] || panic
+ parse_DISTCC_HOSTS false "$1"
+}
+
+main "$@"
diff --git a/src/chroot-tools/hooks-chcleanup.sh b/src/chroot-tools/hooks-chcleanup.sh
new file mode 100644
index 0000000..7afb13d
--- /dev/null
+++ b/src/chroot-tools/hooks-chcleanup.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_pre_build+=("clean_chroot")
+
+clean_chroot() (
+ set +x
+ local copydir=$1
+ if $INCHROOT; then
+ cd /startdir
+ "$(librelib chroot/chcleanup)"
+ else
+ librechroot "${librechroot_flags[@]}" clean-pkgs
+ fi
+)
diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh
new file mode 100644
index 0000000..850516b
--- /dev/null
+++ b/src/chroot-tools/hooks-check.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_check_pkgbuild+=("check_pkgbuild_nonfree")
+check_pkgbuild_nonfree() {
+ local s=0
+ sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$?
+ pkgbuild-summarize-nonfree $s
+}
+
+#hook_check_pkgbuild+=("check_pkgbuild_namcap")
+check_pkgbuild_namcap() {
+ sudo -EH -u "$LIBREUSER" namcap PKGBUILD
+}
+
+#hook_check_pkg+=("check_pkg")
+check_pkg() {
+ # TODO
+ :
+}
diff --git a/src/chroot-tools/hooks-distcc.sh b/src/chroot-tools/hooks-distcc.sh
new file mode 100644
index 0000000..bb234b8
--- /dev/null
+++ b/src/chroot-tools/hooks-distcc.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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 2 of the License, or
+# (at your option) any later version.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_pre_build+=("distcc_start")
+hook_post_build+=("distcc_stop")
+
+_distcc_check() {
+ local copydir=$1
+ local home=$2
+
+ local files=(
+ "$copydir/bin/distcc-tool"
+ "$copydir/run/distcc-tool.pid"
+ "$home/.makepkg.conf"
+ "$home/.ssh/config"
+ )
+
+ local file_err=false
+ for files in "${files[@]}"; do
+ if [[ -f $file ]]; then
+ file_err=true
+ error "Auto-generated file already exists, remove it: %s" "$file"
+ fi
+ done
+ if $file_err; then
+ exit 1
+ fi
+}
+
+distcc_start() {
+ local copydir=$1
+
+ # Because /{,usr/}{,s}bin are all symlinked together for
+ # fileystem 2013.05-2 and up, I can take shortcuts when checking for
+ # existance of programs.
+ if $NONET && [[ -f "$copydir/bin/socat" && -f "$copydir/bin/distcc" ]]; then
+ local home
+ if $INCHROOT; then
+ home=$LIBREHOME
+ else
+ home="$copydir/build"
+ fi
+
+ _distcc_check
+
+ local _distcc_tool="$(librelib chroot/distcc-tool)"
+ install -m755 "$_distcc_tool" "$copydir/bin/distcc-tool"
+
+ mkdir -p "$home/.ssh"
+
+ printf '%s\n' \
+ '/bin/distcc-tool idaemon "$DISTCC_HOSTS" &' \
+ 'DISTCC_HOSTS="$(/bin/distcc-tool rewrite "$DISTCC_HOSTS")"' \
+ > "$home/.makepkg.conf"
+
+ printf '%s\n' \
+ 'Host *' \
+ ' ProxyCommand /bin/distcc-tool client %h %p' \
+ > "$home/.ssh/config"
+
+ "$_distcc_tool" odaemon "$copydir" &
+ echo $! > "$copydir/run/distcc-tool.pid"
+ fi
+}
+
+distcc_stop() {
+ local copydir=$1
+
+ local home
+ if $INCHROOT; then
+ home=$LIBREHOME
+ else
+ home="$copydir/build"
+ fi
+
+ if [[ -f "$copydir/run/distcc-tool.pid" ]]; then
+
+ odaemon=$(< "$copydir/distcc-tool.pid")
+ kill -- "$odaemon"
+
+ rm -f -- \
+ "$home/.makepkg.conf" \
+ "$home/.ssh/config" \
+ "$copydir/bin/distcc-tool" \
+ "$copydir/run/distcc-tool.pid"
+ fi
+}
diff --git a/src/chroot-tools/indent b/src/chroot-tools/indent
new file mode 100755
index 0000000..0f047c6
--- /dev/null
+++ b/src/chroot-tools/indent
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+use warnings;
+use strict;
+
+use constant BUFFER_SIZE => 40;
+binmode(STDIN);
+binmode(STDOUT);
+
+exit(1) if ($#ARGV != 0);
+my $indent = $ARGV[0];
+
+# 0: no indent has been printed for this line, an indent WILL need to be printed
+# 1: an indent needs to be printed for this line IFF there is any more output on it
+# 2: no indent (currently) needs to be printed for this line
+my $print_indent = 0;
+
+my $buffer;
+my $size;
+my $c;
+while (1) {
+ $size = sysread(STDIN, $buffer, BUFFER_SIZE);
+ last if ($size < 1);
+ for (0..$size-1) {
+ $c = substr($buffer, $_, 1);
+ if ($c eq "\n") {
+ syswrite(STDOUT, $indent) if ($print_indent == 0);
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 0;
+ } elsif ($c eq "\r") {
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 1 if ($print_indent == 2);
+ } else {
+ syswrite(STDOUT, $indent) if ($print_indent < 2);
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 2;
+ }
+ }
+}
diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot
new file mode 100755
index 0000000..cf564ed
--- /dev/null
+++ b/src/chroot-tools/librechroot
@@ -0,0 +1,509 @@
+#!/usr/bin/env bash
+set -euE
+# librechroot
+
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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 2 of the License, or
+# (at your option) any later version.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# HACKING: if a command is added or removed, it must be changed in 4 places:
+# - the usage() text
+# - the commands=() array
+# - the case statement in main() that checks the number of arguments
+# - the case statement in main() that runs them
+
+. "$(librelib conf)"
+load_files chroot
+
+. libremessages
+
+shopt -s nullglob
+umask 0022
+
+################################################################################
+# Wrappers for files in ${pkglibexecdir}/chroot/ #
+################################################################################
+
+readonly _arch_nspawn="$(librelib chroot/arch-nspawn)"
+readonly _mkarchroot="$(librelib chroot/mkarchroot)"
+readonly _makechrootpkg="$(librelib chroot/makechrootpkg.sh)"
+
+arch_nspawn_flags=()
+sysd_nspawn_flags=()
+
+hack_arch_nspawn_flags() {
+ local copydir="$1"
+
+ local makepkg_conf="$copydir/etc/makepkg.conf"
+
+ OPTIND=1
+ set -- ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}
+ while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ M) makepkg_conf="$OPTARG" ;;
+ *) :;;
+ esac
+ done
+
+ # Detect the architecture of the chroot
+ local CARCH
+ if [[ -f "$makepkg_conf" ]]; then
+ eval $(grep '^CARCH=' "$makepkg_conf")
+ else
+ CARCH="$(uname -m)"
+ fi
+
+ if [[ "$CARCH" == armv7h ]] && ! setarch armv7l 2>/dev/null; then
+ # We're running an ARM chroot on a non-ARM processor
+
+ # Make sure that qemu-static is set up with binfmt_misc
+ if [[ $(grep -xF \
+ -e 'enabled'\
+ -e 'interpreter /usr/bin/qemu-arm-static' \
+ /proc/sys/fs/binfmt_misc/arm 2>/dev/null |wc -l) -lt 2 ]]; then
+ error 'Cannot cross-compile for ARM on x86'
+ plain 'This requires a binfmt_misc entry for qemu-arm-static,'
+ plain 'which is provided by the %s package.' binfmt-qemu-static
+ plain 'If you have this, you may need to restart %s.' systemd-binfmt.service
+ return 1
+ fi
+
+ # Let qemu/binfmt_misc do its thing
+ arch_nspawn_flags+=(-f /usr/bin/qemu-arm-static -s)
+
+ # The -any packages are built separately for ARM from
+ # x86, so if we use the same CacheDir as the x86 host,
+ # then there will be PGP errors.
+ mkdir -p /var/cache/pacman/pkg-arm
+ arch_nspawn_flags+=(-c /var/cache/pacman/pkg-arm)
+ fi
+}
+
+# Usage: arch-nspawn $copydir $cmd...
+arch-nspawn() {
+ local copydir=$1; shift
+ local cmd=("$@")
+
+ local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"})
+ hack_arch_nspawn_flags "$copydir"
+
+ "$_arch_nspawn" \
+ ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \
+ "$copydir" \
+ ${sysd_nspawn_flags+"${sysd_nspawn_flags[@]}"} \
+ -- \
+ "${cmd[@]}"
+}
+
+# Usage: mkarchroot $copydir $pkgs...
+mkarchroot() {
+ local copydir=$1; shift
+ local pkgs=("$@")
+
+ local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"})
+ hack_arch_nspawn_flags "$copydir"
+
+ unshare -m "$_mkarchroot" \
+ ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \
+ "$copydir" \
+ "${pkgs[@]}"
+}
+
+# Usage: _makechrootpkg $function $arguments...
+# Don't load $_makechrootpkg directly because it doesn't work with -euE
+_makechrootpkg() (
+ set +euE
+ . "$_makechrootpkg"
+ "$@"
+)
+
+################################################################################
+# Utility functions #
+################################################################################
+
+# Usage: make_empty_repo $copydir
+make_empty_repo() {
+ local copydir=$1
+ mkdir -p "${copydir}/repo"
+ bsdtar -czf "${copydir}/repo/repo.db.tar.gz" -T /dev/null
+ ln -s "repo.db.tar.gz" "${copydir}/repo/repo.db"
+}
+
+# Usage: chroot_add_to_local_repo $copydir $pkgfiles...
+chroot_add_to_local_repo() {
+ local copydir=$1; shift
+ mkdir -p "$copydir/repo"
+ local pkgfile
+ for pkgfile in "$@"; do
+ cp "$pkgfile" "$copydir/repo"
+ pushd "$copydir/repo" >/dev/null
+ repo-add repo.db.tar.gz "${pkgfile##*/}"
+ popd >/dev/null
+ done
+}
+
+# Print code to set $rootdir and $copydir; blank them on error
+calculate_directories() {
+ # Don't assume that CHROOTDIR or CHROOT are set,
+ # but assume that COPY is set.
+ local rootdir copydir
+
+ if [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then
+ rootdir="${CHROOTDIR}/${CHROOT}/root"
+ else
+ rootdir=''
+ fi
+
+ if [[ ${COPY:0:1} = / ]]; then
+ copydir=$COPY
+ elif [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then
+ copydir="${CHROOTDIR}/${CHROOT}/${COPY}"
+ else
+ copydir=''
+ fi
+
+ declare -p rootdir
+ declare -p copydir
+}
+
+check_mountpoint() {
+ local file=$1
+ local mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')"
+ local mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }"))
+ ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}"
+}
+
+################################################################################
+# Main program #
+################################################################################
+
+usage() {
+ eval "$(calculate_directories)"
+ print "Usage: %s [OPTIONS] COMMAND [ARGS...]" "${0##*/}"
+ print 'Interacts with an archroot (arch chroot).'
+ echo
+ prose 'This is configured with `chroot.conf`, either in
+ `/etc/libretools.d/`, or `$XDG_CONFIG_HOME/libretools/`.
+ The variables you may set are $CHROOTDIR, $CHROOT, and
+ $CHROOTEXTRAPKG.'
+ echo
+ prose 'There may be multiple chroots; they are stored in $CHROOTDIR.'
+ echo
+ prose 'Each chroot is named; the default is configured with $CHROOT.'
+ echo
+ prose 'Each named chroot has a master clean copy (named `root`), and any
+ number of other named copies; the copy used by default is the
+ current username (or $SUDO_USER, or `copy` if root).'
+ echo
+ prose 'The full path to the chroot copy is "$CHROOTDIR/$CHROOT/$COPY",
+ unless the copy name is manually specified as an absolute path,
+ in which case, that path is used.'
+ echo
+ prose 'The current settings for the above variables are:'
+ printf ' CHROOTDIR : %s\n' "${CHROOTDIR:-$(_ 'ERROR: NO SETTING')}"
+ printf ' CHROOT : %s\n' "${CHROOT:-$(_ 'ERROR: NO SETTING')}"
+ printf ' COPY : %s\n' "$COPY"
+ printf ' rootdir : %s\n' "${rootdir:-$(_ 'ERROR')}"
+ printf ' copydir : %s\n' "${copydir:-$(_ 'ERROR')}"
+ echo
+ prose 'If the chroot or copy does not exist, it will be created
+ automatically. A chroot by default contains the packages in the
+ group "base-devel" and any packages named in $CHROOTEXTRAPKG.
+ Unless the `-C` or `-M` flags are used, the configuration files
+ that this program installs are the stock versions supplied in the
+ packages, not the versions from your host system. Other tools
+ (such as libremakepkg) may alter the configuration.'
+ echo
+ prose 'This command will make the following configuration changes in the
+ chroot:'
+ bullet 'overwrite `/etc/libretools.d/chroot.conf`'
+ bullet 'overwrite `/etc/pacman.d/mirrorlist`'
+ bullet 'set `CacheDir` in `/etc/pacman.conf`'
+ prose 'If a new `pacman.conf` is inserted with the `-C` flag, the change
+ is made after the file is copied in; the `-C` flag doesn'"'"'t
+ stop the change from being effective.'
+ echo
+ prose 'The processor architecture of the chroot is determined
+ by the by `CARCH` variable in the `/etc/makepkg.conf`
+ file inside of the chroot.'
+ echo
+ prose 'The `-A CARCH` flag is *almost* simply an alias for'
+ printf ' %s\n' \
+ '-C "/usr/share/pacman/defaults/pacman.conf.$CARCH" \' \
+ '-M "/usr/share/pacman/defaults/makepkg.conf.$CARCH"'
+ prose 'However, before doing that, it actually makes a temporary copy of
+ `pacman.conf`, and sets the `Architecture` line to match the
+ `CARCH` line in `makepkg.conf`.'
+ echo
+ prose 'Creating a copy, deleting a copy, or syncing a copy can be fairly
+ slow; but are very fast if $CHROOTDIR is on a btrfs partition.'
+ echo
+ print 'Options:'
+ flag "-n <$(_ CHROOT)>" 'Name of the chroot to use'
+ flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the copy to use'
+ flag '-N' 'Disable networking in the chroot'
+ flag "-C <$(_ FILE)>" 'Copy this file to `$copydir/etc/pacman.conf`'
+ flag "-M <$(_ FILE)>" 'Copy this file to `$copydir/etc/makepkg.conf`'
+ flag "-A <$(_ CARCH)>" 'Set the architecture of the copy; simply an
+ alias for the `-C` and `-M` flags, see above.'
+ flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write'
+ flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only'
+ echo
+ print 'Commands:'
+ print ' Create/copy/delete:'
+ flag 'noop|make' 'Do not do anything, but still creates the chroot
+ copy if it does not exist'
+ flag 'sync' 'Sync the copy with the clean (`root`) copy'
+ flag 'delete' 'Delete the chroot copy'
+ print ' Dealing with packages:'
+ flag "install-file $(_ FILES...)" 'Like `pacman -U FILES...`'
+ flag "install-name $(_ NAMES...)" 'Like `pacman -S NAMES...`'
+ flag 'update' 'Like `pacman -Syu`'
+ flag 'clean-pkgs' 'Remove all packages from the chroot copy that
+ are not in base-devel, $CHROOTEXTRAPKG, or named
+ as a dependency in the file `/startdir/PKGBUILD`
+ in the chroot copy'
+ print ' Other:'
+ flag "run $(_ CMD...)" 'Run CMD in the chroot copy'
+ flag 'enter' 'Enter an interactive shell in the chroot copy'
+ flag 'clean-repo' 'Clean /repo in the chroot copy'
+ flag 'help' 'Show this message'
+}
+readonly commands=(
+ noop make sync delete
+ install-file install-name update clean-pkgs
+ run enter clean-repo help
+)
+
+# Globals: $CHROOTDIR, $CHROOT, $COPY, $rootdir and $copydir
+main() {
+ COPY=$LIBREUSER
+ [[ $COPY != root ]] || COPY=copy
+
+ local mode=enter
+ while getopts 'n:l:NC:M:A:w:r:' opt; do
+ case $opt in
+ n) CHROOT=$OPTARG;;
+ l) COPY=$OPTARG;;
+ N) sysd_nspawn_flags+=(--private-network);;
+ C|M) arch_nspawn_flags+=(-$opt "$OPTARG");;
+ A)
+ if ! [[ -f "/usr/share/pacman/defaults/pacman.conf.$OPTARG" && -f "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" ]]; then
+ error 'Unsupported architecture: %s' "$OPTARG"
+ plain 'See the files in %q for valid architectures.' /usr/share/pacman/defaults/
+ return 1;
+ fi
+ trap 'rm -f -- "$tmppacmanconf"' EXIT
+ tmppacmanconf="$(mktemp --tmpdir librechroot-pacman.conf.XXXXXXXXXX)"
+ < "/usr/share/pacman/defaults/pacman.conf.$OPTARG" sed -r "s|^#?\\s*Architecture.+|Architecture = ${OPTARG}|g" > "$tmppacmanconf"
+ arch_nspawn_flags+=(
+ -C "$tmppacmanconf"
+ -M "/usr/share/pacman/defaults/makepkg.conf.$OPTARG"
+ );;
+ w) sysd_nspawn_flags+=("--bind=$OPTARG");;
+ r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# -lt 1 ]]; then
+ error "Must specify a command"
+ usage >&2
+ return 1
+ fi
+ mode=$1
+ if ! in_array "$mode" "${commands[@]}"; then
+ error "Unrecognized command: %s" "$mode"
+ usage >&2
+ return 1
+ fi
+ shift
+ case "$mode" in
+ noop|make|sync|delete|update|enter|clean-pkgs|clean-repo)
+ if [[ $# -gt 0 ]]; then
+ error 'Command `%s` does not take any arguments: %s' "$mode" "$*"
+ usage >&2
+ return 1
+ fi
+ :;;
+ install-file)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one file' "$mode"
+ usage >&2
+ return 1
+ else
+ local missing=()
+ local file
+ for file in "$@"; do
+ if ! [[ -f $file ]]; then
+ missing+=("$file")
+ fi
+ done
+ if [[ ${#missing[@]} -gt 0 ]]; then
+ error "%s: file(s) not found: %s" "$mode" "${missing[*]}"
+ return 1
+ fi
+ fi
+ :;;
+ install-name)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one package name' "$mode"
+ usage >&2
+ return 1
+ fi
+ :;;
+ run)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one argument' "$mode"
+ usage >&2
+ return 1
+ fi
+ :;;
+ esac
+
+
+ if [[ $mode == help ]]; then
+ usage
+ return 0
+ fi
+
+ check_vars chroot CHROOTDIR CHROOT
+ eval "$(calculate_directories)"
+
+ readonly LIBREUSER LIBREHOME
+ readonly CHROOTDIR CHROOT COPY
+ readonly rootdir copydir
+ readonly mode
+
+ ########################################################################
+
+ if (( EUID )); then
+ error "This program must be run as root."
+ return 1
+ fi
+
+ umask 0022
+
+ # XXX: SYSTEMD-STDIN HACK
+ if ! [[ -t 0 ]]; then
+ error "Input is not a TTY"
+ plain "https://labs.parabola.nu/issues/431"
+ plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290"
+ prose "Due to a bug in systemd-nspawn, redirecting stdin is not
+ supported." >&2
+ return 1
+ fi
+
+ # Keep this lock for as long as we are running
+ # Note that '9' is the same FD number as in mkarchroot et al.
+ lock 9 "$copydir.lock" \
+ "Waiting for existing lock on chroot copy to be released: [%s]" "$COPY"
+
+ if [[ $mode != delete ]]; then
+ if ! check_mountpoint "$copydir.lock"; then
+ error "Chroot copy is mounted with nosuid or noexec options: [%s]" "$COPY"
+ return 1
+ fi
+
+ if [[ ! -d $rootdir ]]; then
+ msg "Creating 'root' copy for chroot [%s]" "$CHROOT"
+ mkarchroot "$rootdir" base-devel
+ make_empty_repo "$rootdir"
+ fi
+
+ if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then
+ msg "Syncing copy [%s] with root copy" "$COPY"
+ _makechrootpkg sync_chroot "$CHROOTDIR/$CHROOT" "$COPY"
+ fi
+
+ # Note: the in-chroot pkgconfdir is non-configurable, this is
+ # intentionally hard-coded.
+ mkdir -p "$copydir/etc/libretools.d"
+ {
+ if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then
+ echo 'CHROOTEXTRAPKG=()'
+ else
+ printf 'CHROOTEXTRAPKG=('
+ printf '%q ' "${CHROOTEXTRAPKG[@]}"
+ printf ')\n'
+ fi
+ } > "$copydir"/etc/libretools.d/chroot.conf
+
+ # "touch" the chroot first
+ # this will
+ # - overwrite '/etc/pacman.d/mirrorlist'"
+ # - set 'CacheDir' in \`/etc/pacman.conf'"
+ # - apply -C or -M flags
+ arch-nspawn "$copydir" true
+ trap EXIT # clear the trap to remove the tmp pacman.conf from -A
+ arch_nspawn_flags=() # XXX dirty hack, don't apply -C or -M again
+ fi
+
+ ########################################################################
+
+ case "$mode" in
+ # Creat/copy/delete
+ noop|make|sync) :;;
+ delete)
+ if [[ -d $copydir ]]; then
+ _makechrootpkg delete_chroot "$copydir"
+ fi
+ ;;
+
+ # Dealing with packages
+ install-file)
+ _makechrootpkg install_packages "$copydir" "$@"
+ chroot_add_to_local_repo "$copydir" "$@"
+ ;;
+ install-name)
+ arch-nspawn "$copydir" pacman -Sy -- "$@"
+ ;;
+ update)
+ arch-nspawn "$copydir" pacman -Syu --noconfirm
+ ;;
+ clean-pkgs)
+ trap "rm -f -- $(printf '%q ' "$copydir"/{bin/chcleanup,chrootexec})" EXIT
+ install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup"
+ printf '%s\n' \
+ '#!/bin/bash' \
+ 'mkdir -p /startdir' \
+ 'cd /startdir' \
+ '/bin/chcleanup' \
+ > "$copydir/chrootexec"
+ chmod 755 "$copydir/chrootexec"
+ arch-nspawn "$copydir" /chrootexec
+ ;;
+
+ # Other
+ run)
+ arch-nspawn "$copydir" "$@"
+ ;;
+ enter)
+ arch-nspawn "$copydir" bash
+ ;;
+ clean-repo)
+ rm -rf "${copydir}"/repo/*
+ make_empty_repo "$copydir"
+ ;;
+ esac
+}
+
+main "$@"
diff --git a/src/chroot-tools/libremakepkg b/src/chroot-tools/libremakepkg
new file mode 100755
index 0000000..a226e38
--- /dev/null
+++ b/src/chroot-tools/libremakepkg
@@ -0,0 +1,289 @@
+#!/usr/bin/env bash
+set -euE
+# libremakepkg
+
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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 2 of the License, or
+# (at your option) any later version.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. "$(librelib conf)"
+. "$(librelib messages)"
+. "$(librelib chroot/makechrootpkg.sh)"
+
+set -o pipefail
+shopt -s nullglob
+umask 0022
+
+# Global variables:
+readonly _indent="$(librelib chroot/indent)"
+readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false)
+NONET=true # can be changed with the -N flag
+# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf
+# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf
+# LIBREUSER, LIBREHOME are set by conf.sh
+librechroot_flags=()
+
+# Hooks ########################################################################
+
+hook_pre_build=(:)
+hook_post_build=(:)
+hook_check_pkgbuild=(:)
+hook_check_pkg=(:)
+. "$(librelib chroot/hooks-chcleanup.sh)"
+. "$(librelib chroot/hooks-check.sh)"
+. "$(librelib chroot/hooks-distcc.sh)"
+
+# Boring/mundane functions #####################################################
+
+indent() {
+ "$_indent" ' | '
+}
+
+# Usage: exit_copy $copydir $src_owner
+# End immediately, but copy log files out
+exit_copy() {
+ local copydir=$1
+ local src_owner=$2
+ if ! $INCHROOT; then
+ msg "Copying log and package files out of the chroot..."
+ move_products "$copydir" "$src_owner"
+ fi
+}
+
+# Usage; run_hook $hookname $args...
+run_hook() {
+ local hookname=$1; shift
+ local hookvar="hook_${hookname}[@]"
+
+ local fails=()
+ for hook in "${!hookvar}"; do
+ "$hook" "$@" || fails+=("$hook")
+ done |& indent
+
+ if [[ ${#fails[@]} -gt 0 ]]; then
+ error "Failure(s) in %s: %s" "$hookname" "${fails[*]}"
+ return 1
+ else
+ return 0
+ fi
+}
+
+# Usage: add_to_local_repo $copydir $pkgfiles...
+add_to_local_repo() {
+ local copydir=$1; shift
+ mkdir -p "$copydir/repo"
+ local pkgfile
+ for pkgfile in "$@"; do
+ cp "$pkgfile" "$copydir/repo"
+ pushd "$copydir/repo" >/dev/null
+ repo-add repo.db.tar.gz "${pkgfile##*/}"
+ popd >/dev/null
+ done
+}
+
+hook_post_build+=('cleanup')
+cleanup() {
+ local copydir=$1
+ rm -f -- "$copydir"/chroot{prepare,build}
+}
+
+build() (
+ local copydir=$1
+ local repack=$2
+ local makepkg_args=("${@:3}")
+
+ local run_ynet=()
+ local run_nnet=()
+ if $INCHROOT; then
+ run_ynet=(unshare)
+ run_nnet=(unshare -n)
+ else
+ run_ynet=(librechroot "${librechroot_flags[@]}" run)
+ run_nnet=(librechroot "${librechroot_flags[@]}" -N run)
+ fi
+ $NONET || run_nnet=("${run_ynet[@]}")
+
+ prepare_chroot "$copydir" "$LIBREHOME" "$repack" false
+ "${run_ynet[@]}" /chrootprepare "${makepkg_args[@]}" |& indent
+ run_hook pre_build "$copydir"
+ trap "run_hook post_build $(printf '%q' "$copydir")" EXIT
+ "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" |& indent
+)
+
+# The main program #############################################################
+
+usage() {
+ print "Usage: %s [options]" "${0##*/}"
+ print 'This program will build your package.'
+ echo
+ prose 'If run from outside of a chroot, command will make the following
+ configuration changes in the chroot:'
+ bullet 'whatever changes `librechroot` makes.'
+ bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`'
+ bullet 'set `PACKAGER` in `/etc/makepkg.conf` to reflect the value
+ outside of the chroot.'
+ bullet '(maybe) delete `/build/.makepkg.conf`'
+ bullet '(maybe) delete `/build/.ssh/config`'
+ prose 'If run from inside of a chroot, this command will:'
+ bullet '(maybe) delete `~/.makepkg.conf`'
+ bullet '(maybe) delete `~/.ssh/config`'
+ prose 'The above "maybe"s happen as part of the workarounds to make
+ distcc work in a network-less environment. They will happen if
+ both `socat` and `distcc` are installed in the chroot.'
+ echo
+ prose 'The `-n` and `-l` options behave identically to librechroot, see
+ the documentation there.'
+ echo
+ print 'Options:'
+ print ' %s options:' librechroot
+ flag "-n <$(_ CHROOT)>" 'Name of the chroot to use'
+ flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use'
+ flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write'
+ flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only'
+ print ' %s options:' libremakepkg
+ flag '-N' "Don't disable networking during build() and
+ package(). PLEASE don't use this unless you
+ have a special reason, its use is a violation
+ of Parabola policy."
+ flag '-R' 'Repackage contents of the package without rebuilding'
+ flag '-h' 'Show this message'
+}
+
+# Convenience method for use in option parsing
+err_chflag() {
+ local flag=$1
+ error 'The -%s flag does not make sense inside of a chroot' "$flag"
+ return 1
+}
+
+main() {
+ # Initial variable values ##############################################
+ local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER")
+ local makepkg_args=(-s --noconfirm -L)
+ local repack=false
+ local chroot=''
+
+ # Parse command line options ###########################################
+ while getopts 'n:l:w:r:NRh' flag ; do
+ case "${flag}" in
+ n) if $INCHROOT; then err_chflag "$flag"; else
+ chroot=$OPTARG; fi;;
+ l) if $INCHROOT; then err_chflag "$flag"; else
+ copy=$OPTARG; fi;;
+ w|r) if $INCHROOT; then err_chflag "$flag"; else
+ librechroot_flags+=(-$flag "$OPTARG"); fi;;
+ N) NONET=false;;
+ R) repack=true; makepkg_args+=(-R);;
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# != 0 ]]; then
+ error 'Extra arguments: %s' "$*"
+ return 1
+ fi
+
+ # Resolve the chroot path ##############################################
+ local copydir
+ if $INCHROOT; then
+ copydir='/'
+ else
+ load_files chroot
+ check_vars chroot CHROOTDIR CHROOT
+ [[ -z ${chroot} ]] || CHROOT=$chroot
+ if [[ ${copy:0:1} = / ]]; then
+ copydir=$copy
+ else
+ copydir="${CHROOTDIR}/${CHROOT}/${copy}"
+ fi
+ unset CHROOTDIR CHROOTEXTRAPKG
+ fi
+ unset chroot
+
+ # Load makepkg configuration ###########################################
+ # Note that all of these are globals
+ PKGDEST="$(get_var makepkg PKGDEST "$PWD")"
+ SRCDEST="$(get_var makepkg SRCDEST "$PWD")"
+ SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")"
+ LOGDEST="$(get_var makepkg LOGDEST "$PWD")"
+ MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')"
+ PACKAGER="$(get_var makepkg PACKAGER '')"
+
+ # Quick sanity check ###################################################
+
+ if (( EUID )); then
+ error "This program must be run as root"
+ exit 1
+ fi
+
+ if [[ ! -f PKGBUILD ]]; then
+ # This is the message used by makepkg
+ error "PKGBUILD does not exist."
+ exit 1
+ fi
+
+ # Make sure that the various *DEST directories exist
+ mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST"
+
+ # OK, we are starting now ##############################################
+
+ if $INCHROOT; then
+ lock 9 "/build/.lock" \
+ "Waiting for existing lock on build directory to be released"
+ else
+ librechroot_flags+=(
+ -r "$PWD:/startdir_host"
+ -r "$SRCDEST:/srcdest_host"
+ -n "$CHROOT"
+ -l "$copy"
+ )
+
+ # Obtain a lock on the chroot
+ lock 9 "$copydir.lock" \
+ "Waiting for existing lock on chroot copy to be released: [%s]" "$copy"
+ # Create the chroot if it does not exist
+ msg 'Initializing the chroot...'
+ librechroot "${librechroot_flags[@]}" make |& indent
+ fi
+
+ # Set target CARCH
+ # note that we waited until after locking/creating the chroot to do this
+ export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)"
+
+ # Pre-build
+ msg 'Starting pre-build activities...'
+ run_hook check_pkgbuild
+ msg 'Downloading sources...'
+ download_sources "$copydir" "$LIBREUSER" |& indent
+
+ # Build
+ msg 'Starting to build the package...'
+ trap "exit_copy '$copydir' '$LIBREUSER'" EXIT
+ build "$copydir" "$repack" "${makepkg_args[@]}"
+
+ # Post-build
+ msg 'Starting post-build activities...'
+ run_hook check_pkg
+ add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent
+}
+
+main "$@"
diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch
new file mode 100644
index 0000000..d5c6fca
--- /dev/null
+++ b/src/chroot-tools/makechrootpkg.sh.patch
@@ -0,0 +1,342 @@
+--- makechrootpkg.sh.in 2016-02-08 17:17:11.848386338 -0500
++++ makechrootpkg.sh.ugly 2016-04-14 22:01:05.923288181 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -12,6 +14,7 @@
+
+ shopt -s nullglob
+
++init_variables() {
+ default_makepkg_args=(-s --noconfirm -L --holdver)
+ makepkg_args=("${default_makepkg_args[@]}")
+ repack=false
+@@ -29,9 +32,10 @@
+ bindmounts_rw=()
+
+ copy=$USER
+-[[ -n $SUDO_USER ]] && copy=$SUDO_USER
++[[ -n ${SUDO_USER:-} ]] && copy=$SUDO_USER
+ [[ -z "$copy" || $copy = root ]] && copy=copy
+ src_owner=${SUDO_USER:-$USER}
++}
+
+ usage() {
+ echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]"
+@@ -68,28 +72,51 @@
+ }
+
+ # {{{ functions
++# Usage: load_vars $makepkg_conf
++# Globals:
++# - SRCDEST
++# - SRCPKGDEST
++# - PKGDEST
++# - LOGDEST
++# - MAKEFLAGS
++# - PACKAGER
+ load_vars() {
+ local makepkg_conf="$1" var
+
+ [[ -f $makepkg_conf ]] || return 1
+
+ for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
+- [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf")
++ [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf")
+ done
+
+ return 0
+ }
+
+-create_chroot() {
+- # Lock the chroot we want to use. We'll keep this lock until we exit.
+- lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy"
++# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir>
++sync_chroot() {
++ local chrootdir=$1
++ local copy=$2
++ local copydir=''
++ if [[ ${copy:0:1} = / ]]; then
++ copydir=$copy
++ else
++ copydir="$chrootdir/$copy"
++ fi
++
++ if [[ "$chrootdir/root" -ef "$copydir" ]]; then
++ error 'Cannot sync copy with itself: %s' "$copydir"
++ return 1
++ fi
++
++ # Detect chrootdir filesystem type
++ local chroottype=$(stat -f -c %T "$chrootdir")
+
+- if [[ ! -d $copydir ]] || $clean_first; then
+ # Get a read lock on the root chroot to make
+ # sure we don't clone a half-updated chroot
+- slock 8 "$chrootdir/root.lock" "Locking clean chroot"
++ slock 8 "$chrootdir/root.lock" \
++ "Locking clean chroot [%s]" "$chrootdir/root"
+
+- stat_busy "Creating clean working copy [%s]" "$copy"
++ stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir"
+ if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
+ if [[ -d $copydir ]]; then
+ btrfs subvolume delete "$copydir" >/dev/null ||
+@@ -105,14 +132,18 @@
+
+ # Drop the read lock again
+ lock_close 8
+- fi
+
+ # Update mtime
+ touch "$copydir"
+ }
+
+-clean_temporary() {
+- stat_busy "Removing temporary copy [%s]" "$copy"
++# Usage: delete_chroot $copydir
++delete_chroot() {
++ local copydir=$1
++ # Detect chrootdir filesystem type
++ local chroottype=$(stat -f -c %T "$copydir")
++
++ stat_busy "Removing chroot copy [%s]" "$copydir"
+ if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
+ btrfs subvolume delete "$copydir" >/dev/null ||
+ die "Unable to delete subvolume %s" "$copydir"
+@@ -127,9 +158,14 @@
+ stat_done
+ }
+
++# Usage: install_packages $copydir $pkgs...
+ install_packages() {
++ local copydir=$1
++ local install_pkgs=("${@:2}")
++ declare -i ret=0
+ local pkgname
+
++ local install_pkg
+ for install_pkg in "${install_pkgs[@]}"; do
+ pkgname="${install_pkg##*/}"
+ cp "$install_pkg" "$copydir/$pkgname"
+@@ -142,11 +178,19 @@
+ rm "$copydir/$pkgname"
+ done
+
+- # If there is no PKGBUILD we are done
+- [[ -f PKGBUILD ]] || exit $ret
++ return $ret
+ }
+
++# Usage: prepare_chroot $copydir $HOME $repack $run_namcap
++# Globals:
++# - MAKEFLAGS
++# - PACKAGER
+ prepare_chroot() {
++ local copydir=$1
++ local USER_HOME=$2
++ local repack=$3
++ local run_namcap=$4
++
+ $repack || rm -rf "$copydir/build"
+
+ mkdir -p "$copydir/build"
+@@ -193,12 +237,12 @@
+ printf 'builduser:x:%d:100:builduser:/:/usr/bin/nologin\n' "$builduser_uid" >>"$copydir/etc/passwd"
+ chown -R "$builduser_uid" "$copydir"/{build,pkgdest,srcpkgdest,logdest,srcdest,startdir}
+
+- if [[ -n $MAKEFLAGS ]]; then
++ if [[ -n ${MAKEFLAGS:-} ]]; then
+ sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf"
+ echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf"
+ fi
+
+- if [[ -n $PACKAGER ]]; then
++ if [[ -n ${PACKAGER:-} ]]; then
+ sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf"
+ echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf"
+ fi
+@@ -211,10 +255,25 @@
+ chmod 440 "$copydir/etc/sudoers.d/builduser-pacman"
+ fi
+
++ if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then
++ local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q')
++ local ins='[repo]
++SigLevel = Optional TrustAll
++Server = file:///repo
++'
++ sed -i "${line}i${ins//$'\n'/\\n}" "$copydir/etc/pacman.conf"
++ fi
++
+ # This is a little gross, but this way the script is recreated every time in the
+ # working copy
+ {
+ printf '#!/bin/bash\n'
++ declare -f _chrootprepare
++ printf '_chrootprepare "$@"\n'
++ } > "$copydir/chrootprepare"
++ chmod +x "$copydir/chrootprepare"
++ {
++ printf '#!/bin/bash\n'
+ declare -f _chrootbuild
+ printf '_chrootbuild "$@" || exit\n'
+
+@@ -231,13 +290,19 @@
+ chmod +x "$copydir/chrootbuild"
+ }
+
++# Usage: download_sources $copydir $src_owner
++# Globals:
++# - SRCDEST
+ download_sources() {
++ local copydir=$1
++ local src_owner=$2
++
+ local builddir="$(mktemp -d)"
+ chmod 1777 "$builddir"
+
+ # Ensure sources are downloaded
+- if [[ -n $SUDO_USER ]]; then
+- sudo -u $SUDO_USER env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
++ if [[ $USER != $src_owner ]]; then
++ sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
+ makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o
+ else
+ ( export SRCDEST BUILDDIR="$builddir"
+@@ -247,10 +312,10 @@
+ (( $? != 0 )) && die "Could not download sources."
+
+ # Clean up garbage from verifysource
+- rm -rf $builddir
++ rm -rf "$builddir"
+ }
+
+-_chrootbuild() {
++_chrootprepare() {
+ # This function isn't run in makechrootpkg,
+ # so no global variables
+
+@@ -259,6 +324,7 @@
+ shopt -s nullglob
+
+ # XXX: Workaround makepkg disliking read-only dirs
++ rm -rf -- /srcdest/* /startdir/*
+ ln -sft /srcdest /srcdest_host/*
+ ln -sft /startdir /startdir_host/*
+
+@@ -288,15 +354,42 @@
+ exit 1
+ fi
+
+- sudo -u builduser makepkg "$@"
++ # Sync deps now, as networking may be disabled during _chrootbuild
++ cp /repo/repo.db /var/lib/pacman/sync/repo.db
++ sudo -u builduser makepkg "$@" --nobuild
++}
++
++_chrootbuild() {
++ # This function isn't run in makechrootpkg,
++ # so no global variables
++
++ . /etc/profile
++ export HOME=/build
++ shopt -s nullglob
++
++ cd /startdir
++
++ sudo -u builduser makepkg "$@" --noextract --noprepare
+ }
+
++# Usage: move_products $copydir $owner
++# Globals:
++# - PKGDEST
++# - LOGDEST
+ move_products() {
++ local copydir=$1
++ local src_owner=$2
++
++ local pkgfile
+ for pkgfile in "$copydir"/pkgdest/*; do
+ chown "$src_owner" "$pkgfile"
+ mv "$pkgfile" "$PKGDEST"
++ if [[ $PKGDEST != $PWD ]]; then
++ ln -sf "$PKGDEST/${pkgfile##*/}" .
++ fi
+ done
+
++ local l
+ for l in "$copydir"/logdest/*; do
+ [[ $l == */logpipe.* ]] && continue
+ chown "$src_owner" "$l"
+@@ -310,6 +403,9 @@
+ }
+ # }}}
+
++main() {
++init_variables
++
+ orig_argv=("$@")
+
+ while getopts 'hcur:I:l:nTD:d:' arg; do
+@@ -375,30 +471,45 @@
+ [[ -d $SRCPKGDEST ]] || SRCPKGDEST=$PWD
+ [[ -d $LOGDEST ]] || LOGDEST=$PWD
+
+-create_chroot
++# Lock the chroot we want to use. We'll keep this lock until we exit.
++lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy"
++
++if [[ ! -d $copydir ]] || $clean_first; then
++ sync_chroot "$chrootdir" "$copy"
++fi
+
+ $update_first && arch-nspawn "$copydir" \
+ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
+ pacman -Syu --noconfirm
+
+-[[ -n ${install_pkgs[*]} ]] && install_packages
++if [[ -n ${install_pkgs[*]:-} ]]; then
++ install_packages "$copydir" "${install_pkgs[@]}"
++ ret=$?
++ # If there is no PKGBUILD we have done
++ [[ -f PKGBUILD ]] || exit $ret
++fi
+
+-download_sources
++download_sources "$copydir" "$src_owner"
+
+-prepare_chroot
++prepare_chroot "$copydir" "$USER_HOME" "$repack"
+
+ if arch-nspawn "$copydir" \
+ --bind-ro="$PWD:/startdir_host" \
+ --bind-ro="$SRCDEST:/srcdest_host" \
+ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
++ /chrootprepare "${makepkg_args[@]}" &&
++ arch-nspawn "$copydir" \
++ --bind-ro="$PWD:/startdir_host" \
++ --bind-ro="$SRCDEST:/srcdest_host" \
++ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
+ /chrootbuild "${makepkg_args[@]}"
+ then
+- move_products
++ move_products "$copydir" "$src_owner"
+ else
+ (( ret += 1 ))
+ fi
+
+-$temp_chroot && clean_temporary
++$temp_chroot && delete_chroot "$copydir"
+
+ if (( ret != 0 )); then
+ if $temp_chroot; then
+@@ -409,3 +520,4 @@
+ else
+ true
+ fi
++}
diff --git a/src/chroot-tools/mkarchroot.patch b/src/chroot-tools/mkarchroot.patch
new file mode 100644
index 0000000..3882ec6
--- /dev/null
+++ b/src/chroot-tools/mkarchroot.patch
@@ -0,0 +1,67 @@
+--- mkarchroot.in 2016-04-15 17:38:00.221067734 -0400
++++ mkarchroot.ugly 2016-05-09 22:36:18.284175885 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -14,23 +16,29 @@
+
+ working_dir=''
+
++files=()
++
+ usage() {
+ echo "Usage: ${0##*/} [options] working-dir package-list..."
+ echo ' options:'
+ echo ' -C <file> Location of a pacman config file'
+ echo ' -M <file> Location of a makepkg config file'
+ echo ' -c <dir> Set pacman cache'
++ echo ' -f <file> Copy file from the host to the chroot'
++ echo ' -s Do not run setarch'
+ echo ' -h This message'
+ exit 1
+ }
+
+ orig_argv=("$@")
+
+-while getopts 'hC:M:c:' arg; do
++while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ C) pac_conf="$OPTARG" ;;
+ M) makepkg_conf="$OPTARG" ;;
+ c) cache_dir="$OPTARG" ;;
++ f) files+=("$OPTARG") ;;
++ s) nosetarch=1 ;;
+ h|?) usage ;;
+ *) error "invalid argument '%s'" "$arg"; usage ;;
+ esac
+@@ -68,6 +76,16 @@
+ chmod 0755 "$working_dir"
+ fi
+
++for file in "${files[@]}"; do
++ mkdir -p "$(dirname "$working_dir$file")"
++ cp "$file" "$working_dir$file"
++done
++
++_env=()
++while read -r varname; do
++ _env+=("$varname=${!varname}")
++done < <(declare -x | sed -r 's/^declare -x ([^=]*)=.*/\1/' | grep -i '_proxy$')
++env -i "${_env[@]}" \
+ pacstrap -GMcd ${pac_conf:+-C "$pac_conf"} "$working_dir" \
+ "${cache_dirs[@]/#/--cachedir=}" "$@" || die 'Failed to install all packages'
+
+@@ -75,7 +93,8 @@
+ echo 'LANG=C' > "$working_dir/etc/locale.conf"
+ echo "$CHROOT_VERSION" > "$working_dir/.arch-chroot"
+
+-exec arch-nspawn \
++exec "$(librelib chroot/arch-nspawn)" \
++ ${nosetarch:+-s} \
+ ${pac_conf:+-C "$pac_conf"} \
+ ${makepkg_conf:+-M "$makepkg_conf"} \
+ ${cache_dir:+-c "$cache_dir"} \
diff --git a/src/dagpkg b/src/dagpkg
new file mode 100755
index 0000000..1989b4d
--- /dev/null
+++ b/src/dagpkg
@@ -0,0 +1,222 @@
+#!/usr/bin/env bash
+#
+# dagpkg - create a directed graph of package dependencies and build
+# them in topological order
+
+# Copyright (C) 2014 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2014 Michał Masłowski <mtjm@mtjm.eu>
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+set -e
+
+source libremessages
+source "$(librelib conf)"
+
+# Source variables from libretools
+load_files libretools
+check_vars libretools FULLBUILDCMD || exit 1
+#check_vars libretools HOOKPREBUILD HOOKLOCALRELEASE || exit 1 # optional
+
+# Source variables from makepkg
+load_files makepkg
+check_vars makepkg CARCH || exit 1
+
+# Globals:
+# - temp_dir
+# - log
+# - name (sort of, it's also local to visit_pkgbuild() )
+# - prev
+# - I
+# - marks
+# - various PKGBUILD variables:
+# - pkgbase/pkgname
+# - epoch/pkgver/pkgrel
+# - arch
+# - {,make,check}depends
+
+# End inmediately but print an useful message
+trap_exit() {
+ local signal=$1; shift
+ local msg=("$@")
+ term_title "error!"
+ echo
+ error "(%s) %s (leftovers on %s)" \
+ "${0##*/}" "$(print "${msg[@]}")" "${temp_dir}"
+ trap -- "$signal"
+ kill "-$signal" "$$"
+}
+
+setup_traps trap_exit
+
+source_pkgbuild() {
+ # Source this PKGBUILD, if it doesn't exist, exit
+ if ! load_PKGBUILD &>/dev/null; then
+ error "No PKGBUILD in %s" "$PWD"
+ exit 1
+ fi
+
+ # Save resources
+ # This is intentionally less exhaustive than unset_PKGBUILD()
+ # XXX: document which things we actually *want* to not be unset.
+ unset pkgdesc license groups backup install md5sums sha1sums \
+ sha256sums source options &>/dev/null
+
+ unset build package &>/dev/null
+
+ local _pkg
+ for _pkg in "${pkgname[@]}"; do
+ unset "package_${_pkg}" &>/dev/null || true
+ done
+
+ # This is the name of the package
+ name="${pkgbase:-${pkgname[0]}}"
+}
+
+source_pkgbuild
+
+# A temporary work dir and log file
+temp_dir="${1:-$(mktemp -dt ${name}-testpkg-XXXX)}"
+log="${temp_dir}/buildorder"
+
+# Mark array for DFS-based topological sort. See
+# https://en.wikipedia.org/wiki/Topological_sort for an explanation of
+# the algorithm. Key: package name, value: 0 for unvisited package, 1
+# during visit, 2 after visit.
+declare -A marks
+
+# Visit a PKGBUILD for graph building.
+visit_pkgbuild() {
+ # The name of the previous package
+ prev="${1}"
+
+ local name
+ source_pkgbuild
+
+ # If it's already built we don't bother
+ if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then
+ return
+ fi
+
+ # Detect cycle or already visited package
+ case "${marks[$name]:-0}" in
+ 1) msg2 "cycle found with %s depending on %s" $prev $name
+ exit 1;;
+ 2) return;;
+ esac
+
+ msg "%s (%s)" ${name} ${prev}
+
+ if ! in_array "${CARCH}" "${arch[@]}"; then
+ warning "%s isn't ported to %s yet" ${name} ${CARCH}
+ fi
+
+ # If the envvar I contains this package, ignore it and exit
+ if in_array "$name" $I; then
+ msg2 "%s ignored" ${name}
+ return
+ fi
+
+ # Mark the package as being visited
+ marks[$name]=1
+
+ # Recurse into dependencies
+ local d
+ for d in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}"; do
+ # Cleanup dependency versions
+ d=$(echo $d | sed "s/[<>=].*//")
+
+ # Where's the pkgbuild?
+ local w=$(toru-where $d)
+
+ # Skip if not available
+ test -z "$w" && continue
+
+ # Go to this dir
+ pushd $w &>/dev/null
+
+ visit_pkgbuild "$name"
+
+ popd &>/dev/null
+ done
+
+ # Mark the package as finished
+ marks[$name]=2
+ # Append it to the reversed list of packages to build.
+ echo "$name" >> "${log}"
+}
+
+# If we specified a work dir on the cli it means we want to skip
+# dependency graph creation and jump to build whatever is there
+if [ -z "${1}" ]; then
+ # Visit the root PKGBUILD to make the graph.
+ visit_pkgbuild ""
+else
+ msg "Resuming build..."
+fi
+
+# enter work dir
+pushd "${temp_dir}" &>/dev/null
+nl ${log} | while read order pkg; do
+ # skip if already built
+ if test -f "${pkg}/built_ok"; then
+ warning "tried to build %s twice" "%{pkg}"
+ continue
+ fi
+
+ # where's this package?
+ local w="$(toru-where "$pkg")"
+ test -z "$w" && continue
+
+ # copy to work dir if not already
+ # this means you can make modifications to the pkgbuild during the
+ # graph build or remove the dir after a build failure and let dagpkg
+ # copy a new version
+ test -d "$pkg" || cp -r "$w" "$pkg"
+ pushd "$pkg" &>/dev/null
+
+ term_title "%s(%s)" "$pkg" "$order"
+
+ msg "Building %s" ${pkg}
+
+ # upgrade the system
+ # this would probably have to go on HOOKPREBUILD if you're working
+ # outside chroots
+ sudo -E pacman -Syu --noconfirm
+
+ # run the pre build command from libretools.conf
+ if [[ -n "$HOOKPREBUILD" ]]; then
+ ${HOOKPREBUILD}
+ fi
+
+ # run the build command
+ ${FULLBUILDCMD}
+
+ # Run local release hook with $1 = $repo
+ if [[ -n "$HOOKLOCALRELEASE" ]]; then
+ ${HOOKLOCALRELEASE} "$(basename "$(dirname "$w")")"
+ fi
+
+ # it's built!
+ touch built_ok
+
+ popd &>/dev/null
+done
+
+popd &>/dev/null
+# cleanup
+rm -rf ${log} "${temp_dir}"
+
+term_title "done"
diff --git a/src/devtools/.gitignore b/src/devtools/.gitignore
new file mode 100644
index 0000000..d669bbd
--- /dev/null
+++ b/src/devtools/.gitignore
@@ -0,0 +1,4 @@
+*
+!Makefile
+!.gitignore
+!*.patch
diff --git a/src/devtools/Makefile b/src/devtools/Makefile
new file mode 100644
index 0000000..72907f0
--- /dev/null
+++ b/src/devtools/Makefile
@@ -0,0 +1,12 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+libretools-bins += checkpkg find-libdeps finddeps lddd
+devtools-files = $(addsuffix .in,$(libretools-bins))
+am_sys_files += $(bindir)/find-libprovides
+
+$(DESTDIR)$(bindir)/find-libprovides:
+ install -d $(@D)
+ ln -sf find-libdeps $@
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/devtools/checkpkg.patch b/src/devtools/checkpkg.patch
new file mode 100644
index 0000000..5cfb794
--- /dev/null
+++ b/src/devtools/checkpkg.patch
@@ -0,0 +1,45 @@
+--- checkpkg.in 2014-03-21 13:59:31.849658036 -0400
++++ checkpkg.ugly 2014-03-21 14:20:52.340291982 -0400
+@@ -1,8 +1,30 @@
+ #!/bin/bash
++# License: Unspecified
+
+ shopt -s extglob
+
+-m4_include(lib/common.sh)
++. "$(librelib messages)"
++
++usage() {
++ print 'Usage: %s [-h]' "${0##*/}"
++ print 'Compare a locally built a package with the one in the repositories.'
++ echo
++ prose 'This should be run from a directory containing a
++ PKGBUILD. It searches for a locally built package
++ corresponding to the PKGBUILD, and downloads the last
++ version of that package from the pacman repositories.
++ It then compares the list of .so files provided by each
++ version of the package. It does this for each part of
++ a split package.'
++}
++
++if [[ $1 = '-h' ]]; then
++ usage
++ exit 0
++elif [[ $# -gt 0 ]]; then
++ usage >&2
++ exit 1
++fi
+
+ # Source makepkg.conf; fail if it is not found
+ if [[ -r '/etc/makepkg.conf' ]]; then
+@@ -17,7 +39,9 @@
+ fi
+
+ if [[ ! -f PKGBUILD ]]; then
+- die 'This must be run in the directory of a built package.'
++ error 'This must be run in the directory of a built package.'
++ usage >&2
++ exit 1
+ fi
+
+ . ./PKGBUILD
diff --git a/src/devtools/find-libdeps.patch b/src/devtools/find-libdeps.patch
new file mode 100644
index 0000000..1bcffc9
--- /dev/null
+++ b/src/devtools/find-libdeps.patch
@@ -0,0 +1,47 @@
+--- find-libdeps.in 2014-03-21 13:59:32.059649315 -0400
++++ find-libdeps.ugly 2014-03-21 14:21:18.955744982 -0400
+@@ -1,6 +1,7 @@
+ #!/bin/bash
++# License: Unspecified
+
+-m4_include(lib/common.sh)
++. "$(librelib messages)"
+
+ set -e
+ shopt -s extglob
+@@ -19,12 +20,32 @@
+ *) die "Unknown mode %s" "$script_mode" ;;
+ esac
+
++usage() {
++ print "Usage: find-lib(deps|provides) [options] <package file|extracted package dir>"
++ print "Find library dependencies or provides of a package."
++ echo
++ prose 'Prints a list of library dependencies in the format:'
++ echo
++ print ' <soname>=<soversion>-<soarch>'
++ echo
++ prose 'Where <soversion> is the shared library version, or
++ <soname> repeated if there is no version attached; and
++ <soarch> is the architecture of the library (either `32`
++ or `64`, based on the ELF Class).'
++ echo
++ print "Options:"
++ flag "--ignore-internal" "Ignore internal libraries; libraries
++ without a version attached"
++ flag "-h" "Show this message"
++}
+ if [[ -z $1 ]]; then
+- echo "${0##*/} [options] <package file|extracted package dir>"
+- echo "Options:"
+- echo " --ignore-internal ignore internal libraries"
++ usage >&2
+ exit 1
+ fi
++if [[ $1 = '-h' ]]; then
++ usage
++ exit 0
++fi
+
+ if [[ -d $1 ]]; then
+ pushd $1 >/dev/null
diff --git a/src/devtools/finddeps.patch b/src/devtools/finddeps.patch
new file mode 100644
index 0000000..caf2121
--- /dev/null
+++ b/src/devtools/finddeps.patch
@@ -0,0 +1,35 @@
+--- finddeps.in 2014-03-21 13:59:32.249641424 -0400
++++ finddeps.ugly 2014-03-21 14:21:09.949489174 -0400
+@@ -2,19 +2,26 @@
+ #
+ # finddeps - find packages that depend on a given depname
+ #
++# License: Unspecified
+
+-m4_include(lib/common.sh)
++. "$(librelib messages)"
+
+ match=$1
+
++usage() {
++ print 'Usage: %s <depname>' "${0##*/}"
++ print 'Find packages that depend on a given depname.'
++ echo
++ prose 'Run this script from the top-level directory of your ABS tree.'
++}
+ if [[ -z $match ]]; then
+- echo 'Usage: finddeps <depname>'
+- echo ''
+- echo 'Find packages that depend on a given depname.'
+- echo 'Run this script from the top-level directory of your ABS tree.'
+- echo ''
++ usage >&2
+ exit 1
+ fi
++if [[ $match = '-h' ]]; then
++ usage
++ exit 0
++fi
+
+ find . -type d | while read d; do
+ if [[ -f "$d/PKGBUILD" ]]; then
diff --git a/src/devtools/lddd.patch b/src/devtools/lddd.patch
new file mode 100644
index 0000000..6383aa4
--- /dev/null
+++ b/src/devtools/lddd.patch
@@ -0,0 +1,29 @@
+--- lddd.in 2014-03-21 13:59:32.419634364 -0400
++++ lddd.ugly 2014-03-21 14:21:31.538503947 -0400
+@@ -2,8 +2,25 @@
+ #
+ # lddd - find broken library links on your machine
+ #
++# License: Unspecified
+
+-m4_include(lib/common.sh)
++. "$(librelib messages)"
++
++usage() {
++ print "Usage: %s [-h]" "${0##*/}"
++ print "Find broken library links on your machine."
++ echo
++ prose 'Scans $PATH and library directories for ELF files with
++ references to missing shared libraries.'
++}
++
++if [[ $1 = '-h' ]]; then
++ usage
++ exit 0
++elif [[ $# -gt 0 ]]; then
++ usage >&2
++ exit 1
++fi
+
+ ifs=$IFS
+ IFS="${IFS}:"
diff --git a/src/gitget/Makefile b/src/gitget/Makefile
new file mode 100644
index 0000000..2903f4a
--- /dev/null
+++ b/src/gitget/Makefile
@@ -0,0 +1,4 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/gitget/gitget b/src/gitget/gitget
new file mode 100755
index 0000000..4d127c7
--- /dev/null
+++ b/src/gitget/gitget
@@ -0,0 +1,221 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org>
+# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+
+# from makepkg
+dir_is_empty() {
+ (
+ shopt -s dotglob nullglob
+ files=("$1"/*)
+ (( ${#files} == 0 ))
+ )
+}
+
+# from makepkg
+cd_safe() {
+ if ! cd "$1"; then
+ error "Failed to change to directory %s" "$1"
+ plain "Aborting..."
+ exit 1
+ fi
+}
+
+# from makepkg
+download_git_checkout() {
+ local url=$1
+ local ref=$2
+ local dir=$3
+ local name=$4
+ local push=${5:-}
+
+ if [[ ! -d "$dir/.git" ]] ; then
+ msg2 "Cloning %s %s repo..." "${name}" "git"
+ if ! git clone "$url" "$dir"; then
+ error "Failure while downloading %s %s repo" "${name}" "git"
+ plain "Aborting..."
+ exit 1
+ fi
+ cd_safe "$dir"
+ if [[ -n $push ]]; then
+ git config remote.origin.pushUrl "$push"
+ fi
+ git checkout "$ref"
+ else
+ cd_safe "$dir"
+ # Make sure we are fetching the right repo
+ if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then
+ if $FORCE; then
+ git config remote.origin.url "$url"
+ else
+ error "%s is not a clone of %s" "$dir" "$url"
+ plain "Aborting..."
+ exit 1
+ fi
+ fi
+ if [[ -n $push ]] ; then
+ if $FORCE; then
+ git config remote.origin.pushUrl "$push"
+ else
+ local curpush="$(git config --get remote.origin.pushUrl)"
+ if [[ $? != 0 ]] ; then
+ error "%s does not have a %s configured" pushUrl "$name"
+ plain "Aborting..."
+ exit 1
+ elif [[ $curpush != "$push" ]]; then
+ error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push"
+ plain "Aborting..."
+ exit 1
+ fi
+ fi
+ fi
+ msg2 "Updating %s %s repo..." "${name}" "git"
+ if ! { git fetch --all -p && git checkout "$ref" && git pull origin "$ref"; } ; then
+ # only warn on failure to allow offline builds
+ warning "Failure while updating %s %s repo" "${repo}" "git"
+ fi
+ fi
+}
+
+# from makepkg
+download_git_bare() {
+ local url=$1
+ local dir=$2
+ local name=$3
+ local push=${4:-}
+
+ if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then
+ msg2 "Cloning %s %s repo..." "${name}" "git"
+ if ! git clone --mirror "$url" "$dir"; then
+ error "Failure while downloading %s %s repo" "${name}" "git"
+ plain "Aborting..."
+ exit 1
+ fi
+ if [[ -n $push ]]; then
+ cd_safe "$dir"
+ git config remote.origin.pushUrl "$push"
+ fi
+ else
+ cd_safe "$dir"
+ # Make sure we are fetching the right repo
+ if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then
+ error "%s is not a clone of %s" "$dir" "$url"
+ plain "Aborting..."
+ exit 1
+ fi
+ if [[ -n $push ]] ; then
+ if $FORCE; then
+ git config remote.origin.pushUrl "$push"
+ else
+ local curpush="$(git config --get remote.origin.pushUrl)"
+ if [[ $? != 0 ]] ; then
+ error "%s does not have a %s configured" pushUrl "$name"
+ plain "Aborting..."
+ exit 1
+ elif [[ $curpush != "$push" ]]; then
+ error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push"
+ plain "Aborting..."
+ exit 1
+ fi
+ fi
+ fi
+ msg2 "Updating %s %s repo..." "${name}" "git"
+ if ! git fetch --all -p; then
+ # only warn on failure to allow offline builds
+ warning "Failure while updating %s %s repo" "${name}" "git"
+ fi
+ fi
+}
+
+usage() {
+ print 'Usage: %s [OPTIONS] [bare|checkout] URL DIRECTORY' "${0##*/}"
+ print 'A URL-handler for git urls. Capable of updating or cloning.'
+ echo
+ prose "Clones or pulls from the git URL, to a local DIRECTORY. If
+ \`bare\` is specified, it will create a bare repository; if
+ \`checkout\` is specified, it will create or update a working tree."
+ echo
+ prose 'For a checkout, the tree to checkout is specified by appending
+ `#ANYTHING=TREE-ISH` to the URL. For example, `#branch=stable`,
+ or `#tag=v12.3`. Whatever is on the left side of the equal sign
+ is ignored, this is for compatibility with `makepkg` source
+ URLs.'
+ echo
+ prose "The URL may be prefixed with \`git+\`. This is also for
+ compatibility with \`makepkg\` source URLs."
+ echo
+ prose "It does safety checks, figures out whether to clone or pull, and
+ other helpful things. This exists because the same
+ \`download_git\` function from makepkg was being copied and
+ pasted again and again."
+ echo
+ print "Options:"
+ flag '-f' \
+ 'Instead of checking to make sure configured URLs match, force
+ the update, and set the URLs.'
+ flag "-p $(_ URL)" \
+ 'In addition to setting or checking `remotes.origin.url`, also
+ set or check `remotes.origin.pushUrl`'
+ flag "-n $(_ NAME)" \
+ 'In messages, instead of using the basename of DIRECTORY as the
+ repository name, use NAME'
+ flag '-h' 'Show this message'
+}
+
+FORCE=false
+main() {
+ local push=''
+ local name=''
+ while getopts 'fp:n:h' flag; do
+ case "${flag}" in
+ f) FORCE=true;;
+ p) push=$OPTARG;;
+ n) name=$OPTARG;;
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ [[ $# == 3 ]] || { usage >&2; return 1; }
+ local mode=$1
+ local url=${2#git+}
+ local dir=$3
+
+ local urlmain=${url%%#*}
+ local urlfrag=${url#*#}
+ if [[ "$urlfrag" == "$urlmain" ]]; then
+ urlfrag=''
+ fi
+ local ref=${urlfrag#*=}
+
+ if [[ -z $ref ]]; then
+ ref=master
+ fi
+
+ name=${name:-${dir##*/}}
+
+ case "$mode" in
+ checkout) download_git_checkout "$urlmain" "$ref" "$dir" "$name" "$push";;
+ bare) download_git_bare "$urlmain" "$dir" "$name" "$push";;
+ *) usage >&2; return 1;;
+ esac
+}
+
+main "$@"
diff --git a/src/gitget/libregit b/src/gitget/libregit
new file mode 100755
index 0000000..e45c3a8
--- /dev/null
+++ b/src/gitget/libregit
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+
+usage() {
+ print 'Usage: %s REPO REF DIR' "${0##*/}"
+ print 'A compatibility wrapper around `gitget checkout`'
+ echo
+ prose "This exists because gitget used to be called libregit, and took
+ the arguments in this format, and I'm sure there are a few
+ scripts floating around that use it."
+ echo
+ prose "Clones or pulls from the git URL '<REPO>', and checks out the git
+ ref '<REF>' to the directory '<DIR>'."
+}
+
+main() {
+ [[ $# == 3 ]] || { usage >&2; return 1; }
+ repo=$1
+ ref=$2
+ dir=$3
+
+ gitget checkout "${repo}#ref=${ref}" "${dir}"
+}
+
+main "$@"
diff --git a/src/is_built b/src/is_built
new file mode 100755
index 0000000..29b2744
--- /dev/null
+++ b/src/is_built
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. libremessages
+
+usage() {
+ print "Usage: %s [-h] PKGNAME [PKGVER]" "${0##*/}" "${0##*/}"
+ print 'Detect iv a given package (version) is already in the repos'
+ echo
+ prose "If a version is specified, it assumed that you want a greater or
+ equal version."
+ echo
+ prose "Example usage:"
+ print " $ %s 'pcre' '20'" "${0##*/}"
+ echo
+ print "Exit status:"
+ print " 0: The package is built"
+ print " 1: The package has not built"
+ print " >1: There was an error"
+}
+
+while getopts 'h' arg; do
+ case $arg in
+ h) usage; exit 0 ;;
+ *) usage >&2; exit 2 ;;
+ esac
+done
+if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then
+ usage >&2
+ exit 2
+fi
+
+pkg=${1}
+ver=${2:-0}
+pver=$(LC_ALL=C pacman -Sddp --print-format '%v' "${pkg}" 2>/dev/null)
+
+# if pacman fails or returns nothing
+r=$?
+
+result=$(vercmp "${pver}" "${ver}")
+# result:
+# -1 : pver < ver
+# 0 : pver = ver
+# 1 : pver > ver
+
+if [[ $result -ge 0 ]] && [[ $r -eq 0 ]]; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/src/lib/.gitignore b/src/lib/.gitignore
new file mode 100644
index 0000000..650c85f
--- /dev/null
+++ b/src/lib/.gitignore
@@ -0,0 +1,3 @@
+common.sh
+common.sh.in
+conf.sh
diff --git a/src/lib/HACKING.md b/src/lib/HACKING.md
new file mode 100644
index 0000000..8bebaf6
--- /dev/null
+++ b/src/lib/HACKING.md
@@ -0,0 +1,15 @@
+Special stuff about hacking ih the /src/lib directory:
+
+ - Everything should be GPLv2 AND GPLv3 compatible. No GPLv3 only.
+ - Name a file `libre${NAME}` if it should be executable directly, or
+ `${name}.sh` if it should only be available to be sourced.
+ - When printing a message that is internal to /src/lib, and not part
+ of the programm calling the library; prefix the print command with
+ `_l`. `_l()` is defined in `common.sh` (and `librelib`, since it
+ cannot use any libraries itself).
+ - When changing the message functions, be aware that some are
+ duplicated in:
+ * /src/chroot-tools/chcleanup
+ * /src/chroot-tools/distcc-tool
+ * /src/lib/librelib
+ And that they probably need to be updated as well.
diff --git a/src/lib/Makefile b/src/lib/Makefile
new file mode 100644
index 0000000..9e9b4a8
--- /dev/null
+++ b/src/lib/Makefile
@@ -0,0 +1,45 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+libretools-libs += common.sh conf.sh
+devtools-files = common.sh.in
+
+# Build ##############################################################
+
+$(outdir)/conf.sh: $(var)sysconfdir $(var)pkgconfdir
+
+$(outdir)/common.sh: $(outdir)/%: $(srcdir)/%.in $(srcdir)/%.head $(srcdir)/%.tail $(outdir)/Makefile
+ @echo "OUT $@"
+ @{ \
+ cat '$(<D)/$*.head' && \
+ echo && \
+ sed -r \
+ -e '/encoding problem/d;/LANG=/d' \
+ -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' \
+ -e 's/gettext /_l _ /g' \
+ -e "s/^(\s+)(msg|error) '/\1_l \2 '/" \
+ -e 's|lock\(\)\s*\{|lock()\n{|' \
+ '$(<D)/$*.in' && \
+ echo && \
+ cat '$(<D)/$*.tail' && \
+ :; } > '$@'
+
+# Translate ##########################################################
+
+$(outdir)/blacklist.sh.pot: $(srcdir)/blacklist.sh $(srcdir)/librexgettext
+ @echo "OUT $@"
+ @{ \
+ sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | \
+ sed -r -e 's/^# (.*)/msgid "\1"\nmsgstr ""\n/' \
+ -e 's|^[0-9]*$$|#. embedded usage text\n#: $<:&|' && \
+ $(<D)/librexgettext --simple=_l:2 $< && \
+ :; } | $(pofmt) > $@
+$(outdir)/common.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2
+$(outdir)/conf.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2
+$(outdir)/librelib.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2
+$(outdir)/messages.sh.pot : LIBREXGETTEXT_FLAGS += --simple=_l:2
+$(outdir)/librexgettext.pot: LIBREXGETTEXT_FLAGS += --simple=errusage
+
+######################################################################
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/lib/blacklist.sh b/src/lib/blacklist.sh
new file mode 100644
index 0000000..0a3cc39
--- /dev/null
+++ b/src/lib/blacklist.sh
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+# This may be included with or without `set -euE`
+
+# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+# make sure XDG_CACHE_HOME is set
+. "$(librelib conf)"
+
+# Usage: blacklist-normalize <$file
+# Normalizes the syntax of the blacklist on stdin.
+blacklist-normalize() {
+ sed -e '/^#/d' -e 's/^[^:]*$/&::/' -e 's/^[^:]*:[^:]*$/&:/'
+}
+
+# Usage: blacklist-cat
+# Prints the blacklist.
+# Uses the cache, but downloads it if it doesn't exist. Also normalizes the blacklist for easier parsing.
+blacklist-cat() {
+ local file="$XDG_CACHE_HOME/libretools/blacklist.txt"
+ if ! [[ -e $file ]]; then
+ # exit on failure, whether set -e or not
+ blacklist-update || return $?
+ fi
+ blacklist-normalize < "$file"
+}
+
+# Usage: blacklist-update
+# Updates (or creates) the cached copy of the blacklist.
+blacklist-update() (
+ . libremessages
+ load_files libretools || return 1
+ check_vars libretools BLACKLIST || return 1
+
+ local remote_blacklist="$BLACKLIST"
+ local local_blacklist="$XDG_CACHE_HOME/libretools/blacklist.txt"
+
+ _l stat_busy "Downloading blacklist of proprietary software packages"
+
+ mkdir -p "${local_blacklist%/*}"
+ if wget -N -q -O "${local_blacklist}.part" "$remote_blacklist" 2>/dev/null; then
+ stat_done
+ mv -f "${local_blacklist}.part" "$local_blacklist"
+ else
+ stat_done
+ rm "${local_blacklist}.part"
+ if [[ -e "$local_blacklist" ]]; then
+ _l warning "Using local copy of blacklist"
+ else
+ _l error "Download failed, exiting"
+ return 1
+ fi
+
+ fi
+)
+
+# Usage: blacklist-cat | blacklist-lookup $pkgname
+# Filters to obtain the line for $pkgname from the blacklist on stdin.
+# Exits successfully whether a line is found or not.
+blacklist-lookup() {
+ local pkg=$1
+ # we accept that $pkg contains no regex-nes
+ blacklist-normalize | grep "^$pkg:" || true
+}
+
+# Usage: blacklist-cat | blacklist-get-pkg
+# Prints only the package name field of the blacklist line(s) on stdin.
+blacklist-get-pkg() {
+ blacklist-normalize | cut -d: -f1
+}
+
+# Usage: blacklist-cat | blacklist-get-rep
+# Prints only the replacement package field of the blacklist line(s) on stdin.
+blacklist-get-rep() {
+ blacklist-normalize | cut -d: -f2
+}
+
+# Usage: blacklist-cat | blacklist-get-reason
+# Prints only the reason field of the blacklist line(s) on stdin.
+blacklist-get-reason() {
+ blacklist-normalize | cut -d: -f3-
+}
diff --git a/src/lib/blacklist.sh.3.ronn b/src/lib/blacklist.sh.3.ronn
new file mode 120000
index 0000000..9001445
--- /dev/null
+++ b/src/lib/blacklist.sh.3.ronn
@@ -0,0 +1 @@
+libreblacklist.1.ronn \ No newline at end of file
diff --git a/src/lib/common.sh.3.ronn b/src/lib/common.sh.3.ronn
new file mode 100644
index 0000000..30003e0
--- /dev/null
+++ b/src/lib/common.sh.3.ronn
@@ -0,0 +1,16 @@
+common.sh -- common Bash routines from devtools
+===============================================
+
+## SYNOPSIS
+
+`. "$(librelib common)"`
+
+## DESCRIPTION
+
+In short, don't use this. Use `libremessages`(1) instead.
+`libremessages` uses this internally.
+
+## SEE ALSO
+
+ * librelib(7)
+ * libremessages(1)/messages.sh(3)
diff --git a/src/lib/common.sh.head b/src/lib/common.sh.head
new file mode 100644
index 0000000..23bfeb8
--- /dev/null
+++ b/src/lib/common.sh.head
@@ -0,0 +1,25 @@
+#!/hint/bash
+# This may be included with or without `set -euE`
+
+# This file is included by libremessages.
+# You should probably use libremessages instead of this.
+
+# License: Unspecified
+
+shopt -s extglob
+
+if [[ -z ${_INCLUDE_COMMON_SH:-} ]]; then
+_INCLUDE_COMMON_SH=true
+
+[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools'
+[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale'
+
+if type gettext &>/dev/null; then
+ _() { gettext "$@"; }
+else
+ _() { echo "$@"; }
+fi
+
+_l() {
+ TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@"
+}
diff --git a/src/lib/common.sh.tail b/src/lib/common.sh.tail
new file mode 100644
index 0000000..e133fad
--- /dev/null
+++ b/src/lib/common.sh.tail
@@ -0,0 +1 @@
+fi
diff --git a/src/lib/conf.sh.3.ronn b/src/lib/conf.sh.3.ronn
new file mode 100644
index 0000000..0974bdb
--- /dev/null
+++ b/src/lib/conf.sh.3.ronn
@@ -0,0 +1,126 @@
+conf.sh(3) -- easy loading of configuration files
+=================================================
+
+## SYNOPSIS
+
+`. "$(librelib conf)"`
+
+## DESCRIPTION
+
+`conf.sh` is a Bash(1) library to easily load various configuration
+files related to Arch Linux/Parabola(7) and libretools(7).
+
+### VARIABLES
+
+When loading configuration files in a program run with `sudo`(8), it
+is often desirable to load the configuration files from the home
+directory of the user who called `sudo`, instead of from /root.
+
+To accommodate this, instead of using the usual $<USER> and $<HOME>,
+`conf.sh` sets $<LIBREUSER> and $<LIBREHOME>, which it then uses.
+
+ * <LIBREUSER>:
+ If $<SUDO_USER> is set, then $<LIBREUSER> is set to
+ that. Otherwise, $<LIBREUSER> is set to the value of $<USER>.
+ * <LIBREHOME>:
+ If $<LIBREUSER> == $<USER>, then $<LIBREHOME> is set to the value
+ of $<HOME>. Otherwise, it is set to the default home directory
+ of the user $<LIBREUSER>.
+
+Further, `conf.sh` works with XDG; it sets and uses
+$<XDG_CONFIG_HOME> and $<XDG_CACHE_HOME> with the following values:
+
+ * <XDG_CONFIG_HOME>:
+ If it isn't already set, it is set to "$<LIBREHOME>/.config" and
+ exported.
+ * <XDG_CACHE_HOME>:
+ If it isn't already set, it is set to "$<LIBREHOME>/.cache" and
+ exported.
+
+Note that only the XDG_* variables are exported.
+
+### GENERIC ROUTINES
+
+The following routines take a "slug" to refer to a group of
+configuration files; that is the basename of the files. For example,
+<SLUG>='abs' will identify `/etc/abs.conf` and `$<LIBREHOME>/.abs.conf`.
+
+The routines you will likely actually use are:
+
+ * `load_files` <SLUG>:
+ Loads the configuration files for <SLUG>, loading the files in
+ the correct order, and checking the appropriate environmental
+ variables.
+ * `check_vars` <SLUG> <VARS>...:
+ Checks to see if all of <VARS> are defined. If any of them
+ aren't, it prints a message telling the user to configure them in
+ the configuration files for <SLUG>, and returns with a non-zero
+ status.
+ * `get_var` <SLUG> <VAR> <DEFAULT>:
+ If <VAR> is set in the configuration for <SLUG>, print its
+ value, considering environmental variables. If it is not set,
+ return <DEFAULT>. This does NOT work for array variables.
+ * `set_var` <SLUG> <VAR> <VALUE>:
+ Set the variable <VAR> equal to <VALUE> in the configuration file
+ for <SLUG> of highest precedence that already exists and is
+ writable. If no files fit this description, the routine does
+ nothing and returns a non-zero exit status. This does NOT work
+ for array variables.
+
+There are two more routines the above routines use internally that are
+used internally by by the above routines. You are unlikely to use
+them directly, but they might be useful for debugging, or at least
+describing behavior.
+
+ * `list_files` <SLUG>:
+ Lists (newline-separated) the configuration files that must be
+ considered for <SLUG>. Files listed later take precedence over
+ files listed earlier.
+ * `list_envvars` <SLUG>:
+ Lists (newline-separated) the environmental variables that take
+ precedence over the settings in the configuration files for
+ <SLUG>. For example, in `makepkg.conf`(8) (<SLUG>=makepkg), if the
+ <PACKAGER> environmental variable is set, the value in the
+ configuration file is ignored.
+
+### PKGBUILD ROUTINES
+
+These two routines deal with loading `PKGBUILD`(5) files.
+
+ * `unset_PKGBUILD`:
+ Unsets all variables and functions that might be set in a
+ `PKGBUILD`(5) file, including those specific to `librefetch`(8).
+ * `load_PKGBUILD` [<FILE>]:
+ "Safely" loads a PKGBUILD. Everything that it would normally set
+ is unset first, $<CARCH> is set according to `makepkg.conf`(5),
+ then the file is loaded. The file to be loaded is <FILE>, or
+ "./PKGBUILD" by default. This isn't safe, security wise, in that
+ the PKGBUILD is free to execute code.
+
+### SLUGS
+
+The differences in behavior for anything that takes a slug comes down
+to the differences in the output for `list_files` and `list_envvars`.
+
+The "known" slugs are "abs", "makepkg", "libretools", and anything
+beginning with "xbs". If anything else is given, then:
+
+ * `list_files` will give back "/etc/libretools.d/<SLUG>.conf" and
+ "$<XDG_CONFIG_HOME>/libretools/<SLUG>.conf"
+ * `list_envvars` will give back an empty list.
+
+The rules for <SLUG>=(abs|makepkg|libretools) are more easily
+expressed in code than prose, so it is recommended that you look at
+that.
+
+## BUGS
+
+`get_var` and `set_var` do not work with arrays.
+
+## SEE ALSO
+
+librelib(7)
+
+abs.conf(5), makepkg.conf(5), libretools.conf(5), PKGBUILD(5)
+
+chroot.conf(5), librefetch.conf(5)
diff --git a/src/lib/conf.sh.in b/src/lib/conf.sh.in
new file mode 100644
index 0000000..8394801
--- /dev/null
+++ b/src/lib/conf.sh.in
@@ -0,0 +1,243 @@
+#!/hint/bash
+# This may be included with or without `set -euE`
+
+# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+LIBREUSER="${SUDO_USER:-$USER}"
+if [[ $LIBREUSER == $USER ]]; then
+ LIBREHOME=$HOME
+else
+ eval "LIBREHOME=~$LIBREUSER"
+fi
+if [[ -z ${XDG_CONFIG_HOME:-} ]]; then
+ export XDG_CONFIG_HOME="${LIBREHOME}/.config"
+fi
+if [[ -z ${XDG_CACHE_HOME:-} ]]; then
+ export XDG_CACHE_HOME="${LIBREHOME}/.cache"
+fi
+
+# Low-level generic functions ##################################################
+
+# Usage: list_files $slug
+# Lists the configuration files to be considered for $slug.
+# Later files should take precedence over earlier files.
+list_files() {
+ local slug=$1
+ local sysconfdir=${_librelib_conf_sh_sysconfdir:-@sysconfdir@}
+ local pkgconfdir=${_librelib_conf_sh_pkgconfdir:-@pkgconfdir@}
+ case $slug in
+ abs)
+ echo "${sysconfdir}/$slug.conf"
+ echo "$LIBREHOME/.$slug.conf"
+ ;;
+ makepkg)
+ local manual="${MAKEPKG_CONF:-}"
+ local system="${sysconfdir}/$slug.conf"
+ local olduser="$LIBREHOME/.$slug.conf"
+ local newuser="$XDG_CONFIG_HOME/pacman/$slug.conf"
+ if [[ "$manual" != "$system" && -r "$manual" ]]; then
+ # Manually-specified file
+ echo "$manual"
+ else
+ # Normal file lookup
+ echo "$system"
+ if [[ -r "$olduser" && ! -r "$newuser" ]]; then
+ echo "$olduser"
+ else
+ echo "$newuser"
+ fi
+ fi
+ ;;
+ xbs*)
+ echo "${sysconfdir}/xbs/$slug.conf"
+ echo "$XDG_CONFIG_HOME/xbs/$slug.conf"
+ ;;
+ libretools)
+ echo "${sysconfdir}/$slug.conf"
+ echo "$XDG_CONFIG_HOME/libretools/$slug.conf"
+ ;;
+ *)
+ echo "${pkgconfdir}/$slug.conf"
+ echo "$XDG_CONFIG_HOME/libretools/$slug.conf"
+ ;;
+ esac
+}
+
+# Usage: list_envvars $slug
+# Lists the environmental variables that take precedence over the configuration
+# files for $slug.
+list_envvars() {
+ local slug=$1
+ case $slug in
+ makepkg)
+ printf '%s\n' \
+ PKGDEST SRCDEST SRCPKGDEST LOGDEST \
+ BUILDDIR \
+ PKGEXT SRCEXT \
+ GPGKEY PACKAGER \
+ CARCH
+ ;;
+ libretools)
+ printf '%s\n' DIFFPROG
+ ;;
+ xbs)
+ printf '%s\n' BUILDSYSTEM
+ ;;
+ *) :;;
+ esac
+}
+
+# High-level generic functions #################################################
+
+# Usage: load_files $slug
+# Loads the configuration files for $slug in the proper order.
+load_files() {
+ local slug=$1
+ local var
+ local file
+
+ # Save the existing versions at _VARNAME
+ for var in $(list_envvars $slug); do
+ [[ -n ${!var:-} ]] && eval "_$var=\${$var}"
+ done
+
+ # Load the files
+ for file in $(list_files $slug); do
+ if [[ -r $file ]]; then
+ . "$file" || return 1
+ fi
+ done
+
+ # Restore the _SAVED versions
+ for var in $(list_envvars $slug); do
+ eval "$var=\${_$var:-\${$var:-}}"
+ done
+}
+
+# Usage: check_vars $slug VAR1 VAR2...
+# Check whether the variables listed are properly set.
+# If not, it prints a message saying to set them in the configuration file(s)
+# for $slug.
+check_vars() (
+ local slug=$1; shift
+
+ local ret=0
+
+ local VAR
+ for VAR in "$@"; do
+ if [[ -z ${!VAR:-} ]]; then
+ type print &>/dev/null || . libremessages
+ if [[ $(list_files $slug|wc -l) -gt 1 ]]; then
+ _l print "Configure '%s' in one of:" "$VAR"
+ list_files $slug | sed 's/./ -> &/'
+ else
+ _l print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)"
+ fi
+ ret=1
+ fi
+ done >&2
+
+ if [[ $ret != 0 ]]; then
+ return 1
+ fi
+)
+
+# Usage: get_var <slug> <var_name> <default_value>
+# Does not work with arrays
+get_var() (
+ set +euE
+ local slug=$1
+ local setting=$2
+ local default=$3
+ load_files "$slug"
+ printf '%s' "${!setting:-${default}}"
+)
+
+# Usage: set_var <slug> <var_name> <value>
+# Does not work with arrays
+set_var() {
+ local slug=$1
+ local key=$2
+ local val=$3
+ local file
+ for file in $(list_files "$slug"|tac); do
+ if [[ -w $file ]]; then
+ sed -i "/^\s*$key=/d" "$file"
+ printf '%s=%q\n' "$key" "$val" >> "$file"
+ return 0
+ fi
+ done
+ return 1
+}
+
+# PKGBUILD (not configuration, per se) #########################################
+
+unset_PKGBUILD() {
+ # This routine is based primarily off of the PKGBUILD(5) man-page,
+ # version 4.2.0, dated 2014-12-31
+
+ # This is kinda weird, but everything is more readable with
+ # this as a utility function, but I didn't want to introduce a
+ # new global function, so I just introduced it with the name
+ # of a function that we get to unset anyway. So it can't
+ # clash with anything!
+ mksource() {
+ # For each arg, `unset -v` all variables matching ${arg} and ${arg}_*
+ local v
+ for v in "$@"; do
+ unset -v "$v" $(declare -p|sed -rn "s/^declare -\S+ (${v}_[a-zA-Z0-9_]*)=.*/\1/p")
+ done
+ }
+
+ # This line is taken from the makepkg source
+ local known_hash_algos=('md5' 'sha1' 'sha224' 'sha256' 'sha384' 'sha512')
+
+ # From the "OPTIONS AND DIRECTIVES" section (in order of mention)
+ unset -v pkgname pkgver
+ unset -f pkgver
+ unset -v pkgrel pkgdesc epoch url license install changelog
+
+ mksource source
+ unset -v validpgpkeys noextract
+ local sums=("${known_hash_algos[@]/%/sums}")
+ mksource "${sums[@]}"
+
+ unset -v groups arch backup
+ mksource depends makedepends checkdepends optdepends
+ mksource conflicts provides replaces
+ unset -v options
+
+ # From the "PACKAGING FUNCTIONS" section (in order of mention)
+ unset -f package prepare build check
+
+ # From the "PACKAGE SPLITTING" section
+ unset -f $(declare -f|sed -n 's/^\(package_\S*\) ()\s*$/\1/p')
+ unset -v pkgbase
+
+ # These are used by the `librefetch` program
+ unset -v mksource mknoextract "${sums[@]/#/mk}"
+ unset -v mkdepends
+ unset -f mksource
+}
+
+load_PKGBUILD() {
+ local file=${1:-./PKGBUILD}
+ unset_PKGBUILD
+ CARCH="$(get_var makepkg CARCH "`uname -m`")"
+ . "$file"
+}
diff --git a/src/lib/libreblacklist b/src/lib/libreblacklist
new file mode 100755
index 0000000..6c354fe
--- /dev/null
+++ b/src/lib/libreblacklist
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+if [[ "${0##*/}" != libreblacklist ]]; then
+ . "$(librelib blacklist)"
+else
+ set -euE
+
+ lib_file="$(librelib blacklist)"
+ . "$lib_file"
+
+ usage-outside() {
+ sed -n '/^# Usage:/,/()/p' "$lib_file" |
+ tr '\n' '\r' | sed 's/\s*()\s*[{(]/\n/g'
+ }
+ # The output format of this is:
+ # - The first line is "Usage:"
+ # - The second line is a brief description
+ # - The last line is the command name (prefixed with "blacklist-")
+ # - The in-between lines are the extended description.
+ usage-inside() {
+ sed 's/\r/\n/g'<<<"$1"|sed -e '/^$/d' -e 's/^# //'
+ }
+
+ usage() {
+ export TEXTDOMAIN='librelib'
+ export TEXTDOMAINDIR='/usr/share/locale'
+ . "$(librelib messages)"
+ if [[ $# -eq 0 ]]; then
+ print "Usage: %s [-h] COMMAND [ARGUMENTS]" "${0##*/}"
+ print "Tool for working with the nonfree software blacklist"
+ echo
+ print "Commands:"
+ usage-outside | while read -r sec; do sec="$(usage-inside "$sec")"
+ cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p')
+ desc="$(_ "$(sed -n 2p <<<"$sec")")"
+ flag "$cmd" "${desc//blacklist-/${0##*/} }"
+ done
+ else
+ usage-outside | while read -r sec; do sec="$(usage-inside "$sec")"
+ cmd=$(<<<"$sec" sed -n '$s/^blacklist-//p')
+ if [[ "$cmd" == "$1" ]]; then
+ <<<"$sec" sed '$d' |
+ while read -r line; do print "$line"; done |
+ sed "s/blacklist-/${0##*/} /g" |
+ fmt -us
+ return 0
+ fi
+ done
+ fi
+ }
+
+ main() {
+ if [[ $# -eq 0 ]]; then
+ usage >&2
+ exit 1
+ fi
+ _blacklist_cmd=$1
+ shift
+ if [[ $_blacklist_cmd == -h ]]; then
+ usage "$@"
+ else
+ "blacklist-$_blacklist_cmd" "$@"
+ fi
+ }
+
+ main "$@"
+fi
diff --git a/src/lib/libreblacklist.1.ronn b/src/lib/libreblacklist.1.ronn
new file mode 100644
index 0000000..d56eb60
--- /dev/null
+++ b/src/lib/libreblacklist.1.ronn
@@ -0,0 +1,23 @@
+libreblacklist(1) -- Tools for working with the your-freedom blacklist
+======================================================================
+
+## SYNOPSIS
+
+`. "$(librelib blacklist)"`<br>
+`. libreblacklist`<br>
+`libreblacklist` [-h] <COMMAND> [<ARGS>...]
+
+## DESCRIPTION
+
+`libreblacklist` is a set of tools for working with the your-freedom
+blacklist.
+
+It may be included into a `Bash`(1) program as a library, which will
+expose it's routines as "blacklist-<COMMAND>", or it may be invoked on
+the command line as "libreblacklist <COMMAND>".
+
+See `libreblacklist -h` for more information.
+
+## SEE ALSO
+
+librelib(7)
diff --git a/src/lib/librelib b/src/lib/librelib
new file mode 100755
index 0000000..e9d184e
--- /dev/null
+++ b/src/lib/librelib
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+default_libdir=/usr/lib/libretools
+
+if type gettext &>/dev/null; then
+ _() { gettext "$@"; }
+else
+ _() { echo "$@"; }
+fi
+
+_l() {
+ TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@"
+}
+
+print() {
+ local mesg="$(_ "$1")"
+ shift
+ printf -- "$mesg\n" "$@"
+}
+
+whitespace_collapse() {
+ tr '\n' '\r' | sed -r \
+ -e 's/\r/ /g' -e 's/\t/ /g' \
+ -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g'
+}
+
+prose() {
+ local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift
+ printf -- "$mesg" "$@" | fmt -u
+}
+
+cmd=${0##*/}
+usage() {
+ . libremessages
+ print 'Usage: . $(%s LIBRARY)' "$cmd"
+ print 'Usage: %s -h' "$cmd"
+ print "Finds a Bash library file"
+ echo
+ prose "While some libraries can be sourced just by their name because
+ they are installed in PATH (like libremessages), some are not
+ installed there (like conf.sh), so a path must be given.
+ Hardcoding that path is the way of the dark side."
+ echo
+ prose 'By default, it looks for the files in `%s`, but this can be
+ changed with the environmental variable LIBRETOOLS_LIBDIR.' "$default_libdir"
+ echo
+ print "Example usage:"
+ printf ' . $(%s conf)\n' "$cmd"
+ echo
+ print "Options:"
+ flag '-h' 'Show this message'
+}
+
+main() {
+ if [[ $# != 1 ]]; then
+ _l usage >&2
+ return 2
+ fi
+ if [[ $1 == '-h' ]]; then
+ _l usage
+ return 0;
+ fi
+
+ if [[ -z $LIBRETOOLS_LIBDIR ]]; then
+ export LIBRETOOLS_LIBDIR=$default_libdir
+ fi
+
+ lib=$1
+ lib=${lib#libre}
+ lib=${lib%.sh}
+
+ for file in ${lib} libre${lib} ${lib}.sh libre${lib}.sh; do
+ if [[ -f "$LIBRETOOLS_LIBDIR/$file" ]]; then
+ printf '%s\n' "$LIBRETOOLS_LIBDIR/$file"
+ return 0;
+ fi
+ done
+ _l print '%s: could not find library: %s' "$cmd" "$lib" >&2
+ return 1
+}
+
+main "$@"
diff --git a/src/lib/librelib.1.ronn b/src/lib/librelib.1.ronn
new file mode 100644
index 0000000..fe64e92
--- /dev/null
+++ b/src/lib/librelib.1.ronn
@@ -0,0 +1,55 @@
+librelib(1) -- finds a Bash library file
+========================================
+
+## SYNOPSIS
+
+`. "$(librelib LIBRARY)"`<br>
+`librelib -h`
+
+## DESCRIPTION
+
+`librelib` is a program to find a Bash(1) library file at run-time.
+This way, the path does not need to be hard-coded into the
+application; think of it as a sort of dynamic-linker for shell
+programs.
+
+There are several reasons for doing this, instead of hard-coding the
+path:
+
+ * The install path can change in the future without having to change
+ programs that use them.
+ * The install directory can be configured at runtime, by setting
+ `LIBRETOOLS_LIBDIR`, similar to `LD_PRELOAD` (this is used when
+ running the test suite).
+ * The naming scheme of a library can change (such as between
+ `libreNAME` and `NAME.sh` without changing programs that use it.
+
+By default, `librelib` looks in `/usr/lib/libretools`, but that can be
+changed by setting the `LIBRETOOLS_LIBDIR` environmental variable to
+the directory it should look in.
+
+When searching for a library, `librelib` first strips `libre` from the
+beginning of the name, and `.sh` from the end. This means that all of
+the following are equivalent:
+
+ . "$(librelib messages)"
+ . "$(librelib messages.sh)"
+ . "$(librelib libremessages)"
+ . "$(librelib libremessages.sh)"
+
+Once it has the 'base' name of the library it is looking for, it looks
+for a file with that 'base' name (allowing for, but not requiring
+`libre` to be prepended, or `.sh` to be appended) in whichever
+directory it is looking in.
+
+If it cannot find a suitable library file, it will print an error
+message to standard error, and exit with a code of 1.
+
+## Examples
+
+ . "$(librelib messages)"
+ . "$(librelib conf)"
+
+## SEE ALSO
+
+librelib(7)
diff --git a/src/lib/librelib.7.ronn b/src/lib/librelib.7.ronn
new file mode 100644
index 0000000..33b0c55
--- /dev/null
+++ b/src/lib/librelib.7.ronn
@@ -0,0 +1,49 @@
+librelib(7) -- Suite of Bash libraries
+======================================
+
+## SYNOPSIS
+
+Overview of the librelib Bash library suite.
+
+## DESCRIPTION
+
+There are three parts to librelib:
+
+ 1. The `librelib`(1) executable.
+ 2. The non-executable libraries installed in `/usr/lib/libretools`
+ 3. The executable libraries installed in `/usr/bin`
+
+The `librelib` executable isn't very exciting, it just finds the
+libraries installed in `/usr/lib/libretools`. Think of it as a sort
+of dynamic-linker.
+
+The 'core' of librelib are the libraries installed in
+`/usr/lib/libretools`. These are `Bash`(1) libraries that may be
+sourced in Bash programs.
+
+Some of these libraries also make sense as stand-alone programs, where
+if they are invoked directly, the first argument is the library
+routine to be executed. For example, the `messages` library may be
+included, or executed:
+
+ . "$(librelib messages)"
+ msg2 "Foo was found: %s" "$foo"
+ # or
+ libremessages msg2 "Foo was found: %s" "$foo"
+
+The `blacklist` library is similar:
+
+ . "$(librelib blacklist)"
+ blacklist-update
+ # or
+ libreblacklist update
+
+
+
+## SEE ALSO
+
+ * librelib(1)
+ * libremessages(1)/messages.sh(3)
+ * libreblacklist(1)/blacklist.sh(3)
+ * conf.sh(3)
+ * common.sh(3)
diff --git a/src/lib/libremessages b/src/lib/libremessages
new file mode 100755
index 0000000..647204a
--- /dev/null
+++ b/src/lib/libremessages
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+if [[ "${0##*/}" != libremessages ]]; then
+ . "$(librelib messages)"
+else
+ set -euE
+ . "$(librelib messages)"
+ "$@"
+fi
diff --git a/src/lib/libremessages.1.ronn b/src/lib/libremessages.1.ronn
new file mode 100644
index 0000000..d4fac85
--- /dev/null
+++ b/src/lib/libremessages.1.ronn
@@ -0,0 +1,241 @@
+libremessages(1) -- common Bash routines
+========================================
+
+## SYNOPSIS
+
+`. "$(librelib messages)"`<br>
+`. libremessages`<br>
+`libremessages` <COMMAND>
+
+## DESCRIPTION
+
+`libremessages` is a shell library containing many common routines.
+The name is a bit of a misnomer, it mostly deals with printing
+messages, but also has other things.
+
+`libremessages` uses `common.sh`(3) internally for a large portion of
+it's functionality. The authors make no promises that functionality
+that is implemented in `libremessages` won't move into `common.sh` or
+vice-versa. So, it is recommended that you use `libremessages`, not
+`common.sh`.
+
+### STAND ALONE USAGE
+
+The "normal" way to use libremessages is to source it, then call the
+provided routines.
+
+However, if you call libremessages directly, the first argument is
+taken as the function to call, and the remaining arguments are passed
+to it. The only cases where this doesn't work are the lockfile
+routines (`lock`, `slock`, and `lock_close`), because lockfiles are
+managed as file descriptors.
+
+### VARIABLES
+
+The following variables for printing terminal color codes are set:
+`ALL_OFF`, `BOLD`, `BLUE`, `GREEN`, `RED`, `YELLOW`. If standard
+error is not a terminal (see `isatty`(3)), they are set, but empty.
+They are marked as readonly, so it is an error to try to set them
+afterwords.
+
+### MESSAGE FORMAT
+
+All routines feed the message/format string through `gettext`(1), if
+it is available.
+
+The descriptions will frequently reference `printf`(1)--this isn't
+really that `printf`. The program described by the manual page
+`printf`(1) is probably the version from GNU coreutils, every time it
+is used here, it is `bash`(1)'s internal implementation; try running
+the command `help printf` from a Bash shell for more information.
+
+### GENERAL ROUTINES
+
+Unless otherwise noted, these do not implicitly call `gettext`.
+
+ * `_` <MESSAGE>:
+ If `gettext` is available, calls `gettext`, otherwise just prints
+ the arguments given.
+
+ * `in_array` <NEEDLE> <HAYSTACK>...:
+ Evaluates whether <HAYSTACK> includes <NEEDLE>; returns 0 if it
+ does, non-zero if it doesn't.
+
+ * `panic`:
+ For the times when you can't reasonably continue, similar to
+ "assert" in some programming languages.
+
+ * `setup_traps` [<HANDLER>]:
+ Sets traps on TERM, HUP, QUIT and INT signals, as sell as the ERR
+ event, similar to makepkg. If <HANDLER> is specified, instead of
+ using the default handler (which is good for most purposes), it
+ will call <HANDLER> with the arguments
+ `<HANDLER> <SIGNAL_NAME> <MESSAGE> [<MESSAGE_ARGS>...]`, where
+ <MESSAGE> is a `printf`(1)-formatted string that is fed through
+ `gettext`, and <MESSAGE_ARGS> are its arguments.
+
+ * `whitespace_collapse`:
+ Collapses whitespace on stadard I/O, similar to HTML whitespace
+ collapsing, with the exception that it puts two spaces between
+ sentences. It considers newline, tab, and space to be
+ whitespace.
+
+### PROSE ROUTINES
+
+These routines print to standard output, and are useful for printing
+word-wrapped prose.
+
+For each of these, <MESSAGE> is fed through `gettext` automatically.
+
+To generate gettext `.pot` files for programs using these routines,
+plain `xgettext`(1) won't work because it doesn't know about
+word-wrapping. Instead, you should use the `librexgettext`(1) program
+which knows how do handle word-wrapping, and knows about each of these
+routines by default.
+
+ * `prose` <MESSAGE> [<ARGS>...]:
+ Takes a `printf`(1)-formatted string, collapses whitespace
+ (HTML-style), and then word-wraps it.
+
+ * `bullet` <MESSAGE> [<ARGS>...]:
+ Similar to `prose`, but prints a bullet point before the first
+ line, and indents the remaining lines.
+
+ * `flag` [<FLAG> <DESCRIPTION>|<HEADING>:]...:
+ Print a flag and description formatted for `--help` text. For
+ example:<br>
+ `flag '-N' 'Disable networking in the chroot'`<br>
+ The description is fed through `gettext`, the flag is not, so if
+ part of the flag needs to be translated, you must do that
+ yourself:<br>
+ `flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'`<br>
+ Newlines in the description are ignored; it is
+ whitespace-collapsed (so newlines are stripped), then it is
+ re-word-wrapped, in the same way as `prose` and `bullet`. If you
+ pass in multiple flag/description pairs to the same invocation,
+ the descriptions are all aligned together. The ability to do
+ insert headings without resetting the alignment is the motivation
+ for also allowing headings to be in the list. In order to tell
+ the difference between a flag and a heading, a heading must end
+ with a colon (':'), and a flag must not.
+
+### NOTIFICATION ROUTINES
+
+These routines print to standard error, and all take arguments in the
+same format as `printf`(1), except for `stat_done`, which doesn't take
+any arguments. Each of these print to stderr, not stdout.
+
+For each of these, <MESSAGE> is fed through `gettext` automatically.
+
+To generate gettext `.pot` files for programs using these routines,
+plain `xgettext`(1) will work if given correct flags, but you'll find
+it easier to use `librexgettext`(1), which will handle each of these
+without any extra flags.
+
+ * `plain` <MESSAGE> [<ARGS>...]:
+ Prints a "plain" message in bold, indented with 4 spaces.
+
+ * `msg` <MESSAGE> [<ARGS>...]:
+ Prints a top-level priority notification.
+
+ * `msg2` <MESSAGE> [<ARGS>...]:
+ Prints a secondary notification.
+
+ * `warning` <MESSAGE> [<ARGS>...]:
+ Prints a warning.
+
+ * `error` <MESSAGE> [<ARGS>...]:
+ Prints an error message.
+
+ * `stat_busy` <MESSAGE> [<ARGS>...]:
+ Prints a "working..." type message without a trailing newline.
+
+ * `stat_done`:
+ Prints a "done" type message to terminate `stat_busy`.
+
+ * `print` <MESSAGE> [<ARGS>...]:
+ Like `printf`(1), but `gettext`-aware, and automatically prints a
+ trailing newline.
+
+ * `term_title` <MESSAGE> [<ARGS>...]:
+ Sets the terminal title to the specified message.
+
+### TEMPORARY DIRECTORY MANAGEMENT
+
+These are used by devtools, and not used within the rest of
+libretools.
+
+They work by creating and removing the directory referred to by the
+variable $<WORKDIR>; `libretools.conf`(5) uses the same variable to
+where the user saves all their work. If you aren't careful with
+these, you could end up deleting a lot of someone's work.
+
+ * `setup_workdir`:
+ Creates a temporary directory, and sets the environmental
+ variable $<WORKDIR> to it. Also sets traps for the signals INT,
+ QUIT, TERM and HUP to run `abort`; and EXIT to run `cleanup`
+ (see `signal`(7)).
+
+ * `cleanup` [<EXIT_STATUS>]:
+ *If* `setup_workdir` has been run, `rm -rf "$WORKDIR"`. If given
+ a numeric argument, it will then call `exit`(1) with that
+ argument, otherwise it calls `exit`(1) with a status of 0.
+
+ * `abort`:
+ Calls `msg` with the message "Aborting...", then calls
+ `cleanup 255`.
+
+ * `die` <MESSAGE> [<ARGS>...]:
+ Exactly like `error`, but calls `cleanup` and calls `exit`(1)
+ with a status of 255.
+
+### LOCKFILE ROUTINES
+
+ * `lock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]:
+ Opens (creating if necessary) the file <LOCKFILE> with file
+ descriptor <FD> in the current process, and gets an exclusive
+ lock on it. If another program already has a lock on the file,
+ and this program needs to wait for the lock to be released, then
+ it uses `stat_busy`/`stat_done` to print <MESSAGE>.
+
+ * `slock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]:
+ Identical like `lock`, but opens a shared lock. This is also
+ known as a "read lock". Many programs can have a shared lock at
+ the same time, as long as no one has an exclusive lock on it.
+
+ * `lock_close` <FD>:
+ Closes file descriptor <FD>, releasing the lock opened on it.
+
+### MAKEPKG ROUTINES
+
+These routines relate to `makepkg`(8).
+
+ * `find_cached_package` <PKGNAME> <PKGVER>[-<PKGREL] <ARCH>:
+ Searches for a locally built copy of the specified package, in
+ <PKGDEST> and the current working directory. If <PKGREL> is not
+ specified, any value will match. If multiple matching files are
+ found (not counting duplicate links), then an error is printed to
+ stderr and nothing is printed to stdout.
+
+ * `get_full_version` [<PKGNAME>]:
+ Inspects variables that are set, and prints the full version
+ spec, including <epoch> if necessary, <pkgver>, and <pkgrel>. By
+ default, it will print the information for <pkgbase>, following
+ the normal rules for finding <pkgbase>. If <PKGNAME> is given,
+ it will print the information for that sub-package. The versions
+ for different parts of a split package don't have to be the same!
+
+## BUGS
+
+`term_title` currently only knows about the terminals screen, tmux,
+xterm and rxvt (and their various <TERM> values;
+"rxvt-unicode-256color" is still rxvt).
+
+## SEE ALSO
+
+librexgettext(1), librelib(7), gettext(1), common.sh(3)
+
+Things that were mentioned:
+
+bash(1), xgettext(1), exit(1), isatty(3), libretools.conf(5),
+makepkg(8), printf(1), signal(7)
diff --git a/src/lib/librexgettext b/src/lib/librexgettext
new file mode 100755
index 0000000..db575d6
--- /dev/null
+++ b/src/lib/librexgettext
@@ -0,0 +1,226 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+export TEXTDOMAIN='librelib'
+export TEXTDOMAINDIR='/usr/share/locale'
+
+default_simple=(
+ --keyword={eval_,}{gettext,'ngettext:1,2'}
+ --keyword={_,print,term_title}
+ --keyword={msg,msg2,warning,error,stat_busy,die}
+ --keyword={lock,slock}:3
+)
+default_prose=(--keyword={prose,bullet})
+
+readonly default_simple default_prose
+
+if ! type gettext &>/dev/null; then
+ gettext() { echo "$@"; }
+fi
+
+errusage() {
+ if [[ $# -gt 0 ]]; then
+ fmt="$(gettext "$1")"; shift
+ printf "${0##*/}: $fmt\n" "$@"
+ fi
+ usage >&2
+}
+
+usage() {
+ . libremessages
+ print 'Usage: %s [OPTIONS] FILES...' "${0##*/}"
+ print 'Generates .pot files for programs using libremessages'
+ echo
+ prose 'In librexgettext, there are 2 types of keywords:'
+ bullet 'simple: Simple keywords are just like normal xgettext'
+ bullet 'prose: Prose keywords are similar, but the text is
+ word-wrapped'
+ prose 'The keyword format is the same as in GNU xgettext.'
+ echo
+ prose 'The libremessages `flag` command is also handled
+ specially, and is not configurable as a keyword.'
+ echo
+ prose 'The default simple keywords are: %s' "${default_simple[*]#--keyword=}"
+ echo
+ prose 'The default prose keywords are: %s' "${default_prose[*]#--keyword=}"
+ echo
+ print 'Options:'
+ flag \
+ '--simple=KEYWORD' 'Look for KEYWORD as an additional simple keyword' \
+ '--prose=KEYWORD' 'Look for KEYWORD as an additional prose keyword' \
+ '-k' 'Disable using the default keywords' \
+ '-h, --help' 'Show this text'
+}
+
+xgettext-sh() {
+ xgettext --omit-header --from-code=UTF-8 -L shell -k -o - "$@"
+}
+
+xgettext-flag() {
+ {
+ # Stage 1: Generate
+ #
+ # Get all of the arguments to `flag`. Because `flag`
+ # takes an arbitrary number of arguments, just iterate
+ # through arg1, arg2, ... argN; until we've come up
+ # empty 3 times. Why 3? Because each flag takes 2
+ # arguments, and because we don't keep track of which
+ # one of those we're on, waiting for 3 empties ensures
+ # us that we've had a complete "round" with nothing.
+ #
+ # Why can't I just do i+=2, and not have to keep track
+ # of empties? Because, we also allow for arguments
+ # ending in a colon to be headings, which changes the
+ # offsets.
+ declare -i empties=0
+ declare -i i
+ for (( i=1; empties < 3; i++ )); do
+ local out
+ out="$(xgettext-sh --keyword="flag:$i,\"$i\"" "$@")"
+ if [[ -n $out ]]; then
+ printf -- '%s\n' "$out"
+ else
+ empties+=1
+ fi
+ done
+ } | whitespace-collapse | sed '/^\#, sh-format/d' | {
+ # Stage 2: Parse
+ #
+ # Read in the lines, and group them into an array of
+ # (multi-line) msgs. This just makes working with
+ # them easier.
+ local msgs=()
+ declare -i i=-1
+ local re='^#\. ([0-9]+)$'
+ IFS=''
+ local line
+ while read -r line; do
+ if [[ $line =~ $re ]]; then
+ i+=1
+ fi
+ msgs[$i]+="$line"$'\n'
+ done
+ # Stage 3: Sort
+ #
+ # Now, we have the `msgs` array, and it is
+ # sorted such that it is all of the arg1's to `flag`,
+ # then all of the arg2's, then all of the arg3's, and
+ # so on. We want to re-order them such that it's all
+ # of the args for the first invocation then all of the
+ # args for the second; and so on.
+ #
+ # We do this by simply sorting them by the location
+ # that they appear in the file. Then, when we see the
+ # argument number go back down to 1, we know that a
+ # new invocation has started!
+ IFS=$'\n'
+ local locations=($(
+ local i
+ for i in "${!msgs[@]}"; do
+ declare -i arg row
+ local lines=(${msgs[$i]})
+ arg=${lines[0]#'#. '}
+ row=${lines[1]##*:}
+ printf '%d.%d %d\n' "$row" "$arg" "$i"
+ done | sort -n
+ ))
+ # Stage 4: Output
+ #
+ # Now, we prune out the arguments that aren't
+ # localizable. Also, remove the "#." comment lines.
+ # As explained above (in stage 3), when we see $arg go
+ # to 1, that's the beginning of a new invocation.
+ local expectflag=true
+ local location
+ for location in "${locations[@]}"; do
+ IFS=' .'
+ local row arg i
+ read -r row arg i <<<"$location"
+ local msg="${msgs[$i]#*$'\n'}"
+ # Now we operate based on $row, $arg, and $msg
+ if [[ $arg == 1 ]]; then
+ expectflag=true
+ fi
+ if $expectflag; then
+ IFS=$'\n'
+ local lines=(${msg})
+ if [[ ${lines[1]} == *':"' ]]; then
+ # We expected a flag, but got
+ # a heading
+ printf -- '%s\n' "$msg"
+ else
+ # We expected a flag, and got
+ # one!
+ expectflag=false
+ fi
+ else
+ printf -- '%s\n' "$msg"
+ expectflag=true
+ fi
+ done
+ }
+}
+
+whitespace-collapse() {
+ tr '\n' '\r' | sed 's/"\r\s*"//g' | tr '\r' '\n' | # This removes the awkward word-wrapping done by xgettext
+ sed -r -e 's/(\\n|\\t|\t)/ /g' -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g' # This collapses whitespace
+}
+
+main() {
+ local simple=()
+ local prose=()
+ local files=()
+ local use_defaults=true
+ local error=false
+
+ declare -i i
+ for (( i=1; i <= $#; i++ )); do
+ case "${!i}" in
+ --simple) i+=1; simple+=(--keyword="${!i}");;
+ --simple=*) simple+=(--keyword="${!i#*=}");;
+ --prose) i+=1; prose+=(--keyword="${!i}");;
+ --prose=*) prose+=(--keyword="${!i#*=}");;
+ -k) use_defaults=false;;
+ --help|-h) usage; return 0;;
+ --) i+=1; break;;
+ -*) errusage "unrecognized option: %s" "${!i}"; error=true;;
+ *) files+=("${!i}");;
+ esac
+ done
+ files+=("${@:$i}")
+ if [[ ${#files[@]} -lt 1 ]]; then
+ errusage "no input file given"
+ error=true
+ fi
+ if "$error"; then
+ return 1
+ fi
+ if "$use_defaults"; then
+ simple+=("${default_simple[@]}")
+ prose+=("${default_prose[@]}")
+ fi
+
+ # Main code
+ {
+ xgettext-sh "${simple[@]}" -- "${files[@]}"
+ xgettext-sh "${prose[@]}" -- "${files[@]}" | whitespace-collapse
+ xgettext-flag -- "${files[@]}"
+ } | sed '/^\#, sh-format/d' | msguniq -Fi --to-code=UTF-8
+}
+
+main "$@"
diff --git a/src/lib/librexgettext.1.ronn b/src/lib/librexgettext.1.ronn
new file mode 100644
index 0000000..de6abde
--- /dev/null
+++ b/src/lib/librexgettext.1.ronn
@@ -0,0 +1,18 @@
+librexgettext(1) -- Extrct libremessages gettext strings from sources
+=====================================================================
+
+## SYNOPSIS
+
+`librexgettext` [OPTIONS] FILES<br>
+`librexgettext` [-h|--help]
+
+## DESCRIPTION
+
+`librexgettext` is a tool for extracting gettext strings from programs
+using the fancy word-wrapping utilities of `libremessages`(1).
+
+See `librexgettext --help` for more information.
+
+## SEE ALSO
+
+libremessages(1)
diff --git a/src/lib/messages.sh b/src/lib/messages.sh
new file mode 100644
index 0000000..0125003
--- /dev/null
+++ b/src/lib/messages.sh
@@ -0,0 +1,212 @@
+#!/usr/bin/env bash
+# This may be included with or without `set -euE`
+
+# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012-2014, 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# For just the setup_traps() function:
+# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org>
+# Copyright (C) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
+# Copyright (C) 2005 Aurelien Foret <orelien@chez.com>
+# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu>
+# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk>
+# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org>
+# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+################################################################################
+# Inherit most functions from devtools #
+################################################################################
+
+. "$(librelib common.sh)"
+
+################################################################################
+# Own functions #
+################################################################################
+
+# Usage: panic
+#
+# For programming errors, bails immediately with little fanfare.
+panic() {
+ echo "$(_l _ 'panic: malformed call to internal function')" >&2
+ exit 1
+}
+
+# Usage: print MESG [ARGS...]
+#
+# Like printf, but gettext-aware, and prints a trailing newline
+print() {
+ [[ $# -ge 1 ]] || panic
+ local mesg="$(_ "$1")"
+ shift
+ printf -- "$mesg\n" "$@"
+}
+
+# Usage: whitespace_collapse <<<STRING
+#
+# Collapses whitespace on stadard I/O, similar to HTML whitespace
+# collapsing, with the exception that it puts two spaces between
+# sentences. It considers newline, tab, and space to be whitespace.
+whitespace_collapse() {
+ [[ $# == 0 ]] || panic
+
+ tr '\n' '\r' | sed -r \
+ -e 's/\r/ /g' -e 's/\t/ /g' \
+ -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?]) +/\1 /g'
+}
+
+
+# Usage: prose MESG [ARGS...]
+#
+# Do HTML-style whitespace collapsing on the first argument, translate
+# it (gettext), then word-wrap it to 75 columns. This is useful for
+# printing a paragraph of prose in --help text.
+prose() {
+ [[ $# -ge 1 ]] || panic
+ local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift
+ printf -- "$mesg" "$@" | fmt -u
+}
+
+# Usage: bullet MESG [ARGS...]
+# Like prose, but print a bullet "-" before the first line, and indent the
+# remaining lines.
+bullet() {
+ [[ $# -ge 1 ]] || panic
+ local mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift
+ # Wrap the text to 71 columns; 75 (the default) minus a 4 column indent
+ printf -- "$mesg" "$@" | fmt -u -w 71 | sed -e '1s/^/ - /' -e '2,$s/^/ /'
+}
+
+# Usage: flag [FLAG DESCRIPTION|HEADING:]...
+#
+# Print a flag and description formatted for --help text.
+#
+# ex: flag '-C <FILE>' 'Use this file instead of pacman.conf'
+#
+# The descriptions and headings are fed through gettext, the flags ar
+# not, so if part of a flag needs to be translated, you must do that
+# yourself:
+#
+# ex: flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'
+#
+# If you want to line-break the description in the source, so it isn't
+# crazy-long, feel free, it is reflowed/wrapped the same way as prose
+# and bullet. If you pass in multiple flag/description pairs at once,
+# the descriptions are all alligned together.
+#
+# A heading MUST end with a colon (':'), this is how it knows that it
+# is a heading. Similarly, a flag MUST NOT end with a colon.
+flag() {
+ local args=("$@")
+
+ declare -i flaglen=0
+ while [[ $# -gt 0 ]]; do
+ if [[ $1 == *: ]]; then
+ shift 1
+ else
+ if [[ ${#1} -gt $flaglen ]]; then
+ flaglen=${#1}
+ fi
+ shift 2
+ fi
+ done
+ set -- "${args[@]}"
+
+ # Unless the $flaglen is extra-wide, the $desc should start at
+ # column 16 (that is two literal-tabs). If $flaglen is wide,
+ # this should be increased in increments of 8 (that is, a
+ # literal-tab). Everything should be wrapped to 75 columns.
+
+ # The printf-format we use has 4 spaces built into it (two at
+ # the beginning, and two for a seperator). Therefore, the
+ # default indent is 16-4=12 columns. And the width left for
+ # $desc is (75-4)-indent = 71-indent.
+
+ declare -i indent=12
+ while [[ $indent -lt $flaglen ]]; do
+ indent+=8
+ done
+ local fmt2 fmt1
+ fmt2=" %-${indent}s %s\n"
+ printf -v fmt1 " %-${indent}s %%s\n" ''
+
+ while [[ $# -gt 0 ]]; do
+ if [[ $1 == *: ]]; then
+ printf -- ' %s\n' "$(_ "$1")"
+ shift
+ else
+ [[ $# -gt 1 ]] || panic
+ local flag=$1
+ local desc="$(_ "$(whitespace_collapse <<<"$2")")"
+ shift 2
+
+ local lines
+ IFS=$'\n' lines=($(fmt -u -w $((71-indent)) <<<"$desc"))
+ printf -- "$fmt2" "$flag" "${lines[0]}"
+ [[ ${#lines[@]} -lt 2 ]] || printf -- "$fmt1" "${lines[@]:1}"
+ fi
+ done
+}
+
+# Usage: term_title MESG [ARGS...]
+#
+# Sets the terminal title.
+term_title() {
+ [[ $# -ge 1 ]] || panic
+ local fmt=''
+ case "$TERM" in
+ screen|tmux) fmt='\ek%s\e\\';;
+ xterm*|rxvt*) fmt='\e]0;%s\a';;
+ esac
+ printf "$fmt" "$(printf -- "$@")"
+}
+
+# Usage: setup_traps [handler]
+#
+# Sets up traps on TERM, HUP, QUIT and INT signals, as well as the ERR
+# event, similar to makepkg.
+#
+# If `handler` is specified, instead of using the default handler
+# (which is good for most purposes), it will call the command handler
+# with the arguments:
+#
+# ${handler} SIGNAL_NAME MESSAGE_FMT [MESSAGE_PARAMS...]
+#
+# where MESSAGE_* are printf-like stuff.
+#
+# This function is based on code from pacman:makepkg
+setup_traps() {
+ [[ $# -le 1 ]] || panic
+ if [[ $# == 1 ]]; then
+ eval "_libremessages_trap_exit() { $1 \"\$@\"; }"
+ else
+ _libremessages_trap_exit() {
+ local signal=$1; shift
+ echo
+ error "$@"
+ trap -- "$signal"
+ kill "-$signal" "$$"
+ }
+ fi
+ set -E
+ for signal in TERM HUP QUIT; do
+ trap "_libremessages_trap_exit $signal '%s signal caught. Exiting...' $signal" $signal
+ done
+ trap '_libremessages_trap_exit INT "Aborted by user! Exiting..."' INT
+ trap '_libremessages_trap_exit USR1 "An unknown error has occurred. Exiting..."' ERR
+}
diff --git a/src/lib/messages.sh.3.ronn b/src/lib/messages.sh.3.ronn
new file mode 120000
index 0000000..391ecbd
--- /dev/null
+++ b/src/lib/messages.sh.3.ronn
@@ -0,0 +1 @@
+libremessages.1.ronn \ No newline at end of file
diff --git a/src/librefetch/.gitignore b/src/librefetch/.gitignore
new file mode 100644
index 0000000..d34a2fc
--- /dev/null
+++ b/src/librefetch/.gitignore
@@ -0,0 +1,2 @@
+librefetch-install
+librefetch-makepkg.conf
diff --git a/src/librefetch/Makefile b/src/librefetch/Makefile
new file mode 100644
index 0000000..26ee2ee
--- /dev/null
+++ b/src/librefetch/Makefile
@@ -0,0 +1,13 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+libretools-bins = librefetch librefetch-install
+libretools-confs += librefetch-makepkg.conf
+libretools-libexecs += $(filter librefetchdir/%,$(detect-exec))
+libretools-libs += $(filter-out $(libretools-libexecs),$(filter librefetchdir/%,$(detect-all)))
+pots = $(libretools-bins)
+
+$(outdir)/librefetch-install: $(var)pkgconfdir
+$(outdir)/librefetch-makepkg.conf: $(var)bindir
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/librefetch/librefetch b/src/librefetch/librefetch
new file mode 100755
index 0000000..2b8af61
--- /dev/null
+++ b/src/librefetch/librefetch
@@ -0,0 +1,400 @@
+#!/usr/bin/env bash
+# librefetch
+#
+# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# For just the create_signature() function:
+# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org>
+# Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org>
+# Copyright (C) 2005 Aurelien Foret <orelien@chez.com>
+# Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org>
+# Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu>
+# Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk>
+# Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org>
+#
+# License: GNU GPLv3+
+#
+# This file is part of LibreFetch.
+#
+# LibreFetch 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.
+#
+# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>.
+
+# create_signature() is taken from pacman:makepkg, which is GPLv2+,
+# so we take the '+' to combine it with our GPLv3+.
+
+. "$(librelib conf)"
+. "$(librelib messages)"
+setup_traps
+
+tmpfiles=()
+tmpdirs=()
+trap 'rm -f -- "${tmpfiles[@]}"; rm -rf -- "${tmpdirs[@]}"' EXIT
+
+cmd=${0##*/}
+usage() {
+ print "Usage: %s [OPTIONS] SOURCE_URL [OUTPUT_FILE]" "$cmd"
+ print "Usage: %s -[g|S|M|h]" "$cmd"
+ print "Downloads or creates a liberated source tarball."
+ echo
+ prose "The default mode is to create OUTPUT_FILE, first by trying
+ download mode, then create mode."
+ echo
+ prose "If OUTPUT_FILE isn't specified, it defaults to the non-directory
+ part of SOURCE_URL, in the current directory."
+ echo
+ prose "Unless '-C' is specified, if SOURCE_URL does not begin with a
+ configured mirror, create mode is inhibited."
+ echo
+ prose "In download mode, it simply tries to download SOURCE_URL. At the
+ beginning of a URL, 'libre://' expands to the first configured
+ mirror."
+ echo
+ prose "In create mode, it either looks at a build script and uses that
+ to create the source tarball, or it uses GPG to create a
+ signature (if OUTPUT_FILE ends with \`.sig\` or \`.sig.part\`).
+ If it is using GPG to create a signature, but the file that it is
+ trying to sign doesn't exist yet, it recurses on itself to first
+ create that file. SOURCE_URL is ignored, except that it is used
+ to set the default value of OUTPUT_FILE, and that it may be used
+ when recursing."
+ echo
+ prose "The default build script is 'PKGBUILD', or 'SRCBUILD' if it
+ exists."
+ echo
+ prose "Other options, if they are valid \`makepkg\` options, are passed
+ straight to makepkg."
+ echo
+ print "Example usage:"
+ print ' $ %s https://repo.parabola.nu/other/mypackage/mypackage-1.0.tar.gz' "$cmd"
+ echo
+ print "Options:"
+ flag 'Settings:' \
+ "-C" "Force create mode (don't download)" \
+ "-D" "Force download mode (don't create)" \
+ "-p <$(_ FILE)>" "Use an alternate build script (instead of
+ 'PKGBUILD'). If an SRCBUILD exists in the same
+ directory, it is used instead" \
+ 'Alternate modes:' \
+ "-g, --geninteg" "Generate integrity checks for source files" \
+ "-S, --srcbuild" "Print the effective build script (SRCBUILD)" \
+ "-M, --makepkg" "Generate and print the location of the
+ effective makepkg script" \
+ "-h, --help" "Show this message"
+}
+
+main() {
+ BUILDFILE="$(realpath -Lm PKGBUILD)"
+ makepkg_opts=()
+ extra_opts=()
+ mode=download-create
+ if ! parse_options "$@"; then
+ usage >&2
+ exit 1
+ fi
+
+ doit
+}
+
+doit() {
+ # Mode: help ###########################################################
+
+ if [[ $mode =~ help ]]; then
+ usage
+ exit 0
+ fi
+
+ ########################################################################
+
+ makepkg="$(modified_makepkg)"
+
+ # Mode: makepkg ########################################################
+
+ if [[ $mode =~ makepkg ]]; then
+ printf '%s\n' "$makepkg"
+ exit 0
+ else
+ tmpdirs+=("${makepkg%/*}")
+ fi
+
+ ########################################################################
+
+ local BUILDFILEDIR="${BUILDFILE%/*}"
+ if [[ -f "${BUILDFILEDIR}/SRCBUILD" ]]; then
+ BUILDFILE="${BUILDFILEDIR}/SRCBUILD"
+ fi
+ if [[ ! -f "$BUILDFILE" ]]; then
+ error "%s does not exist." "$BUILDFILE"
+ exit 1
+ fi
+ case "$BUILDFILE" in
+ */SRCBUILD) srcbuild="$(modified_srcbuild "$BUILDFILE")";;
+ *) srcbuild="$(modified_pkgbuild "$BUILDFILE")";;
+ esac
+ tmpfiles+=("$srcbuild")
+
+ # Mode: checksums ######################################################
+
+ if [[ $mode =~ checksums ]]; then
+ "$makepkg" "${makepkg_opts[@]}" -g -p "$srcbuild" |
+ case ${BUILDFILE##*/} in
+ PKGBUILD) sed -e 's/^[a-z]/mk&/' -e 's/^\s/ &/';;
+ SRCBUILD) cat;;
+ esac
+ exit 0
+ fi
+
+ # Mode: srcbuild #######################################################
+
+ if [[ $mode =~ srcbuild ]]; then
+ cat "$srcbuild"
+ exit 0
+ fi
+
+ ########################################################################
+
+ local src="${extra_opts[0]}"
+ local dst="${extra_opts[1]:-${src##*/}}"
+
+ # Don't canonicalize $src unless mode =~ download, and we've validated
+ # that $MIRRORS is configured.
+
+ # Canonicalize $dst
+ dst="$(realpath -Lm -- "$dst")"
+
+ # Mode: download #######################################################
+
+ if [[ $mode =~ download ]]; then
+ load_files librefetch
+ check_vars librefetch MIRRORS DOWNLOADER || exit 1
+
+ # Canonicalize $src
+ if [[ "$src" == libre://* ]]; then
+ src="${MIRRORS[0]}/${src#libre://}"
+ fi
+
+ # check to see if $src is a candidate for create mode
+ local inmirror=false;
+ local mirror
+ for mirror in "${MIRRORS[@]}"; do
+ if [[ "$src" == "$mirror"* ]]; then
+ inmirror=true
+ break
+ fi
+ done
+ if ! $inmirror; then
+ # inhibit create
+ mode=download
+ fi
+
+ local dlcmd="${DOWNLOADER}"
+ [[ $dlcmd = *%u* ]] || dlcmd="$dlcmd %u"
+ dlcmd="${dlcmd//\%o/\"\$dst\"}"
+ dlcmd="${dlcmd//\%u/\"\$src\"}"
+
+ { eval "$dlcmd"; } >&2 && exit 0
+ fi
+
+ # Mode: create #########################################################
+
+ if [[ $mode =~ create ]]; then
+ local base_dst=${dst%.part}
+ local suffix=${dst#"$base_dst"}
+
+ if [[ $base_dst == *.sig ]]; then
+ if ! [[ -e $base_dst ]]; then
+ extra_opts=("${src%.sig}" "${base_dst%.sig}")
+ doit || exit $?
+ fi
+ create_signature "${base_dst%.sig}" || exit $?
+ if [[ -n $suffix ]]; then
+ mv -f "$base_dst" "$dst"
+ fi
+ else
+ export PKGEXT=${base_dst##*/}
+ export PKGDEST=${dst%/*}
+ export pkg_file=$dst
+
+ cd "$BUILDFILEDIR"
+ "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >&2 || exit $?
+ fi
+ fi
+}
+
+# sets the variables BUILDFILE, makepkg_opts, extra_opts, mode
+parse_options() {
+ declare -i ret=0
+ local {shrt,long}{1,2}
+
+ # makepkg options
+ local makepkg_orig="$(which makepkg)"
+ shrt1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.)(,| [^<]).*/\1/p'))
+ shrt2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +-(.) <.*/\1/p'))
+ long1=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn -e 's/^ +(-., )?--(\S*) [^<].*/\2/p'))
+ long2=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/^ +--(\S*) <.*/\1/p'))
+
+ # librefetch options
+ shrt1+=(C D g S M h)
+ shrt2+=(p)
+ long1+=(geninteg srcbuild makepkg help)
+ long2+=()
+
+ # Feed the options through getopt (sanitize them)
+ local shrt="$({ printf '%s\0' "${shrt1[@]}"; printf '%s:\0' "${shrt2[@]}"; } | sort -zu | xargs -0 printf '%s')"
+ local long="$({ printf '%s\0' "${long1[@]}"; printf '%s:\0' "${long2[@]}"; } | sort -zu | xargs -0 printf '%s,')"
+ local args="$(getopt -n "$cmd" -o "$shrt" -l "${long%,}" -- "$@")"
+ ret=$?
+ eval set -- "$args"
+ unset shrt long args
+
+ # Parse the options.
+ local opt optarg have_optarg
+ while [[ $# -gt 0 ]]; do
+ opt=$1; shift
+ have_optarg=false
+
+ if { [[ $opt == --?* ]] && in_array "${opt#--}" "${long2[@]}"; } \
+ || { [[ $opt == -? ]] && in_array "${opt#-}" "${shrt2[@]}"; }
+ then
+ optarg=$1; shift
+ have_optarg=true
+ fi
+
+ case "$opt" in
+ -C) mode=create;;
+ -D) mode=download;;
+ -g|--geninteg) mode=checksums;;
+ -S|--srcbuild) mode=srcbuild;;
+ -M|--makepkg) mode=makepkg;;
+ -p) BUILDFILE="$(realpath -Lm -- "$optarg")";;
+ -h|--help) mode=help;;
+ --) break;;
+ *)
+ makepkg_opts+=("$opt")
+ if $have_optarg; then makepkg_opts+=("$optarg"); fi
+ ;;
+ esac
+ done
+ extra_opts+=("$@")
+
+ # check the number of extra_opts
+ case "$mode" in
+ help) # don't worry about it
+ :;;
+ checksums|srcbuild|makepkg) # don't take any extra arguments
+ if [[ ${#extra_opts[@]} != 0 ]]; then
+ print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >&2
+ ret=1
+ fi
+ ;;
+ *download*|*create*) # take 1 or 2 extra arguments
+ if [[ ${#extra_opts[@]} != 1 ]] && [[ ${#extra_opts[@]} != 2 ]]; then
+ print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >&2
+ ret=1
+ fi
+ ;;
+ esac
+
+ return $ret
+}
+
+# Modify makepkg ###############################################################
+
+modified_makepkg() {
+ local dir="$(mktemp --tmpdir --directory "${cmd}.XXXXXXXXXXX.makepkg")"
+ make -s -f "$(librelib librefetchdir/Makefile)" new="$dir"
+ realpath -es "$dir/makepkg"
+}
+
+# Modify PKGBUILD ##############################################################
+
+# a string to be appended
+pkgbuild_append='
+# do not do split packages
+if [[ ${#pkgname[@]} -gt 1 ]]; then
+ if [[ -n $pkgbase ]]; then
+ pkgname=("$pkgbase")
+ else
+ pkgname=("$pkgname")
+ fi
+fi
+
+# copy source variables
+source=("${mksource[@]}") ; unset "source_${CARCH}"
+noextract=("${mknoextract[@]}")
+
+declare algo
+for algo in "${known_hash_algos[@]}"; do
+ eval "${algo}sums=(\"\${mk${algo}sums[@]}\")"
+ unset "${algo}sums_${CARCH}"
+done
+
+depends=() ; unset "depends_${CARCH}"
+checkdepends=() ; unset "checkdepends_${CARCH}"
+makedepends=("${mkdepends[@]}") ; unset "makedepends_${CARCH}"
+
+####
+options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx !optipng !debug)
+PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/)
+
+####
+if ! declare -f mksource >/dev/null; then
+ mksource() { :; }
+fi
+prepare() { :; }
+build() { mksource; }
+check() { :; }
+package() { cp -a "$srcdir"/*/ "$pkgdir/"; }
+'
+
+modified_pkgbuild() {
+ local pkgbuild=$1
+ local srcbuild="$(mktemp "${pkgbuild%/*}/${cmd}.XXXXXXXXXXX.PKGBUILD.to.SRCBUILD")"
+ printf '%s' "$pkgbuild_append" | cat "$pkgbuild" - > "$srcbuild"
+ printf '%s\n' "$srcbuild"
+}
+
+
+# Modify SRCBUILD ##############################################################
+
+modified_srcbuild() {
+ local orig=$1
+ local srcbuild="$(mktemp "${orig%/*}/${cmd}.XXXXXXXXXXX.SRCBUILD.to.SRCBUILD")"
+ sed -e '/PKGDEST=/d' -e '/PKGEXT=/d' < "$orig" > "$new"
+ printf '%s\n' "$new"
+}
+
+################################################################################
+
+# This function is taken almost verbatim from makepkg
+create_signature() {
+ local ret=0
+ local filename="$1"
+ msg "Signing package..."
+
+ local SIGNWITHKEY=()
+ if [[ -n $GPGKEY ]]; then
+ SIGNWITHKEY=(-u "${GPGKEY}")
+ fi
+
+ gpg --detach-sign --use-agent "${SIGNWITHKEY[@]}" --no-armor "$filename" &>/dev/null || ret=$?
+
+
+ if (( ! ret )); then
+ msg2 "Created signature file %s." "$filename.sig"
+ else
+ error "Failed to sign package file."
+ return $ret
+ fi
+}
+
+main "$@"
diff --git a/src/librefetch/librefetch-install.in b/src/librefetch/librefetch-install.in
new file mode 100644
index 0000000..34815a7
--- /dev/null
+++ b/src/librefetch/librefetch-install.in
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# lirefetch-install: (un)install librefetch to /etc/makepkg.conf
+#
+# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+set -ueE
+. "$(librelib messages)"
+
+# This line should be installed
+new_code='. @pkgconfdir@/librefetch-makepkg.conf # This line was added by librefetch-install'
+
+# These lines were installed by previous versions of this script
+old_code=(
+ '# The following line is added by the libretools post_install script'
+ '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=("libre::/usr/bin/librefetch -p \"\$BUILDFILE\" %u %o")'
+ '[[ ! -x /usr/bin/librefetch ]] || DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p \"\$BUILDFILE\" -- %u %o")'
+ 'DLAGENTS+=({https,libre}"::/usr/bin/librefetch -p $(printf "%q" "$BUILDFILE") -- %u %o")'
+ 'DLAGENTS+=({https,libre}'\''::/usr/bin/librefetch -p "$BUILDFILE" -- %u %o'\'')'
+)
+
+# has_line $file $line
+has_line() {
+ local file=$1
+ local line=$2
+ grep -Fxq -- "$line" "$file"
+}
+# del_line $file $line
+del_line() {
+ local file=$1
+ local line=$2
+ local lineno=($(grep -Fxn -- "$line" "$file" | cut -d: -f1))
+ if [[ "${#lineno[@]}" -gt 0 ]]; then
+ sed -i "$(printf '%dd;' "${lineno[@]}")" "$file"
+ fi
+}
+
+# post_install $file
+post_install() {
+ local file=$1
+ if grep -q 'librefetch' "$file"; then
+ msg2 "%s: librefetch is already in %q" "${0##*/}" "$(realpath -s "$file")"
+ local line del=false
+ for line in "${old_code[@]}"; do
+ if has_line "$file" "$line"; then
+ msg2 "%s: ... but it's an old version" "${0##*/}"
+ pre_remove "$file"
+ post_install "$file"
+ return $?
+ fi
+ done
+ else
+ msg2 "%s: adding librefetch to %q" "${0##*/}" "$(realpath -s "$file")"
+ printf '%s\n' "$new_code" >> "$file"
+ fi
+}
+
+# pre_remove $file
+pre_remove() {
+ local file=$1
+ msg2 "%s: removing librefetch from %q" "${0##*/}" "$(realpath -s "$file")"
+
+ # Check for the old stuff
+ sed -ri 's/^#(.*) # commented out by the libretools post_install script/\1/' "$file"
+ local line
+ for line in "${old_code[@]}"; do
+ del_line "$file" "$line"
+ done
+
+ # Check for the current stuff
+ del_line "$file" "$new_code"
+}
+
+usage() {
+ print "Usage: %s [install|remove] MAKEPKG_CONF_FILE" "${0##*/}"
+ print "Adds or removes librefetch to/from makepkg.conf:DLAGENTS"
+}
+
+main() {
+ if [[ $# != 2 ]]; then
+ usage >&2
+ return 1
+ fi
+ local file=$2
+ if [[ ! -f "$file" ]]; then
+ msg2 "%s: does not exist: %q" "${0##*/}" "$(realpath -s "$file")"
+ fi
+ if [[ ! -w "$file" ]]; then
+ msg2 "%s: cannot write to file: %q" "${0##*/}" "$(realpath -s "$file")"
+ fi
+ case "$1" in
+ install) post_install "$file";;
+ remove) pre_remove "$file";;
+ *) usage >&2; return 1;;
+ esac
+}
+
+main "$@"
diff --git a/src/librefetch/librefetch-makepkg.conf.in b/src/librefetch/librefetch-makepkg.conf.in
new file mode 100644
index 0000000..723bc15
--- /dev/null
+++ b/src/librefetch/librefetch-makepkg.conf.in
@@ -0,0 +1,16 @@
+#!/hint/bash
+
+# This file should be 'source'd by makepkg.conf to enable librefetch
+edit_dlagents() {
+ local dlagent="@bindir@/librefetch -p $(printf %q "$(realpath -Lm "${BUILDFILE:-${BUILDSCRIPT:-PKGBUILD}}")") -- %u %o"
+
+ local i
+ for i in "${!DLAGENTS[@]}"; do
+ if [[ "${DLAGENTS[$i]}" = https::* ]]; then
+ DLAGENTS[$i]="https::$dlagent"
+ fi
+ done
+ DLAGENTS+=("libre::$dlagent")
+}
+edit_dlagents
+unset -f edit_dlagents
diff --git a/src/librefetch/librefetch.8.ronn b/src/librefetch/librefetch.8.ronn
new file mode 100644
index 0000000..cf009de
--- /dev/null
+++ b/src/librefetch/librefetch.8.ronn
@@ -0,0 +1,214 @@
+librefetch(8) -- downloads or creates a liberated source tarball
+================================================================
+
+## SYNOPSIS
+
+`librefetch` [<OPTIONS>] <SOURCE-URL> [<OUTPUT-FILE>]<br>
+`librefetch` `-`[`g`|`S`|`M`|`h`]
+
+## DESCRIPTION
+
+`librefetch` is a program to streamline creation of custom source
+tarballs for `PKGBUILD(5)` files.
+
+If a URL mentioned in the <source> array in a `PKGBUILD` is in a
+location that Parabola uploads "custom" source tarballs to (or
+configured locations), and no file is at that URL, librefetch will
+automatically create it for you.
+
+This works because a post-install script for the package configures
+`librefetch` as the download agent for `https://` URLs in
+`makepkg.conf`; allowing it to jump in and create a file if need be.
+Because of this, it is almost never necessary to call `librefetch`
+manually.
+
+The post-install script also configures `librefetch` as the download
+agent for `libre://` URLs, for compatibility with `PKGBUILD` files
+that used a previous version of librefetch.
+
+There are 5 modes:
+
+ * `download`: Download the tarball from the configured mirror.
+ * `create`: Create the tarball from a `PKGBUILD`/`SRCBUILD`.
+ * `checksums`: Generate integrity checks for source files.
+ * `srcbuild`: Print the effective build script.
+ * `makepkg`: Generate and print the location of the effective makepkg
+ script.
+ * `help`: Print `librefetch` usage information.
+
+The normal mode of operation is `download` mode. If `download` mode
+fails, it may choose to try `create` mode.
+
+## OPTIONS
+
+ * `-C`: Force `create` mode (don't download)
+ * `-D`: Force `download` mode (don't create)
+ * `-p` <file>: Use an alternate build script for `create` mode
+ (instead of `PKGBUILD`). If an `SRCBUILD` file exists in the same
+ directory, it is used instead.
+ * `-g` | `--geninteg`: Use `checksums` mode: Generate integrity
+ checks for source files.
+ * `-S` | `--srcbuild`: Use `srcbuild` mode: print the effective build
+ script.
+ * `-M` | `--makepkg`: Use `makepkg` mode: generate and print the
+ location of the effective makepkg script.
+ * `-h` | `--help`: Use `help` mode: Show usage information.
+
+Other options, if they are documented in `makepkg -h`, are passed to
+the modified copy of makepkg created during `create` mode.
+
+## DOWNLOAD MODE
+
+If <SOURCE-URL> begins with the string `libre://`, it is replaced with
+the first value in <MIRRORS>, as configured in `librefetch.conf(5)`;
+this is for compatibility with `PKGBUILD` files that used a previous
+version of librefetch.
+
+It uses <DOWNLOADER>, as configured in `librefetch.conf` to attempt to
+download the source tarball from that URL. If that fails, and
+following conditions are met, it proceeds to `create` mode:
+
+ * The `-D` flag has not been specified to inhibit `create` mode.
+ * The <SOURCE-URL> begins with one of the values in <MIRRORS>.
+
+The latter requirement allows librefetch to be used as a generic
+HTTP(S) download agent, that can automatically create files from
+whitelisted locations.
+
+## CREATE MODE
+
+The principle of `create` mode is that a special `PKGBUILD(5)` (called
+`SRCBUILD(5)`) installs source files to <$pkgdir>, and the resulting
+"package" is then used as a source tarball. The `SRCBUILD` exists in
+the same directory as the `PKGBUILD`. It can be created manually, or
+generated on-the-fly from the `PKGBUILD`. Extra steps are taken to
+ensure that as long as the same directory contents go in, an identical
+tarball will come out--the checksum of the file should not change
+based on when it is built or who builds it.
+
+The `SRCBUILD` is either created, or sanitized if it already exists.
+If the output filename does not end with `.sig` or `.sig.part`, then
+the `SRCBUILD` is fed to a modified version of `makepkg(8)`. If the
+output filename does end with `.sig` or `.sig.part`, then it uses GPG
+to create a signature. If the file it is trying to sign does not
+exist yet, librefetch recurses on itself to create it.
+
+The reason `makepkg` must be modified is that we need the resulting
+tarball to be deterministic (as well as not containing package
+metadata).
+
+When this documentation speaks of a file being modified, it is a
+temporary copy of the file that is modified, your original file will
+remain intact.
+
+## SRCBUILD GENERATION
+
+As explained in the `CREATE MODE` section, in `create` mode, this
+program generates an `SRCBUILD` file. For debugging purposes, this
+file can be printed instead of executed with `srcbuild` mode.
+
+### PRE-EXISTING SRCBUILD
+
+The use of `SRCBUILD` files pre-dates `librefetch`. By convention,
+they set <PKGDEST> and <PKGEXT> in `package()` in order to modify the
+behavior of `makepkg`. Because a modified version of `makepkg` is
+used, this interferes with the correct behavior. To compensate for
+this, lines containing "`PKGDEST=`" or "`PKGEXT=`" are deleted from
+the `SRCBUILD`.
+
+The general idea is that `build()` makes any modifications to the
+source, then `package()` copies it from <$srcdir> to <$pkgdir>.
+
+### SRCBUILD FROM PKGBUILD
+
+Possibly more elegant than having a separate `SRCBUILD` file is having
+an `mksource()` function in the main `PKGBUILD`. This results in less
+boilerplate and fewer files to edit.
+
+Note that this only happens if a file named `SRCBUILD` doesn't already
+exist; when migrating a package from a manually created `SRCBUILD` to
+this method, the `SRCBUILD` must be deleted (or renamed) for this to
+work.
+
+The dynamically created `SRCBUILD` is created by copying `PKGBUILD` to
+a temporary file, then re-setting variables and re-defining functions.
+Following is a table of the translations.
+
+ Variables
+ source = mksource
+ noextract = mknoextract
+ *sums = mk*sums (md5, sha1, sha224, sha256, sha384, sha512)
+ depends = <empty>
+ checkdepends = <empty>
+ makedepends = mkdepends
+ *_$CARCH = <unset>
+ Functions
+ prepare() { :; }
+ build() { mksource; }
+ check() { :; }
+ package() { cp -a "$srcdir"/*/ "$pkgdir/"; }
+
+The `mksource()` function does not need to be defined. If it isn't
+defined, then no transformations will be made to the source between it
+being extracted to <$srcdir> and copied to <$pkgdir>.
+
+In summary:
+
+ * Set <mksource=()> and <mkmd5sums=()> to act as <source=()> and
+ <md5sums=()>, respectively.
+ * Declare a `mksource()` function to make modifications to the
+ source, if necessary.
+
+Other changes:
+
+ * <pkgname> is set to <pkgbase>, or the first element of the
+ <pkgname> array (the effect is that split packaging is turned
+ off).
+ * <options=()> is set have `makepkg` avoid making changes to
+ <$pkgdir>. The exact change is:
+
+ options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx)
+
+ * <PURGE_TARGETS=()> has VCS directories added to it:
+
+ PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/)
+
+### MAKEPKG MODIFICATIONS
+
+The following modifications are made to makepkg:
+
+ * Allow us to manipulate the output file (<$pkg_file>)
+ * Do not include metadata in the output file (<${comp_files[@]}>)
+ * Force 'ustar' tar format, don't allow it to upgrade to 'pax' to
+ store extended file attributes.
+ * Don't symlink the resulting file into the current directory.
+ * <PURGE_TARGETS> interprets an item as a directory if it ends with a
+ slash ("/").
+ * Timestamps in <$pkgdir> are reset to the date specified in
+ <SOURCE_DATE_EPOCH>[1], or "1990-01-01 0:0:0 +0" if it's not set, so
+ that the resulting tarball will be the same, regardless of when it
+ was created.
+ * Sort the files included in the tarball; normally the order of files
+ in a tarball is essentially random (even if it tends to be the same
+ when re-created on the same machine).
+ * append `-libre` to <$srcdir>
+ * append `-libre` to <$pkgbasedir> (which becomes <$pkgdir>)
+ * Don't check if the package has already been built.
+
+For debugging purposes, this modified makepkg can be printed instead
+of executed with `makepkg` mode. Before it is run in create mode,
+`PKGEXT`, `PKGDEST`, and `pkg_file` are set as environment variables.
+
+## CONFIGURATION
+
+See `librefetch.conf(5)` for details on configuring librefetch using
+the `librefetch.conf` file.
+
+## SEE ALSO
+
+librefetch.conf(5), makepkg(8), PKGBUILD(5), SRCBUILD(5)
+
+## NOTES
+
+ 1. <SOURCE_DATE_EPOCH> specification for build systems
+ https://reproducible-builds.org/specs/source-date-epoch/
diff --git a/src/librefetch/librefetch.conf b/src/librefetch/librefetch.conf
new file mode 100644
index 0000000..7251ff3
--- /dev/null
+++ b/src/librefetch/librefetch.conf
@@ -0,0 +1,7 @@
+MIRRORS=(
+ 'https://repo.parabola.nu/sources/'
+ 'https://repo.parabola.nu/other/'
+ 'https://repo.parabolagnulinux.org/sources/'
+ 'https://repo.parabolagnulinux.org/other/'
+)
+DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u'
diff --git a/src/librefetch/librefetch.conf.5.ronn b/src/librefetch/librefetch.conf.5.ronn
new file mode 100644
index 0000000..29df4d2
--- /dev/null
+++ b/src/librefetch/librefetch.conf.5.ronn
@@ -0,0 +1,42 @@
+librefetch.conf(5) -- librefetch configuration file
+===================================================
+
+## SYNOPSIS
+
+`/etc/libretools.d/librefetch.conf`, `~/.config/libretools/librefetch.conf`
+
+## DESCRIPTION
+
+Configuration for librefetch is stored in `librefetch.conf`. The
+several places it looks for the file are:
+
+ * `/etc/libretools.d/librefetch.conf`
+ * `$XDG_CONFIG_HOME/libretools/librefetch.conf`
+
+The later files take precedence over earlier files, but earlier files
+are loaded, so that later files only need to set the values they want
+to override.
+
+If `$XDG_CONFIG_HOME` is not set, a default value is set:
+
+ * if `$SUDO_USER` is set: `$(eval echo ~$SUDO_USER)/.config`
+ * else: `$HOME/.config`
+
+## OPTIONS
+
+ * `MIRRORS=( ... )`:
+ A list of locations that generated source tarballs may be located
+ at. If a URL begins with `libre://`, then `libre://` is replaced
+ with the first location listed here.
+ * `DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u'`:
+ The HTTP client to use when downloading pre-built source tarballs
+ in download mode. The format and semantics are similar to
+ `DLAGENTS` in `makepkg.conf`(5). If present, `%u` will be replaced
+ with the download URL (correctly quoted), otherwise the download
+ URL will be appended to the end of the command. If present, `%o`
+ will be replaced with the local filename that it should be
+ downloaded to.
+
+## SEE ALSO
+
+librefetch(8), makepkg.conf(5)
diff --git a/src/librefetch/librefetchdir/Makefile b/src/librefetch/librefetchdir/Makefile
new file mode 100644
index 0000000..58116bb
--- /dev/null
+++ b/src/librefetch/librefetchdir/Makefile
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+# librefetchdir/Makefile
+#
+# Copyright (C) 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of LibreFetch.
+#
+# LibreFetch 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.
+#
+# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>.
+
+# new =
+librefetchdir := $(dir $(lastword $(MAKEFILE_LIST)))
+
+old_makepkg := $(shell which makepkg)
+old_library := $(shell $(shell grep LIBRARY= $(old_makepkg)); echo $$LIBRARY)
+
+new_makepkg = $(new)/makepkg
+new_library = $(new)/libmakepkg
+
+targets += $(new_makepkg)
+targets += $(patsubst $(old_library)/%,$(new_library)/%,$(shell find $(old_library) -type f))
+targets += $(new_library)/tidy/~source_date_epoch.sh
+
+all: $(targets)
+.PHONY: all
+
+.SECONDARY:
+.DELETE_ON_ERROR:
+
+$(new_makepkg): $(old_makepkg) $(librefetchdir)/makepkg.gen
+ <$^ | install -Dm755 /dev/stdin $@
+
+$(new_library)/source.sh: \
+$(new_library)/%: $(old_library)/% $(librefetchdir)/libmakepkg/%.gen
+ <$^ | install -Dm755 /dev/stdin $@
+
+$(new_library)/tidy/purge.sh $(new_library)/tidy/~source_date_epoch.sh: \
+$(new_library)/%: $(librefetchdir)/libmakepkg/%
+ mkdir -p $(@D)
+ ln -sfT $(abspath $< $@)
+
+$(new_library)/%: $(old_library)/%
+ mkdir -p $(@D)
+ ln -sfT $(abspath $< $@)
diff --git a/src/librefetch/librefetchdir/libmakepkg/source.sh.gen b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen
new file mode 100755
index 0000000..269f9fd
--- /dev/null
+++ b/src/librefetch/librefetchdir/libmakepkg/source.sh.gen
@@ -0,0 +1,25 @@
+#!/usr/bin/sed -rf
+# librefetchdir/makepkg.gen
+#
+# Copyright (C) 2013, 2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of LibreFetch.
+#
+# LibreFetch 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.
+#
+# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>.
+
+/download_sources\(\) \{/ {
+ arm -rf "$srcdir"\nmkdir "$srcdir"
+}
diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh
new file mode 100755
index 0000000..acb0030
--- /dev/null
+++ b/src/librefetch/librefetchdir/libmakepkg/tidy/purge.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/bash
+#
+# purge.sh - Remove unwanted files from the package
+#
+# Copyright (c) 2008-2016 Pacman Development Team <pacman-dev@archlinux.org>
+# Copyright (c) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of LibreFetch
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+#
+
+[[ -n "$LIBMAKEPKG_TIDY_PURGE_SH" ]] && return
+LIBMAKEPKG_TIDY_PURGE_SH=1
+
+LIBRARY=${LIBRARY:-'/usr/share/makepkg'}
+
+source "$LIBRARY/util/message.sh"
+source "$LIBRARY/util/option.sh"
+
+
+packaging_options+=('purge')
+tidy_remove+=('tidy_purge')
+
+tidy_purge() {
+ if check_option "purge" "y" && [[ -n ${PURGE_TARGETS[*]} ]]; then
+ msg2 "$(gettext "Purging unwanted files...")"
+ local pt
+ for pt in "${PURGE_TARGETS[@]}"; do
+ if [[ ${pt} = "${pt%/}" ]]; then
+ if [[ ${pt} = "${pt//\/}" ]]; then
+ find . ! -type d -name "${pt}" -exec rm -f -- '{}' +
+ else
+ rm -f "${pt}"
+ fi
+ else
+ if [[ ${pt%/} = "${pt//\/}" ]]; then
+ find . -type d -name "${pt%/}" -exec rm -rf -- '{}' +
+ else
+ rm -rf "${pt}"
+ fi
+ fi
+ done
+ fi
+}
diff --git a/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh
new file mode 100755
index 0000000..9141d67
--- /dev/null
+++ b/src/librefetch/librefetchdir/libmakepkg/tidy/~source_date_epoch.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/bash
+#
+# librefetchdir/libmakepkg/tidy/~source_date_epoch.sh
+#
+# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of LibreFetch.
+#
+# LibreFetch 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.
+#
+# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>.
+
+# This filename starts with a ~ because it sorts after every other
+# (printable) ASCII character, and we want this to run last.
+
+[[ -n "$LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH" ]] && return
+LIBMAKEPKG_TIDY_SOURCE_DATE_EPOCH_SH=1
+
+tidy_modify+=('tidy_source_date_epoch')
+
+tidy_source_date_epoch() {
+ local date='1990-01-01 0:0:0 +0'
+ if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
+ date="@$SOURCE_DATE_EPOCH"
+ fi
+ find . -exec touch --no-dereference --date="$date" -- {} +
+}
diff --git a/src/librefetch/librefetchdir/makepkg.gen b/src/librefetch/librefetchdir/makepkg.gen
new file mode 100755
index 0000000..8928d91
--- /dev/null
+++ b/src/librefetch/librefetchdir/makepkg.gen
@@ -0,0 +1,42 @@
+#!/usr/bin/sed -rf
+# librefetchdir/makepkg.gen
+#
+# Copyright (C) 2013-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of LibreFetch.
+#
+# LibreFetch 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.
+#
+# LibreFetch 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 LibreFetch. If not, see <http://www.gnu.org/licenses/>.
+
+/LIBRARY=/iexport LIBRARY="${0%/*}/libmakepkg"
+
+/create_package\(\) \{/,/^\}$/ {
+ /pkg_file=/d # allow us to set pkg_file
+ s/"?\$\{comp_files\[@\]\}"?// # do not include .{PKGINFO,BUILDINGO,CHANGELOG,INSTALL,MTREE}
+ # This is long/gross. What it does:
+ # - pass --format=ustar to bsdtar, to inhibit it using the pax format
+ # - take the files that would be included in the tarball, and use
+ # find/sort/--files-from to order them for bsdtar
+ s/bsdtar(.*) - ([^|]*) \|/find \2 -print0 | LC_ALL=C sort --zero-terminated | bsdtar --null --files-from - --format=ustar --no-recursion \1 - |/
+ s/create_signature .*/&; return $?/ # do not procede to create symlinks
+}
+
+s|Making package:|Making source:|
+s|Checking runtime dependencies\.\.\.|Checking source dependencies...|
+ /Checking buildtime dependencies\.\.\./d
+
+s|srcdir=.*|&-libre|
+s|pkgdirbase=.*|&-libre|
+s|check_build_status$|:|
diff --git a/src/libretools.conf b/src/libretools.conf
new file mode 100644
index 0000000..fb4593c
--- /dev/null
+++ b/src/libretools.conf
@@ -0,0 +1,72 @@
+#!/hint/bash
+
+################################################################################
+# misc #
+################################################################################
+
+# The dir where you work on
+WORKDIR="$LIBREHOME/packages"
+
+## Blacklist URL
+BLACKLIST=https://projects.parabola.nu/blacklist.git/plain/blacklist.txt
+
+## Diff tool (vimdiff, gvimdiff, meld, etc)
+## Used by `aur`, `diff-unfree`
+DIFFPROG=$(which $([ -z "${DISPLAY:-}" ]||echo kdiff3 meld gvimdiff) vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q')
+
+## The architectures you'll be packaging for
+## Used by `librestage`, `xbs-abslibre`
+ARCHES=($(printf '%s\n' /usr/share/pacman/defaults/pacman.conf.*|sed 's|.*\.||'))
+
+## ABSLibre
+# Used by xbs-abslibre
+ABSLIBRERECV=git://projects.parabola.nu/abslibre/abslibre.git
+ABSLIBRESEND=ssh://git@projects.parabola.nu/srv/git/abslibre/abslibre.git
+ABSLIBREDEST="$WORKDIR/staging/abslibre"
+
+################################################################################
+# librerelease #
+################################################################################
+
+## Where to upload packages to
+# '/staging/' is appended; this is for compatibility with previous versions.
+REPODEST=repo@parabola.nu:staging/$LIBREUSER
+
+## These are run before and after uploading packages
+HOOKPRERELEASE="ssh -fN ${REPODEST%%:*}"
+HOOKPOSTRELEASE="sudo librechroot clean-repo"
+
+################################################################################
+# dagpkg #
+################################################################################
+
+# Note: Not being set is valid for any of the HOOK* settings.
+
+# Run a command before running FULLBUILDCMD
+HOOKPREBUILD=""
+
+## Uncomment one of those or make one of your choice
+# Normal
+FULLBUILDCMD="sudo libremakepkg"
+# Cross compiling
+#FULLBUILDCMD="sudo libremakepkg -n cross-compile-chroot"
+# Don't use a chroot
+#FULLBUILDCMD="makepkg -sL --noconfirm"
+
+# Locally release the package or any other action after running FULLBUILDCMD
+# successfully. When run, it is given a repository name as a single argument.
+HOOKLOCALRELEASE="librestage"
+
+################################################################################
+# toru #
+################################################################################
+
+TORUPATH=/var/lib/libretools/toru
+
+## The repos you'll be packaging for
+## Used by: `toru-path`
+# Tip: As early repos take precedence on $REPOS loops, you can use this as
+# inverted order of precedence. Put testing repos first so dagpkg will find new
+# PKGBUILDs first, for instance. `toru-path` uses reverse order to enforce repo
+# precedence on the path cache (the last path added replaces the rest)
+REPOS=('core' 'libre' 'extra' 'community' 'libre-testing' 'social' 'sugar' 'pcr' 'java')
diff --git a/src/pkgbuild-check-nonfree b/src/pkgbuild-check-nonfree
new file mode 100755
index 0000000..67f07bc
--- /dev/null
+++ b/src/pkgbuild-check-nonfree
@@ -0,0 +1,309 @@
+#!/usr/bin/env bash
+# -*- tab-width: 4 ; sh-basic-offset: 4 -*-
+# pkgbuild-check-nonfree
+
+# Copyright (C) 2011 Joseph Graham (Xylon) <joe@t67.eu>
+# Copyright (C) 2010-2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# I appologize that this program got *huge*.
+# It's not complicated, just long.
+
+. "$(librelib messages)"
+. "$(librelib conf)"
+. "$(librelib blacklist)"
+
+usage() {
+ print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}"
+ print "Analyzes a PKGBUILD for freedom issues"
+ echo
+ prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.'
+ echo
+ print "Exit status (add them for combinations):"
+ print " 0: Everything OK, no freedom issues"
+ print " 1: Ran with error"
+ print "Warning-level freedom issues:"
+ print " 2: Uses unrecognized licenses, check them"
+ print " 4: Uses GPL-incompatible licenses"
+ print "Error-level freedom issues:"
+ print " 8: Uses known unacceptable licenses"
+ print " 16: Has nonfree dependencies"
+ print " 32: Is a known nonfree package"
+ echo
+ print "Options:"
+ flag '-c' 'Use the cached blacklist, do not try downloading'
+ flag '-f' 'Allow running as root user'
+ echo
+ flag '-q' 'Be quiet'
+ flag '-v' 'Be verbose'
+ echo
+ flag '-h' 'Show this message'
+}
+# Make sure these match pkgbuild-summarize-nonfree
+declare -ri _E_OK=0
+declare -ri _E_ERROR=1
+declare -ri _E_LIC_UNKNOWN=2
+declare -ri _E_LIC_NOGPL=4
+declare -ri _E_LIC_NONFREE=8
+declare -ri _E_DEP_NONFREE=16
+declare -ri _E_PKG_NONFREE=32
+
+main() {
+ # Parse flags
+ local cache=false
+ local asroot=false
+ local v=1
+ while getopts 'cfqvh' arg; do
+ case "$arg" in
+ c) cache=true;;
+ f) asroot=true;;
+ q) v=0;;
+ v) v=2;;
+ h) usage; return $_E_OK;;
+ *) usage >&2; return $_E_ERROR;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# -lt 1 ]]; then
+ pkgbuilds=("`pwd`/PKGBUILD")
+ else
+ pkgbuilds=("$@")
+ fi
+
+ # Do a check to see if we are running as root
+ if [[ -w / ]] && ! $asroot; then
+ error "Run as normal user, or use the -f option to run as root."
+ return 1
+ fi
+
+ # Adjust the verbosity
+ if [[ $v == 0 ]]; then
+ error() { :; }
+ warning() { :; }
+ plain() { :; }
+ info() { :; }
+ elif [[ $v == 1 ]]; then
+ info() { :; }
+ elif [[ $v == 2 ]]; then
+ info() { plain "$@"; }
+ fi
+
+ # Update the blacklist
+ $cache || blacklist-update || return $_E_ERROR
+
+ # Do the work
+ declare -i ret=0
+ local pkgbuild
+ for pkgbuild in "${pkgbuilds[@]}"; do
+ pkgbuild_check "$pkgbuild" || ret=$(($ret|$?))
+ done
+ return $ret
+}
+
+# Helper functions #############################################################
+# These should maybe be moved into lib/conf.sh
+
+# Usage: var="$(pkgbuild_get_pkg_str ${pkgname} ${varname})"
+# Gets a package-level string for a split-package
+pkgbuild_get_pkg_str() {
+ [[ $# == 2 ]] || panic 'malformed call to pkgbuild_get_pkg_str'
+ local pkg=$1
+ local var=$2
+
+ local indirect=${!var}
+ eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p")
+ printf '%s' "${indirect}"
+}
+# Usage: eval $(pkgbuild_get_pkg_ary ${pkgname} ${varname} [$variable_name_to_set])
+# Gets a package-level array for a split-package
+pkgbuild_get_pkg_ary() {
+ [[ $# == 2 ]] || [[ $# == 3 ]] || panic 'malformed call to pkgbuild_get_pkg_ary'
+ local pkg=$1
+ local var=$2
+ local out="${3:-$var}"
+
+ local ary="${var}[@]"
+ local indirect=("${!ary}")
+ eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p")
+ declare -p indirect|sed "s/ indirect=/ ${out}=/"
+}
+
+# Checker functions ############################################################
+
+# Usage: check_lic "${licence}"
+# Check a license name to see if it is OK
+check_lic() {
+ [[ $# == 1 ]] || panic 'malformed call to check_license'
+ local license=$1
+
+ info 'Checking license: %s' "$license"
+
+ if [[ -e "/usr/share/licenses/common/$license" ]]; then
+ return $_E_OK
+ else
+ case "${license#custom:}" in
+ WTFPL)
+ # accept as common, I think it should be in the licenses package
+ return $_E_OK;;
+ BSD1|BSD2|BSD3|MIT|X11)
+ # accept these as common; they can't be included in the
+ # 'licenses' package because some text must be customized
+ return $_E_OK;;
+ BSD4)
+ warning "The 4-clause BSD license is free but has practical problems."
+ return $_E_LIC_NOGPL;;
+ BSD)
+ warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses."
+ return $_E_LIC_UNKNOWN;;
+ JSON)
+ error "License '%s' is a known non-free license." "$license"
+ return $_E_LIC_NONFREE;;
+ *)
+ warning "License '%s' is not a common (recognized) license." "$license"
+ return $_E_LIC_UNKNOWN;;
+ esac
+ fi
+ panic 'code should never be reached'
+}
+
+# Usage: check_dep "${dependency}"
+# Checks for ${dependency} in the blacklist
+check_dep() {
+ [[ $# == 1 ]] || panic 'malformed call to check_dep'
+ local pkg=$1
+
+ local line="$(blacklist-cat|blacklist-lookup "$pkg")"
+ local rep="$(blacklist-get-rep <<<"$line")"
+ if [[ -z $line ]]; then
+ # not mentioned in blacklist; free
+ info '%s: not blacklisted' "$pkg"
+ return $_E_OK
+ elif [[ -z $rep ]]; then
+ # non-free with no replacement
+ plain '%s: blacklisted' "$pkg"
+ return $_E_DEP_NONFREE
+ else
+ # non-free with free replacement
+ if [[ "$rep" == "$pkg" ]]; then
+ info '%s: repackaged with the same name' "$pkg"
+ else
+ info '%s: replaced by %s' "$pkg" "$rep"
+ fi
+ return $_E_OK
+ fi
+ panic 'code should never be reached'
+}
+
+# Usage: check_pkg "${pkgname}"
+# Checks for ${pkgname} in the blacklist
+check_pkg() {
+ [[ $# == 1 ]] || panic 'malformed call to check_pkg'
+ check_dep "$@"
+ case $? in
+ $_E_OK)
+ return $_E_OK;;
+ $_E_DEP_NONFREE)
+ return $_E_PKG_NONFREE;;
+ *)
+ panic 'unexpected return code from check_dep';;
+ esac
+ panic 'code should never be reached'
+}
+
+# Usage: pkgbuild_ckec $pkgbuild
+# Check whether a PKGBUILD has any issues (using the above)
+pkgbuild_check() (
+ [[ $# == 1 ]] || panic 'malformed call to pkgbuild_check'
+ local pkgbuild=$1
+
+ load_PKGBUILD "$pkgbuild"
+ if [[ -z $pkgname ]]; then
+ return $_E_ERROR # not a PKGBUILD
+ fi
+
+ declare -i ret=0 # the return status
+ local dep lic # iterators for us in `for` loops
+ local ck_deps ck_lics # lists of deps and licenses that have been checked
+
+ if [[ ${#pkgname[@]} == 1 ]]; then
+ msg2 'Inspecting package pkgname=%q (%s)' "$pkgname" "$(get_full_version)"
+ else
+ msg2 'Inspecting split package pkgbase=%q (%s)' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)"
+ fi
+
+ # Check if this is blacklisted
+ check_pkg "${pkgbase:-${pkgname[0]}}" || ret=$(($ret|$?))
+ # Check if dependencies are blacklisted
+ for dep in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" \
+ "${optdepends[@]%%:*}" "${mkdepends[@]}"
+ do
+ check_dep "$dep" || ret=$(($ret|$?))
+ ck_deps+=("$dep")
+ done
+ # Check the licenses
+ for lic in "${license[@]}"; do
+ check_lic "$lic" || ret=$(($ret|$?))
+ ck_lics+=("$lic")
+ done
+
+ if [[ ${#pkgname[@]} == 1 ]]; then
+ # Non-split package
+ # Make sure a license is set
+ if [[ ${#ck_lics[@]} == 0 ]]; then
+ error "The license array is empty"
+ ret=$(($ret|$_E_ERROR))
+ fi
+ else
+ # Split package
+ # Check the individual split packages
+ local _pkgname _license _depends _optdepends
+ for _pkgname in "${pkgname[@]}"; do
+ msg2 'Inspecting split package pkgname=%q (%s)' "$_pkgname" "$(get_full_version "$_pkgname")"
+ eval $(pkgbuild_get_pkg_ary "$_pkgname" license _license)
+ eval $(pkgbuild_get_pkg_ary "$_pkgname" depends _depends)
+ eval $(pkgbuild_get_pkg_ary "$_pkgname" optdepends _optdepends)
+
+ # Check if this is blacklisted
+ check_pkg "$_pkgname" || ret=$(($ret|$?))
+ # Check if dependencies are blacklisted
+ for dep in "${_depends[@]}" "${_optdepends[@]%%:*}"; do
+ if ! in_array "$dep" "${ck_deps[@]}"; then
+ check_dep "$dep" || ret=$(($ret|$?))
+ fi
+ done
+ # Check the licenses
+ for lic in "${_license[@]}"; do
+ if ! in_array "$lic" "${ck_lics[@]}"; then
+ check_lic "$lic" || ret=$(($ret|$?))
+ fi
+ done
+
+ if [[ ${#_license[@]} == 0 ]]; then
+ error "The license array is empty"
+ ret=$(($ret|$_E_ERROR))
+ fi
+ done
+ fi
+
+ return $ret
+)
+
+main "$@"
diff --git a/src/pkgbuild-summarize-nonfree b/src/pkgbuild-summarize-nonfree
new file mode 100755
index 0000000..1e39e87
--- /dev/null
+++ b/src/pkgbuild-summarize-nonfree
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. "$(librelib messages)"
+
+# Make sure these match pkgbuild-check-nonfree
+declare -ri _E_OK=0
+declare -ri _E_ERROR=1
+declare -ri _E_LIC_UNKNOWN=2
+declare -ri _E_LIC_NOGPL=4
+declare -ri _E_LIC_NONFREE=8
+declare -ri _E_DEP_NONFREE=16
+declare -ri _E_PKG_NONFREE=32
+
+usage() {
+ print "Usage: %s [OPTIONS] STATUS" "${0##*/}"
+ print "Summarizes a status code from pkgbuild-check-nonfree"
+ echo
+ prose 'It thresholds the issues it finds, only failing for error-level
+ issues, and ignoring warnings. Unless `-q` is specified, it also
+ prints a summary of the issues it found.'
+ echo
+ print 'Options:'
+ flag '-q' 'Be quiet'
+ flag '-h' 'Show this message'
+}
+
+main() {
+ local quiet=false
+ while getopts 'qh' arg; do
+ case "$arg" in
+ q) quiet=true;;
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# -ne 1 ]]; then
+ usage >&2
+ return 1
+ fi
+ if ! [[ $1 =~ ^[0-9]+$ ]]; then
+ error 'STATUS must be an integer'
+ usage >&2
+ return 1
+ fi
+
+ if $quiet; then
+ error() { :; }
+ warning() { :; }
+ fi
+
+ parse $1;
+ return $?
+}
+
+parse() {
+ [[ $# == 1 ]] || panic 'malformed call to parse'
+ declare -i s=$1;
+
+ declare -i ret=0
+ declare -i i
+ for i in 1 2 4 8 16 32; do
+ if [[ $(($s & $i)) -gt 0 ]]; then
+ case $i in
+ $_E_ERROR)
+ # could be anything, assume the worst
+ error "There was an error processing the PKGBUILD"
+ ret=1;;
+ $_E_LIC_UNKNOWN)
+ warning "This PKGBUILD has an unknown license";;
+ $_E_LIC_NOGPL)
+ warning "This PKGBUILD has a GPL-incompatible license";;
+ $_E_LIC_NONFREE)
+ error "This PKGBUILD has a known nonfree license"
+ ret=1;;
+ $_E_DEP_NONFREE)
+ error "This PKGBUILD depends on known nonfree packages"
+ ret=1;;
+ $_E_PKG_NONFREE)
+ error "This PKGBUILD is for a known nonfree package"
+ ret=1;;
+ esac
+ fi
+ done
+ return $ret
+}
+
+main "$@"
diff --git a/src/repo-diff b/src/repo-diff
new file mode 100755
index 0000000..d11732b
--- /dev/null
+++ b/src/repo-diff
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# Shows a diff between repo databases
+
+# Copyright (C) 2013 Nicolás Reynolds <fauno@parabola.nu>
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+
+usage() {
+ print "Usage: %s arch/core/i686 parabola/core/i686" "${0##*/}"
+ print "Compares two repo databases using distro/repo/architecture format."
+ echo
+ print 'Shortcuts:'
+ flag 'arch' 'expands to Arch Linux repo url'
+ flag 'parabola' 'expands to Parabola GNU/Linux-libre repo url'
+}
+
+if test $# -eq 0; then
+ usage
+ exit 0
+fi
+
+b() { bsdtar ztf $1 | cut -d "/" -f1 | sort -u ; }
+n() { echo "$1".db | tr "/" "-"; }
+
+# hopefully simple way to convert
+# parabola/libre/i686
+# to
+# http://repo.parabola.nu/libre/os/i686/libre.db
+# add more distros here
+g() {
+ echo "$1" | sed -e "s,^\([^/]\+\)/\([^/]\+\)/\([^/]\+\)$,\1/\2/os/\3/\2.db," \
+ -e "s,^parabola/,http://repo.parabola.nu/," \
+ -e "s,^arch\(linux\)\?/,http://mirrors.kernel.org/archlinux/,"
+}
+
+mkdir ${0##*/}.$$
+pushd ${0##*/}.$$ >/dev/null
+
+d=""
+for i in $1 $2; do
+ n=$(n "$i")
+
+ test -z "$n" && exit 1
+
+ wget -O "$n" -nv $(g "$i")
+ b "$n" >${n}.orig
+
+ d+=" ${n}.orig"
+done
+
+{
+ printf "$(gettext "Difference between %s and %s")\n---\n" $1 $2
+ which diffstat &>/dev/null && diff -auN "${d[@]}" | diffstat
+ diff -auN "${d[@]}"
+} >../${n}.diff
+
+popd >/dev/null
+rm -r ${0##*/}.$$
+
+print "Difference save on %s" "${n}.diff"
diff --git a/src/toru/Makefile b/src/toru/Makefile
new file mode 100644
index 0000000..2903f4a
--- /dev/null
+++ b/src/toru/Makefile
@@ -0,0 +1,4 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/toru/toru-info b/src/toru/toru-info
new file mode 100755
index 0000000..a7081cb
--- /dev/null
+++ b/src/toru/toru-info
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# Prints info about a given pkgname
+
+# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu>
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+. "$(librelib conf)"
+
+for _pkg in "$@"; do
+ _pkgbuild="$(toru-where $_pkg)"
+
+ if [ -f "$_pkgbuild/PKGBUILD" ]; then
+ if ! load_PKGBUILD "$_pkgbuild/PKGBUILD" 2>/dev/null; then
+ warning "Errors on %s" "$_pkg"
+ continue
+ fi
+
+ deps=("${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}")
+ repo="$(basename -- "$(dirname -- "$_pkgbuild")")"
+
+ msg "%s/%s %s-%s" "$repo" "$_pkg" "$pkgver" "$pkgrel"
+ msg2 '%s' "$pkgdesc"
+ msg2 '%s' "$url"
+ msg2 'Depends: %s' "${deps[*]}"
+ else
+ warning "%s doesn't exist" "$_pkg"
+ fi
+done
diff --git a/src/toru/toru-path b/src/toru/toru-path
new file mode 100755
index 0000000..4600a5c
--- /dev/null
+++ b/src/toru/toru-path
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+setup_traps
+
+# TODO: better option parsing
+TORUPATH=${T:-${TORUPATH}}
+VERBOSE=${V:-false}
+FORCE=${F:-false}
+
+. "$(librelib conf.sh)"
+load_files libretools
+check_vars libretools TORUPATH REPOS || exit 1
+load_files abs
+check_vars abs ABSROOT || exit 1
+
+if [ ! -w "$TORUPATH" ]; then
+ error "Toru's path isn't writable. Please check $TORUPATH"
+ exit 1
+fi
+
+lastsyncfile=${TORUPATH}/lastsync.paths
+pathfile=${TORUPATH}/paths.tch
+
+if [ ! -e "${pathfile}" ]; then
+ tcamgr create "${pathfile}"
+fi
+
+# TODO: ability to use flags to pass in other directories to fullrepos
+
+# This loops over ${REPOS[@]} backward. This is because early entries
+# in REPOS have higher precidence, but the way this is implemented,
+# the later entries have precedence, so we need to flip the order.
+fullrepos=()
+for (( i = ${#REPOS[@]}-1 ; i >= 0 ; i-- )); do
+ $VERBOSE && msg "Processing [%s]" "${REPOS[$i]}"
+
+ # ABSROOT has trailing slash
+ if [ -d "${ABSROOT}${REPOS[$i]}" ]; then
+ fullrepos+=("${ABSROOT}${REPOS[$i]}")
+ fi
+done
+
+# Find PKGBUILDs in ${fullrepos[@]}
+find_args=("${fullrepos[@]}" -mindepth 2 -maxdepth 3 -type f -name PKGBUILD)
+if ! $FORCE && [[ -e $lastsyncfile ]]; then
+ # if lastfilesync exists, only look at things that have
+ # changed since then (unless $FORCE is on)
+ find_args+=(-newer "${lastsyncfile}")
+fi
+IFS=$'\n'
+pkgbuilds=($(find "${find_args[@]}"))
+
+# Add information from each of the PKGBUILDs to the toru cache.
+msg "Updating path cache"
+msg2 "%d PKGBUILDs to update" ${#pkgbuilds[@]}
+for pkgbuild in "${pkgbuilds[@]}"; do
+ # plain "$_pkgbuild"
+ if ! load_PKGBUILD "${_pkgbuild}" >/dev/null 2>&1; then
+ error "%q contains errors, skipping" "${_pkgbuild}"
+ continue
+ fi
+
+ fullpath="$(dirname -- "${_pkgbuild}")"
+
+ for _pkg in "${pkgbase}" "${pkgname[@]}" "${provides[@]}"; do
+ $VERBOSE && msg2 '%s -> %s' "${_pkg}" "${fullpath}"
+ tcamgr put "${pathfile}" "${_pkg%%[<>=]*}" "${fullpath}"
+ done
+done
+
+date +%s > "${lastsyncfile}"
diff --git a/src/toru/toru-where b/src/toru/toru-where
new file mode 100755
index 0000000..f6df15f
--- /dev/null
+++ b/src/toru/toru-where
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# Locates a PKGBUILD dir on toru's path cache
+
+# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu>
+#
+# 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 Affero 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 <http://www.gnu.org/licenses/>.
+
+. "$(librelib conf.sh)"
+load_files libretools
+check_vars libretools TORUPATH || exit 1
+
+tcamgr get "${TORUPATH}/paths.tch" "$1" 2>/dev/null || echo ""
diff --git a/src/workflows.md b/src/workflows.md
new file mode 100644
index 0000000..03dca4f
--- /dev/null
+++ b/src/workflows.md
@@ -0,0 +1,64 @@
+# Workflows
+
+Describe your packaging workflow here!
+
+
+## fauno's way
+
+During packaging, I don't usually restart a build from scratch if I have to make
+changes to the PKGBUILD. I use a lot of commenting out commands already ran,
+`makepkg -R`, etc. When I used `libremakepkg` I ended up using a lot more
+`librechroot` and working from inside the unconfigured chroot, because
+`makechrootpkg` (the underlying technology for `libremakepkg`) tries to be too
+smart.
+
+When I started writing `treepkg` I found that mounting what I need directly on
+the chroot and working from inside it was much more comfortable and simple than
+having a makepkg wrapper doing funny stuff (for instance, mangling
+`makepkg.conf` and breaking everything.)
+
+This is how the chroot is configured:
+
+* Create the same user (with same uid) on the chroot that the one I use
+ regularly.
+
+* Give it password-less sudo on the chroot.
+
+* Bind mount `/home` to `/chroot/home`, where I have the abslibre-mips64el
+ clone.
+
+* Bind mount `/var/cache/pacman/pkg` to `/chroot/var/cache/pacman/pkg`
+
+* Put these on system's `fstab` so I don't have to do it everytime
+
+* Configure `makepkg.conf` to `PKGDEST=CacheDir` and `SRCDEST` to something on
+ my home.
+
+Workflow:
+
+* Enter the chroot with `systemd-nspawn -D/chroot` and `su - fauno`.
+
+* From another shell (I use tmux) edit the abslibre or search for updates with
+ `git log --no-merges --numstat`.
+
+* Pick a package and run `treepkg` from its dir on the chroot, or retake a build
+ with `treepkg /tmp/package-treepkg-xxxx`.
+
+ > Note: `treepkg` has been deprecated in favor of `dagpkg`.
+
+What this allows:
+
+* Not having to worry about the state of the chroot. `chcleanup` removes and
+ adds packages in a smart way so shared dependencies stay and others move along
+ (think of installing and removing qt for a complete kde rebuild).
+
+* Building many packages in a row without recreating a chroot for every one of
+ them.
+
+* Knowing that any change you made to the chroot stays as you want (no one
+ touches your makepkg.conf)
+
+* Hability to run regular commands, not through a chroot wrapper. I can `cd` to
+ a dir and use `makepkg -whatever` on it and nothing breaks.
+
+* No extra code spent on wrappers.
diff --git a/src/xbs-abs/.gitignore b/src/xbs-abs/.gitignore
new file mode 100644
index 0000000..d472431
--- /dev/null
+++ b/src/xbs-abs/.gitignore
@@ -0,0 +1,3 @@
+commitpkg*
+archrelease*
+!*.patch
diff --git a/src/xbs-abs/Makefile b/src/xbs-abs/Makefile
new file mode 100644
index 0000000..248848f
--- /dev/null
+++ b/src/xbs-abs/Makefile
@@ -0,0 +1,27 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+pkgconfdir = $(sysconfdir)/xbs
+pkglibexecdir = $(libexecdir)/xbs
+
+_helpers = archrelease commitpkg
+libretools-bins =
+libretools-libexecs = helper-abs
+pots += $(_helpers)
+devtools-files += $(addsuffix .in,$(_helpers))
+am_out_files += $(_helpers)
+am_sys_files += $(addprefix $(pkglibexecdir)/helper-abs.d/,$(_helpers))
+
+$(outdir)/commitpkg: $(srcdir)/commitpkg.in
+ @echo "OUT $@"
+ @{ \
+ echo '#!/usr/bin/env bash'; \
+ echo '. "$$(librelib common)"'; \
+ echo '. ./PKGBUILD'; \
+ echo 'repo=$$1; arch=$$2;'; \
+ sed -n "/== 'any'/,\$$p" $<; \
+ } | install -Dm755 /dev/stdin $@
+
+$(DESTDIR)$(pkglibexecdir)/helper-abs.d/%: $(srcdir)/%
+ install -Dm755 '$<' '$@'
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/xbs-abs/archrelease.patch b/src/xbs-abs/archrelease.patch
new file mode 100644
index 0000000..bdf2deb
--- /dev/null
+++ b/src/xbs-abs/archrelease.patch
@@ -0,0 +1,8 @@
+--- archrelease.in 2014-06-20 09:42:13.367553434 -0400
++++ archrelease.ugly 2014-06-27 17:42:14.392917837 -0400
+@@ -1,4 +1,5 @@
+ #!/bin/bash
++# License: Unspecified
+
+ m4_include(lib/common.sh)
+ m4_include(lib/valid-tags.sh)
diff --git a/src/xbs-abs/helper-abs b/src/xbs-abs/helper-abs
new file mode 100755
index 0000000..d7e2499
--- /dev/null
+++ b/src/xbs-abs/helper-abs
@@ -0,0 +1,201 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# For code from db-functions (arch_svn):
+# Copyright (C) 2012 Pierre Schmitz <pierre@archlinux.de>
+# For code from db-move+db-remove (move+unrelease):
+# Copyright (C) 2008-2009 Aaron Griffin <aaronmgriffin@gmail.com>
+# Copyright (C) 2009 Abhishek Dasgupta <abhidg@gmail.com>
+# Copyright (C) 2009, 2011 Dan McGee <dan@archlinux.org>
+# Copyright (C) 2009-2012 Pierre Schmitz <pierre@archlinux.de>
+# For code just from db-move (move):
+# Copyright (C) 2011 Rémy Oudompheng <remyoudompheng@gmail.com>
+# Copyright (C) 2012 Florian Pritz <bluewind@xinu.at>
+# For code just from db-remove (unrelease):
+# Copyright (C) 2009 Eric Bélanger <snowmaniscool@gmail.com>
+# Copyright (C) 2011 Florian Pritz <bluewind@xinu.at>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+load_config() {
+ . "$(librelib conf.sh)"
+ load_files xbs-abs
+ # SVNUSER is optional
+ check_vars xbs-abs SVNDIR SVNREPOS ARCHES || exit 1
+}
+
+# This is taken from dbscripts:db-fuctions
+arch_svn() {
+ if [[ -z "${SVNUSER}" ]]; then
+ /usr/bin/svn "${@}"
+ else
+ sudo -u "${SVNUSER}" -- /usr/bin/svn --username "${USER}" "${@}"
+ fi
+}
+
+pac2svn() {
+ local pacrepo=$1
+
+ # Figure out which svn repo we need
+ local svnrepoStr
+ for svnrepoStr in "${SVNREPOS[@]}"; do
+ local svnrepoAry=($svnrepoStr)
+ local svnrepo=${svnrepoAry[0]}
+ local svnurl=${svnrepoAry[1]}
+ local pacrepos=("${svnrepoAry[@]:2}")
+
+ if in_array "$pacrepo" "${pacrepos[@]}"; then
+ echo "$svnrepo"
+ return 0
+ fi
+ done
+ return 1
+}
+
+status() {
+ load_config
+ [[ -z $(arch_svn status -q) ]]
+}
+
+download() {
+ load_config
+
+ local svnrepoStr
+ for svnrepoStr in "${SVNREPOS[@]}"; do
+ local svnrepoAry=($svnrepoStr)
+ local svnrepo=${svnrepoAry[0]}
+ local svnurl=${svnrepoAry[1]}
+ local pacrepos=("${svnrepoAry[@]:2}")
+
+ if [[ -d "$SVNDIR/$svnrepo/.svn" ]]; then
+ arch_svn -q up "$SVNDIR/$svnrepo"/*
+ else
+ # checkout non-recursive, then lazy load
+ # necessary because:
+ # > DO NOT CHECK OUT THE ENTIRE SVN REPO. Your address
+ # > may be blocked.
+ arch_svn -q checkout -N "$svnurl" "$SVNDIR/$svnrepo"
+ fi
+ done
+}
+
+release-client() {
+ local repo=$1
+ local arch=$2
+
+ # Hack to use arch_svn as 'svn' in external scripts
+ local tmpdir="$(mktemp -dt "xbs-abs-release.XXXXXXXXXX")"
+ trap "$(printf 'rm -rf -- %q' "$tmpdir")" EXIT
+ printf '%s\n' \
+ '#!/bin/bash' \
+ "$(declare -p SVNUSER 2>/dev/null)" \
+ "$(declare -f arch_svn)" \
+ 'arch_svn "$@"' \
+ > "$tmpdir/svn"
+ chmod 755 "$tmpdir/svn"
+ export PATH="$tmpdir:$PATH"
+
+ "${0}.d/archrelease" -f "${repo}-${arch}"
+ "${0}.d/commitpkg" "${repo}" "${arch}"
+}
+
+release-server() {
+ # Do nothing
+ :
+}
+
+unrelease() {
+ local pkgbase=$1
+ local repo=$2
+ local arch=$3
+
+ local tag="$repo-$arch"
+
+ load_config
+ local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}"
+ arch_svn up -q "$svndir"
+
+ # This is based off code from dbscripts:db-remove
+ arch_svn rm --force -q "${svndir}/repos/${tag}"
+ arch_svn commit -q "${svndir}" -m "${0##*/}: $pkgbase removed by $(id -un)"
+}
+
+move() {
+ local repo_from=$1
+ local repo_to=$2
+ local pkgbase=$3
+
+ load_config
+ local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}"
+ arch_svn up -q "$svndir"
+
+ local tag_list=""
+ local pkgarch
+ local arches=()
+ # this is based off code from dbscripts:db-move
+ for pkgarch in "${ARCHES[@]}" 'any'; do
+ local dir_from="${svndir}/repos/${repo_from}-${pkgarch}"
+ local dir_to="${svndir}/repos/${repo_to}-${pkgarch}"
+
+ if [ -f "${dir_from}/PKGBUILD" ]; then
+ if [ -d "${dir_to}" ]; then
+ for file in $(arch_svn ls "${dir_to}"); do
+ arch_svn rm -q "${dir_to}/$file@"
+ done
+ else
+ mkdir "${dir_to}"
+ arch_svn add -q "${dir_to}"
+ fi
+
+ local file
+ for file in $(arch_svn ls "${dir_from}"); do
+ arch_svn mv -q -r HEAD "${dir_from}/$file@" "${dir_to}/"
+ done
+ arch_svn rm --force -q "${dir_from}"
+ tag_list="$tag_list, $pkgarch"
+ arches+=("$pkgarch")
+ fi
+ done
+ tag_list="${tag_list#, }"
+ arch_svn commit -q "${svndir}" -m "${0##*/}: moved ${pkgbase} from [${repo_from}] to [${repo_to}] (${tag_list})"
+ echo "${arches[*]}"
+}
+
+releasepath() {
+ local pkgbase=$1
+ local repo=$2
+ local arch=$3
+
+ load_config
+ local svndir="${SVNDIR}/$(pac2svn "$repo")/${pkgbase}"
+ arch_svn up -q "${svndir}"
+ local releasepath="${svndir}/repos/${repo}-${arch}"
+ if [[ -f "${releasepath}/PKGBUILD" ]]; then
+ printf '%s\n' "$releasepath"
+ return 0
+ fi
+ return 1
+}
+
+name() {
+ echo SVN
+}
+
+case "$1" in
+ status|download|release-client|release-server|unrelease|move|releasepath|name) "$@";;
+ *) exit 127;;
+esac
diff --git a/src/xbs-abs/xbs-abs.conf b/src/xbs-abs/xbs-abs.conf
new file mode 100644
index 0000000..6b1585f
--- /dev/null
+++ b/src/xbs-abs/xbs-abs.conf
@@ -0,0 +1,13 @@
+SVNDIR=/var/lib/xbs-abs
+#SVNUSER=
+
+_packages_repos=(core extra testing staging {gnome,kde}-unstable)
+_community_repos=({community,multilib}{,-testing,-staging})
+
+# name url repos...
+SVNREPOS=(
+ "packages svn://svn.archlinux.org/packages ${_packages_repos[*]}"
+ "community svn://svn.archlinux.org/community ${_community_repos[*]}"
+)
+
+ARCHES=(i686 x86_64)
diff --git a/src/xbs-abslibre/Makefile b/src/xbs-abslibre/Makefile
new file mode 100644
index 0000000..c946d5d
--- /dev/null
+++ b/src/xbs-abslibre/Makefile
@@ -0,0 +1,8 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+pkglibexecdir = $(libexecdir)/xbs
+
+libretools-bins =
+libretools-libexecs = helper-abslibre
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/xbs-abslibre/helper-abslibre b/src/xbs-abslibre/helper-abslibre
new file mode 100755
index 0000000..91e7361
--- /dev/null
+++ b/src/xbs-abslibre/helper-abslibre
@@ -0,0 +1,202 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+. libremessages
+setup_traps
+set -u
+
+lockarch() {
+ local arch=$1
+ lock 9 "${ABSLIBREDEST}/${arch}.lock" \
+ "Waiting for a lock on the ABSLibre release directory for %s" "${arch}"
+}
+
+unlockarch() {
+ lock_close 9
+}
+
+checkgit() {
+ if [[ ! -d "${ABSLIBREDEST}/${arch}/.git" ]]; then
+ error 'Not a git repository: %s' "${ABSLIBREDEST}/${arch}"
+ exit 1
+ fi
+}
+
+conf() {
+ . "$(librelib conf)"
+ load_files libretools
+ check_vars libretools "$@" || exit 1
+}
+
+################################################################################
+
+# Operates on the `abslibre` format[0]. On the client/developer side,
+# release-client *copies* files from the `abslibre` tree to an
+# `abstree`[1] tree located at `devbox:${ABSLIBREDEST}/${arch}`. On
+# the server side, after those `abstree`s have been uploaded
+# (ssh/rsync), release-server *copies* them out of the `abstree` into
+# a different `abslibre` on the server, similarly located at
+# `repobox:${ABSLIBREDEST}/${arch}`.
+#
+# [0]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abslibre.md
+# [1]: https://projects.parabola.nu/packages/pbs-tools.git/tree/docs/format-abstree.md
+
+# The number of arguments and CWD constraints are enforced by the main
+# XBS program, no need to check those things here!
+
+# Args: none
+# CWD: a directory with a PKGBUILD, in an 'abslibre' tree.
+status() {
+ [[ -z $(git status -s .) ]]
+}
+
+# Args: none
+# CWD: anywhere
+# Host: developer box
+download() {
+ conf WORKDIR ABSLIBRERECV ABSLIBRESEND
+
+ gitget -f -p "$ABSLIBRESEND" checkout "$ABSLIBRERECV" "$WORKDIR/abslibre"
+}
+
+# Args: REPO ARCH
+# CWD: a directory with a PKGBUILD, in an 'abslibre' tree.
+# Host: developer box
+release-client() {
+ local repo=$1
+ local arch=$2
+
+ conf ABSLIBREDEST
+ local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")"
+ local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}"
+ lockarch "$arch"
+
+ if [[ -e $pkgdir ]]; then
+ rm -rf -- "$pkgdir"
+ fi
+
+ mkdir -p -- "$pkgdir"
+ git ls-files -z | xargs -0 -I{} cp -- {} "$pkgdir"
+}
+
+# Args: REPO ARCH
+# CWD: a directory with a PKGBUILD, outside of ${ABSLIBREDEST}
+# Host: repo
+release-server() {
+ local repo=$1
+ local arch=$2
+
+ conf ABSLIBREDEST
+ local pkgbase="$(load_PKGBUILD >/dev/null; printf '%s\n' "${pkgbase:-${pkgname}}")"
+ local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}"
+ lockarch "$arch"
+ checkgit
+
+ if [[ -e $pkgdir ]]; then
+ rm -rf -- "$pkgdir"
+ fi
+
+ mkdir -p -- "$pkgdir"
+ cp -- * "$pkgdir"
+
+ cd "$pkgdir"
+ git add .
+ git commit -q -m "xbs-abslibre: Release ${repo}/${pkgbase} for ${arch} (by $(id -un))"
+}
+
+# Args: PKGBASE REPO ARCH
+# CWD: anywhere
+# Host: repo
+unrelease() {
+ local pkgbase=$1
+ local repo=$2
+ local arch=$3
+
+ conf ABSLIBREDEST
+ local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}"
+ lockarch "$arch"
+ checkgit
+
+ if [[ -f "${pkgdir}/PKGBUILD" ]]; then
+ git rm -qrf -- "$pkgdir"
+ git commit -q -m "xbs-abslibre: Remove ${repo}/${pkgbase} from ${arch} (by $(id -un))"
+ fi
+}
+
+# Args: FROMREPO TOREPO PKGBASE
+# CWD: anywhere
+# Host: repo
+move() {
+ local repo_from=$1
+ local repo_to=$2
+ local pkgbase=$3
+
+ conf ABSLIBREDEST ARCHES
+
+ # Execute each iteration in a subshell so that 'checkgit'
+ # bailing for an architecture doesn't abort the entire thing.
+ local arch
+ for arch in "${ARCHES[@]}" any; do (
+ lockarch "$arch"
+ checkgit
+
+ local dir_from="${ABSLIBREDEST}/${arch}/${repo_from}/${pkgbase}"
+ local dir_to="${ABSLIBREDEST}/${arch}/${repo_to}/${pkgbase}"
+
+ if [[ -f "${dir_from}/PKGBUILD" ]]; then
+ if [[ -e "${dir_to}" ]]; then
+ git rm -qrf -- "$dir_to"
+ fi
+ mkdir -p -- "${dir_to%/*}"
+ git mv -- "$dir_from" "$dir_to"
+ git commit -q -m "xbs-abslibre: Move ${pkgbase} from ${repo_from} to ${repo_to} on ${arch} (by $(id -un))"
+ fi
+ unlock arch
+ ) done
+}
+
+# Args: PKGBASE REPO ARCH
+# CWD: anywhere
+# Host: repo
+releasepath() {
+ local pkgbase=$1
+ local repo=$2
+ local arch=$3
+
+ conf ABSLIBREDEST
+ local pkgdir="${ABSLIBREDEST}/${arch}/${repo}/${pkgbase}"
+
+ lockarch "$arch"
+
+ if [[ -f "${pkgdir}/PKGBUILD" ]]; then
+ printf '%s\n' "$pkgdir"
+ return 0
+ fi
+
+ return 1
+}
+
+name() {
+ echo ABSLibre
+}
+
+case "$1" in
+ status|download|release-client|release-server|unrelease|move|releasepath|name) "$@" || exit $?;;
+ *) exit 127;;
+esac
diff --git a/src/xbs/Makefile b/src/xbs/Makefile
new file mode 100644
index 0000000..974586e
--- /dev/null
+++ b/src/xbs/Makefile
@@ -0,0 +1,5 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+pkgconfdir = $(sysconfdir)/xbs
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/xbs/xbs b/src/xbs/xbs
new file mode 100755
index 0000000..6319110
--- /dev/null
+++ b/src/xbs/xbs
@@ -0,0 +1,186 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2013-2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 2 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 <http://www.gnu.org/licenses/>.
+
+default_libdir=/usr/lib/xbs
+
+. libremessages
+. "$(librelib conf)"
+
+errusage() {
+ if [[ $# -gt 0 ]]; then
+ error "$@"
+ fi
+ usage >&2
+ exit 1
+}
+
+usage() {
+ print 'Usage: %s [-b BUILDSYSTEM|-h] COMMAND [ARGUMENTS]' "${0##*/}"
+ print 'Tool for working with arbitrary ABS-like build systems'
+ echo
+ prose 'This is a pluggable tool. The BUILDSYSTEM it uses is
+ configured in:'
+ bullet '/etc/xbs.conf'
+ bullet '${XDG_CONFIG_HOME}/xbs.conf'
+ bullet 'with the `-b` flag'
+ prose 'Later items take precedence over earlier ones.'
+ echo
+ prose 'It looks for a helper programs named helper-${BUILDSYSTEM}, in
+ the directory `%q` by default, but this directory can be changed
+ with the environmental variable XBS_LIBDIR.' "$default_libdir"
+ echo
+ print 'Options:'
+ flag "-b $(_ BUILDSYSTEM)" 'Use BUILDSYSTEM instead of the one
+ configured in xbs.conf' \
+ '-h' 'Show this message'
+ echo
+ prose "Whether a command is intended for use on a developer's box or on
+ the server is indicated by (dev), (srv), or (any) before the
+ command"
+ echo
+ print 'Commands:'
+ flag "$(_ '(dev) status')" \
+ 'Are there uncommitted changes in `.`?' \
+ "$(_ '(dev) download')" \
+ 'Download or update the tree' \
+ "$(_ '(dev) release-client REPO ARCH')" \
+ 'Release `.` (for developer boxes)' \
+ "$(_ '(srv) release-server REPO ARCH')" \
+ 'Release `.` (for server boxes)' \
+ "$(_ '(srv) unrelease PKGBASE REPO ARCH')" \
+ 'Unrelease a pkgbase' \
+ "$(_ '(srv) move FROMREPO TOREPO PKGBASE')" \
+ 'Move a pkgbase from one repo to another' \
+ "$(_ '(srv) releasepath PKGBASE REPO ARCH')" \
+ 'Print the path to the staged version of pkgbase, or exit with
+ non-zero if not released' \
+ "$(_ '(any) name')" \
+ 'Print a human-friendly version of the BUILDSYSTEM name' \
+ "$(_ '(any) help')" \
+ 'Show this message'
+}
+
+status() {
+ if [[ ! -f PKGBUILD ]]; then
+ error 'PKGBUILD not found'
+ # Though in this file in general it doesn't matter, in
+ # this case it does: Using "exit" instead of "return"
+ # is imporant because it prevents flow returning to
+ # release-client.
+ exit 1
+ fi
+ "$HELPER" status "$@"
+}
+
+download() {
+ "$HELPER" download "$@"
+}
+
+release-client() {
+ if ! status; then
+ error 'You have not committed your changes yet!'
+ exit 1
+ fi
+ "$HELPER" release-client "$@"
+}
+
+release-server() {
+ if [[ ! -f PKGBUILD ]]; then
+ error 'PKGBUILD not found'
+ exit 1
+ fi
+ "$HELPER" release-server "$@"
+}
+
+unrelease() {
+ "$HELPER" unrelease "$@"
+}
+
+move() {
+ "$HELPER" move "$@"
+}
+
+releasepath() {
+ "$HELPER" releasepath "$@"
+}
+
+help() {
+ usage
+}
+
+name() {
+ "$HELPER" name "$@"
+}
+
+main() {
+ BUILDSYSTEM=''
+ while getopts 'b:h' arg; do
+ case $arg in
+ b) BUILDSYSTEM=$OPTARG;;
+ h) usage; return 0;;
+ *) errusage;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+
+ if [[ -z $BUILDSYSTEM ]]; then
+ load_files xbs || return 1
+ check_vars xbs BUILDSYSTEM || {
+ prose 'or specify the `-b` flag.' >&2
+ return 1
+ }
+ fi
+
+ if [[ -z $XBS_LIBDIR ]]; then
+ export XBS_LIBDIR=$default_libdir
+ fi
+
+ HELPER="${XBS_LIBDIR}/helper-${BUILDSYSTEM}"
+ if [[ ! -x "$HELPER" ]]; then
+ error 'No helper for build system found: %s' "$BUILDSYSTEM"
+ return 1;
+ fi
+
+ if [[ $# -lt 1 ]]; then
+ errusage "Must specify a command"
+ fi
+
+ if [[ -w / ]]; then
+ error 'Run as a normal user'
+ fi
+
+ local cmd=$1; shift
+ case "$cmd" in
+ status|download|name|help)
+ [[ $# -eq 0 ]] || errusage 'bad number of argments'
+ "$cmd" "$@"
+ ;;
+ release-client|release-server)
+ [[ $# -eq 2 ]] || errusage 'bad number of argments'
+ "$cmd" "$@"
+ ;;
+ move|unrelease|releasepath)
+ [[ $# -eq 3 ]] || errusage 'bad number of argments'
+ "$cmd" "$@"
+ ;;
+ *) errusage 'unknown command: %s' "$cmd";;
+ esac
+}
+
+main "$@"
diff --git a/src/xbs/xbs.conf b/src/xbs/xbs.conf
new file mode 100644
index 0000000..556c133
--- /dev/null
+++ b/src/xbs/xbs.conf
@@ -0,0 +1 @@
+BUILDSYSTEM=abslibre