#!/bin/bash -e
#
#   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 myver='@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' 'license' \
                    'groups' 'depends' 'optdepends' 'provides' 'conflicts' \
                    'replaces' 'backup' 'options' 'install' 'changelog')
readonly -a packaging_options other_options splitpkg_overrides

# Options
ASROOT=0
CLEANUP=0
DEP_BIN=0
FORCE=0
INFAKEROOT=0
GENINTEG=0
SKIPCHECKSUMS=0
SKIPPGPCHECK=0
INSTALL=0
NOBUILD=0
NODEPS=0
NOEXTRACT=0
RMDEPS=0
REPKG=0
LOGGING=0
SOURCEONLY=0
IGNOREARCH=0
HOLDVER=0
BUILDFUNC=0
CHECKFUNC=0
PKGFUNC=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}-*-*-${CARCH}{${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"

	if [[ -n $newpkgver ]]; then
		fakeroot -- $0 --forcever $newpkgver -F "${ARGLIST[@]}" || exit $?
	else
		fakeroot -- $0 -F "${ARGLIST[@]}" || exit $?
	fi
}


# 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
#
# This function accepts a source entry or the already extracted filename of a
# source entry as input
get_filepath() {
	local file="$(get_filename "$1")"

	if [[ -f "$startdir/$file" ]]; then
		file="$startdir/$file"
	elif [[ -f "$SRCDEST/$file" ]]; then
		file="$SRCDEST/$file"
	else
		return 1
	fi

	echo "$file"
}

# 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
}

# extract the filename from a source entry
get_filename() {
	# if a filename is specified, use it
	local filename="${1%%::*}"
	# if it is just an URL, we only keep the last component
	echo "${filename##*/}"
}

# extract the URL from a source entry
get_url() {
	# strip an eventual filename
	echo "${1#*::}"
}

##
#  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
			echo $pkgver-$pkgrel
		else
			echo $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
			echo $pkgver_override-$pkgrel_override
		else
			echo $epoch_override:$pkgver_override-$pkgrel_override
		fi
	fi
}

##
# Checks to see if options are present in makepkg.conf or PKGBUILD;
# PKGBUILD options always take precedence.
#
#  usage : check_option( $option )
# return : y - enabled
#          n - disabled
#          ? - not found
##
check_option() {
	local ret=$(in_opt_array "$1" ${options[@]})
	if [[ $ret != '?' ]]; then
		echo $ret
		return
	fi

	# fall back to makepkg.conf options
	ret=$(in_opt_array "$1" ${OPTIONS[@]})
	if [[ $ret != '?' ]]; then
		echo $ret
		return
	fi

	echo '?' # Not Found
}


##
# Check if option is present in BUILDENV
#
#  usage : check_buildenv( $option )
# return : y - enabled
#          n - disabled
#          ? - not found
##
check_buildenv() {
	in_opt_array "$1" ${BUILDENV[@]}
}


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

	local opt
	for opt in "$@"; do
		if [[ $opt = $needle ]]; then
			echo 'y' # Enabled
			return
		elif [[ $opt = "!$needle" ]]; then
			echo 'n' # Disabled
			return
		fi
	done

	echo '?' # Not Found
}


##
#  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
}

get_downloadclient() {
	# $1 = URL with valid protocol prefix
	local url=$1
	local proto="${url%%://*}"

	# 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 "There is no agent set up to handle %s URLs. Check %s.")" "$proto" "$MAKEPKG_CONF"
		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

	echo "$agent"
}

download_file() {
	# download command
	local dlcmd=$1
	# URL of the file
	local url=$2
	# destination file
	local file=$3
	# 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/\"$file.part\"}
		dlfile="$file.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"
		return $ret
	fi

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

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

	# Disable error trap in pacman subshell call as this breaks bash-3.2 compatibility
	# Also, a non-zero return value is not unexpected and we are manually dealing them
	set +E
	local ret=0
	local pmout
	pmout=$(run_pacman -T "$@") || ret=$?
	set -E

	if (( ret == 127 )); then #unresolved deps
		echo "$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
	# avoid triggering the ERR trap and exiting
	set +e
	local restoretrap=$(trap -p ERR)
	trap - ERR
	source /etc/profile &>/dev/null
	eval $restoretrap
	set -e

	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
}

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

	pushd "$SRCDEST" &>/dev/null

	local netfile
	for netfile in "${source[@]}"; do
		local file=$(get_filepath "$netfile" || true)
		if [[ -n "$file" ]]; then
			msg2 "$(gettext "Found %s")" "${file##*/}"
			rm -f "$srcdir/${file##*/}"
			ln -s "$file" "$srcdir/"
			continue
		fi

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

		# if we get here, check to make sure it was a URL, else fail
		if [[ $file = $url ]]; then
			error "$(gettext "%s was not found in the build directory and is not a URL.")" "$file"
			exit 1 # $E_MISSING_FILE
		fi

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

		msg2 "$(gettext "Downloading %s...")" "$file"
		# fix flyspray bug #3289
		local ret=0
		download_file "$dlclient" "$url" "$file" || ret=$?
		if (( ret )); then
			error "$(gettext "Failure while downloading %s")" "$file"
			plain "$(gettext "Aborting...")"
			exit 1
		fi
		rm -f "$srcdir/$file"
		ln -s "$SRCDEST/$file" "$srcdir/"
	done

	popd &>/dev/null
}

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
		echo ${integlist[@]}
	else
		echo ${INTEGRITY_CHECK[@]}
	fi
}

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

	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[@]}
		echo -n "${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 file
			file="$(get_filepath "$netfile")" || missing_source_file "$netfile"
			local sum="$(openssl dgst -${integ} "$file")"
			sum=${sum##* }
			(( ct )) && echo -n "$indent"
			echo -n "'$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")"
				echo -n "    $file ... " >&2

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

				if (( $found )) ; then
					if [[ ${integrity_sums[$idx]} = 'SKIP' ]]; then
						echo "$(gettext "Skipped")" >&2
					else
						local expectedsum=$(tr '[:upper:]' '[:lower:]' <<< "${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
				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
}

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 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 "$srcdir"

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

	local ret=0
	local restoretrap
	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=$!

		restoretrap=$(trap -p ERR)
		trap 'error_function $pkgfunc' ERR
		$pkgfunc &>"$logpipe"
		eval $restoretrap

		wait $teepid
		rm "$logpipe"
	else
		restoretrap=$(trap -p ERR)
		trap 'error_function $pkgfunc' ERR
		$pkgfunc 2>&1
		eval $restoretrap
	fi
	# reset our shell options
	eval "$shellopts"
}

run_build() {
	# use distcc if it is requested (check buildenv and PKGBUILD opts)
	if [[ $(check_buildenv distcc) = "y" && $(check_option distcc) != "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 "build"
}

run_check() {
	run_function "check"
}

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

	run_function "$pkgfunc"
}

tidy_install() {
	cd "$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 f -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 manpage ext file link hardlinks hl
		find ${MAN_DIRS[@]} -type f 2>/dev/null |
		while read manpage ; do
			ext="${manpage##*.}"
			file="${manpage##*/}"
			if [[ $ext != gz && $ext != bz2 ]]; then
				# update symlinks to this manpage
				find ${MAN_DIRS[@]} -lname "$file" 2>/dev/null |
				while read link ; do
					rm -f "$link" "${link}.gz"
					ln -s -- "${file}.gz" "${link}.gz"
				done

				# check file still exists (potentially already compressed due to hardlink)
				if [[ -f ${manpage} ]]; then
					# find hard links and remove them
					#   the '|| true' part keeps the script from bailing on the EOF returned
					#   by read at the end of the find output
					IFS=$'\n' read -rd '' -a hardlinks < \
						<(find ${MAN_DIRS[@]} \! -name "$file" -samefile "$manpage" \
								2>/dev/null || true) || true
					rm -f "${hardlinks[@]}"
					# compress the original
					gzip -9 "$manpage"
					# recreate hard links removed earlier
					for hl in "${hardlinks[@]}"; do
						ln "${manpage}.gz" "${hl}.gz"
						chmod 644 ${hl}.gz
					done
				fi
			fi
		done
	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 2>/dev/null | while read 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 -empty -delete
	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 libdepends
	find "$pkgdir" -type f -perm -u+x | 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 in_array "${soname}" ${depends[@]}; then
				if ! in_array "${soname}=${soversion}-${soarch}" ${libdepends[@]}; then
					# libfoo.so=1-64
					echo "${soname}=${soversion}-${soarch}"
					libdepends=(${libdepends[@]} "${soname}=${soversion}-${soarch}")
				fi
			fi
		done
	done
}

find_libprovides() {
	local libprovides missing
	for p in ${provides[@]}; do
		missing=0
		case "$p" in
			*.so)
				local 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=(${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=(${libprovides[@]} "${p}=${soversion}-${soarch}")
						else
							warning "$(gettext "Library listed in %s is not a shared object: %s")" "'provides'" "$p"
							libprovides=(${libprovides[@]} "$p")
						fi
					done
				else
					libprovides=(${libprovides[@]} "$p")
					missing=1
				fi
				;;
			*)
				libprovides=(${libprovides[@]} "$p")
				;;
		esac

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

	echo ${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
	local size="$(find . -print0 | xargs -0 @SIZECMD@ | awk '{ sum += $1 } END { print sum }')"

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

	[[ $license ]]    && printf "license = %s\n"   "${license[@]}"
	[[ $replaces ]]   && printf "replaces = %s\n"  "${replaces[@]}"
	[[ $groups ]]     && printf "group = %s\n"     "${groups[@]}"
	[[ $optdepends ]] && printf "optdepend = %s\n" "${optdepends[@]//+([[:space:]])/ }"
	[[ $conflicts ]]  && printf "conflict = %s\n"  "${conflicts[@]}"

	provides=($(find_libprovides))
	[[ $provides ]]   && printf "provides = %s\n"  "${provides[@]}"

	[[ $backup ]]     && printf "backup = %s\n"    "${backup[@]}"


	local it
	libdepends=$(find_libdepends)
	depends=("${depends[@]}" ${libdepends})

	for it in "${depends[@]}"; do
		if [[ $it = *.so ]]; then
			# check if the entry has been found by find_libdepends
			# if not, it's unneeded; tell the user so he can remove it
			printf -v re '(^|\s)%s=.*' "$it"
			if [[ ! $libdepends =~ $re ]]; then
				error "$(gettext "Cannot find library listed in %s: %s")" "'depends'" "$it"
				return 1
			fi
		else
			echo "depend = $it"
		fi
	done

	for it in "${packaging_options[@]}"; do
		local ret="$(check_option $it)"
		if [[ $ret != "?" ]]; then
			if [[ $ret = y ]]; then
				echo "makepkgopt = $it"
			else
				echo "makepkgopt = !$it"
			fi
		fi
	done

	check_license
}

check_package() {
	cd "$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 "$pkgdir"
	msg "$(gettext "Creating package...")"

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

	if [[ $arch = "any" ]]; then
		PKGARCH="any"
	else
		PKGARCH=$CARCH
	fi

	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 "${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 "${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 pkg pkglist
	for pkg in ${pkgname[@]}; do
		fullver=$(get_full_version $pkg)
		if [[ -f $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT} ]]; then
			pkglist+=" $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT}"
		else
			pkglist+=" $PKGDEST/${pkg}-${fullver}-any${PKGEXT}"
		fi
	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' 'pkgver'; 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
	done

	if [[ ${pkgbase:0:1} = "-" ]]; then
		error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgbase"
		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

	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_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 "Sudo can not be found. Will use su to acquire root privileges.")"
		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
}

devel_check() {
	newpkgver=""

	# Do not update pkgver if --holdver is set, when building a source package, repackaging,
	# reading PKGBUILD from pipe (-f), or if we cannot write to the file (-w)
	if (( HOLDVER || SOURCEONLY || REPKG )) ||
		[[ ! -f $BUILDFILE || ! -w $BUILDFILE || $BUILDFILE = /dev/stdin ]]; then
		return
	fi

	if [[ -z $FORCE_VER ]]; then
		# Check if this is a svn/cvs/etc PKGBUILD; set $newpkgver if so.
		# This will only be used on the first call to makepkg; subsequent
		# calls to makepkg via fakeroot will explicitly pass the version
		# number to avoid having to determine the version number twice.
		# Also do a check to make sure we have the VCS tool available.
		oldpkgver=$pkgver
		if [[ -n ${_darcstrunk} && -n ${_darcsmod} ]] ; then
			if ! type -p darcs >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "darcs" "darcs"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'darcs'
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_cvsroot} && -n ${_cvsmod} ]] ; then
			if ! type -p cvs >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "cvs" "cvs"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'cvs'
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_gitroot} && -n ${_gitname} ]] ; then
			if ! type -p git >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "git" "git"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'git'
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_svntrunk} && -n ${_svnmod} ]] ; then
			if ! type -p svn >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "svn" "svn"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'svn'
			newpkgver=$(LC_ALL=C svn info $_svntrunk | sed -n 's/^Last Changed Rev: \([0-9]*\)$/\1/p')
		elif [[ -n ${_bzrtrunk} && -n ${_bzrmod} ]] ; then
			if ! type -p bzr >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "bzr" "bzr"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'bzr'
			newpkgver=$(bzr revno ${_bzrtrunk})
		elif [[ -n ${_hgroot} && -n ${_hgrepo} ]] ; then
			if ! type -p hg >/dev/null; then
				warning "$(gettext "Cannot find the %s binary required to determine latest %s revision.")" "hg" "hg"
				return 0
			fi
			msg "$(gettext "Determining latest %s revision...")" 'hg'
			if [[ -d ./src/$_hgrepo ]] ; then
				cd ./src/$_hgrepo
				local ret=0
				hg pull || ret=$?
				if (( ! ret )); then
					hg update
				elif (( ret != 1 )); then
					return 1
				fi
			else
				[[ ! -d ./src/ ]] && mkdir ./src/
				hg clone $_hgroot/$_hgrepo ./src/$_hgrepo
				cd ./src/$_hgrepo
			fi
			newpkgver=$(hg tip --template "{rev}")
			cd ../../
		fi

		if [[ -n $newpkgver ]]; then
			msg2 "$(gettext "Version found: %s")" "$newpkgver"
		fi

	else
		# Version number retrieved from fakeroot->makepkg argument
		newpkgver=$FORCE_VER
	fi
}

devel_update() {
	# This is lame, but if we're wanting to use an updated pkgver for
	# retrieving svn/cvs/etc sources, we'll update the PKGBUILD with
	# the new pkgver and then re-source it. This is the most robust
	# method for dealing with PKGBUILDs that use, e.g.:
	#
	#  pkgver=23
	#  ...
	#  _foo=pkgver
	#
	if [[ -n $newpkgver ]]; then
		if [[ $newpkgver != "$pkgver" ]]; then
			if [[ -f $BUILDFILE && -w $BUILDFILE ]]; then
				@SEDINPLACE@ "s/^pkgver=[^ ]*/pkgver=$newpkgver/" "$BUILDFILE"
				@SEDINPLACE@ "s/^pkgrel=[^ ]*/pkgrel=1/" "$BUILDFILE"
				source "$BUILDFILE"
			fi
		fi
	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 "$path"
			pwd -P
		)
	else
		echo "$path"
	fi
}

m4_include(library/parse_options.sh)

usage() {
	printf "makepkg (pacman) %s\n" "$myver"
	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        Prevent automatic version bumping for development %ss")\n" "$BUILDSCRIPT"
	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 "  --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" "$myver"
	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() {
		echo "$@"
	}
fi

ARGLIST=("$@")

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

# Pacman Options
OPT_LONG+=",noconfirm,noprogressbar"
if ! parse_options $OPT_SHORT $OPT_LONG "$@"; then
	echo; usage; exit 1 # E_INVALID_OPTION;
fi
set -- "${OPTRET[@]}"
unset OPT_SHORT OPT_LONG OPTRET

while true; do
	case "$1" in
		# Pacman Options
		--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 ;;
		#hidden opt used by fakeroot call for svn/cvs/etc PKGBUILDs to set pkgver
		--forcever)       shift; FORCE_VER=$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; PKGLIST=($1) ;;
		-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;;
		*)                usage; exit 1 ;; # E_INVALID_OPTION
	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
set -E

# 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 "$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 ~/.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 (( HOLDVER )) && [[ -n $FORCE_VER ]]; then
	# The '\\0' is here to prevent gettext from thinking --holdver is an option
	error "$(gettext "\\0%s and %s cannot both be specified" )" "--holdver" "--forcever"
	exit 1
fi

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
		shopt -u extglob
		source "$BUILDFILE"
		shopt -s extglob
	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
	shopt -u extglob
	source "$BUILDFILE"
	shopt -s extglob
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 "$srcdir"
	download_sources
	generate_checksums
	exit 0 # $E_OK
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

# We need to run devel_update regardless of whether we are in the fakeroot
# build process so that if the user runs makepkg --forcever manually, we
# 1) output the correct pkgver, and 2) use the correct filename when
# checking if the package file already exists - fixes FS #9194
devel_check
devel_update

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

# test for available PKGBUILD functions
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
[[ -z $SIGNPKG ]] && SIGNPKG=$(check_buildenv sign)
if [[ $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 (( ! SPLITPKG )); then
	fullver=$(get_full_version)
	if [[ -f $PKGDEST/${pkgname}-${fullver}-${CARCH}${PKGEXT} \
	     || -f $PKGDEST/${pkgname}-${fullver}-any${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)
		if [[ -f $PKGDEST/${pkg}-${fullver}-${CARCH}${PKGEXT} \
		     || -f $PKGDEST/${pkg}-${fullver}-any${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 )); 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

# 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 "$srcdir"
	if ( (( ! SKIPCHECKSUMS )) || \
			( (( ! SKIPPGPCHECK )) && source_has_signatures ) ) || \
			(( SOURCEONLY == 2 )); then
		download_sources
	fi
	check_source_integrity
	cd "$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 || REPKG) && !DEP_BIN ) )); then
	# no warning message needed for nobuild, repkg
	if (( NODEPS || ( REPKG && PKGFUNC ) )); 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 "$srcdir"

if (( NOEXTRACT )); then
	warning "$(gettext "Skipping source retrieval        -- using existing %s tree")" "src/"
	warning "$(gettext "Skipping source integrity checks -- using existing %s tree")" "src/"
	warning "$(gettext "Skipping source extraction       -- using existing %s tree")" "src/"

	if (( NOEXTRACT )) && [[ -z $(ls "$srcdir" 2>/dev/null) ]]; then
		error "$(gettext "The source directory is empty, there is nothing to build!")"
		plain "$(gettext "Aborting...")"
		exit 1
	fi
elif (( REPKG )); then
	if (( ! PKGFUNC && ! SPLITPKG )) \
	     && [[ ! -d $pkgdir || -z $(ls "$pkgdir" 2>/dev/null) ]]; 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
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 "$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
			devel_update
			(( 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
			devel_update
			(( BUILDFUNC )) && run_build
			(( CHECKFUNC )) && run_check
			cd "$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: