#!/bin/bash
#
#   makepkg - make packages compatible for use with pacman
#   @configure_input@
#
#   Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
#   Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
#   Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
#   Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org>
#   Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
#   Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk>
#   Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org>
#
#   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/>.
#

# makepkg uses quite a few external programs during its execution. You
# need to have at least the following installed for makepkg to function:
#   awk, bsdtar (libarchive), bzip2, coreutils, fakeroot, file, find (findutils),
#   gettext, gpg, grep, gzip, openssl, sed, tput (ncurses), xz

# gettext initialization
export TEXTDOMAIN='pacman-scripts'
export TEXTDOMAINDIR='@localedir@'

# file -i does not work on Mac OSX unless legacy mode is set
export COMMAND_MODE='legacy'
# Ensure CDPATH doesn't screw with our cd calls
unset CDPATH

declare -r makepkg_version='@PACKAGE_VERSION@'
declare -r confdir='@sysconfdir@'
declare -r BUILDSCRIPT='@BUILDSCRIPT@'
declare -r startdir="$PWD"

packaging_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'purge' 'upx')
other_options=('ccache' 'distcc' 'buildflags' 'makeflags')
splitpkg_overrides=('pkgver' 'pkgrel' 'epoch' 'pkgdesc' 'arch' 'url' 'license' \
                    'groups' 'depends' 'optdepends' 'provides' 'conflicts' \
                    'replaces' 'backup' 'options' 'install' 'changelog')
readonly -a packaging_options other_options splitpkg_overrides

# Options
ASDEPS=0
ASROOT=0
CLEANUP=0
DEP_BIN=0
FORCE=0
INFAKEROOT=0
GENINTEG=0
HOLDVER=0
SKIPCHECKSUMS=0
SKIPPGPCHECK=0
INSTALL=0
NOBUILD=0
NODEPS=0
NOEXTRACT=0
RMDEPS=0
REPKG=0
LOGGING=0
SOURCEONLY=0
IGNOREARCH=0
PREPAREFUNC=0
BUILDFUNC=0
CHECKFUNC=0
PKGFUNC=0
PKGVERFUNC=0
SPLITPKG=0
PKGLIST=()
SIGNPKG=''

# Forces the pkgver of the current PKGBUILD. Used by the fakeroot call
# when dealing with svn/cvs/etc PKGBUILDs.
FORCE_VER=""

PACMAN_OPTS=

shopt -s extglob

### SUBROUTINES ###

plain() {
	local mesg=$1; shift
	printf "${BOLD}    ${mesg}${ALL_OFF}\n" "$@" >&2
}

msg() {
	local mesg=$1; shift
	printf "${GREEN}==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}

msg2() {
	local mesg=$1; shift
	printf "${BLUE}  ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}

warning() {
	local mesg=$1; shift
	printf "${YELLOW}==> $(gettext "WARNING:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}

error() {
	local mesg=$1; shift
	printf "${RED}==> $(gettext "ERROR:")${ALL_OFF}${BOLD} ${mesg}${ALL_OFF}\n" "$@" >&2
}


##
# Special exit call for traps, Don't print any error messages when inside,
# the fakeroot call, the error message will be printed by the main call.
##
trap_exit() {
	local signal=$1; shift

	if (( ! INFAKEROOT )); then
		echo
		error "$@"
	fi
	[[ -n $srclinks ]] && rm -rf "$srclinks"

	# unset the trap for this signal, and then call the default handler
	trap -- "$signal"
	kill "-$signal" "$$"
}


##
# Clean up function. Called automatically when the script exits.
##
clean_up() {
	local EXIT_CODE=$?

	if (( INFAKEROOT )); then
		# Don't clean up when leaving fakeroot, we're not done yet.
		return
	fi

	if (( ! EXIT_CODE && CLEANUP )); then
		local pkg file

		# If it's a clean exit and -c/--clean has been passed...
		msg "$(gettext "Cleaning up...")"
		rm -rf "$pkgdir" "$srcdir"
		if [[ -n $pkgbase ]]; then
			local fullver=$(get_full_version)
			# Can't do this unless the BUILDSCRIPT has been sourced.
			if (( BUILDFUNC )); then
				rm -f "${pkgbase}-${fullver}-${CARCH}-build.log"*
			fi
			if (( CHECKFUNC )); then
				rm -f "${pkgbase}-${fullver}-${CARCH}-check.log"*
			fi
			if (( PKGFUNC )); then
				rm -f "${pkgbase}-${fullver}-${CARCH}-package.log"*
			elif (( SPLITPKG )); then
				for pkg in ${pkgname[@]}; do
					rm -f "${pkgbase}-${fullver}-${CARCH}-package_${pkg}.log"*
				done
			fi

			# clean up dangling symlinks to packages
			for pkg in ${pkgname[@]}; do
				for file in ${pkg}-*-*-*{${PKGEXT},${SRCEXT}}; do
					if [[ -h $file && ! -e $file ]]; then
						rm -f "$file"
					fi
				done
			done
		fi
	fi

	remove_deps
}


enter_fakeroot() {
	msg "$(gettext "Entering %s environment...")" "fakeroot"
	fakeroot -- $0 -F "${ARGLIST[@]}" || exit $?
}


# a source entry can have two forms :
# 1) "filename::http://path/to/file"
# 2) "http://path/to/file"

# Return the absolute filename of a source entry
get_filepath() {
	local file="$(get_filename "$1")"
	local proto="$(get_protocol "$1")"

	case $proto in
		bzr*|git*|hg*|svn*)
			if [[ -d "$startdir/$file" ]]; then
				file="$startdir/$file"
			elif [[ -d "$SRCDEST/$file" ]]; then
				file="$SRCDEST/$file"
			else
				return 1
			fi
			;;
		*)
			if [[ -f "$startdir/$file" ]]; then
				file="$startdir/$file"
			elif [[ -f "$SRCDEST/$file" ]]; then
				file="$SRCDEST/$file"
			else
				return 1
			fi
			;;
	esac

	printf "%s\n" "$file"
}

# extract the filename from a source entry
get_filename() {
	local netfile=$1

	# if a filename is specified, use it
	if [[ $netfile = *::* ]]; then
		printf "%s\n" ${netfile%%::*}
		return
	fi

	local proto=$(get_protocol "$netfile")

	case $proto in
		bzr*|git*|hg*|svn*)
			filename=${netfile%%#*}
			filename=${filename%/}
			filename=${filename##*/}
			if [[ $proto = git* ]]; then
				filename=${filename%%.git*}
			fi
			;;
		*)
			# if it is just an URL, we only keep the last component
			filename="${netfile##*/}"
			;;
	esac
	printf "%s\n" "${filename}"
}

# extract the URL from a source entry
get_url() {
	# strip an eventual filename
	printf "%s\n" "${1#*::}"
}

