#!/bin/bash -e
#
#   makepkg - make packages compatible for use with pacman
#   @configure_input@
#
#   Copyright (c) 2006-2010 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:
#   bsdtar (libarchive), bzip2, coreutils, fakeroot, find (findutils),
#   gettext, grep, gzip, openssl, sed, tput (ncurses), xz

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

# file -i does not work on Mac OSX unless legacy mode is set
export COMMAND_MODE='legacy'

myver='@PACKAGE_VERSION@'
confdir='@sysconfdir@'
BUILDSCRIPT='@BUILDSCRIPT@'
startdir="$PWD"
srcdir="$startdir/src"
pkgdir="$startdir/pkg"

packaging_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'purge')
other_options=('ccache' 'distcc' 'makeflags' 'force')
splitpkg_overrides=('pkgver' 'pkgrel' '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
CLEANCACHE=0
DEP_BIN=0
FORCE=0
INFAKEROOT=0
GENINTEG=0
SKIPINTEG=0
INSTALL=0
NOBUILD=0
NODEPS=0
NOEXTRACT=0
RMDEPS=0
REPKG=0
LOGGING=0
SOURCEONLY=0
IGNOREARCH=0
HOLDVER=0
BUILDFUNC=0
PKGFUNC=0
SPLITPKG=0
PKGLIST=""

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

PACMAN_OPTS=

### 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() {
	if (( ! INFAKEROOT )); then
		echo
		error "$@"
	fi
	[[ -n $srclinks ]] && rm -rf "$srclinks"
	exit 1
}


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

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

	remove_deps
}


##
# Signal Traps
##
set -E
trap 'clean_up' 0
trap 'trap_exit "$(gettext "TERM signal caught. Exiting...")"' TERM HUP QUIT
trap 'trap_exit "$(gettext "Aborted by user! Exiting...")"' INT
trap 'trap_exit "$(gettext "An unknown error has occurred. Exiting...")"' ERR

# 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#*::}"
}

##
# 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() {
	echo $(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
		opt="${opt,,}"
		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
	[[ -z $1 ]] && return 1 # Not Found
	local item
	for item in "$@"; do
		[[ $item = $needle ]] && return 0 # Found
	done
	return 1 # Not Found
}

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
			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=$(basename $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 ret=0
	if (( ! ASROOT )) && [[ $1 != "-T" && $1 != "-Qq" ]]; then
		if [ "$(type -p sudo)" ] && sudo -l $PACMAN &>/dev/null; then
			sudo $PACMAN $PACMAN_OPTS "$@" || ret=$?
		else
			su -c "$PACMAN $PACMAN_OPTS $*" || ret=$?
		fi
	else
		$PACMAN $PACMAN_OPTS "$@" || ret=$?
	fi
	return $ret
}

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

	local ret=0
	pmout=$(run_pacman -T "$@")
	ret=$?
	if (( ret == 127 )); then #unresolved deps
		echo "$pmout"
	elif (( ret )); then
		error "$(gettext "'%s' returned a fatal error (%i): %s")" "$PACMAN" "$ret" "$pmout"
		exit 1
	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
	local restoretrap=$(trap -p ERR)
	trap - ERR
	source /etc/profile &>/dev/null
	eval $restoretrap

	return $R_DEPS_SATISFIED
}

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

	local deplist="$(check_deps $*)"
	if [[ -z $deplist ]]; then
		return $R_DEPS_SATISFIED
	fi

	if handle_deps $deplist; then
		# check deps again to make sure they were resolved
		deplist="$(check_deps $*)"
		[[ -z $deplist ]] && return $R_DEPS_SATISFIED
	elif (( DEP_BIN )); then
		error "$(gettext "Failed to install all missing dependencies.")"
	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  $(comm -23 <(printf "%s\n" "${original_pkglist[@]}") \
			<(printf "%s\n" "${current_pkglist[@]}")) ]]; then
	  warning "$(gettext "Failed to remove installed dependencies.")"
	  return 0
	fi

	local deplist=($(comm -13  <(printf "%s\n" "${original_pkglist[@]}") \
			<(printf "%s\n" "${current_pkglist[@]}")))
	(( ${#deplist[@]} == 0 )) && return

	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
		if file=$(get_filepath "$netfile"); then
			msg2 "$(gettext "Found %s")" "${file##*/}"
			ln -sf "$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=$(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) ]; then
		error "$(gettext "Cannot find 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
		integ="${integ,,}"
		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="$(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() {
	(( ! ${#source[@]} )) && return 0

	if [ ! $(type -p openssl) ]; then
		error "$(gettext "Cannot find openssl.")"
		exit 1 # $E_MISSING_PROGRAM
	fi

	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
					echo "$(gettext "NOT FOUND")" >&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
						echo "$(gettext "Passed")" >&2
					else
						echo "$(gettext "FAILED")" >&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
}

extract_sources() {
	msg "$(gettext "Extracting Sources...")"
	local netfile
	for netfile in "${source[@]}"; do
		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 ;;
			*)
				# Don't know what to use to extract this file,
				# skip to the next file
				continue;;
		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
		plain "$(gettext "Aborting...")"
		remove_deps
	fi
	exit 2 # $E_BUILD_FAILED
}

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

	# clear user-specified makeflags if requested
	if [[ $(check_option makeflags) = "n" ]]; then
		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
	if (( LOGGING )); then
		BUILDLOG="${startdir}/${pkgbase}-${pkgver}-${pkgrel}-${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")
		mknod "$logpipe" p
		exec 3>&1
		tee "$BUILDLOG" < "$logpipe" &
		exec 1>"$logpipe" 2>"$logpipe"
		restoretrap=$(trap -p ERR)
		trap 'error_function' ERR
		$pkgfunc 2>&1
		eval $restoretrap
		sync
		exec 1>&3 2>&3 3>&-
		rm "$logpipe"
	else
		restoretrap=$(trap -p ERR)
		trap 'error_function' 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
	elif [[ $(check_option distcc) = "n" ]]; then
		# if it is not wanted, clear the makeflags too
		MAKEFLAGS=""
	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_package() {
	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 other 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"
					ln -sf "${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 if find returned an
					#   error, such as when one of the man directories doesn't exist
					hardlinks="$(find ${MAN_DIRS[@]} \! -name "$file" -samefile "$manpage" 2>/dev/null)" || true
					for hl in ${hardlinks}; do
						rm -f "${hl}";
					done
					# 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 && -n ${STRIP_DIRS[*]} ]]; then
		msg2 "$(gettext "Stripping unneeded symbols from binaries and libraries...")"
		local binary
		find ${STRIP_DIRS[@]} -type f -perm -u+w 2>/dev/null | while read binary ; do
			case "$(file -bi "$binary")" in
				*application/x-sharedlib*)  # Libraries (.so)
					/usr/bin/strip $STRIP_SHARED "$binary";;
				*application/x-archive*)    # Libraries (.a)
					/usr/bin/strip $STRIP_STATIC "$binary";;
				*application/x-executable*) # Binaries
					/usr/bin/strip $STRIP_BINARIES "$binary";;
			esac
		done
	fi

	if [[ $(check_option libtool) = "n" ]]; then
		msg2 "$(gettext "Removing libtool .la files...")"
		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
}

write_pkginfo() {
	local builddate=$(date -u "+%s")
	if [[ -n $PACKAGER ]]; then
		local packager="$PACKAGER"
	else
		local packager="Unknown Packager"
	fi
	local size="$(du -sk)"
	size="$(( ${size%%[^0-9]*} * 1024 ))"

	msg2 "$(gettext "Generating .PKGINFO file...")"
	echo "# Generated by makepkg $myver" >.PKGINFO
	if (( INFAKEROOT )); then
		echo "# using $(fakeroot -v)" >>.PKGINFO
	fi
	echo "# $(LC_ALL=C date -u)" >>.PKGINFO
	echo "pkgname = $1" >>.PKGINFO
	(( SPLITPKG )) && echo pkgbase = $pkgbase >>.PKGINFO
	echo "pkgver = $pkgver-$pkgrel" >>.PKGINFO
	echo "pkgdesc = $pkgdesc" >>.PKGINFO
	echo "url = $url" >>.PKGINFO
	echo "builddate = $builddate" >>.PKGINFO
	echo "packager = $packager" >>.PKGINFO
	echo "size = $size" >>.PKGINFO
	echo "arch = $PKGARCH" >>.PKGINFO
	if [[ $(check_option force) = "y" ]]; then
		echo "force = true" >> .PKGINFO
	fi

	local it
	for it in "${license[@]}"; do
		echo "license = $it" >>.PKGINFO
	done
	for it in "${replaces[@]}"; do
		echo "replaces = $it" >>.PKGINFO
	done
	for it in "${groups[@]}"; do
		echo "group = $it" >>.PKGINFO
	done
	for it in "${depends[@]}"; do
		echo "depend = $it" >>.PKGINFO
	done
	for it in "${optdepends[@]}"; do
		echo "optdepend = $it" >>.PKGINFO
	done
	for it in "${conflicts[@]}"; do
		echo "conflict = $it" >>.PKGINFO
	done
	for it in "${provides[@]}"; do
		echo "provides = $it" >>.PKGINFO
	done
	for it in "${backup[@]}"; do
		echo "backup = $it" >>.PKGINFO
	done
	for it in "${packaging_options[@]}"; do
		local ret="$(check_option $it)"
		if [[ $ret != "?" ]]; then
			if [[ $ret = y ]]; then
				echo "makepkgopt = $it" >>.PKGINFO
			else
				echo "makepkgopt = !$it" >>.PKGINFO
			fi
		fi
	done

	# 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: license=('GPL').")"
	fi
}

check_package() {
	cd "$pkgdir"

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

	# check for references to the build directory
	if grep -R "${srcdir}" "${pkgdir}" &>/dev/null; then
		warning "$(gettext "Package contains reference to %s")" "\$srcdir"
	fi
}

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

	check_package

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

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

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

	write_pkginfo $nameofpkg

	local comp_files=".PKGINFO"

	# check for an install script
	if [[ -n $install ]]; then
		msg2 "$(gettext "Adding install script...")"
		cp "$startdir/$install" .INSTALL
		chmod 644 .INSTALL
		comp_files="$comp_files .INSTALL"
	fi

	# do we have a changelog?
	if [[ -n $changelog ]]; then
		msg2 "$(gettext "Adding package changelog...")"
		cp "$startdir/$changelog" .CHANGELOG
		chmod 644 .CHANGELOG
		comp_files="$comp_files .CHANGELOG"
	fi

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

	case "$PKGEXT" in
		*tar.gz)  EXT=${PKGEXT%.gz} ;;
		*tar.bz2) EXT=${PKGEXT%.bz2} ;;
		*tar.xz)  EXT=${PKGEXT%.xz} ;;
		*) warning "$(gettext "'%s' is not a valid archive extension.")" \
		"$PKGEXT" ; EXT=$PKGEXT ;;
	esac
	local tar_file="$PKGDEST/${nameofpkg}-${pkgver}-${pkgrel}-${PKGARCH}${EXT}"
	local pkg_file="$PKGDEST/${nameofpkg}-${pkgver}-${pkgrel}-${PKGARCH}${PKGEXT}"

	local ret=0

	# when fileglobbing, we want * in an empty directory to expand to
	# the null string rather than itself
	shopt -s nullglob
	bsdtar -cf - $comp_files * > "$tar_file" || ret=$?
	shopt -u nullglob

	if (( ! ret )); then
		case "$PKGEXT" in
			*tar.gz)  gzip -f -n "$tar_file" ;;
			*tar.bz2) bzip2 -f "$tar_file" ;;
			*tar.xz)  xz -z -f "$tar_file" ;;
		esac
		ret=$?
	fi

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

	if (( ! ret )) && [[ "$PKGDEST" != "${startdir}" ]]; then
		ln -sf "${pkg_file}" "${pkg_file/$PKGDEST/$startdir}"
		ret=$?
	fi

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

create_srcpackage() {
	cd "$startdir"

	# Get back to our src directory so we can begin with sources.
	mkdir -p "$srcdir"
	chmod a-s "$srcdir"
	cd "$srcdir"
	if (( ! SKIPINTEG || SOURCEONLY == 2 )); then
		download_sources
	fi
	if (( ! SKIPINTEG )); then
		# We can only check checksums if we have all files.
		check_checksums
	else
		warning "$(gettext "Skipping integrity checks.")"
	fi
	cd "$startdir"

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

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

	local file
	for file in "${source[@]}"; do
		if [[ -f $file ]]; then
			msg2 "$(gettext "Adding %s...")" "$file"
			ln -s "$file" "$srclinks/$pkgbase"
		elif (( SOURCEONLY == 2 )); then
			local 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 $i=$(sed -n "s/^[[:space:]]*$i=//p" "$BUILDSCRIPT")
		local file
		for file in ${!i}; do
			# evaluate any bash variables used
			eval file='${srclinks}/${pkgbase}/'${file}
			if [[ ! -f $file ]]; then
				msg2 "$(gettext "Adding %s file (%s)...")" "$i" "${file##*/}"
				ln -s "${startdir}/$file" "${srclinks}/${pkgbase}/"
			fi
		done
	done

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

	local pkg_file="$SRCPKGDEST/${pkgbase}-${pkgver}-${pkgrel}${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
	cd "${startdir}"
	rm -rf "${srclinks}"
}

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

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

	local pkglist
	for pkg in ${pkgname[@]}; do
		if [[ -f $PKGDEST/${pkg}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT} ]]; then
			pkglist="${pkglist} $PKGDEST/${pkg}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT}"
		else
			pkglist="${pkglist} $PKGDEST/${pkg}-${pkgver}-${pkgrel}-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
	if [[ -z $pkgname ]]; then
		error "$(gettext "%s is not allowed to be empty.")" "pkgname"
		return 1
	fi
	if [[ -z $pkgver ]]; then
		error "$(gettext "%s is not allowed to be empty.")" "pkgver"
		return 1
	fi
	if [[ -z $pkgrel ]]; then
		error "$(gettext "%s is not allowed to be empty.")" "pkgrel"
		return 1
	fi

	local name
	for name in "${pkgname[@]}"; do
		if [[ ${name:0:1} = "-" ]]; then
			error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgname"
			return 1
		fi
	done

	if [[ ${pkgbase:0:1} = "-" ]]; then
		error "$(gettext "%s is not allowed to start with a hyphen.")" "pkgbase"
		return 1
	fi
	if [[ $pkgver != ${pkgver//-/} ]]; then
		error "$(gettext "%s is not allowed to contain hyphens.")" "pkgver"
		return 1
	fi
	if [[ $pkgrel != ${pkgrel//-/} ]]; then
		error "$(gettext "%s is not allowed to contain hyphens.")" "pkgrel"
		return 1
	fi

	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 arch=('%s').")" "$CARCH"
				return 1
			fi
		fi
	fi

	local provide
	for provide in ${provides[@]}; do
		if [[ $provide != ${provide//</} || $provide != ${provide//>/} ]]; then
			error "$(gettext "Provides array cannot contain comparison (< or >) operators.")"
			return 1
		fi
	done

	local file
	for file in "${backup[@]}"; do
		if [[ ${file:0:1} = "/" ]]; then
			error "$(gettext "Invalid backup entry : %s")" "$file"
			return 1
		fi
	done

	local optdepend
	for optdepend in "${optdepends[@]}"; do
		pkg=${optdepend%%:*}
		if [[ ! $pkg =~ ^[[:alnum:]\>\<\=\.\+\_\-]*$ ]]; then
			error "$(gettext "Invalid syntax for optdepend : '%s'")" "$optdepend"
		fi
	done

	local i
	for i in 'changelog' 'install'; do
		local $i=$(sed -n "s/^[[:space:]]*$i=//p" "$BUILDSCRIPT")
		local file
		for file in ${!i}; do
			# evaluate any bash variables used
			eval file=${file}
			if [[ ! -f $file ]]; then
				error "$(gettext "%s file (%s) does not exist.")" "${i^}" "$file"
				return 1
			fi
		done
	done

	local valid_options=1
	local opt known kopt
	for opt in ${options[@]}; do
		known=0
		# check if option matches a known option or its inverse
		for kopt in ${packaging_options[@]} ${other_options[@]}; do
			if [[ ${opt} = ${kopt} || ${opt} = "!${kopt}" ]]; then
				known=1
			fi
		done
		if (( ! known )); then
			error "$(gettext "options array contains unknown option '%s'")" "$opt"
			valid_options=0
		fi
	done
	if (( ! valid_options )); then
		return 1
	fi

	if (( ${#pkgname[@]} > 1 )); then
		for pkg in ${pkgname[@]}; do
			if [ "$(type -t package_${pkg})" != "function" ]; then
				error "$(gettext "missing package function for split package '%s'")" "$pkg"
				return 1
			fi
		done
	fi

	if [[ -n "${PKGLIST[@]}" ]]; then
		for pkg in ${PKGLIST[@]}; do
			if ! in_array $pkg ${pkgname[@]}; then
				error "$(gettext "requested package %s is not provided in %s")" "$pkg" "$BUILDSCRIPT"
				return 1
			fi
		done
	fi

	return 0
}

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 ]]; 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 brief check to make sure we have the VCS tool available.
		oldpkgver=$pkgver
		if [[ -n ${_darcstrunk} && -n ${_darcsmod} ]] ; then
			[ $(type -p darcs) ] || return 0
			msg "$(gettext "Determining latest darcs revision...")"
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_cvsroot} && -n ${_cvsmod} ]] ; then
			[ $(type -p cvs) ] || return 0
			msg "$(gettext "Determining latest cvs revision...")"
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_gitroot} && -n ${_gitname} ]] ; then
			[ $(type -p git) ] || return 0
			msg "$(gettext "Determining latest git revision...")"
			newpkgver=$(date +%Y%m%d)
		elif [[ -n ${_svntrunk} && -n ${_svnmod} ]] ; then
			[ $(type -p svn) ] || return 0
			msg "$(gettext "Determining latest svn revision...")"
			newpkgver=$(LC_ALL=C svn info $_svntrunk | sed -n 's/^Last Changed Rev: \([0-9]*\)$/\1/p')
		elif [[ -n ${_bzrtrunk} && -n ${_bzrmod} ]] ; then
			[ $(type -p bzr) ] || return 0
			msg "$(gettext "Determining latest bzr revision...")"
			newpkgver=$(bzr revno ${_bzrtrunk})
		elif [[ -n ${_hgroot} && -n ${_hgrepo} ]] ; then
			[ $(type -p hg) ] || return 0
			msg "$(gettext "Determining latest hg revision...")"
			if [[ -d ./src/$_hgrepo ]] ; then
				cd ./src/$_hgrepo
				hg pull
				hg update
			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() {
	for var in ${splitpkg_overrides[@]}; do
		indirect="${var}_backup"
		eval "${indirect}=(\"\${$var[@]}\")"
	done
}

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

# getopt like parser
parse_options() {
	local short_options=$1; shift;
	local long_options=$1; shift;
	local ret=0;
	local unused_options=""

	while [[ -n $1 ]]; do
		if [[ ${1:0:2} = '--' ]]; then
			if [[ -n ${1:2} ]]; then
				local match=""
				for i in ${long_options//,/ }; do
					if [[ ${1:2} = ${i//:} ]]; then
						match=$i
						break
					fi
				done
				if [[ -n $match ]]; then
					if [[ ${1:2} = $match ]]; then
						printf ' %s' "$1"
					else
						if [[ -n $2 ]]; then
							printf ' %s' "$1"
							shift
							printf " '%s'" "$1"
						else
							echo "makepkg: option '$1' $(gettext "requires an argument")" >&2
							ret=1
						fi
					fi
				else
					echo "makepkg: $(gettext "unrecognized option") '$1'" >&2
					ret=1
				fi
			else
				shift
				break
			fi
		elif [[ ${1:0:1} = '-' ]]; then
			for ((i=1; i<${#1}; i++)); do
				if [[ $short_options =~ ${1:i:1} ]]; then
					if [[ $short_options =~ "${1:i:1}:" ]]; then
						if [[ -n ${1:$i+1} ]]; then
							printf ' -%s' "${1:i:1}"
							printf " '%s'" "${1:$i+1}"
						else
							if [[ -n $2 ]]; then
								printf ' -%s' "${1:i:1}"
								shift
								printf " '%s'" "${1}"
							else
								echo "makepkg: option $(gettext "requires an argument") -- '${1:i:1}'" >&2
								ret=1
							fi
						fi
						break
					else
						printf ' -%s' "${1:i:1}"
					fi
				else
					echo "makepkg: $(gettext "invalid option") -- '${1:i:1}'" >&2
					ret=1
				fi
			done
		else
			unused_options="${unused_options} '$1'"
		fi
		shift
	done

	printf " --"
	if [[ -n $unused_options ]]; then
		for i in ${unused_options[@]}; do
			printf ' %s' "$i"
		done
	fi
	if [[ -n $1 ]]; then
		while [[ -n $1 ]]; do
			printf " '%s'" "${1}"
			shift
		done
	fi
	printf "\n"

	return $ret
}

usage() {
	printf "makepkg (pacman) %s\n" "$myver"
	echo
	printf "$(gettext "Usage: %s [options]")\n" "$0"
	echo
	echo "$(gettext "Options:")"
	printf "$(gettext "  -A, --ignorearch Ignore incomplete arch field in %s")\n" "$BUILDSCRIPT"
	echo "$(gettext "  -c, --clean      Clean up work files after build")"
	echo "$(gettext "  -C, --cleancache Clean up source files from the cache")"
	echo "$(gettext "  -d, --nodeps     Skip all dependency checks")"
	echo "$(gettext "  -e, --noextract  Do not extract source files (use existing src/ dir)")"
	echo "$(gettext "  -f, --force      Overwrite existing package")"
	echo "$(gettext "  -g, --geninteg   Generate integrity checks for source files")"
	echo "$(gettext "  -h, --help       This help")"
	echo "$(gettext "  -i, --install    Install package after successful build")"
	echo "$(gettext "  -L, --log        Log package build process")"
	echo "$(gettext "  -m, --nocolor    Disable colorized output messages")"
	echo "$(gettext "  -o, --nobuild    Download and extract files only")"
	printf "$(gettext "  -p <file>        Use an alternate build script (instead of '%s')")\n" "$BUILDSCRIPT"
	echo "$(gettext "  -r, --rmdeps     Remove installed dependencies after a successful build")"
	echo "$(gettext "  -R, --repackage  Repackage contents of the package without rebuilding")"
	echo "$(gettext "  -s, --syncdeps   Install missing dependencies with pacman")"
	echo "$(gettext "  --allsource      Generate a source-only tarball including downloaded sources")"
	echo "$(gettext "  --asroot         Allow makepkg to run as root user")"
	printf "$(gettext "  --config <file>  Use an alternate config file (instead of '%s')")\n" "$confdir/makepkg.conf"
	echo "$(gettext "  --holdver        Prevent automatic version bumping for development PKGBUILDs")"
	echo "$(gettext "  --pkg <list>     Only build listed packages from a split package")"
	echo "$(gettext "  --skipinteg      Do not fail when integrity checks are missing")"
	echo "$(gettext "  --source         Generate a source-only tarball without downloaded sources")"
	echo
	echo "$(gettext "These options can be passed to pacman:")"
	echo
	echo "$(gettext "  --noconfirm      Do not ask for confirmation when resolving dependencies")"
	echo "$(gettext "  --noprogressbar  Do not show a progress bar when downloading files")"
	echo
	printf "$(gettext "If -p is not specified, makepkg will look for '%s'")\n" "$BUILDSCRIPT"
	echo
}

version() {
	printf "makepkg (pacman) %s\n" "$myver"
	printf "$(gettext "\
Copyright (c) 2006-2010 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 -t gettext) ]; then
	gettext() {
		echo "$@"
	}
fi

ARGLIST=("$@")

# Parse Command Line Options.
OPT_SHORT="AcCdefFghiLmop:rRsV"
OPT_LONG="allsource,asroot,ignorearch,clean,cleancache,nodeps"
OPT_LONG="$OPT_LONG,noextract,force,forcever:,geninteg,help,holdver"
OPT_LONG="$OPT_LONG,install,log,nocolor,nobuild,pkg:,rmdeps,repackage,skipinteg"
OPT_LONG="$OPT_LONG,source,syncdeps,version,config:"
# Pacman Options
OPT_LONG="$OPT_LONG,noconfirm,noprogressbar"
OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@" || echo 'PARSE_OPTIONS FAILED')"
if [[ $OPT_TEMP = *'PARSE_OPTIONS FAILED'* ]]; then
	# This is a small hack to stop the script bailing with 'set -e'
	echo; usage; exit 1 # E_INVALID_OPTION;
fi
eval set -- "$OPT_TEMP"
unset OPT_SHORT OPT_LONG OPT_TEMP

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

		# Makepkg Options
		--allsource)      SOURCEONLY=2 ;;
		--asroot)         ASROOT=1 ;;
		-A|--ignorearch)  IGNOREARCH=1 ;;
		-c|--clean)       CLEANUP=1 ;;
		-C|--cleancache)  CLEANCACHE=1 ;;
		--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 ;;
		-L|--log)         LOGGING=1 ;;
		-m|--nocolor)     USE_COLOR='n' ;;
		-o|--nobuild)     NOBUILD=1 ;;
		-p)               shift; BUILDFILE=$1 ;;
		--pkg)            shift; PKGLIST=$1 ;;
		-r|--rmdeps)      RMDEPS=1 ;;
		-R|--repackage)   REPKG=1 ;;
		--skipinteg)      SKIPINTEG=1 ;;
		--source)         SOURCEONLY=1 ;;
		-s|--syncdeps)    DEP_BIN=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

#preserve environment variables
_PKGDEST=${PKGDEST}
_SRCDEST=${SRCDEST}
_SRCPKGDEST=${SRCPKGDEST}

# 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
if [[ -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
	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)"
fi
readonly ALL_OFF BOLD BLUE GREEN RED YELLOW

# override settings with an environment variable for batch processing
PKGDEST=${_PKGDEST:-$PKGDEST}
PKGDEST=${PKGDEST:-$startdir} #default to $startdir if undefined
if [[ ! -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:-$PKGDEST} #default to $PKGDEST if undefined


if (( HOLDVER )) && [[ -n $FORCE_VER ]]; then
	# The '\\0' is here to prevent gettext from thinking --holdver is an option
	error "$(gettext "\\0--holdver and --forcever cannot both be specified" )"
	exit 1
fi

if (( CLEANCACHE )); then
	#fix flyspray feature request #5223
	if [[ -n $SRCDEST && $SRCDEST != $startdir ]]; then
		msg "$(gettext "Cleaning up ALL files from %s.")" "$SRCDEST"
		echo -n "$(gettext "    Are you sure you wish to do this? ")"
		echo -n "$(gettext "[y/N]")"
		read answer
		answer="${answer^^}"
		if [[ $answer = $(gettext YES) || $answer = $(gettext Y) ]]; then
			rm "$SRCDEST"/*
			if (( $? )); then
				error "$(gettext "Problem removing files; you may not have correct permissions in %s")" "$SRCDEST"
				exit 1
			else
				# removal worked
				msg "$(gettext "Source cache cleaned.")"
				exit 0
			fi
		else
			# answer = no
			msg "$(gettext "No files have been removed.")"
			exit 0
		fi
	else
		# $SRCDEST is $startdir, two possibilities
		error "$(gettext "Source destination must be defined in %s.")" "$MAKEPKG_CONF"
		plain "$(gettext "In addition, please run makepkg -C outside of your cache directory.")"
		exit 1
	fi
fi

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

# check for sudo if we will need it during makepkg execution
if (( ! ( ASROOT || INFAKEROOT ) && ( DEP_BIN || RMDEPS || INSTALL ) )); then
	if [ ! "$(type -p sudo)" ]; then
		warning "$(gettext "Sudo can not be found. Will use su to acquire root privileges.")"
	fi
fi

unset pkgname pkgbase pkgver pkgrel 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 "$BUILDFILE"
	fi
else
	crlftest=$(file "$BUILDFILE" | grep -F 'CRLF' || true)
	if [[ -n $crlftest ]]; then
		error "$(gettext "%s contains CRLF characters and cannot be sourced.")" "$BUILDFILE"
		exit 1
	fi

	if [[ ${BUILDFILE:0:1} != "/" ]]; then
		BUILDFILE="$startdir/$BUILDFILE"
	fi
	source "$BUILDFILE"
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

# 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
# The exclamation mark is required here to avoid triggering the ERR trap when
# a tested function does not exist.
if [[ $(! type -t build) = "function" ]]; then
	BUILDFUNC=1
fi
if [ "$(type -t package)" = "function" ]; then
	PKGFUNC=1
elif [ $SPLITPKG -eq 0 -a "$(type -t package_${pkgname})" = "function" ]; then
	SPLITPKG=1
fi

pkgbase=${pkgbase:-${pkgname[0]}}

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

if (( ! SPLITPKG )); then
	if [[ -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT} \
	     || -f $PKGDEST/${pkgname}-${pkgver}-${pkgrel}-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 -f to overwrite)")"
			exit 1
		fi
	fi
else
	allpkgbuilt=1
	somepkgbuilt=0
	for pkg in ${pkgname[@]}; do
		if [[ -f $PKGDEST/${pkg}-${pkgver}-${pkgrel}-${CARCH}${PKGEXT} \
		     || -f $PKGDEST/${pkg}-${pkgver}-${pkgrel}-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 -f to overwrite)")"
				exit 1
			fi
		fi
		if (( somepkgbuilt )); then
			error "$(gettext "Part of the package group has already been built. (use -f to overwrite)")"
			exit 1
		fi
	fi
	unset allpkgbuilt somepkgbuilt
fi

# Run the bare minimum in fakeroot
if (( INFAKEROOT )); then
	if (( ! SPLITPKG )); then
		if (( ! PKGFUNC )); then
			if (( ! REPKG )); then
				if (( BUILDFUNC )); then
					run_build
					tidy_install
				fi
			else
				warning "$(gettext "Repackaging without the use of a package() function is deprecated.")"
				plain "$(gettext "File permissions may not be preserved.")"
			fi
		else
			run_package
			tidy_install
		fi
		create_package
	else
		for pkg in ${pkgname[@]}; do
			pkgdir="$pkgdir/$pkg"
			mkdir -p "$pkgdir"
			chmod a-s "$pkgdir"
			backup_package_variables
			run_package $pkg
			tidy_install
			create_package $pkg
			restore_package_variables
			pkgdir="${pkgdir%/*}"
		done
	fi

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

msg "$(gettext "Making package: %s")" "$pkgbase $pkgver-$pkgrel ($(date))"

# if we are creating a source-only package, go no further
if (( SOURCEONLY )); then
	if [[ -f $SRCPKGDEST/${pkgbase}-${pkgver}-${pkgrel}${SRCEXT} ]] \
	     && (( ! FORCE )); then
		error "$(gettext "A source package has already been built. (use -f to overwrite)")"
		exit 1
	fi
	create_srcpackage
	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%% *}") ]; then
	if (( RMDEPS )); then
		original_pkglist=($(run_pacman -Qq | sort))    # required by remove_dep
	fi
	deperr=0

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

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

	if (( RMDEPS )); then
		current_pkglist=($(run_pacman -Qq | sort))    # 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 PATH; skipping dependency checks.")" "${PACMAN%% *}"
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 src/ tree")"
	warning "$(gettext "Skipping source integrity checks -- using existing src/ tree")"
	warning "$(gettext "Skipping source extraction       -- using existing src/ tree")"

	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
	if (( ! SKIPINTEG )); then
		check_checksums
	else
		warning "$(gettext "Skipping integrity checks.")"
	fi
	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 pkg/ directory...")"
		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
		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 package() function is deprecated.")"
					plain "$(gettext "File permissions may not be preserved.")"
				fi
			fi
			create_package
		else
			for pkg in ${pkgname[@]}; do
				pkgdir="$pkgdir/$pkg"
				mkdir -p "$pkgdir"
				chmod a-s "$pkgdir"
				backup_package_variables
				run_package $pkg
				tidy_install
				create_package $pkg
				restore_package_variables
				pkgdir="${pkgdir%/*}"
			done
		fi
	else
		if (( ! REPKG && ( PKGFUNC || SPLITPKG ) )); then
			devel_update
			(( BUILDFUNC )) && run_build
			cd "$startdir"
		fi

		msg "$(gettext "Entering fakeroot environment...")"

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

msg "$(gettext "Finished making: %s")" "$pkgbase $pkgver-$pkgrel ($(date))"

install_package

exit 0 #E_OK

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