# extract the protocol from a source entry - return "local" for local sources
get_protocol() {
	if [[ $1 = *://* ]]; then
		# strip leading filename
		local proto="${1##*::}"
		printf "%s\n" "${proto%%://*}"
	else
		printf "%s\n" local
	fi
}

get_downloadclient() {
	local proto=$1

	# loop through DOWNLOAD_AGENTS variable looking for protocol
	local i
	for i in "${DLAGENTS[@]}"; do
		local handler="${i%%::*}"
		if [[ $proto = "$handler" ]]; then
			local agent="${i##*::}"
			break
		fi
	done

	# if we didn't find an agent, return an error
	if [[ -z $agent ]]; then
		error "$(gettext "Unknown download protocol: %s")" "$proto"
		plain "$(gettext "Aborting...")"
		exit 1 # $E_CONFIG_ERROR
	fi

	# ensure specified program is installed
	local program="${agent%% *}"
	if [[ ! -x $program ]]; then
		local baseprog="${program##*/}"
		error "$(gettext "The download program %s is not installed.")" "$baseprog"
		plain "$(gettext "Aborting...")"
		exit 1 # $E_MISSING_PROGRAM
	fi

	printf "%s\n" "$agent"
}

download_local() {
	local netfile=$1
	local filepath=$(get_filepath "$netfile")

	if [[ -n "$filepath" ]]; then
		msg2 "$(gettext "Found %s")" "${filepath##*/}"
		rm -f "$srcdir/${filepath##*/}"
		ln -s "$filepath" "$srcdir/"
		continue
	else
		local filename=$(get_filename "$netfile")
		error "$(gettext "%s was not found in the build directory and is not a URL.")" "$filename"
		exit 1 # $E_MISSING_FILE
	fi
}

download_file() {
	local netfile=$1

	local filepath=$(get_filepath "$netfile")
	if [[ -n "$filepath" ]]; then
		msg2 "$(gettext "Found %s")" "${filepath##*/}"
		rm -f "$srcdir/${filepath##*/}"
		ln -s "$filepath" "$srcdir/"
		return
	fi

	local proto=$(get_protocol "$netfile")

	# find the client we should use for this URL
	local dlcmd
	dlcmd=$(get_downloadclient "$proto") || exit $?

	local filename=$(get_filename "$netfile")
	local url=$(get_url "$netfile")

	if [[ $proto = "scp" ]]; then
		# scp downloads should not pass the protocol in the url
		url="${url##*://}"
	fi

	msg2 "$(gettext "Downloading %s...")" "$filename"

	# temporary download file, default to last component of the URL
	local dlfile="${url##*/}"

	# replace %o by the temporary dlfile if it exists
	if [[ $dlcmd = *%o* ]]; then
		dlcmd=${dlcmd//\%o/\"$filename.part\"}
		dlfile="$filename.part"
	fi
	# add the URL, either in place of %u or at the end
	if [[ $dlcmd = *%u* ]]; then
		dlcmd=${dlcmd//\%u/\"$url\"}
	else
		dlcmd="$dlcmd \"$url\""
	fi

	local ret=0
	eval "$dlcmd || ret=\$?"
	if (( ret )); then
		[[ ! -s $dlfile ]] && rm -f -- "$dlfile"
		error "$(gettext "Failure while downloading %s")" "$filename"
		plain "$(gettext "Aborting...")"
		exit 1
	fi

	# rename the temporary download file to the final destination
	if [[ $dlfile != "$filename" ]]; then
		mv -f "$SRCDEST/$dlfile" "$SRCDEST/$filename"
	fi

	rm -f "$srcdir/$filename"
	ln -s "$SRCDEST/$filename" "$srcdir/"
}

download_bzr() {
	local netfile=$1

	local url=$(get_url "$netfile")
	url=${url##*bzr+}
	url=${url%%#*}

	local fragment=${netfile#*#}
	if [[ $fragment = "$netfile" ]]; then
		unset fragment
	fi

	local displaylocation="$url"
	local revision=('-r-1')

	if [[ -n $fragment ]]; then
		case ${fragment%%=*} in
			revision)
				revision=("-r${fragment##*=}")
				displaylocation="$url -r ${fragment##*=}"
				;;
			*)
				error "$(gettext "Unrecognized reference: %s")" "${fragment}"
				plain "$(gettext "Aborting...")"
				exit 1
		esac
	fi

	local dir=$(get_filepath "$netfile")
	[[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")"

	if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then
		msg2 "$(gettext "Branching %s ...")" "${displaylocation}"
		if ! bzr branch "$url" "$dir" ${revision[@]} --no-tree --use-existing-dir; then
			error "$(gettext "Failure while branching %s")" "${displaylocation}"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	elif (( ! HOLDVER )); then
		# Make sure we are fetching the right repo
		if [[ "$url" != "$(bzr config parent_location -d $dir)"  ]] ; then
			error "$(gettext "%s is not a branch of %s")" "$dir" "$url"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
		msg2 "$(gettext "Pulling %s ...")" "${displaylocation}"
		cd_safe "$dir"
		if ! bzr pull "$url" ${revision[@]} --overwrite; then
			# only warn on failure to allow offline builds
			warning "$(gettext "Failure while pulling %s")" "${displaylocation}"
		fi
	fi

	msg2 "$(gettext "Creating working copy of %s %s repo...")" "${dir}" "bzr"
	pushd "$srcdir" &>/dev/null
	rm -rf "${dir##*/}"

	if ! bzr checkout "$dir" --lightweight; then
		error "$(gettext "Failure while creating working copy of %s %s repo")" "${dir}" "bzr"
		plain "$(gettext "Aborting...")"
		exit 1
	fi

	popd &>/dev/null
}

download_git() {
	local netfile=$1

	local fragment=${netfile#*#}
	if [[ $fragment = "$netfile" ]]; then
		unset fragment
	fi

	local dir=$(get_filepath "$netfile")
	[[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")"

	local repo=${netfile##*/}
	repo=${repo%%#*}
	repo=${repo%%.git*}

	local url=$(get_url "$netfile")
	url=${url##*git+}
	url=${url%%#*}

	if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then
		msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "git"
		if ! git clone --mirror "$url" "$dir"; then
			error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "git"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	elif (( ! HOLDVER )); then
		cd_safe "$dir"
		# Make sure we are fetching the right repo
		if [[ "$url" != "$(git config --get remote.origin.url)"  ]] ; then
			error "$(gettext "%s is not a clone of %s")" "$dir" "$url"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
		msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "git"
		if ! git fetch --all -p; then
			# only warn on failure to allow offline builds
			warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "git"
		fi
	fi

	msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "git"
	pushd "$srcdir" &>/dev/null
	rm -rf "${dir##*/}"

	if ! git clone "$dir"; then
		error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git"
		plain "$(gettext "Aborting...")"
		exit 1
	fi

	cd_safe "${dir##*/}"

	local ref
	if [[ -n $fragment ]]; then
		case ${fragment%%=*} in
			commit|tag)
				ref=${fragment##*=}
				;;
			branch)
				ref=origin/${fragment##*=}
				;;
			*)
				error "$(gettext "Unrecognized reference: %s")" "${fragment}"
				plain "$(gettext "Aborting...")"
				exit 1
		esac
	fi

	if [[ -n $ref ]]; then
		if ! git checkout -b makepkg $ref; then
			error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "git"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	fi

	popd &>/dev/null
}

download_hg() {
	local netfile=$1

	local fragment=${netfile#*#}
	if [[ $fragment = "$netfile" ]]; then
		unset fragment
	fi

	local dir=$(get_filepath "$netfile")
	[[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")"

	local repo=${netfile##*/}
	repo=${repo%%#*}

	local url=$(get_url "$netfile")
	url=${url##*hg+}
	url=${url%%#*}

	if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then
		msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "hg"
		if ! hg clone -U "$url" "$dir"; then
			error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "hg"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	elif (( ! HOLDVER )); then
		msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "hg"
		cd_safe "$dir"
		if ! hg pull; then
			# only warn on failure to allow offline builds
			warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "hg"
		fi
	fi

	msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "hg"
	pushd "$srcdir" &>/dev/null
	rm -rf "${dir##*/}"

	local ref
	if [[ -n $fragment ]]; then
		case ${fragment%%=*} in
			branch|revision|tag)
				ref=('-u' "${fragment##*=}")
				;;
			*)
				error "$(gettext "Unrecognized reference: %s")" "${fragment}"
				plain "$(gettext "Aborting...")"
				exit 1
		esac
	fi

	if ! hg clone "${ref[@]}" "$dir" "${dir##*/}"; then
		error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "hg"
		plain "$(gettext "Aborting...")"
		exit 1
	fi

	popd &>/dev/null
}

download_svn() {
	local netfile=$1

	local fragment=${netfile#*#}
	if [[ $fragment = "$netfile" ]]; then
		unset fragment
	fi

	local dir=$(get_filepath "$netfile")
	[[ -z "$dir" ]] && dir="$SRCDEST/$(get_filename "$netfile")"

	local repo=${netfile##*/}
	repo=${repo%%#*}

	local url=$(get_url "$netfile")
	if [[ $url != svn+ssh* ]]; then
		url=${url##*svn+}
	fi
	url=${url%%#*}

	if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then
		msg2 "$(gettext "Cloning %s %s repo...")" "${repo}" "svn"
		if ! svn checkout --config-dir "$dir" "$url" "$dir"; then
			error "$(gettext "Failure while downloading %s %s repo")" "${repo}" "svn"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	elif (( ! HOLDVER )); then
		msg2 "$(gettext "Updating %s %s repo...")" "${repo}" "svn"
		cd_safe "$dir"
		if ! svn update; then
			# only warn on failure to allow offline builds
			warning "$(gettext "Failure while updating %s %s repo")" "${repo}" "svn"
		fi
	fi

	msg2 "$(gettext "Creating working copy of %s %s repo...")" "${repo}" "svn"
	pushd "$srcdir" &>/dev/null
	rm -rf "${dir##*/}"

	local ref
	if [[ -n $fragment ]]; then
		case ${fragment%%=*} in
			revision)
				ref=('-r' "${fragment##*=}")
				;;
			*)
				error "$(gettext "Unrecognized reference: %s")" "${fragment}"
				plain "$(gettext "Aborting...")"
				exit 1
		esac
	fi

	if ! svn export ${ref[@]} "$dir"; then
		error "$(gettext "Failure while creating working copy of %s %s repo")" "${repo}" "svn"
		plain "$(gettext "Aborting...")"
	fi

	popd &>/dev/null
}

download_sources() {
	msg "$(gettext "Retrieving Sources...")"

	local GET_VCS=1
	if [[ $1 == "fast" ]]; then
		GET_VCS=0
	fi

	pushd "$SRCDEST" &>/dev/null

	local netfile
	for netfile in "${source[@]}"; do
		local proto=$(get_protocol "$netfile")

		case "$proto" in
			local)
				download_local "$netfile"
				;;
			bzr*)
				(( GET_VCS )) && download_bzr "$netfile"
				;;
			git*)
				(( GET_VCS )) && download_git "$netfile"
				;;
			hg*)
				(( GET_VCS )) && download_hg "$netfile"
				;;
			svn*)
				(( GET_VCS )) && download_svn "$netfile"
				;;
			*)
				download_file "$netfile"
				;;
		esac
	done

	if (( PKGVERFUNC && GET_VCS )); then
		update_pkgver
		check_pkgver || exit 1
		check_build_status
	fi

	popd &>/dev/null
}

# Automatically update pkgver variable if a pkgver() function is provided
# Re-sources the PKGBUILD afterwards to allow for other variables that use $pkgver
update_pkgver() {
	newpkgver=$(run_function_safe pkgver)

	if [[ -n $newpkgver && $newpkgver != "$pkgver" ]]; then
		if [[ -f $BUILDFILE && -w $BUILDFILE ]]; then
			@SEDINPLACE@ "s/^pkgver=[^ ]*/pkgver=$newpkgver/" "$BUILDFILE"
			@SEDINPLACE@ "s/^pkgrel=[^ ]*/pkgrel=1/" "$BUILDFILE"
			source "$BUILDFILE"
		else
			warning "$(gettext "%s is not writeable -- pkgver will not be updated")" \
					"$BUILDFILE"
		fi
	fi
}

# Print 'source not found' error message and exit makepkg
missing_source_file() {
	error "$(gettext "Unable to find source file %s.")" "$(get_filename "$1")"
	plain "$(gettext "Aborting...")"
	exit 1 # $E_MISSING_FILE
}

##
#  usage : get_full_version( [$pkgname] )
# return : full version spec, including epoch (if necessary), pkgver, pkgrel
##
get_full_version() {
	if [[ -z $1 ]]; then
		if [[ $epoch ]] && (( ! $epoch )); then
			printf "%s\n" "$pkgver-$pkgrel"
		else
			printf "%s\n" "$epoch:$pkgver-$pkgrel"
		fi
	else
		for i in pkgver pkgrel epoch; do
			local indirect="${i}_override"
			eval $(declare -f package_$1 | sed -n "s/\(^[[:space:]]*$i=\)/${i}_override=/p")
			[[ -z ${!indirect} ]] && eval ${indirect}=\"${!i}\"
		done
		if (( ! $epoch_override )); then
			printf "%s\n" "$pkgver_override-$pkgrel_override"
		else
			printf "%s\n" "$epoch_override:$pkgver_override-$pkgrel_override"
		fi
	fi
}

##
#  usage : get_pkg_arch( [$pkgname] )
# return : architecture of the package
##
get_pkg_arch() {
	if [[ -z $1 ]]; then
		if [[ $arch = "any" ]]; then
			printf "%s\n" "any"
		else
			printf "%s\n" "$CARCH"
		fi
	else
		local arch_override
		eval $(declare -f package_$1 | sed -n 's/\(^[[:space:]]*arch=\)/arch_override=/p')
		(( ${#arch_override[@]} == 0 )) && arch_override=("${arch[@]}")
		if [[ $arch_override = "any" ]]; then
			printf "%s\n" "any"
		else
			printf "%s\n" "$CARCH"
		fi
	fi
}

##
# Checks to see if options are present in makepkg.conf or PKGBUILD;
# PKGBUILD options always take precedence.
#
#  usage : check_option( $option, $expected_val )
# return : 0   - matches expected
#          1   - does not match expected
#          127 - not found
##
check_option() {
	in_opt_array "$1" ${options[@]}
	case $? in
		0) # assert enabled
			[[ $2 = y ]]
			return ;;
		1) # assert disabled
			[[ $2 = n ]]
			return
	esac

	# fall back to makepkg.conf options
	in_opt_array "$1" ${OPTIONS[@]}
	case $? in
		0) # assert enabled
			[[ $2 = y ]]
			return ;;
		1) # assert disabled
			[[ $2 = n ]]
			return
	esac

	# not found
	return 127
}


##
# Check if option is present in BUILDENV
#
#  usage : check_buildenv( $option, $expected_val )
# return : 0   - matches expected
#          1   - does not match expected
#          127 - not found
##
check_buildenv() {
	in_opt_array "$1" ${BUILDENV[@]}
	case $? in
		0) # assert enabled
			[[ $2 = "y" ]]
			return ;;
		1) # assert disabled
			[[ $2 = "n" ]]
			return ;;
	esac

	# not found
	return 127
}


##
#  usage : in_opt_array( $needle, $haystack )
# return : 0   - enabled
#          1   - disabled
#          127 - not found
##
in_opt_array() {
	local needle=$1; shift

	local opt
	for opt in "$@"; do
		if [[ $opt = "$needle" ]]; then
			# enabled
			return 0
		elif [[ $opt = "!$needle" ]]; then
			# disabled
			return 1
		fi
	done

	# not found
	return 127
}


##
#  usage : in_array( $needle, $haystack )
# return : 0 - found
#          1 - not found
##
in_array() {
	local needle=$1; shift
	local item
	for item in "$@"; do
		[[ $item = "$needle" ]] && return 0 # Found
	done
	return 1 # Not Found
}

source_has_signatures() {
	local file
	for file in "${source[@]}"; do
		if [[ ${file%%::*} = *.@(sig?(n)|asc) ]]; then
			return 0
		fi
	done
	return 1
}

run_pacman() {
	local cmd
	if [[ ! $1 = -@(T|Qq) ]]; then
		cmd=("$PACMAN" $PACMAN_OPTS "$@")
	else
		cmd=("$PACMAN" "$@")
	fi
	if (( ! ASROOT )) && [[ ! $1 = -@(T|Qq) ]]; then
		if type -p sudo >/dev/null; then
			cmd=(sudo "${cmd[@]}")
		else
			cmd=(su root -c "$(printf '%q ' "${cmd[@]}")")
		fi
	fi
	"${cmd[@]}"
}

check_deps() {
	(( $# > 0 )) || return 0

	local ret=0
	local pmout
	pmout=$(run_pacman -T "$@")
	ret=$?

	if (( ret == 127 )); then #unresolved deps
		printf "%s\n" "$pmout"
	elif (( ret )); then
		error "$(gettext "'%s' returned a fatal error (%i): %s")" "$PACMAN" "$ret" "$pmout"
		return "$ret"
	fi
}

handle_deps() {
	local R_DEPS_SATISFIED=0
	local R_DEPS_MISSING=1

	(( $# == 0 )) && return $R_DEPS_SATISFIED

	local deplist="$*"

	if (( ! DEP_BIN )); then
		return $R_DEPS_MISSING
	fi

	if (( DEP_BIN )); then
		# install missing deps from binary packages (using pacman -S)
		msg "$(gettext "Installing missing dependencies...")"

		if ! run_pacman -S --asdeps $deplist; then
			error "$(gettext "'%s' failed to install missing dependencies.")" "$PACMAN"
			exit 1 # TODO: error code
		fi
	fi

	# we might need the new system environment
	# save our shell options and turn off extglob
	local shellopts=$(shopt -p)
	shopt -u extglob
	source /etc/profile &>/dev/null
	eval "$shellopts"

	return $R_DEPS_SATISFIED
}

resolve_deps() {
	local R_DEPS_SATISFIED=0
	local R_DEPS_MISSING=1

	# deplist cannot be declared like this: local deplist=$(foo)
	# Otherwise, the return value will depend on the assignment.
	local deplist
	deplist="$(set +E; check_deps $*)" || exit 1
	[[ -z $deplist ]] && return $R_DEPS_SATISFIED

	if handle_deps $deplist; then
		# check deps again to make sure they were resolved
		deplist="$(set +E; check_deps $*)" || exit 1
		[[ -z $deplist ]] && return $R_DEPS_SATISFIED
	fi

	msg "$(gettext "Missing Dependencies:")"
	local dep
	for dep in $deplist; do
		msg2 "$dep"
	done

	return $R_DEPS_MISSING
}

remove_deps() {
	(( ! RMDEPS )) && return

	# check for packages removed during dependency install (e.g. due to conflicts)
	# removing all installed packages is risky in this case
	if [[ -n $(grep -xvFf <(printf '%s\n' "${current_packagelist[@]}") \
			<(printf '%s\n' "${original_packagelist[@]}") || true) ]]; then
		warning "$(gettext "Failed to remove installed dependencies.")"
		return 0
	fi

	local deplist
	deplist=($(grep -xvFf <(printf "%s\n" "${original_pkglist[@]}") \
			<(printf "%s\n" "${current_pkglist[@]}") || true))
	if [[ -z $deplist ]]; then
		return
	fi

	msg "Removing installed dependencies..."
	# exit cleanly on failure to remove deps as package has been built successfully
	if ! run_pacman -Rn ${deplist[@]}; then
		warning "$(gettext "Failed to remove installed dependencies.")"
		return 0
	fi
}

get_integlist() {
	local integ
	local integlist=()

	for integ in md5 sha1 sha256 sha384 sha512; do
		local integrity_sums=($(eval echo "\${${integ}sums[@]}"))
		if [[ -n "$integrity_sums" ]]; then
			integlist=(${integlist[@]} $integ)
		fi
	done

	if (( ${#integlist[@]} > 0 )); then
		printf "%s\n" "${integlist[@]}"
	else
		printf "%s\n" "${INTEGRITY_CHECK[@]}"
	fi
}

generate_checksums() {
	msg "$(gettext "Generating checksums for source files...")"

	if ! type -p openssl >/dev/null; then
		error "$(gettext "Cannot find the %s binary required for generating sourcefile checksums.")" "openssl"
		exit 1 # $E_MISSING_PROGRAM
	fi

	local integlist
	if (( $# == 0 )); then
		integlist=$(get_integlist)
	else
		integlist=$@
	fi

	local integ
	for integ in ${integlist[@]}; do
		case "$integ" in
			md5|sha1|sha256|sha384|sha512) : ;;
			*)
				error "$(gettext "Invalid integrity algorithm '%s' specified.")" "$integ"
				exit 1;; # $E_CONFIG_ERROR
		esac

		local ct=0
		local numsrc=${#source[@]}
		printf "%s" "${integ}sums=("

		local i
		local indent=''
		for (( i = 0; i < ${#integ} + 6; i++ )); do
			indent="$indent "
		done

		local netfile
		for netfile in "${source[@]}"; do
			local proto sum
			proto="$(get_protocol "$netfile")"

			case $proto in
				bzr*|git*|hg*|svn*)
					sum="SKIP"
					;;
				*)
					local file
					file="$(get_filepath "$netfile")" || missing_source_file "$netfile"
					sum="$(openssl dgst -${integ} "$file")"
					sum=${sum##* }
					;;
			esac

			(( ct )) && printf "%s" "$indent"
			printf "%s" "'$sum'"
			ct=$(($ct+1))
			(( $ct < $numsrc )) && echo
		done

		echo ")"
	done
}

check_checksums() {
	(( SKIPCHECKSUMS )) && return 0
	(( ! ${#source[@]} )) && return 0

	local correlation=0
	local integ required
	for integ in md5 sha1 sha256 sha384 sha512; do
		local integrity_sums=($(eval echo "\${${integ}sums[@]}"))
		if (( ${#integrity_sums[@]} == ${#source[@]} )); then
			msg "$(gettext "Validating source files with %s...")" "${integ}sums"
			correlation=1
			local errors=0
			local idx=0
			local file
			for file in "${source[@]}"; do
				local found=1
				file="$(get_filename "$file")"
				printf "%s" "    $file ... " >&2

				if [[ ${integrity_sums[$idx]} = 'SKIP' ]]; then
					echo "$(gettext "Skipped")" >&2
					idx=$((idx + 1))
					continue
				fi

				if ! file="$(get_filepath "$file")"; then
					printf -- "$(gettext "NOT FOUND")\n" >&2
					errors=1
					found=0
				fi

				if (( $found )) ; then
					local expectedsum="${integrity_sums[idx],,}"
					local realsum="$(openssl dgst -${integ} "$file")"
					realsum="${realsum##* }"
					if [[ $expectedsum = "$realsum" ]]; then
						printf -- "$(gettext "Passed")\n" >&2
					else
						printf -- "$(gettext "FAILED")\n" >&2
						errors=1
					fi
				fi

				idx=$((idx + 1))
			done

			if (( errors )); then
				error "$(gettext "One or more files did not pass the validity check!")"
				exit 1 # TODO: error code
			fi
		elif (( ${#integrity_sums[@]} )); then
			error "$(gettext "Integrity checks (%s) differ in size from the source array.")" "$integ"
			exit 1 # TODO: error code
		fi
	done

	if (( ! correlation )); then
		error "$(gettext "Integrity checks are missing.")"
		exit 1 # TODO: error code
	fi
}

check_pgpsigs() {
	(( SKIPPGPCHECK )) && return 0
	! source_has_signatures && return 0

	msg "$(gettext "Verifying source file signatures with %s...")" "gpg"

	local file pubkey
	local warning=0
	local errors=0
	local statusfile=$(mktemp)

	for file in "${source[@]}"; do
		file="$(get_filename "$file")"
		if [[ ! $file = *.@(sig?(n)|asc) ]]; then
			continue
		fi

		printf "    %s ... " "${file%.*}" >&2

		if ! file="$(get_filepath "$file")"; then
			printf '%s\n' "$(gettext "SIGNATURE NOT FOUND")" >&2
			errors=1
			continue
		fi

		if ! sourcefile="$(get_filepath "${file%.*}")"; then
			printf '%s\n' "$(gettext "SOURCE FILE NOT FOUND")" >&2
			errors=1
			continue
		fi

		if ! gpg --quiet --batch --status-file "$statusfile" --verify "$file" "$sourcefile" 2> /dev/null; then
			printf '%s' "$(gettext "FAILED")" >&2
			if ! pubkey=$(awk '/NO_PUBKEY/ { print $3; exit 1; }' "$statusfile"); then
				printf ' (%s)' "$(gettext "unknown public key") $pubkey" >&2
				warnings=1
			else
				errors=1
			fi
			printf '\n' >&2
		else
			if grep -q "REVKEYSIG" "$statusfile"; then
				printf '%s (%s)' "$(gettext "FAILED")" "$(gettext "the key has been revoked.")" >&2
				errors=1
			else
				printf '%s' "$(gettext "Passed")" >&2
				if grep -q "EXPSIG" "$statusfile"; then
					printf ' (%s)' "$(gettext "WARNING:") $(gettext "the signature has expired.")" >&2
					warnings=1
				elif grep -q "EXPKEYSIG" "$statusfile"; then
					printf ' (%s)' "$(gettext "WARNING:") $(gettext "the key has expired.")" >&2
					warnings=1
				fi
			fi
			printf '\n' >&2
		fi
	done

	rm -f "$statusfile"

	if (( errors )); then
		error "$(gettext "One or more PGP signatures could not be verified!")"
		exit 1
	fi

	if (( warnings )); then
		warning "$(gettext "Warnings have occurred while verifying the signatures.")"
		plain "$(gettext "Please make sure you really trust them.")"
	fi
}

check_source_integrity() {
	if (( SKIPCHECKSUMS && SKIPPGPCHECK )); then
		warning "$(gettext "Skipping all source file integrity checks.")"
	elif (( SKIPCHECKSUMS )); then
		warning "$(gettext "Skipping verification of source file checksums.")"
		check_pgpsigs
	elif (( SKIPPGPCHECK )); then
		warning "$(gettext "Skipping verification of source file PGP signatures.")"
		check_checksums
	else
		check_checksums
		check_pgpsigs
	fi
}

extract_sources() {
	msg "$(gettext "Extracting Sources...")"
	local netfile
	for netfile in "${source[@]}"; do
		local file=$(get_filename "$netfile")
		if in_array "$file" "${noextract[@]}"; then
			#skip source files in the noextract=() array
			#  these are marked explicitly to NOT be extracted
			continue
		fi


		# fix flyspray #6246
		local file_type=$(file -bizL "$file")
		local ext=${file##*.}
		local cmd=''
		case "$file_type" in
			*application/x-tar*|*application/zip*|*application/x-zip*|*application/x-cpio*)
				cmd="bsdtar" ;;
			*application/x-gzip*)
				case "$ext" in
					gz|z|Z) cmd="gzip" ;;
					*) continue;;
				esac ;;
			*application/x-bzip*)
				case "$ext" in
					bz2|bz) cmd="bzip2" ;;
					*) continue;;
				esac ;;
			*application/x-xz*)
				case "$ext" in
					xz) cmd="xz" ;;
					*) continue;;
				esac ;;
			*)
				# See if bsdtar can recognize the file
				if bsdtar -tf "$file" -q '*' &>/dev/null; then
					cmd="bsdtar"
				else
					continue
				fi ;;
		esac

		local ret=0
		msg2 "$(gettext "Extracting %s with %s")" "$file" "$cmd"
		if [[ $cmd = "bsdtar" ]]; then
			$cmd -xf "$file" || ret=$?
		else
			rm -f -- "${file%.*}"
			$cmd -dcf "$file" > "${file%.*}" || ret=$?
		fi
		if (( ret )); then
			error "$(gettext "Failed to extract %s")" "$file"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
	done

	if (( EUID == 0 )); then
		# change perms of all source files to root user & root group
		chown -R 0:0 "$srcdir"
	fi
}

error_function() {
	if [[ -p $logpipe ]]; then
		rm "$logpipe"
	fi
	# first exit all subshells, then print the error
	if (( ! BASH_SUBSHELL )); then
		error "$(gettext "A failure occurred in %s().")" "$1"
		plain "$(gettext "Aborting...")"
		remove_deps
	fi
	exit 2 # $E_BUILD_FAILED
}

cd_safe() {
	if ! cd "$1"; then
		error "$(gettext "Failed to change to directory %s")" "$1"
		plain "$(gettext "Aborting...")"
		exit 1
	fi
}

source_safe() {
	shopt -u extglob
	if ! source "$@"; then
		error "$(gettext "Failed to source %s")" "$1"
		exit 1
	fi
	shopt -s extglob
}

run_function_safe() {
	local restoretrap

	set -e
	set -E

	restoretrap=$(trap -p ERR)
	trap 'error_function $pkgfunc' ERR

	run_function "$1"

	eval $restoretrap

	set +E
	set +e
}

run_function() {
	if [[ -z $1 ]]; then
		return 1
	fi
	local pkgfunc="$1"

	# clear user-specified buildflags if requested
	if check_option "buildflags" "n"; then
		unset CPPFLAGS CFLAGS CXXFLAGS LDFLAGS
	fi

	# clear user-specified makeflags if requested
	if check_option "makeflags" "n"; then
		unset MAKEFLAGS
	fi

	msg "$(gettext "Starting %s()...")" "$pkgfunc"
	cd_safe "$srcdir"

	# ensure all necessary build variables are exported
	export CPPFLAGS CFLAGS CXXFLAGS LDFLAGS MAKEFLAGS CHOST
	# save our shell options so pkgfunc() can't override what we need
	local shellopts=$(shopt -p)

	local ret=0
	if (( LOGGING )); then
		local fullver=$(get_full_version)
		local BUILDLOG="${startdir}/${pkgbase}-${fullver}-${CARCH}-$pkgfunc.log"
		if [[ -f $BUILDLOG ]]; then
			local i=1
			while true; do
				if [[ -f $BUILDLOG.$i ]]; then
					i=$(($i +1))
				else
					break
				fi
			done
			mv "$BUILDLOG" "$BUILDLOG.$i"
		fi

		# ensure overridden package variables survive tee with split packages
		logpipe=$(mktemp -u "$startdir/logpipe.XXXXXXXX")
		mkfifo "$logpipe"
		tee "$BUILDLOG" < "$logpipe" &
		local teepid=$!

		$pkgfunc &>"$logpipe"

		wait $teepid
		rm "$logpipe"
	else
		$pkgfunc 2>&1
	fi
	# reset our shell options
	eval "$shellopts"
}

run_prepare() {
	run_function_safe "prepare"
}

run_build() {
	# use distcc if it is requested (check buildenv and PKGBUILD opts)
	if check_buildenv "distcc" "y" && ! check_option "distc" "n"; then
		[[ -d /usr/lib/distcc/bin ]] && export PATH="/usr/lib/distcc/bin:$PATH"
		export DISTCC_HOSTS
	fi

	# use ccache if it is requested (check buildenv and PKGBUILD opts)
	if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then
		[[ -d /usr/lib/ccache/bin ]] && export PATH="/usr/lib/ccache/bin:$PATH"
	fi

	run_function_safe "build"
}

run_check() {
	run_function_safe "check"
}

run_package() {
	local pkgfunc
	if [[ -z $1 ]]; then
		pkgfunc="package"
	else
		pkgfunc="package_$1"
	fi

	run_function_safe "$pkgfunc"
}

tidy_install() {
	cd_safe "$pkgdir"
	msg "$(gettext "Tidying install...")"

	if check_option "docs" "n" && [[ -n ${DOC_DIRS[*]} ]]; then
		msg2 "$(gettext "Removing doc files...")"
		rm -rf -- ${DOC_DIRS[@]}
	fi

	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
				find . ! -type d -name "${pt}" -exec rm -f -- '{}' \;
			else
				rm -f ${pt}
			fi
		done
	fi

	if check_option "zipman" "y" && [[ -n ${MAN_DIRS[*]} ]]; then
		msg2 "$(gettext "Compressing man and info pages...")"
		local file files inode link
		while read -rd ' ' inode; do
			read file
			find ${MAN_DIRS[@]} -type l 2>/dev/null |
			while read link ; do
				if [[ "${file}" -ef "${link}" ]] ; then
					rm -f "$link" "${link}.gz"
					if [[ ${file%/*} = ${link%/*} ]]; then
						ln -s -- "${file##*/}.gz" "${link}.gz"
					else
						ln -s -- "/${file}.gz" "${link}.gz"
					fi
				fi
			done
			if [[ -z ${files[$inode]} ]]; then
				files[$inode]=$file
				gzip -9 -n -f "$file"
			else
				rm -f "$file"
				ln "${files[$inode]}.gz" "${file}.gz"
				chmod 644 "${file}.gz"
			fi
		done < <(find ${MAN_DIRS[@]} -type f \! -name "*.gz" \! -name "*.bz2" \
			-exec @INODECMD@ '{}' + 2>/dev/null)
	fi

	if check_option "strip" "y"; then
		msg2 "$(gettext "Stripping unneeded symbols from binaries and libraries...")"
		# make sure library stripping variables are defined to prevent excess stripping
		[[ -z ${STRIP_SHARED+x} ]] && STRIP_SHARED="-S"
		[[ -z ${STRIP_STATIC+x} ]] && STRIP_STATIC="-S"
		local binary
		find . -type f -perm -u+w -print0 2>/dev/null | while read -d '' binary ; do
			case "$(file -bi "$binary")" in
				*application/x-sharedlib*)  # Libraries (.so)
					strip $STRIP_SHARED "$binary";;
				*application/x-archive*)    # Libraries (.a)
					strip $STRIP_STATIC "$binary";;
				*application/x-executable*) # Binaries
					strip $STRIP_BINARIES "$binary";;
			esac
		done
	fi

	if check_option "libtool" "n"; then
		msg2 "$(gettext "Removing "%s" files...")" "libtool"
		find . ! -type d -name "*.la" -exec rm -f -- '{}' \;
	fi

	if check_option "emptydirs" "n"; then
		msg2 "$(gettext "Removing empty directories...")"
		find . -depth -type d -exec rmdir '{}' + 2>/dev/null
	fi

	if check_option "upx" "y"; then
		msg2 "$(gettext "Compressing binaries with %s...")" "UPX"
		local binary
		find . -type f -perm -u+w 2>/dev/null | while read binary ; do
			if [[ $(file -bi "$binary") = *'application/x-executable'* ]]; then
				upx $UPXFLAGS "$binary" &>/dev/null ||
						warning "$(gettext "Could not compress binary : %s")" "${binary/$pkgdir\//}"
			fi
		done
	fi
}

find_libdepends() {
	local d sodepends;

	sodepends=0;
	for d in "${depends[@]}"; do
		if [[ $d = *.so ]]; then
			sodepends=1;
			break;
		fi
	done

	if (( sodepends == 0 )); then
		printf '%s\n' "${depends[@]}"
		return;
	fi

	local libdeps filename soarch sofile soname soversion;
	declare -A libdeps;

	while read filename; do
		# get architecture of the file; if soarch is empty it's not an ELF binary
		soarch=$(LC_ALL=C readelf -h "$filename" 2>/dev/null | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p')
		[[ -n "$soarch" ]] || continue

		# process all libraries needed by the binary
		for sofile in $(LC_ALL=C readelf -d "$filename" 2>/dev/null | sed -nr 's/.*Shared library: \[(.*)\].*/\1/p')
		do
			# extract the library name: libfoo.so
			soname="${sofile%.so?(+(.+([0-9])))}".so
			# extract the major version: 1
			soversion="${sofile##*\.so\.}"

			if [[ ${libdeps[$soname]} ]]; then
				if [[ ${libdeps[$soname]} != *${soversion}-${soarch}* ]]; then
					libdeps[$soname]+=" ${soversion}-${soarch}"
				fi
			else
				libdeps[$soname]="${soversion}-${soarch}"
			fi
		done
	done < <(find "$pkgdir" -type f -perm -u+x)

	local libdepends v
	for d in "${depends[@]}"; do
		case "$d" in
			*.so)
				if [[ ${libdeps[$d]} ]]; then
					for v in ${libdeps[$d]}; do
						libdepends+=("$d=$v")
					done
				else
					warning "$(gettext "Library listed in %s is not required by any files: %s")" "'depends'" "$d"
					libdepends+=("$d")
				fi
				;;
			*)
				libdepends+=("$d")
				;;
		esac
	done

	printf '%s\n' "${libdepends[@]}"
}


find_libprovides() {
	local p libprovides missing
	for p in "${provides[@]}"; do
		missing=0
		case "$p" in
			*.so)
				mapfile -t filename < <(find "$pkgdir" -type f -name $p\*)
				if [[ $filename ]]; then
					# packages may provide multiple versions of the same library
					for fn in "${filename[@]}"; do
						# check if we really have a shared object
						if LC_ALL=C readelf -h "$fn" 2>/dev/null | grep -q '.*Type:.*DYN (Shared object file).*'; then
							# get the string binaries link to (e.g. libfoo.so.1.2 -> libfoo.so.1)
							local sofile=$(LC_ALL=C readelf -d "$fn" 2>/dev/null | sed -n 's/.*Library soname: \[\(.*\)\].*/\1/p')
							if [[ -z "$sofile" ]]; then
								warning "$(gettext "Library listed in %s is not versioned: %s")" "'provides'" "$p"
								libprovides+=("$p")
								continue
							fi

							# get the library architecture (32 or 64 bit)
							local soarch=$(LC_ALL=C readelf -h "$fn" | sed -n 's/.*Class.*ELF\(32\|64\)/\1/p')

							# extract the library major version
							local soversion="${sofile##*\.so\.}"

							libprovides+=("${p}=${soversion}-${soarch}")
						else
							warning "$(gettext "Library listed in %s is not a shared object: %s")" "'provides'" "$p"
							libprovides+=("$p")
						fi
					done
				else
					libprovides+=("$p")
					missing=1
				fi
				;;
			*)
				libprovides+=("$p")
				;;
		esac

		if (( missing )); then
			warning "$(gettext "Can not find library listed in %s: %s")" "'provides'" "$p"
		fi
	done

	printf '%s\n' "${libprovides[@]}"
}

check_license() {
	# TODO maybe remove this at some point
	# warn if license array is not present or empty
	if [[ -z $license ]]; then
		warning "$(gettext "Please add a license line to your %s!")" "$BUILDSCRIPT"
		plain "$(gettext "Example for GPL\'ed software: %s.")" "license=('GPL')"
	fi
}

write_pkginfo() {
	local builddate=$(date -u "+%s")
	if [[ -n $PACKAGER ]]; then
		local packager="$PACKAGER"
	else
		local packager="Unknown Packager"
	fi

	# btrfs's delayed allocation causes the stat buffers from the kernel to "lie"
	# to us momentarily and report 0 blocks allocated (which is how du calculates
	# size). Sleeping for a second here is about the dirtiest thing possible,
	# but avoids reporting entirely bogus install sizes.
	sleep 1
	local size="$(@DUPATH@ -sk)"
	size="$(( ${size%%[^0-9]*} * 1024 ))"

	msg2 "$(gettext "Generating %s file...")" ".PKGINFO"
	echo "# Generated by makepkg $makepkg_version"
	if (( INFAKEROOT )); then
		echo "# using $(fakeroot -v)"
	fi
	echo "# $(LC_ALL=C date -u)"
	printf "pkgname = %s\n" "$1"
	(( SPLITPKG )) && echo pkgbase = $pkgbase
	echo "pkgver = $(get_full_version)"
	printf "pkgdesc = %s\n" "$pkgdesc"
	printf "url = %s\n" "$url"
	printf "builddate = %s\n" "$builddate"
	printf "packager = %s\n" "$packager"
	printf "size = %s\n" "$size"
	printf "arch = %s\n" "$pkgarch"

	mapfile -t provides < <(find_libprovides)
	mapfile -t depends < <(find_libdepends)

	[[ $license ]]      && printf "license = %s\n"     "${license[@]}"
	[[ $replaces ]]     && printf "replaces = %s\n"    "${replaces[@]}"
	[[ $groups ]]       && printf "group = %s\n"       "${groups[@]}"
	[[ $conflicts ]]    && printf "conflict = %s\n"    "${conflicts[@]}"
	[[ $provides ]]     && printf "provides = %s\n"    "${provides[@]}"
	[[ $backup ]]       && printf "backup = %s\n"      "${backup[@]}"
	[[ $depends ]]      && printf "depend = %s\n"      "${depends[@]}"
	[[ $optdepends ]]   && printf "optdepend = %s\n"   "${optdepends[@]//+([[:space:]])/ }"
	[[ $makedepends ]]  && printf "makedepend = %s\n"  "${makedepends[@]}"
	[[ $checkdepends ]] && printf "checkdepend = %s\n" "${checkdepends[@]}"

	local it
	for it in "${packaging_options[@]}"; do
		check_option "$it" "y"
		case $? in
			0)
				printf "makepkgopt = %s\n" "$it"
				;;
			1)
				printf "makepkgopt = %s\n" "!$it"
				;;
		esac
	done

	check_license
}

check_package() {
	cd_safe "$pkgdir"

	# check existence of backup files
	local file
	for file in "${backup[@]}"; do
		if [[ ! -f $file ]]; then
			warning "$(gettext "%s entry file not in package : %s")" "backup" "$file"
		fi
	done

	# check for references to the build and package directory
	if find "${pkgdir}" -type f -print0 | xargs -0  grep -q -I "${srcdir}" ; then
		warning "$(gettext "Package contains reference to %s")" "\$srcdir"
	fi
	if find "${pkgdir}" -type f -print0 | xargs -0  grep -q -I "${pkgdir}" ; then
		warning "$(gettext "Package contains reference to %s")" "\$pkgdir"
	fi

}

create_package() {
	if [[ ! -d $pkgdir ]]; then
		error "$(gettext "Missing %s directory.")" "pkg/"
		plain "$(gettext "Aborting...")"
		exit 1 # $E_MISSING_PKGDIR
	fi

	check_package

	cd_safe "$pkgdir"
	msg "$(gettext "Creating package...")"

	local nameofpkg
	if [[ -z $1 ]]; then
		nameofpkg="$pkgname"
	else
		nameofpkg="$1"
	fi

	pkgarch=$(get_pkg_arch)

	write_pkginfo $nameofpkg > .PKGINFO

	local comp_files=('.PKGINFO')

	# check for changelog/install files
	for i in 'changelog/.CHANGELOG' 'install/.INSTALL'; do
		IFS='/' read -r orig dest < <(printf '%s\n' "$i")

		if [[ -n ${!orig} ]]; then
			msg2 "$(gettext "Adding %s file...")" "$orig"
			cp "$startdir/${!orig}" "$dest"
			chmod 644 "$dest"
			comp_files+=("$dest")
		fi
	done

	# tar it up
	msg2 "$(gettext "Compressing package...")"

	local fullver=$(get_full_version)
	local pkg_file="$PKGDEST/${nameofpkg}-${fullver}-${pkgarch}${PKGEXT}"
	local ret=0

	[[ -f $pkg_file ]] && rm -f "$pkg_file"
	[[ -f $pkg_file.sig ]] && rm -f "$pkg_file.sig"

	# when fileglobbing, we want * in an empty directory to expand to
	# the null string rather than itself
	shopt -s nullglob
	# TODO: Maybe this can be set globally for robustness
	shopt -s -o pipefail
	# bsdtar's gzip compression always saves the time stamp, making one
	# archive created using the same command line distinct from another.
	# Disable bsdtar compression and use gzip -n for now.
	bsdtar -cf - "${comp_files[@]}" * |
	case "$PKGEXT" in
		*tar.gz)  ${COMPRESSGZ[@]:-gzip -c -f -n} ;;
		*tar.bz2) ${COMPRESSBZ2[@]:-bzip2 -c -f} ;;
		*tar.xz)  ${COMPRESSXZ[@]:-xz -c -z -} ;;
		*tar.Z)   ${COMPRESSZ[@]:-compress -c -f} ;;
		*tar)     cat ;;
		*) warning "$(gettext "'%s' is not a valid archive extension.")" \
			"$PKGEXT"; cat ;;
	esac > "${pkg_file}" || ret=$?

	shopt -u nullglob
	shopt -u -o pipefail

	if (( ret )); then
		error "$(gettext "Failed to create package file.")"
		exit 1 # TODO: error code
	fi

	create_signature "$pkg_file"

	if (( ! ret )) && [[ ! "$PKGDEST" -ef "${startdir}" ]]; then
		rm -f "${pkg_file/$PKGDEST/$startdir}"
		ln -s "${pkg_file}" "${pkg_file/$PKGDEST/$startdir}"
		ret=$?
		if [[ -f $pkg_file.sig ]]; then
			rm -f "${pkg_file/$PKGDEST/$startdir}.sig"
			ln -s "$pkg_file.sig" "${pkg_file/$PKGDEST/$startdir}.sig"
		fi
	fi

	if (( ret )); then
		warning "$(gettext "Failed to create symlink to package file.")"
	fi
}

create_signature() {
	if [[ $SIGNPKG != 'y' ]]; then
		return
	fi
	local ret=0
	local filename="$1"
	msg "$(gettext "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" &>/dev/null || ret=$?


	if (( ! ret )); then
		msg2 "$(gettext "Created signature file %s.")" "$filename.sig"
	else
		warning "$(gettext "Failed to sign package file.")"
	fi
}

create_srcpackage() {
	local ret=0
	msg "$(gettext "Creating source package...")"
	local srclinks="$(mktemp -d "$startdir"/srclinks.XXXXXXXXX)"
	mkdir "${srclinks}"/${pkgbase}

	check_license

	msg2 "$(gettext "Adding %s...")" "$BUILDSCRIPT"
	ln -s "${BUILDFILE}" "${srclinks}/${pkgbase}/${BUILDSCRIPT}"

	local file
	for file in "${source[@]}"; do
		if [[ "$file" = "$(get_filename "$file")" ]] || (( SOURCEONLY == 2 )); then
			local absfile
			absfile=$(get_filepath "$file") || missing_source_file "$file"
			msg2 "$(gettext "Adding %s...")" "${absfile##*/}"
			ln -s "$absfile" "$srclinks/$pkgbase"
		fi
	done

	local i
	for i in 'changelog' 'install'; do
		local file
		while read -r file; do
			# evaluate any bash variables used
			eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\"
			if [[ $file && ! -f "${srclinks}/${pkgbase}/$file" ]]; then
				msg2 "$(gettext "Adding %s file (%s)...")" "$i" "${file}"
				ln -s "${startdir}/$file" "${srclinks}/${pkgbase}/"
			fi
		done < <(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE")
	done

	local TAR_OPT
	case "$SRCEXT" in
		*tar.gz)  TAR_OPT="z" ;;
		*tar.bz2) TAR_OPT="j" ;;
		*tar.xz)  TAR_OPT="J" ;;
		*tar.Z)   TAR_OPT="Z" ;;
		*tar)     TAR_OPT=""  ;;
		*) warning "$(gettext "'%s' is not a valid archive extension.")" \
		"$SRCEXT" ;;
	esac

	local fullver=$(get_full_version)
	local pkg_file="$SRCPKGDEST/${pkgbase}-${fullver}${SRCEXT}"

	# tar it up
	msg2 "$(gettext "Compressing source package...")"
	cd_safe "${srclinks}"
	if ! bsdtar -c${TAR_OPT}Lf "$pkg_file" ${pkgbase}; then
		error "$(gettext "Failed to create source package file.")"
		exit 1 # TODO: error code
	fi

	if [[ ! "$SRCPKGDEST" -ef "${startdir}" ]]; then
		rm -f "${pkg_file/$SRCPKGDEST/$startdir}"
		ln -s "${pkg_file}" "${pkg_file/$SRCPKGDEST/$startdir}"
		ret=$?
	fi

	if (( ret )); then
		warning "$(gettext "Failed to create symlink to source package file.")"
	fi

	cd_safe "${startdir}"
	rm -rf "${srclinks}"
}

install_package() {
	(( ! INSTALL )) && return

	if (( ! SPLITPKG )); then
		msg "$(gettext "Installing package %s with %s...")" "$pkgname" "$PACMAN -U"
	else
		msg "$(gettext "Installing %s package group with %s...")" "$pkgbase" "$PACMAN -U"
	fi

	local fullver pkgarch pkg pkglist
	(( ASDEPS )) && pkglist+=('--asdeps')

	for pkg in ${pkgname[@]}; do
		fullver=$(get_full_version $pkg)
		pkgarch=$(get_pkg_arch $pkg)
		pkglist+=("$PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT}")
	done

	if ! run_pacman -U ${pkglist[@]}; then
		warning "$(gettext "Failed to install built package(s).")"
		return 0
	fi
}

check_sanity() {
	# check for no-no's in the build script
	local i
	local ret=0
	for i in 'pkgname' 'pkgrel'; do
		if [[ -z ${!i} ]]; then
			error "$(gettext "%s is not allowed to be empty.")" "$i"
			ret=1
		fi
	done

	for i in "${pkgname[@]}"; do
		if [[ ${i:0:1} = "-" ]]; then
			error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgname"
			ret=1
		fi
		if [[ $i = *[^[:alnum:]+_.@-]* ]]; then
			error "$(gettext "%s contains invalid characters: '%s'")" \
					'pkgname' "${pkgname//[[:alnum:]+_.@-]}"
			ret=1
		fi
	done

	if [[ ${pkgbase:0:1} = "-" ]]; then
		error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgbase"
		ret=1
	fi

	if (( ! PKGVERFUNC )) ; then
		check_pkgver || ret=1
	fi

	awk -F'=' '$1 ~ /^[[:space:]]*pkgrel$/' "$BUILDFILE" | sed "s/[[:space:]]*#.*//" |
	while IFS='=' read -r _ i; do
		eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\"
		if [[ $i != +([0-9])?(.+([0-9])) ]]; then
			error "$(gettext "%s must be a decimal.")" "pkgrel"
			return 1
		fi
	done || ret=1

	awk -F'=' '$1 ~ /^[[:space:]]*epoch$/' "$BUILDFILE" |
	while IFS='=' read -r _ i; do
		eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\"
		if [[ $i != *([[:digit:]]) ]]; then
			error "$(gettext "%s must be an integer.")" "epoch"
			return 1
		fi
	done || ret=1

	if [[ $arch != 'any' ]]; then
		if ! in_array $CARCH ${arch[@]}; then
			if (( ! IGNOREARCH )); then
				error "$(gettext "%s is not available for the '%s' architecture.")" "$pkgbase" "$CARCH"
				plain "$(gettext "Note that many packages may need a line added to their %s")" "$BUILDSCRIPT"
				plain "$(gettext "such as %s.")" "arch=('$CARCH')"
				ret=1
			fi
		fi
	fi

	if (( ${#pkgname[@]} > 1 )); then
		for i in ${pkgname[@]}; do
			local arch_list=""
			eval $(declare -f package_${i} | sed -n 's/\(^[[:space:]]*arch=\)/arch_list=/p')
			if [[ ${arch_list[@]} && ${arch_list} != 'any' ]]; then
				if ! in_array $CARCH ${arch_list[@]}; then
					if (( ! IGNOREARCH )); then
						error "$(gettext "%s is not available for the '%s' architecture.")" "$i" "$CARCH"
						ret=1
					fi
				fi
			fi
		done
	fi

	local provides_list=()
	eval $(awk '/^[[:space:]]*provides=/,/\)/' "$BUILDFILE" | \
		sed -e "s/provides=/provides_list+=/" -e "s/#.*//" -e 's/\\$//')
	for i in ${provides_list[@]}; do
		if [[ $i == *['<>']* ]]; then
			error "$(gettext "%s array cannot contain comparison (< or >) operators.")" "provides"
			ret=1
		fi
	done

	local backup_list=()
	eval $(awk '/^[[:space:]]*backup=/,/\)/' "$BUILDFILE" | \
		sed -e "s/backup=/backup_list+=/" -e "s/#.*//" -e 's/\\$//')
	for i in "${backup_list[@]}"; do
		if [[ ${i:0:1} = "/" ]]; then
			error "$(gettext "%s entry should not contain leading slash : %s")" "backup" "$i"
			ret=1
		fi
	done

	local optdepends_list=()
	eval $(awk '/^[[:space:]]*optdepends=\(/,/\)[[:space:]]*(#.*)?$/' "$BUILDFILE" | \
		sed -e "s/optdepends=/optdepends_list+=/" -e "s/#.*//" -e 's/\\$//')
	for i in "${optdepends_list[@]}"; do
		local pkg=${i%%:[[:space:]]*}
		# the '-' character _must_ be first or last in the character range
		if [[ $pkg != +([-[:alnum:]><=.+_:]) ]]; then
			error "$(gettext "Invalid syntax for %s : '%s'")" "optdepend" "$i"
			ret=1
		fi
	done

	for i in 'changelog' 'install'; do
		local file
		while read -r file; do
			# evaluate any bash variables used
			eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\"
			if [[ $file && ! -f $file ]]; then
				error "$(gettext "%s file (%s) does not exist.")" "$i" "$file"
				ret=1
			fi
		done < <(sed -n "s/^[[:space:]]*$i=//p" "$BUILDFILE")
	done

	local valid_options=1
	local known kopt options_list
	eval $(awk '/^[[:space:]]*options=/,/\)/' "$BUILDFILE" | \
		sed -e "s/options=/options_list+=/" -e "s/#.*//" -e 's/\\$//')
	for i in ${options_list[@]}; do
		known=0
		# check if option matches a known option or its inverse
		for kopt in ${packaging_options[@]} ${other_options[@]}; do
			if [[ ${i} = "${kopt}" || ${i} = "!${kopt}" ]]; then
				known=1
			fi
		done
		if (( ! known )); then
			error "$(gettext "%s array contains unknown option '%s'")" "options" "$i"
			valid_options=0
		fi
	done
	if (( ! valid_options )); then
		ret=1
	fi

	if (( ${#pkgname[@]} > 1 )); then
		for i in ${pkgname[@]}; do
			if ! declare -f package_${i} >/dev/null; then
				error "$(gettext "Missing %s function for split package '%s'")" "package_$i()" "$i"
				ret=1
			fi
		done
	fi

	for i in ${PKGLIST[@]}; do
		if ! in_array $i ${pkgname[@]}; then
			error "$(gettext "Requested package %s is not provided in %s")" "$i" "$BUILDFILE"
			ret=1
		fi
	done

	return $ret
}

check_pkgver() {
	local ret=0

	if [[ -z ${pkgver} ]]; then
		error "$(gettext "%s is not allowed to be empty.")" "pkgver"
		ret=1
	fi

	awk -F'=' '$1 ~ /^[[:space:]]*pkgver$/' "$BUILDFILE" | sed "s/[[:space:]]*#.*//" |
	while IFS='=' read -r _ i; do
		eval i=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "${i%%+([[:space:]])}")\"
		if [[ $i = *[[:space:]:-]* ]]; then
			error "$(gettext "%s is not allowed to contain colons, hyphens or whitespace.")" "pkgver"
			return 1
		fi
	done || ret=1

	return $ret
}

check_software() {
	# check for needed software
	local ret=0

	# check for sudo if we will need it during makepkg execution
	if (( ! ( ASROOT || INFAKEROOT ) && ( DEP_BIN || RMDEPS || INSTALL ) )); then
		if ! type -p sudo >/dev/null; then
			warning "$(gettext "Cannot find the %s binary. Will use %s to acquire root privileges.")" "sudo" "su"
		fi
	fi

	# fakeroot - building as non-root user
	if check_buildenv "fakeroot" "y" && (( EUID > 0 )); then
		if ! type -p fakeroot >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for building as non-root user.")" "fakeroot"
			ret=1
		fi
	fi

	# gpg - package signing
	if [[ $SIGNPKG == 'y' ]] || { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; }; then
		if ! type -p gpg >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for signing packages.")" "gpg"
			ret=1
		fi
	fi

	# gpg - source verification
	if (( ! SKIPPGPCHECK )) && source_has_signatures; then
		if ! type -p gpg >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for verifying source files.")" "gpg"
			ret=1
		fi
	fi

	# openssl - checksum operations
	if (( ! SKIPCHECKSUMS )); then
		if ! type -p openssl >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for validating sourcefile checksums.")" "openssl"
			ret=1
		fi
	fi

	# upx - binary compression
	if check_option "upx" "y"; then
		if ! type -p upx >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for compressing binaries.")" "upx"
			ret=1
		fi
	fi

	# distcc - compilation with distcc
	if check_buildenv "distcc" "y" && ! check_option "distcc" "n" ]]; then
		if ! type -p distcc >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for distributed compilation.")" "distcc"
			ret=1
		fi
	fi

	# ccache - compilation with ccache
	if check_buildenv "ccache" "y" && ! check_option "ccache" "n"; then
		if ! type -p ccache >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for compiler cache usage.")" "ccache"
			ret=1
		fi
	fi

	# strip - strip symbols from binaries/libraries
	if check_option "strip" "y"; then
		if ! type -p strip >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for object file stripping.")" "strip"
			ret=1
		fi
	fi

	# gzip - compressig man and info pages
	if check_option "zipman" "y"; then
		if ! type -p gzip >/dev/null; then
			error "$(gettext "Cannot find the %s binary required for compressing man and info pages.")" "gzip"
			ret=1
		fi
	fi

	return $ret
}

check_build_status() {
	if (( ! SPLITPKG )); then
		fullver=$(get_full_version)
		pkgarch=$(get_pkg_arch)
		if [[ -f $PKGDEST/${pkgname}-${fullver}-${pkgarch}${PKGEXT} ]] \
				 && ! (( FORCE || SOURCEONLY || NOBUILD )); then
			if (( INSTALL )); then
				warning "$(gettext "A package has already been built, installing existing package...")"
				install_package
				exit $?
			else
				error "$(gettext "A package has already been built. (use %s to overwrite)")" "-f"
				exit 1
			fi
		fi
	else
		allpkgbuilt=1
		somepkgbuilt=0
		for pkg in ${pkgname[@]}; do
			fullver=$(get_full_version $pkg)
			pkgarch=$(get_pkg_arch $pkg)
			if [[ -f $PKGDEST/${pkg}-${fullver}-${pkgarch}${PKGEXT} ]]; then
				somepkgbuilt=1
			else
				allpkgbuilt=0
			fi
		done
		if ! (( FORCE || SOURCEONLY || NOBUILD )); then
			if (( allpkgbuilt )); then
				if (( INSTALL )); then
					warning "$(gettext "The package group has already been built, installing existing packages...")"
					install_package
					exit $?
				else
					error "$(gettext "The package group has already been built. (use %s to overwrite)")" "-f"
					exit 1
				fi
			fi
			if (( somepkgbuilt && ! PKGVERFUNC )); then
				error "$(gettext "Part of the package group has already been built. (use %s to overwrite)")" "-f"
				exit 1
			fi
		fi
		unset allpkgbuilt somepkgbuilt
	fi
}

backup_package_variables() {
	local var
	for var in ${splitpkg_overrides[@]}; do
		local indirect="${var}_backup"
		eval "${indirect}=(\"\${$var[@]}\")"
	done
}

restore_package_variables() {
	local var
	for var in ${splitpkg_overrides[@]}; do
		local indirect="${var}_backup"
		if [[ -n ${!indirect} ]]; then
			eval "${var}=(\"\${$indirect[@]}\")"
		else
			unset ${var}
		fi
	done
}

run_split_packaging() {
	local pkgname_backup=${pkgname[@]}
	for pkgname in ${pkgname_backup[@]}; do
		pkgdir="$pkgdir/$pkgname"
		mkdir -p "$pkgdir"
		chmod a-s "$pkgdir"
		backup_package_variables
		run_package $pkgname
		tidy_install
		create_package $pkgname
		restore_package_variables
		pkgdir="${pkgdir%/*}"
	done
	pkgname=${pkgname_backup[@]}
}

# Canonicalize a directory path if it exists
canonicalize_path() {
	local path="$1";

	if [[ -d $path ]]; then
		(
			cd_safe "$path"
			pwd -P
		)
	else
		printf "%s\n" "$path"
	fi
}

dir_is_empty() {
	(
		shopt -s dotglob nullglob
		files=("$1"/*)
		(( ${#files} == 0 ))
	)
}

m4_include(library/parseopts.sh)

usage() {
	printf "makepkg (pacman) %s\n" "$makepkg_version"
	echo
	printf -- "$(gettext "Usage: %s [options]")\n" "$0"
	echo
	printf -- "$(gettext "Options:")\n"
	printf -- "$(gettext "  -A, --ignorearch Ignore incomplete %s field in %s")\n" "arch" "$BUILDSCRIPT"
	printf -- "$(gettext "  -c, --clean      Clean up work files after build")\n"
	printf -- "$(gettext "  -d, --nodeps     Skip all dependency checks")\n"
	printf -- "$(gettext "  -e, --noextract  Do not extract source files (use existing %s dir)")\n" "src/"
	printf -- "$(gettext "  -f, --force      Overwrite existing package")\n"
	printf -- "$(gettext "  -g, --geninteg   Generate integrity checks for source files")\n"
	printf -- "$(gettext "  -h, --help       Show this help message and exit")\n"
	printf -- "$(gettext "  -i, --install    Install package after successful build")\n"
	printf -- "$(gettext "  -L, --log        Log package build process")\n"
	printf -- "$(gettext "  -m, --nocolor    Disable colorized output messages")\n"
	printf -- "$(gettext "  -o, --nobuild    Download and extract files only")\n"
	printf -- "$(gettext "  -p <file>        Use an alternate build script (instead of '%s')")\n" "$BUILDSCRIPT"
	printf -- "$(gettext "  -r, --rmdeps     Remove installed dependencies after a successful build")\n"
	printf -- "$(gettext "  -R, --repackage  Repackage contents of the package without rebuilding")\n"
	printf -- "$(gettext "  -s, --syncdeps   Install missing dependencies with %s")\n" "pacman"
	printf -- "$(gettext "  -S, --source     Generate a source-only tarball without downloaded sources")\n"
	printf -- "$(gettext "  --allsource      Generate a source-only tarball including downloaded sources")\n"
	printf -- "$(gettext "  --asroot         Allow %s to run as root user")\n" "makepkg"
	printf -- "$(gettext "  --check          Run the %s function in the %s")\n" "check()" "$BUILDSCRIPT"
	printf -- "$(gettext "  --config <file>  Use an alternate config file (instead of '%s')")\n" "$confdir/makepkg.conf"
	printf -- "$(gettext "  --holdver        Do not update VCS sources")\n"
	printf -- "$(gettext "  --key <key>      Specify a key to use for %s signing instead of the default")\n" "gpg"
	printf -- "$(gettext "  --nocheck        Do not run the %s function in the %s")\n" "check()" "$BUILDSCRIPT"
	printf -- "$(gettext "  --nosign         Do not create a signature for the package")\n"
	printf -- "$(gettext "  --pkg <list>     Only build listed packages from a split package")\n"
	printf -- "$(gettext "  --sign           Sign the resulting package with %s")\n" "gpg"
	printf -- "$(gettext "  --skipchecksums  Do not verify checksums of the source files")\n"
	printf -- "$(gettext "  --skipinteg      Do not perform any verification checks on source files")\n"
	printf -- "$(gettext "  --skippgpcheck   Do not verify source files with PGP signatures")\n"
	echo
	printf -- "$(gettext "These options can be passed to %s:")\n" "pacman"
	echo
	printf -- "$(gettext "  --asdeps         Install packages as non-explicitly installed")\n"
	printf -- "$(gettext "  --noconfirm      Do not ask for confirmation when resolving dependencies")\n"
	printf -- "$(gettext "  --noprogressbar  Do not show a progress bar when downloading files")\n"
	echo
	printf -- "$(gettext "If %s is not specified, %s will look for '%s'")\n" "-p" "makepkg" "$BUILDSCRIPT"
	echo
}

version() {
	printf "makepkg (pacman) %s\n" "$makepkg_version"
	printf -- "$(gettext "\
Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>.\n\
Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org>.\n\n\
This is free software; see the source for copying conditions.\n\
There is NO WARRANTY, to the extent permitted by law.\n")"
}

# PROGRAM START

# determine whether we have gettext; make it a no-op if we do not
if ! type -p gettext >/dev/null; then
	gettext() {
		printf "%s\n" "$@"
	}
fi

ARGLIST=("$@")

# Parse Command Line Options.
OPT_SHORT="AcdefFghiLmop:rRsSV"
OPT_LONG=('allsource' 'asroot' 'check' 'clean' 'config:' 'force' 'geninteg'
          'help' 'holdver' 'ignorearch' 'install' 'key:' 'log' 'nobuild' 'nocolor'
          'nocheck' 'nodeps' 'noextract' 'nosign' 'pkg:' 'repackage' 'rmdeps'
          'skipchecksums' 'skipinteg' 'skippgpcheck' 'skippgpcheck' 'sign'
          'source' 'syncdeps' 'version')

# Pacman Options
OPT_LONG+=('asdeps' 'noconfirm' 'noprogressbar')

if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then
	exit 1 # E_INVALID_OPTION;
fi
set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPTRET

while true; do
	case "$1" in
		# Pacman Options
		--asdeps)         ASDEPS=1;;
		--noconfirm)      PACMAN_OPTS+=" --noconfirm" ;;
		--noprogressbar)  PACMAN_OPTS+=" --noprogressbar" ;;

		# Makepkg Options
		--allsource)      SOURCEONLY=2 ;;
		--asroot)         ASROOT=1 ;;
		-A|--ignorearch)  IGNOREARCH=1 ;;
		-c|--clean)       CLEANUP=1 ;;
		--check)          RUN_CHECK='y' ;;
		--config)         shift; MAKEPKG_CONF=$1 ;;
		-d|--nodeps)      NODEPS=1 ;;
		-e|--noextract)   NOEXTRACT=1 ;;
		-f|--force)       FORCE=1 ;;
		-F)               INFAKEROOT=1 ;;
		-g|--geninteg)    GENINTEG=1 ;;
		--holdver)        HOLDVER=1 ;;
		-i|--install)     INSTALL=1 ;;
		--key)            shift; GPGKEY=$1 ;;
		-L|--log)         LOGGING=1 ;;
		-m|--nocolor)     USE_COLOR='n' ;;
		--nocheck)        RUN_CHECK='n' ;;
		--nosign)         SIGNPKG='n' ;;
		-o|--nobuild)     NOBUILD=1 ;;
		-p)               shift; BUILDFILE=$1 ;;
		--pkg)            shift; IFS=, read -ra p <<<"$1"; PKGLIST+=("${p[@]}"); unset p ;;
		-r|--rmdeps)      RMDEPS=1 ;;
		-R|--repackage)   REPKG=1 ;;
		--skipchecksums)  SKIPCHECKSUMS=1 ;;
		--skipinteg)      SKIPCHECKSUMS=1; SKIPPGPCHECK=1 ;;
		--skippgpcheck)   SKIPPGPCHECK=1;;
		--sign)           SIGNPKG='y' ;;
		-s|--syncdeps)    DEP_BIN=1 ;;
		-S|--source)      SOURCEONLY=1 ;;

		-h|--help)        usage; exit 0 ;; # E_OK
		-V|--version)     version; exit 0 ;; # E_OK

		--)               OPT_IND=0; shift; break 2;;
	esac
	shift
done

# setup signal traps
trap 'clean_up' 0
for signal in TERM HUP QUIT; do
	trap "trap_exit $signal \"$(gettext "%s signal caught. Exiting...")\" \"$signal\"" "$signal"
done
trap 'trap_exit INT "$(gettext "Aborted by user! Exiting...")"' INT
trap 'trap_exit USR1 "$(gettext "An unknown error has occurred. Exiting...")"' ERR

# preserve environment variables and canonicalize path
[[ -n ${PKGDEST} ]] && _PKGDEST=$(canonicalize_path ${PKGDEST})
[[ -n ${SRCDEST} ]] && _SRCDEST=$(canonicalize_path ${SRCDEST})
[[ -n ${SRCPKGDEST} ]] && _SRCPKGDEST=$(canonicalize_path ${SRCPKGDEST})
[[ -n ${BUILDDIR} ]] && _BUILDDIR=$(canonicalize_path ${BUILDDIR})
[[ -n ${PKGEXT} ]] && _PKGEXT=${PKGEXT}
[[ -n ${SRCEXT} ]] && _SRCEXT=${SRCEXT}
[[ -n ${GPGKEY} ]] && _GPGKEY=${GPGKEY}
[[ -n ${PACKAGER} ]] && _PACKAGER=${PACKAGER}

# default config is makepkg.conf
MAKEPKG_CONF=${MAKEPKG_CONF:-$confdir/makepkg.conf}

# Source the config file; fail if it is not found
if [[ -r $MAKEPKG_CONF ]]; then
	source_safe "$MAKEPKG_CONF"
else
	error "$(gettext "%s not found.")" "$MAKEPKG_CONF"
	plain "$(gettext "Aborting...")"
	exit 1 # $E_CONFIG_ERROR
fi

# Source user-specific makepkg.conf overrides, but only if no override config
# file was specified
if [[ $MAKEPKG_CONF = "$confdir/makepkg.conf" && -r ~/.makepkg.conf ]]; then
	source_safe ~/.makepkg.conf
fi

# set pacman command if not already defined
PACMAN=${PACMAN:-pacman}

# check if messages are to be printed using color
unset ALL_OFF BOLD BLUE GREEN RED YELLOW
if [[ -t 2 && ! $USE_COLOR = "n" ]] && check_buildenv "color" "y"; then
	# prefer terminal safe colored and bold text when tput is supported
	if tput setaf 0 &>/dev/null; then
		ALL_OFF="$(tput sgr0)"
		BOLD="$(tput bold)"
		BLUE="${BOLD}$(tput setaf 4)"
		GREEN="${BOLD}$(tput setaf 2)"
		RED="${BOLD}$(tput setaf 1)"
		YELLOW="${BOLD}$(tput setaf 3)"
	else
		ALL_OFF="\e[1;0m"
		BOLD="\e[1;1m"
		BLUE="${BOLD}\e[1;34m"
		GREEN="${BOLD}\e[1;32m"
		RED="${BOLD}\e[1;31m"
		YELLOW="${BOLD}\e[1;33m"
	fi
fi
readonly ALL_OFF BOLD BLUE GREEN RED YELLOW

# override settings with an environment variable for batch processing
BUILDDIR=${_BUILDDIR:-$BUILDDIR}
BUILDDIR=${BUILDDIR:-$startdir} #default to $startdir if undefined
if [[ ! -d $BUILDDIR ]]; then
	if ! mkdir -p "$BUILDDIR"; then
		error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR"
		plain "$(gettext "Aborting...")"
		exit 1
	fi
	chmod a-s "$BUILDDIR"
fi
if [[ ! -w $BUILDDIR ]]; then
	error "$(gettext "You do not have write permission to create packages in %s.")" "$BUILDDIR"
	plain "$(gettext "Aborting...")"
	exit 1
fi

PKGDEST=${_PKGDEST:-$PKGDEST}
PKGDEST=${PKGDEST:-$startdir} #default to $startdir if undefined
if (( ! (NOBUILD || GENINTEG) )) && [[ ! -w $PKGDEST ]]; then
	error "$(gettext "You do not have write permission to store packages in %s.")" "$PKGDEST"
	plain "$(gettext "Aborting...")"
	exit 1
fi

SRCDEST=${_SRCDEST:-$SRCDEST}
SRCDEST=${SRCDEST:-$startdir} #default to $startdir if undefined
if [[ ! -w $SRCDEST ]] ; then
	error "$(gettext "You do not have write permission to store downloads in %s.")" "$SRCDEST"
	plain "$(gettext "Aborting...")"
	exit 1
fi

SRCPKGDEST=${_SRCPKGDEST:-$SRCPKGDEST}
SRCPKGDEST=${SRCPKGDEST:-$startdir} #default to $startdir if undefined
if (( SOURCEONLY )) && [[ ! -w $SRCPKGDEST ]]; then
	error "$(gettext "You do not have write permission to store source tarballs in %s.")" "$SRCPKGDEST"
	plain "$(gettext "Aborting...")"
	exit 1
fi

PKGEXT=${_PKGEXT:-$PKGEXT}
SRCEXT=${_SRCEXT:-$SRCEXT}
GPGKEY=${_GPGKEY:-$GPGKEY}
PACKAGER=${_PACKAGER:-$PACKAGER}

if (( ! INFAKEROOT )); then
	if (( EUID == 0 && ! ASROOT )); then
		# Warn those who like to live dangerously.
		error "$(gettext "Running %s as root is a BAD idea and can cause permanent,\n\
catastrophic damage to your system. If you wish to run as root, please\n\
use the %s option.")" "makepkg" "--asroot"
		exit 1 # $E_USER_ABORT
	elif (( EUID > 0 && ASROOT )); then
		# Warn those who try to use the --asroot option when they are not root
		error "$(gettext "The %s option is meant for the root user only. Please\n\
rerun %s without the %s flag.")" "--asroot" "makepkg" "--asroot"
		exit 1 # $E_USER_ABORT
	elif (( EUID > 0 )) && ! check_buildenv "fakeroot" "y"; then
		warning "$(gettext "Running %s as an unprivileged user will result in non-root\n\
ownership of the packaged files. Try using the %s environment by\n\
placing %s in the %s array in %s.")" "makepkg" "fakeroot" "'fakeroot'" "BUILDENV" "$MAKEPKG_CONF"
		sleep 1
	fi
else
	if [[ -z $FAKEROOTKEY ]]; then
		error "$(gettext "Do not use the %s option. This option is only for use by %s.")" "'-F'" "makepkg"
		exit 1 # TODO: error code
	fi
fi

unset pkgname pkgbase pkgver pkgrel epoch pkgdesc url license groups provides
unset md5sums replaces depends conflicts backup source install changelog build
unset makedepends optdepends options noextract

BUILDFILE=${BUILDFILE:-$BUILDSCRIPT}
if [[ ! -f $BUILDFILE ]]; then
	if [[ -t 0 ]]; then
		error "$(gettext "%s does not exist.")" "$BUILDFILE"
		exit 1
	else
		# PKGBUILD passed through a pipe
		BUILDFILE=/dev/stdin
		source_safe "$BUILDFILE"
	fi
else
	crlftest=$(file "$BUILDFILE" | grep -F 'CRLF' || true)
	if [[ -n $crlftest ]]; then
		error "$(gettext "%s contains %s characters and cannot be sourced.")" "$BUILDFILE" "CRLF"
		exit 1
	fi

	if [[ ${BUILDFILE:0:1} != "/" ]]; then
		BUILDFILE="$startdir/$BUILDFILE"
	fi
	source_safe "$BUILDFILE"
fi

# set defaults if they weren't specified in buildfile
pkgbase=${pkgbase:-${pkgname[0]}}
epoch=${epoch:-0}

if [[ $BUILDDIR = "$startdir" ]]; then
	srcdir="$BUILDDIR/src"
	pkgdir="$BUILDDIR/pkg"
else
	srcdir="$BUILDDIR/$pkgbase/src"
	pkgdir="$BUILDDIR/$pkgbase/pkg"
fi

if (( GENINTEG )); then
	mkdir -p "$srcdir"
	chmod a-s "$srcdir"
	cd_safe "$srcdir"
	download_sources fast
	generate_checksums
	exit 0 # $E_OK
fi

if declare -f pkgver >/dev/null; then
	PKGVERFUNC=1
fi

# check the PKGBUILD for some basic requirements
check_sanity || exit 1

# check we have the software required to process the PKGBUILD
check_software || exit 1

if (( ${#pkgname[@]} > 1 )); then
	SPLITPKG=1
fi

# test for available PKGBUILD functions
if declare -f prepare >/dev/null; then
	PREPAREFUNC=1
fi
if declare -f build >/dev/null; then
	BUILDFUNC=1
fi
if declare -f check >/dev/null; then
	# "Hide" check() function if not going to be run
	if [[ $RUN_CHECK = 'y' ]] || { ! check_buildenv "check" "n" && [[ $RUN_CHECK != "n" ]]; }; then
		CHECKFUNC=1
	fi
fi
if declare -f package >/dev/null; then
	PKGFUNC=1
elif [[ $SPLITPKG -eq 0 ]] && declare -f package_${pkgname} >/dev/null; then
	SPLITPKG=1
fi

if [[ -n "${PKGLIST[@]}" ]]; then
	unset pkgname
	pkgname=("${PKGLIST[@]}")
fi

# check if gpg signature is to be created and if signing key is valid
if { [[ -z $SIGNPKG ]] && check_buildenv "sign" "y"; } || [[ $SIGNPKG == 'y' ]]; then
	if ! gpg --list-key ${GPGKEY} &>/dev/null; then
		if [[ ! -z $GPGKEY ]]; then
			error "$(gettext "The key %s does not exist in your keyring.")" "${GPGKEY}"
		else
			error "$(gettext "There is no key in your keyring.")"
		fi
		exit 1
	fi
fi

if (( ! PKGVERFUNC )); then
	check_build_status
fi

# Run the bare minimum in fakeroot
if (( INFAKEROOT )); then
	if (( SOURCEONLY )); then
		create_srcpackage
		msg "$(gettext "Leaving %s environment.")" "fakeroot"
		exit 0 # $E_OK
	fi

	if (( ! SPLITPKG )); then
		if (( ! PKGFUNC )); then
			if (( ! REPKG )); then
				if (( BUILDFUNC )); then
					run_build
					(( CHECKFUNC )) && run_check
					tidy_install
				fi
			else
				warning "$(gettext "Repackaging without the use of a %s function is deprecated.")" "package()"
				plain "$(gettext "File permissions may not be preserved.")"
			fi
		else
			run_package
			tidy_install
		fi
		create_package
	else
		run_split_packaging
	fi

	msg "$(gettext "Leaving %s environment.")" "fakeroot"
	exit 0 # $E_OK
fi

fullver=$(get_full_version)
msg "$(gettext "Making package: %s")" "$pkgbase $fullver ($(date))"

# if we are creating a source-only package, go no further
if (( SOURCEONLY )); then
	if [[ -f $SRCPKGDEST/${pkgbase}-${fullver}${SRCEXT} ]] \
	     && (( ! FORCE )); then
		error "$(gettext "A source package has already been built. (use %s to overwrite)")" "-f"
		exit 1
	fi

	# Get back to our src directory so we can begin with sources.
	mkdir -p "$srcdir"
	chmod a-s "$srcdir"
	cd_safe "$srcdir"
	if (( SOURCEONLY == 2 )); then
		download_sources
	elif ( (( ! SKIPCHECKSUMS )) || \
			( (( ! SKIPPGPCHECK )) && source_has_signatures ) ); then
		download_sources fast
	fi
	check_source_integrity
	cd_safe "$startdir"

	# if we are root or if fakeroot is not enabled, then we don't use it
	if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then
		create_srcpackage
	else
		enter_fakeroot
	fi

	msg "$(gettext "Source package created: %s")" "$pkgbase ($(date))"
	exit 0
fi

if (( NODEPS || (NOBUILD && !DEP_BIN ) )); then
	# no warning message needed for nobuild
	if (( NODEPS )); then
		warning "$(gettext "Skipping dependency checks.")"
	fi
elif type -p "${PACMAN%% *}" >/dev/null; then
	if (( RMDEPS && ! INSTALL )); then
		original_pkglist=($(run_pacman -Qq))    # required by remove_dep
	fi
	deperr=0

	msg "$(gettext "Checking runtime dependencies...")"
	resolve_deps ${depends[@]} || deperr=1

	if (( RMDEPS && INSTALL )); then
		original_pkglist=($(run_pacman -Qq))    # required by remove_dep
	fi

	msg "$(gettext "Checking buildtime dependencies...")"
	resolve_deps ${makedepends[@]} || deperr=1

	if (( CHECKFUNC )); then
		resolve_deps ${checkdepends[@]} || deperr=1
	fi

	if (( RMDEPS )); then
		current_pkglist=($(run_pacman -Qq))    # required by remove_deps
	fi

	if (( deperr )); then
		error "$(gettext "Could not resolve all dependencies.")"
		exit 1
	fi
else
	warning "$(gettext "%s was not found in %s; skipping dependency checks.")" "${PACMAN%% *}" "PATH"
fi

# ensure we have a sane umask set
umask 0022

# get back to our src directory so we can begin with sources
mkdir -p "$srcdir"
chmod a-s "$srcdir"
cd_safe "$srcdir"

if (( NOEXTRACT )); then
	warning "$(gettext "Using existing %s tree")" "src/"
elif (( REPKG )); then
	if (( ! PKGFUNC && ! SPLITPKG )) \
	     && { [[ ! -d $pkgdir ]] || dir_is_empty "$pkgdir"; }; then
		error "$(gettext "The package directory is empty, there is nothing to repackage!")"
		plain "$(gettext "Aborting...")"
		exit 1
	fi
else
	download_sources
	check_source_integrity
	extract_sources
	if (( PREPAREFUNC )); then
		run_prepare
	fi
fi

if (( NOBUILD )); then
	msg "$(gettext "Sources are ready.")"
	exit 0 #E_OK
else
	# check for existing pkg directory; don't remove if we are repackaging
	if [[ -d $pkgdir ]] && (( ! REPKG || PKGFUNC || SPLITPKG )); then
		msg "$(gettext "Removing existing %s directory...")" "pkg/"
		rm -rf "$pkgdir"
	fi
	mkdir -p "$pkgdir"
	chmod a-s "$pkgdir"
	cd_safe "$startdir"

	# if we are root or if fakeroot is not enabled, then we don't use it
	if ! check_buildenv "fakeroot" "y" || (( EUID == 0 )); then
		if (( ! REPKG )); then
			(( BUILDFUNC )) && run_build
			(( CHECKFUNC )) && run_check
		fi
		if (( ! SPLITPKG )); then
			if (( PKGFUNC )); then
				run_package
				tidy_install
			else
				if (( ! REPKG )); then
					tidy_install
				else
					warning "$(gettext "Repackaging without the use of a %s function is deprecated.")" "package()"
					plain "$(gettext "File permissions may not be preserved.")"
				fi
			fi
			create_package
		else
			run_split_packaging
		fi
	else
		if (( ! REPKG && ( PKGFUNC || SPLITPKG ) )); then
			(( BUILDFUNC )) && run_build
			(( CHECKFUNC )) && run_check
			cd_safe "$startdir"
		fi

		enter_fakeroot
	fi
fi

fullver=$(get_full_version)
msg "$(gettext "Finished making: %s")" "$pkgbase $fullver ($(date))"

install_package

exit 0 #E_OK

# vim: set ts=2 sw=2 noet:
