#!/bin/bash
################################################################################
#
# cygport - Cygwin packaging application
#
# Copyright (C) 2006, 2007, 2008 Yaakov Selkowitz
# Provided by the Cygwin Ports project <http://cygwinports.dotsrc.org/>
#
# cygport is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cygport 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 cygport.  If not, see <http://www.gnu.org/licenses/>.
#
# $Id: cygport.in 6006 2009-03-12 04:35:22Z yselkowitz $
#
################################################################################

################################################################################
#
# Language extensions
#
# public:
#	defined VARIABLE_NAME
#   popd DIR
#   pushd DIR
#
################################################################################

defined() {
	if (( $# != 1 ))
	then
		error "defined accepts exactly one argument"
	fi

	if [ -n "${!1}" ]
	then
		return 0;
	else
		return 1;
	fi
}

pushd() {
	builtin pushd ${@} > /dev/null
}

popd() {
	builtin popd ${@} > /dev/null
}

readonly -f defined pushd popd
export -f defined pushd popd


################################################################################
#
# Initialization
#
# mutable:
#	PATH
#
# private:
#	argc
#	argv
#	os_name
#	_name
#	_prefix
#	_privbindir
#	_privdatadir
#	_privdocdir
#	_privlibdir
#	_sysconfdir
#	_version
#
################################################################################
set -e;

# Make sure we are on 1.5 before proceeding
case $(uname -r) in
	1.5.*)	;;
	1.7.*)	echo -e " \e[1;31m*\e[0;0m cygport 0.9 is required for Cygwin 1.7."; exit 127 ;;
	*)		echo -e " \e[1;31m*\e[0;0m cygport: Unknown cygwin version: $(uname -r)"; exit 127 ;;
esac

declare -rx _name=cygport;
declare -r  _version=0.4.4;

if defined CYGPORT_BOOTSTRAP && defined top_srcdir
then
	case ${top_srcdir} in
		.*) declare -rx _prefix=$(pwd)/${top_srcdir} ;;
		/*) declare -rx _prefix=${top_srcdir} ;;
    esac

	declare -r _privdatadir=${_prefix}/data;
	declare -r _privdocdir=${_prefix};
	declare -r _sysconfdir=${_prefix}/data;
else
	declare -rx _prefix=/usr/lib/cygport;
	declare -r  _privdatadir=/usr/share/cygport;
	declare -r  _privdocdir=/usr/share/doc/cygport-0.4.4;
	declare -r  _sysconfdir=/etc;
fi

declare -r  _privbindir=${_prefix}/bin;
declare -rx _privlibdir=${_prefix}/lib;

declare -r os_path="$PATH";
declare -x PATH="${_privbindir}:$PATH";

declare -ar argv=(${0} ${@})
declare -ir argc=$(( $# + 1 ))


################################################################################
#
# Help and version messages
#
# private:
#	__show_help
#	__show_version
#
################################################################################

__show_help() {
	cat <<-_EOF
		${_name} is a utility for creating and building Cygwin software packages.

		Usage: ${_name} [/path/to/]PKGNAME-VER-REL[.cygport] COMMAND [COMMAND2 ...]

		COMMAND may be one or more of the following:

		  download     download upstream sources from Internet
		  prep         create working directory, unpack sources and apply patches
		  compile      run all compilation steps
		  install      install into a DESTDIR, and run post-installation steps
		  package      create binary and source packages
		  finish       delete the working directory
		  almostall    run all of the above, excluding finish
		  all          run all of the above, including finish

		See ${_privdocdir}/README for further instructions.

		Report bugs to <cygwin-ports-general@lists.sourceforge.net>.
		_EOF
}

__show_version() {
	cat <<-_EOF
		${_name} ${_version}
		Copyright (C) 2006, 2007, 2008 Yaakov Selkowitz

		This program comes with NO WARRANTY, to the extent permitted by law.

		You may redistribute copies of this program under the terms of
		the GNU General Public License as published by the Free Software
		Foundation, either version 3 of the License, or (at your option) any
		later version.

		For more information about these matters, see the file named COPYING.

		Written by Yaakov Selkowitz for the Cygwin Ports project
		<http://cygwinports.dotsrc.org/>.
		_EOF
}

# Accept --help and --version arguments without specifying a cygport file
case ${argv[1]} in
	--help|-h|-\?)
		__show_help;
		exit 0;
		;;
	--version|-v)
		__show_version;
		exit 0;
		;;
	-*)
		echo "${argv[0]}: unknown argument ${argv[1]}";
		__show_help;
		exit 1;
		;;
esac

# Show help if no commands are given
if ! defined argv[1] || ! defined argv[2]
then
	__show_help;
	exit 1;
fi


################################################################################
#
# Messaging functions
#
# public:
#	error MESSAGE
#	warning MESSAGE
#	inform MESSAGE
#	verbose COMMAND
#
# private:
#	__stage MESSAGE
#	__step MESSAGE
#
################################################################################

# displays error message and exits
error() {
	case $? in
		0) local errorcode=1 ;;
		*) local errorcode=$? ;;
	esac

	echo -e "\e[1;31m*** ERROR:\e[0;0m ${1:-no error message provided}";
	exit ${errorcode};
}

# displays warning message only
warning() {
	echo -e "\e[1;33m*** Warning:\e[0;0m ${1}";
}

# displays information message
inform() {
	echo -e "\e[1;32m*** Info:\e[0;0m ${1}";
}

# displays command to stdout before execution
verbose() {
	echo "${@}"
	"${@}"
	return $?
}

# for internal use only
__stage() {
	echo -e "\e[1;39m>>> ${1} ${PF}\e[0;0m";
}

__step() {
	echo -e ">>> ${1}";
}

# protect functions
readonly -f __show_help __show_version error warning inform verbose __stage __step
export -f error warning inform verbose


################################################################################
#
# Locate the cygport file and set package name and version
#
# public:
#	P
#	PF
#	PN
#	PR
#	PV
#	PV_MAJ
#	PV_MAJ_MIN
#	PVP[]
#	PVR
#
# private:
#	cygportfile
#	top
#
################################################################################
declare -r PF=$(echo ${argv[1]##*/} | sed -e 's!\.cygport!!')
declare -r cygportfile=${PF}.cygport;

_topdir=${argv[1]%/*};

if [ "x${_topdir}" = "x${argv[1]}" ]
then
	if [ -f /usr/src/${cygportfile} ]
	then
		_topdir=/usr/src;
	else
		_topdir=.;
	fi
fi

declare -r top=$(cd ${_topdir}; pwd);
unset _topdir;

if [ ! -e ${top}/${cygportfile} ]
then
	error "${cygportfile} not found.";
fi

declare -rx  PN=$(echo ${PF} | sed -e 's/\-[^\-]*\-[^\-]*$//');
             PV=$(echo ${PF} | sed -e "s/${PN}\-//" -e 's/\-[^\-]*$//');
declare -rx  PR=${PF#${PN}-${PV}-};
declare -rx  P=${PN}-${PV};
declare -rx  PVR=${PV}-${PR};
declare -arx PVP=(${PV//[-\._]/ });
declare -rx  PV=(${PV} ${PVP[*]});
declare -rx  PV_MAJ=${PV[1]};
declare -rx  PV_MAJ_MIN="${PV[1]}.${PV[2]}";


################################################################################
#
# Sanity check functions
#
# mutable:
#   CYGPORT_USE_UNSTABLE_API
#
# public:
#	check_prog EXE
#	check_prog_req EXE [EXE_PACKAGE]
#
# private:
#	__check_function FUNCTION
#	__check_unstable COMMAND [ARGS...]
#	__version_at_least VERSION_REQUIRED VERSION_HAVE
#
################################################################################

# check that a program is found in PATH
check_prog() {
	local _prog;

	for _prog
	do
		if ! hash ${_prog} &> /dev/null
		then
			return 1;
		fi
	done

	return 0;
}

# check for mandatory program, else error
check_prog_req() {
	local prog=${1};
	local pkg=${2:-${1}};

	if ! check_prog ${prog}
	then
		error "${pkg} is required to build this package";
	fi

	return 0;
}

# Check if function has been defined
__check_function() {
	declare -F ${1} &> /dev/null
	return $?
}

# Unstable API guard
__check_unstable() {
	local unstable_var=CYGPORT_USE_UNSTABLE_API

	if defined ${unstable_var}
	then
		"${@}"
		return $?
	else
		warning "The ${1} API is not yet stable and is subject to change"
		warning "You must define ${unstable_var} in order to use it"
	fi
}

# determine if version is at least the given minimum
# FIXME: public api?
__version_at_least() {
	local want_v=${1%-*};
	local want_r=${1#*-};
	local have_v=${2%-*};
	local have_r=${2#*-};
	local -a want_va=(${want_v//[-\._]/ });
	local -a have_va=(${have_v//[-\._]/ });
	local -i n=0;

	while defined want_ver[${n}]
	do
		if (( want_ver[n] < ${have_ver[${n}]:-0} ))
		then
			return 0;
		elif (( want_ver[n] > ${have_ver[${n}]:-0} ))
		then
			return 1;
		else
			n+=1;
			continue;
		fi
	done

	if [ "${want_r}" != "${want_v}" ]
	then
		if (( want_r > have_r ))
		then
			return 1;
		fi
	fi

	return 0;	# equal
}

# assure that _privbindir is in PATH
if ! check_prog doins
then
	error "${_name} does not seem to be installed properly";
fi

# check now for all mandatory programs
for _myprog in bzip2 cat chmod cp cygcheck diff find grep gzip install ln \
               mkdir mv objdump patch rm rsync sed sort strip tar xargs
do
	if ! check_prog ${_myprog}
	then
		error "${_myprog} is required to run ${_name}";
	fi
done

unset _myprog;

# protect functions
export -f check_prog check_prog_req
readonly -f check_prog check_prog_req __check_function __check_unstable __version_at_least


################################################################################
#
# src_prep functions
#
# mutable:
#	DISTCLEANFILES
#	USE_MANAGED_MOUNT
#	src_patch_hook()
#	src_unpack_hook()
#
# public:
#	cygpatch PATCHFILE [PATCHFILE2 ...]
#
# private:
#	fetch WGET_URI
#	unpack ARCHIVE
#	__gpg_verify FILENAME FILE_DESCRIPTION [SIGFILE_EXTENSION]
#	__mirror_fetch MIRROR_URI
#	__mkdirs
#	__oldpatch OLDPATCH_VERSION
#	__src_fetch
#	__src_prep
#
################################################################################

# downloads file(s) from Internet
fetch() {
	local uri;
	local urifile;

	uri=${1};
	urifile=${uri%\?*};
	urifile=${urifile##*/};

	if check_prog wget
	then
		if wget --no-check-certificate -O ${urifile}.tmp ${uri}
		then
			mv -f ${urifile}.tmp ${urifile}
		else
			rm -f ${urifile}.tmp
			error "wget ${uri} failed"
		fi
	elif check_prog curl
	then
		if curl -k --url ${uri} -o ${urifile}.tmp
		then
			mv -f ${urifile}.tmp ${urifile}
		else
			rm -f ${urifile}.tmp
			error "curl ${uri} failed"
		fi
	else
		error "Either wget or curl are required to fetch sources.";
	fi
}

__mirror_fetch() {
	local miruri;
	local mirname;
	local mirvar;
	local -a mirlist;
	local -i n;

	miruri=${1#mirror://};
	mirname=${miruri%%/*};
	mirvar=mirror_${mirname};

	if ! defined ${mirvar}
	then
		error "unknown mirror ${mirname}";
	fi

	mirlist=(${!mirvar});

	n=0;
	while (( n < ${#mirlist[*]} ))
	do
		if fetch ${mirlist[${n}]}/${miruri#*/}
		then
			return 0;
		fi
		n+=1;
	done

	error "Could not download ${1##*/}";
}

# downloads all sources through method-specific functions
__src_fetch() {
	local rcs;
	local uri;

	# FIXME: Versions < 0.3.1 would allow only one in any case.
	# While theoretically possible to use more than one of the RCS_fetch cmds
	# it's not assured because the tarball names could be the same.
	# Unfortunately, changing the RCS tarball names would break existing
	# source packages.  So for now, follow the previous behavior.
	for rcs in cvs svn git bzr hg mtn fossil
	do
		if defined _USE_${rcs}_FETCH
		then
			${rcs}_fetch;
			break;
		fi
	done

	# the RCS_fetch functions change PWD
	cd ${top};

	for uri in ${SRC_URI} ${PATCH_URI}
	do
		case ${uri%%://*} in
			mirror)			__mirror_fetch ${uri} ;;
			http|https|ftp)	fetch ${uri} || error "Download ${uri##*/} failed" ;;
			file|/*)		cp -f ${uri#file://} . || error "Copying ${uri##*/} failed" ;;
			${uri})			;; # file in working directory
			*)				error "Invalid download URI ${uri}" ;;
		esac
	done
}

__srpm_extract() {
	local rpmpath=${1};
	local rpmfile=${1##*/};
	local cpiofile=${rpmfile%.rpm}.cpio;
	local cpiopath=${T}/${cpiofile};
	local srcfile;

	check_prog_req rpm2cpio rpm;
	check_prog_req cpio;

	rpm2cpio ${rpmpath} > ${cpiopath};
	(cd ${T}
	 cpio -i --quiet < ${cpiofile};
	)

	for srcfile in $(cpio -t --quiet < ${cpiopath})
	do
		unpack ${T}/${srcfile};
	done
}

# unpacks archives
# FIXME: public api?
unpack() {
	local unpack_cmd;
	local unpack_file_path;
	local unpack_file_name;
	local unpack_out;

	for unpack_file_path
	do
		unpack_file_name=${unpack_file_path##*/};

		if [ ! -f ${unpack_file_path} ]
		then
			error "Cannot find source package ${unpack_file_name}";
		fi

		# determine correct source decompression command
		case ${unpack_file_path} in
			*.asc|*.md5|*.sig|*.sign)  continue ;;
			*.tar.bz2|*.tbz2|*.tar.bz|*.tbz) unpack_cmd="tar jxf" ;;
			*.tar.gz|*.tgz|*.tar.Z) unpack_cmd="tar zxf" ;;
			*.tar.lzma)
				check_prog_req lzma
				unpack_cmd="tar --lzma -xf"
				;;
			*.tar.lzo)
				check_prog_req lzop
				unpack_cmd="tar --lzop -xf"
				;;
			*.tar)  unpack_cmd="tar xf"  ;;
			*.bz2)
				unpack_cmd="bunzip2 -c";
				unpack_out="${unpack_file_name%.bz2}";
				;;
			*.gz)
				unpack_cmd="gunzip -c";
				unpack_out="${unpack_file_name%.gz}";
				;;
			*.gem)
				check_prog_req gem rubygems;
				unpack_cmd="gem unpack";
				;;
			*.rar)
				check_prog_req unrar;
				unpack_cmd="unrar x -inul";
				;;
			*.src.rpm)
				unpack_cmd="__srpm_extract";
				;;
			*.shar)
				check_prog_req unshar;
				unpack_cmd="unshar";
				;;
			*.xar)
				check_prog_req xar;
				unpack_cmd="xar -xf";
				;;
			*.zip)
				check_prog_req unzip;
				unpack_cmd="unzip -oq";
				;;
			*.7z)
				if check_prog 7zr
				then
					unpack_cmd="7zr x";
				elif check_prog 7za
				then
					unpack_cmd="7za x";
				else
					error "p7zip is required to unpack this source package";
				fi
				;;
			*) unpack_cmd="cp -t ${SRC_DIR}" ;;
		esac

		__step "Unpacking source ${unpack_file_name}";

		if defined unpack_out
		then
			if ! ${unpack_cmd} ${unpack_file_path} > ${unpack_out}
			then
				error "${unpack_cmd} ${unpack_file_name} failed";
			fi
		else
			if ! ${unpack_cmd} ${unpack_file_path}
			then
				error "${unpack_cmd} ${unpack_file_name} failed";
			fi
		fi
	done
}

__gpg_verify() {
	local _file=${1};
	local _filetype=${2};
	local _sigext=${3:-sig};

	if [ -f ${_file}.${_sigext} ]
	then
		inform "${_filetype} signature follows:";
		# we 'check_prog gpg' in __src_prep
		gpg --verify ${_file}.${_sigext} ${_file};
	else
		inform "${_filetype} signature missing.";
	fi
}

__mkdirs() {
	cd ${top};

	mkdir -p ${workdir};

	if defined USE_MANAGED_MOUNT
	then
		mount -u -o managed $(cygpath -w ${workdir}) ${workdir} || error "managed mount creation failed"
	fi

	mkdir -p ${srcdir} ${origsrcdir} ${B} ${D} ${T} ${configdir} ${logdir} ${distdir} ${patchdir} ${spkgdir};
}

cygpatch() {
	local patchfile_name;
	local patchfile_path;
	local -i patchlevel;

	for patchfile_path
	do
		patchfile_name="${patchfile_path##*/}"

		if [ ! -e ${patchfile_path} ]
		then
			inform "patch ${patchfile_name} not found";
			continue;
		fi

		patchlevel=0;

		case ${patchfile_name} in
			${PF}.cygwin.patch)
				if [ "${S}" = "${srcdir}" ]
				then
					patchlevel=1;
				else
					patchlevel=2;
				fi
				;;
			*.bz2|*.gz)
				(
					cd ${T}
					unpack ${patchfile_path}
				)
				patchfile_path=${T}/${patchfile_name%.*}
				;;
		esac

		while (( patchlevel <= 3 ))
		do
			if patch -N -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
			then
				inform "applying patch ${patchfile_name}:";
				patch -N -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
				break;
			elif patch -N -s --binary --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
			then
				inform "applying patch ${patchfile_name}:";
				patch -N --binary -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
				break;
			else
				if (( patchlevel == 3 ))
				then
					error "patch ${patchfile_name} will not apply";
				else
					patchlevel+=1;
					continue;
				fi
			fi
		done
	done
}

# FIXME: deprecated
__oldpatch() {
	local _oldver=${1};

	cd ${S};

	if [ -e ${top}/${PN}-${_oldver}.src.patch -o -e ${top}/${PN}-${_oldver}.cygwin.patch ]
	then
		cygpatch ${top}/${PN}-${_oldver}.cygwin.patch ${top}/${PN}-${_oldver}.src.patch;
	elif [ -e ${top}/${PN}-${_oldver}.patch ]
	then
		cygpatch ${top}/${PN}-${_oldver}.patch;
	else
		error "Patches for version ${_oldver} not found";
	fi
}

__src_prep() {
	local sigext;
	local src_patch;
	local src_pkg;
	local n=1;

	cd ${top};

	__mkdirs;

	if [ ! -f ${configdir}/cygport-version ]
	then
		echo ${_version} > ${configdir}/cygport-version
	fi

	# FIXME: dependent also on $SIG ??
	if check_prog gpg
	then
		for src_pkg in ${_src_orig_pkgs}
		do
			for sigext in asc sig sign
			do
				if [ -f ${src_pkg}.${sigext} ]
				then
					__gpg_verify ${src_pkg} "ORIGINAL PACKAGE $((n++))" ${sigext};
				fi
			done
		done

		__gpg_verify ${PF}.cygport "CYGPORT SCRIPT";

		if [ -f ${top}/${cygwin_patchfile} ]
		then
			__gpg_verify ${top}/${cygwin_patchfile} "CYGWIN PATCH";
		fi

		if [ -f ${top}/${src_patchfile} ]
		then
			__gpg_verify ${top}/${src_patchfile} "SOURCE PATCH";
		fi
	else
		inform "gnupg must be installed in order to check signatures.";
	fi

	cd ${origsrcdir};

	for src_pkg in ${_src_orig_pkgs}
	do
		unpack ${top}/${src_pkg};
	done

	if [ ! -d ${origsrcdir}/${SRC_DIR} ]
	then
		error "SRC_DIR is not correctly defined"
	fi

	cd ${origsrcdir}/${SRC_DIR};

	if defined DISTCLEANFILES
	then
		inform "Removing DISTCLEANFILES..."
		rm -f ${DISTCLEANFILES}
	fi

	# src_unpack_hook() is an optional function which can be defined
	# to alter the original source tree and avoid large patches
	# or long DIFF_EXCLUDES, or to apply patches in tarballs
	if __check_function src_unpack_hook
	then
		__check_unstable src_unpack_hook;
		cd ${origsrcdir}/${SRC_DIR};
	fi

	for src_patch in ${_src_orig_patches}
	do
		cygpatch ${top}/${src_patch};
	done

	# similar to src_unpack_hook(), but is called after SRC_PATCH patches
	# useful for changing permissions, etc., on patched files
	if __check_function src_patch_hook
	then
		__check_unstable src_patch_hook;
		cd ${origsrcdir}/${SRC_DIR};
	fi

	__step "Preparing working source directory";

	rsync -aq --delete-before ${origsrcdir}/ ${srcdir}/;

	mkdir -p ${C};
	ln -sfn ${C} ${workdir}/CYGWIN-PATCHES;

	cd ${S};

	if [ -f ${top}/${cygwin_patchfile} -o -f ${top}/${src_patchfile} ]
	then
		cygpatch ${top}/${cygwin_patchfile} ${top}/${src_patchfile};
	fi
}

# protect functions
readonly -f fetch __mirror_fetch __src_fetch unpack __gpg_verify __mkdirs cygpatch __oldpatch __src_prep


################################################################################
#
# src_compile functions
#
# mutable:
#	CYGCONF_ARGS
#	CYGCONF_SOURCE
#	CYGCMAKE_ARGS
#	CYGCMAKE_SOURCE
#	NO_ACLOCAL
#	NO_AUTOCONF
#	NO_AUTOHEADER
#	NO_AUTOMAKE
#	NO_AUTOPOINT
#	NO_LIBTOOLIZE
#	WANT_AUTOCONF
#	WANT_AUTOMAKE
#	src_compile
#
# public:
#	cygautoreconf
#	cygconf CONF_OPTIONS
#	cygcmake CMAKE_OPTIONS
#	cygmake MAKE_OPTIONS
#	lndirs
#
################################################################################

lndirs() {
	local fromdir=${1};
	local todir=${2};

	if ! defined fromdir
	then
		fromdir="${S}";
		todir="${B}";
	elif ! defined todir
	then
		todir=.;
	fi

	check_prog_req lndir
	lndir -silent ${fromdir} ${todir} || error "lndir failed"
}

# Regenerates auto* files; most importantly, pulls in the Cygwin libtool
cygautoreconf() {
	local d;
	local f;
	local f_in;
	local tool;
	local ac_config_files;
	local ac_config_headers;
	local ac_subdirs;
	local ac_ver;
	local configure_ac;
	local config_h;
	local config_h_in;

	if [ -f configure.ac ]
	then
		configure_ac="configure.ac";
	elif [ -f configure.in ]
	then
		configure_ac="configure.in";
	else
		error "Could not find configure.ac or configure.in";
	fi

	ac_subdirs="$(grep 'AC_CONFIG_SUBDIRS*' ${configure_ac} | sed -e 's!.*AC_CONFIG_SUBDIRS*(\[*\(.*\))!\1!g' -e 's!\]*!!g')"

	for d in . ${ac_subdirs}
	do
		if [ -d ${d} ]
		then
			# e.g. CVS checkouts
			if [ -f ${d}/configure ]
			then
				# Assure that autotool files are writable (e.g. less)
				eval $(grep -h '^ac_config_files=' ${d}/configure)
				# eval $(grep -h '^ac_config_headers=' ${d}/configure)

				chmod +wx ${d}/configure;

				for f in ${ac_config_files} ${ac_config_headers}
				do
					case ${f} in
						*:*)	f_in=${d}/${f#*:} ;;
						*)		f_in=${d}/${f}.in ;;
					esac

					if [ -e ${f_in} ]
					then
						chmod +w ${f_in};
					fi
				done

				unset ac_config_files;
			fi

			if $(grep -Eq 'AM_(GNU_GETTEXT|ICONV)' ${d}/${configure_ac})
			then
				cp -f /usr/share/gettext/config.rpath ${d}/
			fi

			# Automake will exit if these are not present
			# dodoc won't install these, however, since they will be empty
			touch ${d}/{AUTHORS,ChangeLog,NEWS,README};

			# These required files can be overwritten by automake, so we
			# back them up now and restore them afterwards
			for f in COPYING INSTALL
			do
				if [ -f ${d}/${f} ]
				then
					cp ${d}/${f} ${d}/${f}.temp;
					chmod +w ${f}.temp;
				elif [ -d ${d}/${f} ]
				then
					:
				else
					touch ${d}/${f}
				fi
			done
		fi
	done

	check_prog_req autoreconf autoconf

	export WANT_AUTOCONF;
	export WANT_AUTOMAKE;

	if [ "x${WANT_AUTOCONF}" = "x2.1" ]
	then
		check_prog_req autoconf-2.13 autoconf2.1

		if grep -Eq "^(LT_INIT|A[CM]_PROG_LIBTOOL)" ${S}/${configure_ac}
		then
			warning "Current libtool is incompatible with autoconf-2.13";
		fi
	else
		WANT_AUTOCONF=2.5

		case $(autoconf --version 2> /dev/null | head -n 1) in
			autoconf*2.[56]?) ;;
			*)	error "autoconf2.5 is required to build this package" ;;
		esac

		for tool in ACLOCAL LIBTOOLIZE AUTOCONF AUTOHEADER AUTOPOINT AUTOMAKE
		do
			if defined NO_${tool}
			then
				declare ${tool}=true
			fi
			export ${tool}
		done

		if defined ACLOCAL_FLAGS
		then
			declare ACLOCAL="${ACLOCAL:-aclocal} ${ACLOCAL_FLAGS}"
		fi
	fi

	autoreconf --force -i --verbose || error "autoreconf failed"

	for d in . ${ac_subdirs}
	do
		if [ -d ${d} ]
		then
			config_h="$(grep 'A[CM]_CONFIG_HEADERS*' ${d}/${configure_ac} | sed -e 's!A[CM]_CONFIG_HEADERS*(\[*\(.*\))!\1!g' -e 's!\]*!!g')";

			case "${config_h}" in
				'') config_h_in= ;;
				*:*) config_h_in="$(echo "${config_h//:/ }" | cut -d ' ' -f 2-)" ;;
				*) config_h_in="${config_h}.in" ;;
			esac

			for f in ${config_h_in}
			do
				touch ${d}/${f}
			done

			for f in COPYING INSTALL
			do
				if [ -f ${d}/${f}.temp ]
				then
					mv -f ${d}/${f}.temp ${d}/${f};
				fi
			done
		fi
	done
}

# standard configure call
cygconf() {
	local ac_config_files;
	local ac_config_headers;
	local confargs;
	local confdir;
	local configure;
	local f;

	if defined CYGCONF_SOURCE && [ -x ${CYGCONF_SOURCE}/configure ]
	then
		confdir=${CYGCONF_SOURCE};
	elif [ -x ${PWD/${B}/${S}}/configure ]
	then
		confdir=${PWD/${B}/${S}};
	elif [ -x ${S}/configure ]
	then
		confdir=${S};
	else
		error "no configure script found";
	fi

	configure="${confdir}/configure"

	# AC_CONFIG_FILES should not be dist'ed, but it sometimes happens anyway
	eval $(grep -h '^ac_config_files=' ${configure})
	eval $(grep -h '^ac_config_headers=' ${configure})

	for f in ${ac_config_files} ${ac_config_headers}
	do
		if [ -f ${confdir}/${f} ]
		then
			inform "Removing ${f} to be regenerated by configure"
			rm -f ${confdir}/${f}
			ln -sf ${confdir/${S}/${B}}/${f} ${confdir}/${f}
		fi
	done

	# Set default configure arguments
	confargs="--prefix=/usr --exec-prefix=/usr --bindir=/usr/bin \
			  --sbindir=/usr/sbin --libexecdir=/usr/sbin \
			  --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc"

	case "x$(grep -m 1 'GNU Autoconf' ${configure} | cut -d ' ' -f 6)" in
		x2.6[0-9]*)
			confargs+=" --datarootdir=/usr/share --docdir=/usr/share/doc/${P}"
			;;
		*)
			confargs+=" --infodir=/usr/share/info --mandir=/usr/share/man"
			;;
	esac

	# AC_HAVE_MMAP fails despite a working mmap, so we force this to yes
	# (see http://www.cygwin.com/ml/cygwin/2004-09/msg00741.html
	# and following thread for details)
	export ac_cv_func_mmap_fixed_mapped=yes;

	verbose ${configure} \
		--srcdir="${confdir}" ${confargs} "${@}" ${CYGCONF_ARGS} \
		|| error "configure failed"
}

# cmake configuration
# adding -j1 to MAKEOPTS is highly recommended
cygcmake() {
	local cmdir;

	check_prog_req cmake;

	if defined CYGCMAKE_SOURCE && [ -e ${CYGCMAKE_SOURCE}/CMakeLists.txt ]
	then
		cmdir=${CYGCMAKE_SOURCE};
	elif [ -e ${PWD/${B}/${S}}/CMakeLists.txt ]
	then
		cmdir=${PWD/${B}/${S}};
	elif [ -e ${S}/CMakeLists.txt ]
	then
		cmdir=${S};
	else
		error "cygcmake: cmake directory not found";
	fi

	cmake \
		${cmdir} \
		-DCMAKE_BINARY_DIR=. \
		-DCMAKE_BUILD_TYPE=Release \
		-DCMAKE_C_COMPILER=$(type -P gcc) \
		-DCMAKE_C_FLAGS="${CFLAGS}" \
		-DCMAKE_CXX_COMPILER=$(type -P g++) \
		-DCMAKE_CXX_FLAGS="${CXXFLAGS}" \
		-DCMAKE_INSTALL_PREFIX=/usr \
		"${@}" \
		${CYGCMAKE_ARGS} \
		|| error "cmake failed"
}

# standard GNU make call
# if parallel build doesn't work for a package, call 'cygmake -j1'
cygmake() {
	make ${MAKEOPTS} "${@}" || error "make failed"
}

# provides default src_compile
# will be overridden by cygclasses or cygports as necessary
src_compile() {
	cd ${S}
	cygautoreconf
	cd ${B}
	cygconf
	cygmake
}

# protect functions
readonly -f lndirs cygautoreconf cygconf cygmake


################################################################################
#
# src_test functions
#
# mutable:
#	src_test
#
# public:
#	cygtest
#
################################################################################

# runs 'make check' or 'make test'
cygtest() {
	if [ -e Makefile -o -e GNUmakefile -o -e makefile ]
	then
		if make -n check &> /dev/null
		then
			make -k check || true
		elif make -n test &> /dev/null
		then
			make -k test || true
		else
			error "Neither make check nor make test work.  You must define your own src_test().";
		fi
	else
		error "no Makefile found.  You must define your own src_test().";
	fi
}

# provides default src_test
# will be overridden by cygclasses or cygports as necessary
src_test() {
	cd ${B}
	cygtest
}

# protect functions
readonly -f cygtest


################################################################################
#
# src_install functions
#
# mutable:
#	USE_DESTDIR
#	src_install
#
# public:
#	cyginstall
#	docinto
#	dodir
#	exeinto
#	insinto
#	keepdir
#
# private:
#	_docinto_dir
#	_exeinto_dir
#	_insinto_dir
#	__prepinstalldirs
#
################################################################################

# creates directory under install path
dodir() {
	local d;

	for d
	do
		if [ ! -d ${D}${d} ]
		then
			mkdir -p ${D}${d} || error "dodir ${d} failed"
		fi
	done
}

# prevents an otherwise empty directory from being automatically removed
keepdir() {
	for d
	do
		dodir ${d}
		touch ${D}${d}/.keep-${PN}
	done
}

# The foointo functions set _foointo_dir variables for dofoo, and that
# doesn't work from a separate script, hence they are here

# set dodoc install subdir
#
# relative paths go under /usr/share/doc/${P}/, absolute paths (those
# beginning with '/' go under /usr/share/doc/.
docinto() {
	if (( $# != 1 ))
	then
		error "docinto accepts exactly one argument";
	fi

	export _docinto_dir=${1};
}

# set doexe install dir
exeinto() {
	if (( $# != 1 ))
	then
		error "exeinto accepts exactly one argument";
	fi

	dodir ${1};
	export _exeinto_dir=${1};
}

# set doins install dir
insinto() {
	if (( $# != 1 ))
	then
		error "insinto accepts exactly one argument";
	fi

	dodir ${1};
	export _insinto_dir=${1};
}

# Pre-install steps
__prepinstalldirs() {
	rm -fr ${D}/*;
}

# run 'make install'
cyginstall() {
	if $(make -n -s install/fast &> /dev/null)
	then
		inst_target="install/fast"
	else
		inst_target="install"
	fi

	case ${USE_DESTDIR:-1} in
		1|[Yy]|[Yy][Ee][Ss])
			make ${MAKEOPTS} ${inst_target} DESTDIR=${D} "${@}" || error "make install DESTDIR failed"
			;;
		0|[Nn]|[Nn][Oo])
			make ${MAKEOPTS} install \
				prefix=${D}/usr \
				exec_prefix=${D}/usr \
				bindir=${D}/usr/bin/ \
				includedir=${D}/usr/include \
				libdir=${D}/usr/lib \
				sbindir=${D}/usr/sbin \
				libexecdir=${D}/usr/sbin \
				datadir=${D}/usr/share \
				infodir=${D}/usr/share/info \
				mandir=${D}/usr/share/man \
				localstatedir=${D}/var \
				sysconfdir=${D}/etc \
				"${@}" \
				|| error "make install No-DESTDIR failed"
			;;
		*)
			error "USE_DESTDIR should be set to either 0 or 1 (default).";
			;;
	esac
}

# provides a default src_install
# will be overridden by cygclasses or cygports as necessary
src_install() {
	cd ${B}
	cyginstall
}

# protect functions
export -f dodir keepdir docinto exeinto insinto
readonly -f dodir keepdir docinto exeinto insinto __prepinstalldirs cyginstall


################################################################################
#
# automatic src_postinst functions
#
# mutable:
#	DOCS
#	HTMLDOCS
#
# private:
#	__prepdoc
#	__prepetc
#	__prepinfo
#	__prepman
#	__prepstrip
#	__prep_empty_dirs
#	__prep_libtool_modules
#	__prep_symlinks
#	__src_postinst
#
################################################################################

# installs standard docs, html docs, and Cygwin README
__prepdoc() {
	local html;
	local -i n=1;
	local pkg_hint;
	local default_docs="\
		ABOUT-NLS ANNOUNCE ANNOUNCEMENTS AUTHOR AUTHORS \
		BUG-REPORTS BUGS \
		Changes ChangeLog CONTRIBUTORS COPYING COPYING-DOCS COPYING.LIB COPYRIGHT CREDITS \
		FAQ GPL HACKING HISTORY HOW-TO-CONTRIBUTE INSTALL KNOWNBUG \
		LEGAL LICENCE LICENSE MAINTAINERS NEWS NLS NOTES \
		PROGLIST README THANKS TODO WHATSNEW";

	cd ${S};

	if defined _CYGPORT_RESTRICT_postinst_doc_
	then
		inform "Skipping package doc installation per request";
	else
		unset _docinto_dir;
		dodoc ${default_docs} ${DOCS};

		for html in ${HTMLDOCS}
		do
			docinto html;
			dodoc ${html};
		done
	fi

	if [ -e ${C}/README ]
	then
		insinto /usr/share/doc/Cygwin;
		newins ${C}/README ${P}.README;
	elif [ -e ${C}/${PN}.README ]
	then
		insinto /usr/share/doc/Cygwin;
		newins ${C}/${PN}.README ${P}.README;
	else
		warning "Cygwin README is missing"
	fi

	pkg_hint=(${PKG_HINTS})

	while defined pkg_name[${n}]
	do
		if [ -f ${C}/${pkg_name[${n}]}.README ]
		then
			insinto /usr/share/doc/Cygwin;
			newins ${C}/${pkg_name[${n}]}.README ${pkg_name[${n}]}-${PV}.README;
		elif defined pkg_hint[${n}]
		then
			if [ -f ${C}/${pkg_hint[${n}]}.README ]
			then
				insinto /usr/share/doc/Cygwin;
				newins ${C}/${pkg_hint[${n}]}.README ${pkg_name[${n}]}-${PV}.README;
			fi
		fi

		n+=1;
	done
}

# creates and installs postinstall, preremove, and profile.d scripts
__prepetc() {
	local d;
	local f;
	local -i n;
	local s;

	for s in postinstall preremove
	do
		if [ -f ${C}/${s}.sh -a -f ${C}/${PN}.${s} ]
		then
			error "Only one of ${s}.sh and ${PN}.${s} can exist";
		fi

		for f in ${s}.sh ${PN}.${s}
		do
			if [ -f ${C}/${f} ]
			then
				dodir /etc/${s};
				cat >> ${D}/etc/${s}/${PN}.sh < ${C}/${f};
			fi
		done

		n=1;
		while [ -n "${pkg_name[${n}]}" ]
		do
			if [ -f ${C}/${pkg_name[${n}]}.${s} ]
			then
				dodir /etc/${s};
				cat >> ${D}/etc/${s}/${pkg_name[${n}]}.sh < ${C}/${pkg_name[${n}]}.${s};
			fi
			n+=1
		done
	done

	if [ -f ${C}/profile.d.sh ]
	then
		exeinto /etc/profile.d;
		newexe ${C}/profile.d.sh ${PN}.sh;
	fi

	if [ -f ${C}/profile.d.csh ]
	then
		exeinto /etc/profile.d;
		newexe ${C}/profile.d.csh ${PN}.csh;
	fi

	# System fonts
	if [ -d ${D}/usr/share/fonts ]
	then
		prep_fonts_dir.sh || error "Fonts postinstall failed"
	fi

	# GTK+ 2.x modules
	if [ -d ${D}/usr/share/icons ]
	then
		prep_gtk_icon_theme.sh || error "GTK+ icon theme postinstall failed"
	fi

	# GTK+ 2.x modules
	if [ -d ${D}/usr/lib/pango -o -d ${D}/usr/lib/gtk-2.0 ]
	then
		prep_gtk2_modules.sh || error "GTK+ modules postinstall failed"
	fi

	# GConf schemas
	if [ -d ${D}/etc/gconf/schemas ] && ! defined _CYGPORT_RESTRICT_postinst_gconf_
	then
		prep_gconf_schemas.sh || error "GConf schemas postinstall failed"
	fi

	# Freedesktop.org Shared Mime Info
	if [ -d ${D}/usr/share/applications -o -d ${D}/usr/share/mime ] 
	then
		prep_freedesktop_mime.sh || error "Shared Mime Info postinstall failed"
	fi

	for d in /etc/postinstall /etc/preremove
	do
		if [ -d ${D}${d} ]
		then
			find ${D}${d} -type f -exec chmod 0755 '{}' +;
		fi
	done
}

__prepman() {
	local manpage;

	if [ -d ${D}/usr/man ]
	then
		inform "Moving /usr/man to /usr/share/man...";
		dodir /usr/share/man
		# just in case there is already /usr/share/man, e.g. after doman()
		cp -r ${D}/usr/man/* ${D}/usr/share/man/
		rm -fr ${D}/usr/man/
	fi

	if [ -d ${D}/usr/share/man ]
	then
		echo "Compressing man pages:";

		for manlink in $(find ${D}/usr/share/man -type l)
		do
			manref=$(readlink ${manlink})
			rm -f ${manlink};
			echo ".so ${manref##*/}" > ${manlink};
		done

		for manpage in $(find ${D}/usr/share/man -type f ! -name '*.gz' ! -name '*.bz2')
		do
			echo "        ${manpage##*/}";
			gzip -q ${manpage};
		done
	fi
}

__prepinfo() {
	local infopage;

	if [ -d ${D}/usr/info ]
	then
		inform "Moving /usr/info to /usr/share/info...";
		dodir /usr/share/info
		# just in case there is already /usr/share/info
		cp -r ${D}/usr/info/* ${D}/usr/share/info/
		rm -fr ${D}/usr/info/
	fi

	if [ -d ${D}/usr/share/info ]
	then
		rm -f ${D}/usr/share/info/dir

		echo "Compressing info pages:";

		for infopage in $(find ${D}/usr/share/info -type f -name '*.info*' ! -name '*.gz' ! -name '*.bz2')
		do
			echo "        ${infopage##*/}";
			gzip -q ${infopage};
		done
	fi
}

# remove empty directories in D
# if empty directory must remain, call keepdir() in src_install()
__prep_empty_dirs() {
	local d n;

	if defined KEEPDIRS
	then
		keepdir ${KEEPDIRS}
	fi

	# PTC if you find a package deeper than 20 (even perl mods are < 15)
	for (( n=20; n >= 1; n-- ))
	do
		for d in $(find ${D} -mindepth ${n} -type d -empty)
		do
			inform "Removing empty directory: ${d#${D}}";
			rmdir ${d};
		done
	done
}

__prepstrip() {
	if defined _CYGPORT_RESTRICT_strip_
	then
		inform "Skipping strip step per request.";
		return 0;
	fi

	local exe;

	cd ${D};

	echo "Stripping executables:";

	# *.so: Apache2 modules, OCaml stublibs, Ruby modules
	# *.oct: Octave modules
	for exe in $(find * -type f -name '*.dll' -o -name '*.exe' -o -name '*.so' -o -name '*.oct')
	do
		if [ ! -w ${exe} ]
		then
			continue
		fi

		# OCaml bytecode must not be stripped
		# this test generates false positives with the ocaml core and
		# compilers, but should otherwise be accurate
		if grep -q 'Caml1999X008' ${exe}
		then
			continue
		fi

		# Mono assemblies must not be stripped,
		# but .(dll|exe).mdb debug symbols should be removed
		if objdump -p ${exe} 2>&1 | grep -q "DLL Name: mscoree.dll"
		then
			rm -f ${exe}.mdb
			continue
		fi

		# assure this is actually a PE-COFF object
		if objdump -f ${exe} &>/dev/null
		then
			echo "        ${exe}";
			chmod +x ${exe};
			strip ${exe};
		fi
	done
}

__prep_symlinks() {
	local l l_src

	echo "Checking for symlinks:"

	for l in $(find ${D}/ -type l)
	do
		l_src=$(readlink ${l})
		case ${l_src} in
			${D}/*)
				echo "        ${l#${D}}";
				rm -f ${l};
				dosym ${l_src#${D}} ${l#${D}};
				;;
			${workdir}/*)
				echo "        ${l#${D}}";
				rm -f ${l};
				cp -LR ${l_src} ${l};
				;;
			/*)
				if [ ! -e ${D}${l_src} ] && [ ! -e ${l_src} ]
				then
					warning "symlink ${l#${D}} points to nonexistant ${l_src}";
				fi
				;;
		esac
	done
}

__prep_libtool_modules() {
	find * -name '*.la' -exec prep_libtool_modules.sh '{}' + || error "Libtool module postinstall failed"
}

__src_postinst() {
	__prep_symlinks;
	__prepdoc;
	__prepetc;
	__prepman;
	__prepinfo;
	__prep_empty_dirs;
	__prepstrip;
	__prep_libtool_modules;
}

# protect functions
readonly -f __prepdoc __prepetc __prepman __prepinfo __prep_empty_dirs \
            __prepstrip __prep_symlinks __prep_libtool_modules __src_postinst


################################################################################
#
# information functions
#
# mutable:
#	DEPS_PATH
#	DESCRIPTION
#	HOMEPAGE
#
# private:
#	__list_deps
#	__list_files
#	__show_info
#
################################################################################

__show_info() {
	cat <<-_EOF
	Cygport package information
	    Package name:     ${PN}
	    Package version:  ${PV}
	    Cygwin release:   ${PR}
	    Homepage:         ${HOMEPAGE}
	    Description:      ${DESCRIPTION}
		_EOF
}

__list_files() {
	(cd ${D}
	find . ! -type d | sed 's!^\.!  !' | sort
	)
}

__list_deps() {
	(local d

	cd ${D}
	# This prevents a package from listing itself as a dependency if
	# already installed
	#
	# DEPS_PATH is meant for packages with link deps outside of std PATH,
	# e.g. gtk2-perl, ruby-gnome2, ogle
	unset PATH

	for d in ${DEPS_PATH//:/ }
	do
		PATH+="${D}${d}:${d}:"
	done

	export PATH+="${D}/usr/bin:${os_path}";

	# Ruby and Apache2 modules should be *.so, nothing else!!!
	find . \( -name '*.exe' -o -name '*.dll' -o -name '*.so' -o -name '*.oct' \) \
		-exec objdump -p '{}' + | grep "DLL Name:" | sort -u | \
		cut -d ' ' -f 3 | sed -e '/^[A-Z32]*\.[Dd][Ll][Ll]$/d' -e 's!mscoree.dll!mono.exe!' | \
		xargs -r which | xargs -r cygcheck -f | sort -fu | sed 's!^!  !';
	)
}

# protect functions
readonly -f __list_files __list_deps __show_info


################################################################################
#
# packaging functions
#
# mutable:
#	DIFF_EXCLUDES
#	PKG_CONTENTS[]
#	PKG_HINTS
#	PKG_IGNORE
#	PKG_LISTS
#	SIG
#
# private:
#	__finish
#	__gpg_sign
#	__pkg_binpkg
#	__pkg_diff
#	__pkg_dist
#	__pkg_pkgcheck
#	__pkg_srcpkg
#
################################################################################

__pkg_binpkg() {
	local -i n=0;
	local pkg_contents_var;
	local pkg_list;

	pkg_list=(${PKG_LISTS:-${PKG_NAMES}});

	cd ${D};

	__step "Creating binary package(s)";

	if defined PKG_CONTENTS[*]
	then
		while (( n < pkg_count ))
		do
			if [ -f ${C}/${pkg_list[${n}]}.list ]
			then
				PKG_CONTENTS[${n}]=
			fi
			n+=1
		done

		if (( ${#PKG_CONTENTS[*]} != ${pkg_count} ))
		then
			error "PKG_CONTENTS[n] must be defined from 0 through $((pkg_count - 1))"
		fi
	fi

	n=0
	while (( n < pkg_count ))
	do
		__step "${pkg_name[${n}]}-${PVR}.tar.bz2";

		pkg_contents_var=${pkg_name[${n}]//[+-\.]/_}_CONTENTS;

		if defined ${pkg_contents_var}
		then
			tar cvjf ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 \
				${!pkg_contents_var} \
				|| error "Binary package creation failed"
		elif defined PKG_CONTENTS[${n}]
		then
			tar cvjf ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 \
				${PKG_CONTENTS[${n}]} \
				|| error "Binary package creation failed"
		elif [ -f ${C}/${pkg_list[${n}]}.list ]
		then
			tar cvjf ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 \
				$(cat ${C}/${pkg_list[${n}]}.list) \
				|| error "Binary package creation failed"
		elif (( pkg_count == 1 ))
		then
			tar cvjf ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 * \
				|| error "Binary package creation failed"
		else
			inform "Creating empty package"
			bzip2 < /dev/null > ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 \
				|| error "Empty package creation failed"
		fi
		echo;
		n+=1;
	done
}

__pkg_pkgcheck() {
	local -i n=0;
	local tmp1="${T}/tmptar.log";
	local tmp2="${T}/tmpfind.log";

	cd ${D};
	__step "Checking packages for missing or duplicate files";

	rm -f ${tmp1} ${tmp2};

	while defined pkg_name[${n}]
	do
		tar tjf ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 | grep -Ev "/$" >> ${tmp1}
		n+=1
	done

	if defined PKG_IGNORE
	then
		find ${PKG_IGNORE} ! -type d >> ${tmp1}
	fi

	sort < ${tmp1} > "${tmp1}.sorted";
	mv -f "${tmp1}.sorted" ${tmp1};

	find * -type f -o -type l | sort > ${tmp2}

	if ! diff -u0 ${tmp2} ${tmp1} > /dev/null
	then
		# detect duplicates and/or missing files
		diff -u0 ${tmp2} ${tmp1} | grep -E -v '^@' > ${T}/pkgcheck.diff

		if grep -E '^-[^\-]' ${T}/pkgcheck.diff > /dev/null
		then
			warning "Packages are missing files:";
			grep -E '^-[^\-]' ${T}/pkgcheck.diff;
	    fi

		if grep -E '^\+[^\+]' ${T}/pkgcheck.diff > /dev/null
		then
			warning "Packages contain duplicate files:";
			grep -E '^\+[^\+]' ${T}/pkgcheck.diff;
			error "Packages contain duplicate files:";
	    fi
	fi

	rm -f ${tmp1} ${tmp2};
}

__pkg_diff() {
	local default_excludes;
	local diff_excludes_x;
	local difflevel;
	local exclude;

	default_excludes="CYGWIN-PATCHES aclocal.m4~ aclocal.m4t autom4te.cache
		config.cache config.log config.status config.h *config.h.in
		ABOUT-NLS Makefile.in.in Makevars.template *SlackBuild* *.egg-info
		*.jpg *.png *.qm *.svgz *.tif
		*.class *.jlc *.pyc *.mo *.gmo *.orig *.rej *.spec *.temp *~ *.stackdump";

	case ${PN} in
		automake*|libtool) ;;
		*)	default_excludes+=" COPYING INSTALL compile config-ml.in \
				config.guess config.sub depcomp elisp-comp install-sh \
				libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 \
				ltmain.sh mdate-sh missing mkinstalldirs py-compile \
				symlink-tree texinfo.tex ylwrap" ;;
	esac

	case ${PN} in
		gettext) ;;
		*)	default_excludes+=" config.rpath" ;;
	esac

	# exclude aclocal.m4 if generated from aclocal
	if [ -f ${S}/aclocal.m4 ]
	then
		if $(head -n 1 ${S}/aclocal.m4 | grep -q 'generated.* by aclocal')
		then
			default_excludes+=" aclocal.m4";
		fi
	fi

	# exclude Makefile.in only if using automake
	for mf in Makefile GNUmakefile makefile
	do
		if [ -f ${S}/${mf}.am ]
		then
			default_excludes+=" ${mf}.in";
		fi
	done

	# exclude configure only if using autoconf
	if [ -f ${S}/configure.in -o -f ${S}/configure.ac ]
	then
		default_excludes+=" configure";
	fi

	# gtk-doc
	if [ -f ${S}/gtk-doc.make -a "${PN}" != "gtk-doc" ]
	then
		default_excludes+=" *.stamp *.html *.bak *.sgml \
			*.tmpl *.txt *.xml *.args *.devhelp* *.css \
			*.hierarchy *.interfaces *.prerequisites *.signals \
			*.png *.top *.bottom gtk-doc.make gtk-doc.m4";
	fi

	# GNOME miscellaneous
	if [ "${PN}" != "gnome-common" ]
	then
		default_excludes+=" omf.make xmldocs.make";
	fi

	if [ "${PN}" != "gnome-doc-utils" ]
	then
		default_excludes+=" gnome-doc-utils.make gnome-doc-utils.m4";
	fi

	if [ "${PN}" != "intltool" ]
	then
		default_excludes+=" intltool.m4 intltool-extract intltool-extract.in \
			intltool-merge intltool-merge.in intltool-update intltool-update.in";
	fi

	# KDE3
	if [ -f ${S}/admin/libtool.m4.in ]
	then
		default_excludes+=" libtool.m4.in* configure.bak \
			configure.files acinclude.m4 subdirs";

		if [ -f ${S}/configure.in.in ]
		then
			default_excludes+=" configure.in";
		fi
	fi

	for exclude in ${default_excludes}
	do
		diff_excludes_x+=" -x ${exclude}";
	done

	case ${DIFF_EXCLUDES} in
		-x*)
			diff_excludes_x+=" ${DIFF_EXCLUDES}";
			;;
		*)
			for exclude in ${DIFF_EXCLUDES}
			do
				diff_excludes_x+=" -x ${exclude}";
			done
			;;
	esac

	cd ${workdir};

	# diff exits >0 when targets differ, hence catch exception with true
	if [ "${SRC_DIR}" = "." ]
	then
		diff -urN \
			${origsrcdir##*/}/CYGWIN-PATCHES ${srcdir##*/}/CYGWIN-PATCHES \
			> ${patchdir}/${cygwin_patchfile} || true;
		difflevel=1;
	else
		diff -urN \
			${origsrcdir##*/}/${SRC_DIR}/CYGWIN-PATCHES ${srcdir##*/}/${SRC_DIR}/CYGWIN-PATCHES \
			> ${patchdir}/${cygwin_patchfile} || true;
		difflevel=2;
	fi

	if defined _CYGPORT_RESTRICT_diff_
	then
		rm -f ${patchdir}/${src_patchfile};
		touch ${patchdir}/${src_patchfile};
	else
		diff -uprN ${diff_excludes_x} \
			${origsrcdir##*/} ${srcdir##*/} \
			> ${patchdir}/${src_patchfile} || true;
	fi

	sed -e '/^diff -u/d' -i ${patchdir}/${cygwin_patchfile} ${patchdir}/${src_patchfile};

	if check_prog diffstat
	then
		diffstat -p${difflevel} ${patchdir}/${cygwin_patchfile} \
			${patchdir}/${src_patchfile};
	fi
}

__gpg_sign() {
	echo "${2} signature needs to be updated";
	rm -f ${1}.sig;
	# we 'check_prog gpg' in __pkg_srcpkg()
	gpg --detach-sign ${1};
}

__pkg_srcpkg() {
	local src;

	rm -fr ${spkgdir}/*;

	echo;
	__step "Creating source patches";

	__pkg_diff;

	cp ${patchdir}/${cygwin_patchfile} ${spkgdir};

	if [ "x$(file -b ${patchdir}/${src_patchfile})" != "xempty" ]
	then
		cp ${patchdir}/${src_patchfile} ${spkgdir};
	fi

	__step "Creating source package";

	cp ${top}/${PF}.cygport ${spkgdir};

	for src in ${_src_orig_pkgs} ${_src_orig_patches}
	do
		cp ${top}/${src} ${spkgdir};
	done

	cd ${spkgdir};

	if defined SIG && [ "x${SIG}" != "x0" ]
	then
		if check_prog gpg
		then
			__gpg_sign ${spkgdir}/${PF}.cygport "CYGPORT SCRIPT";
			__gpg_sign ${spkgdir}/${cygwin_patchfile} "CYGWIN PATCH";

			if [ -f ${spkgdir}/${src_patchfile} ]
			then
				__gpg_sign ${spkgdir}/${src_patchfile} "SOURCE PATCH";
			fi
		else
			inform "gnupg must be installed in order to make signatures.";
		fi
	fi

	tar cvjf ${top}/${PF}-src.tar.bz2 * || error "Source package creation failed"
}

__pkg_dist() {
	local -i n;
	local -a pkg_hint;

	rm -fr ${distdir}/*;

	cd ${top};

	mkdir -p ${distdir}/${PN};

	cp ${top}/${PF}-src.tar.bz2 ${distdir}/${PN};

	# sources with split binaries do NOT require a corresponding empty binary
	if (( $(du -b ${top}/${PF}.tar.bz2 | cut -f 1) > 46 ))
	then
		cp ${top}/${PF}.tar.bz2 ${distdir}/${PN};
	fi

	if (( pkg_count > 1 ))
	then
		pkg_hint=(${PKG_HINTS:-${PKG_NAMES}})
	else
		pkg_hint=(${PKG_HINTS:-setup})
	fi

	if (( pkg_count != ${#pkg_hint[*]} ))
	then
		error "Different number of packages defined in PKG_NAMES and PKG_HINTS";
	fi

	if [ -f ${C}/${pkg_hint[0]%.hint}.hint ]
	then
		cp ${C}/${pkg_hint[0]%.hint}.hint ${distdir}/${PN}/setup.hint;
	else
		warning "${pkg_hint[0]%.hint}.hint is missing";
	fi

	n=1;
	while defined pkg_name[${n}]
	do
		mkdir -p ${distdir}/${PN}/${pkg_name[${n}]};

		cp ${top}/${pkg_name[${n}]}-${PVR}.tar.bz2 ${distdir}/${PN}/${pkg_name[${n}]};

		if [ -f ${C}/${pkg_hint[${n}]%.hint}.hint ]
		then
			cp ${C}/${pkg_hint[${n}]%.hint}.hint ${distdir}/${PN}/${pkg_name[${n}]}/setup.hint;
		else
			warning "${pkg_hint[${n}]%.hint}.hint is missing";
		fi

		n+=1;
	done
}

__finish() {
	local -i n=0;

	cd ${top};

	__step "Removing work directory in 5 seconds...";

	while (( n < 5 ))
	do
		# FIXME: beep?
		# echo $'\a';
		sleep 1 || exit $?;
		n+=1;
	done

	__step "Removing work directory NOW.";

	rm -rf ${workdir}/*;

	sleep 1;

	if defined USE_MANAGED_MOUNT
	then
		umount -u ${workdir} || warning "managed mount removal failed";
	fi

	rmdir ${workdir};

	__step "Finished.";
}

# protect functions
readonly -f __pkg_binpkg __pkg_diff __gpg_sign __pkg_srcpkg __pkg_dist __finish


################################################################################
#
# import global prefs, then the cygport
#
# mutable:
#	CFLAGS
#	CPPFLAGS
#	CXXFLAGS
#	CYGPORT_DEPEND
#	F77FLAGS
#	GCJFLAGS
#	LDFLAGS
#	MAKEOPTS
#	RESTRICT
#
# public:
#	inherit CYGCLASS [CYGCLASS2 ...]
#	inherited CYGCLASS [CYGCLASS2 ...]
#
# private:
#	_CYGPORT_RESTRICT_*_
#	_*_CYGCLASS_
#
################################################################################

# import functions and variables from a cygclass
inherit() {
	local class;
	local classname;

	for class
	do
		classname=${class%.cygclass}

		if [ ! -f ${_privlibdir}/${classname}.cygclass ]
		then
			error "inherit: unknown cygclass ${class}";
		fi

		if ! defined _${classname//[-\.]/_}_CYGCLASS_
		then
			source ${_privlibdir}/${classname}.cygclass || error "inherit ${classname} failed"
			export _${classname//[-\.]/_}_CYGCLASS_=1
		fi
	done
}

# detect if a given cygclass has been inherit()ed
inherited() {
	local class;
	local classname;

	for class
	do
		classname=${class%.cygclass};

		if ! defined _${classname//[-\.]/_}_CYGCLASS_
		then
			return 1;
		fi
	done

	return 0;
}

export -f inherit inherited
readonly -f inherit inherited

# Values which can be overridden either system-wide or per package
declare -x CFLAGS="-O2 -pipe ";
declare -x CPPFLAGS="";
declare -x CXXFLAGS="-O2 -pipe ";
declare -x F77FLAGS="-O2 -pipe ";
declare -x GCJFLAGS="-O2 -pipe ";
declare -x LDFLAGS="";
declare -x LIBS="";
declare    MAKEOPTS="-j2 ";
declare    CHOST="i686-pc-cygwin";

source ${_privdatadir}/mirrors || warning "could not read mirrors file"

for conf in ${_sysconfdir}/${_name}.conf \
			"${HOME}/.${_name}rc" \
			"${HOME}/.${_name}.conf" \
			"${HOME}/.${_name}/${_name}.conf"
do
	if [ -e "${conf}" ]
	then
		source "${conf}" || error "could not read ${conf}"
	fi
done

unset conf;

source ${top}/${cygportfile} || error "could not read ${cygportfile}"

if defined CYGPORT_DEPEND
then
	if ! __version_at_least ${CYGPORT_DEPEND} ${_version}
	then
		error "This package requires cygport ${CYGPORT_DEPEND} or newer";
	fi
fi

for restrict in ${RESTRICT}
do
	export _CYGPORT_RESTRICT_${restrict//-/_}_=1
done


################################################################################
#
# define package-dependant variables
#
# mutable:
#	PATCH_URI
#	PKG_NAMES
#	SRC_DIR
#	SRC_URI
#
# public:
#	B
#	C
#	D
#	SRC_DIR
#	T
#
# private:
#	configdir
#	cygwin_patchfile
#	distdir
#	logdir
#	origsrcdir
#	patchdir
#	pkg_count
#	pkg_name[]
#	spkgdir
#	srcdir
#	src_patchfile
#	workdir
#	_src_orig_patches
#	_src_orig_pkgs
#
################################################################################
declare -r workdir="${top}/${PF}";
declare -r srcdir="${workdir}/src";
declare -r origsrcdir="${workdir}/origsrc";
declare -r configdir="${workdir}/config";
declare -r logdir="${workdir}/log";
declare -r patchdir="${workdir}/patch";
declare -r spkgdir="${workdir}/spkg";
declare -r distdir="${workdir}/dist";

SRC_DIR=${SRC_DIR:-${ORIG_PN:-${PN}}-${PV}};
if [ "x${SRC_DIR}" = "x." ]
then
	declare -r S=${srcdir};
else
	declare -r S=${srcdir}/${SRC_DIR};
fi

declare -rx B="${workdir}/build";
declare -rx D="${workdir}/inst";
declare -rx T="${workdir}/temp";
declare -rx C="${S}/CYGWIN-PATCHES";

declare -r compilelog="${logdir}/${PF}-compile.log";
declare -r checklog="${logdir}/${PF}-check.log";
declare -r installlog="${logdir}/${PF}-install.log";
declare -r pkglog="${logdir}/${PF}-pkg.log";

if ! defined SRC_URI
then
	error "SRC_URI must be defined";
fi

for _src_uri in ${SRC_URI}
do
	_src_uri="${_src_uri%\?*}"
	_src_orig_pkgs+=" ${_src_uri##*/}";
done
readonly _src_orig_pkgs;
unset _src_uri;

for _patch_uri in ${PATCH_URI}
do
	_patch_uri="${_patch_uri%\?*}"
	_src_orig_patches+=" ${_patch_uri##*/}";
done
readonly _src_orig_patches;
unset _patch_uri;

declare -r cygwin_patchfile=${PF}.cygwin.patch;
declare -r src_patchfile=${PF}.src.patch;

declare -ar pkg_name=(${PKG_NAMES:-${PN}});
declare -r  pkg_count=${#pkg_name[*]};

if [ "x${pkg_name[0]}" != "x${PN}" ]
then
	error "When defining PKG_NAMES, the first package should be ${PN}.";
fi

################################################################################
#
# End of functions
#
################################################################################

declare -i arg_n=2

while (( arg_n < argc ))
do
	case ${argv[${arg_n}]} in
		download|fetch|wget|get)
			__src_fetch;
			_status=$?;
			;;
		prep|unpack)
			__stage Preparing;
			__src_prep;
			_status=$?;
			;;
		oldpatch)
			__oldpatch ${argv[$((++arg_n))]};
			_status=$?;
			;;
		compile|build|make)
			__stage Compiling;
			src_compile 2>&1 | tee ${compilelog};
			_status=$?;
			;;
		check|test)
			__stage Testing;
			src_test 2>&1 | tee ${checklog};
			_status=$?;
			;;
		inst*)
			__stage Installing;
			(__prepinstalldirs && src_install && __src_postinst) 2>&1 | tee ${installlog};
			_status=$?;
			;;
		postinst*)
			__src_postinst;
			_status=$?;
			;;
		list)
			__list_files;
			_status=$?;
			;;
		dep*)
			__list_deps;
			_status=$?;
			;;
		info*)
			__show_info;
			_status=$?;
			;;
		package|pkg)
			__stage Packaging;
			(__pkg_binpkg && __pkg_pkgcheck && __pkg_srcpkg && __pkg_dist) 2>&1 | tee ${pkglog};
			_status=$?;
			;;
		diff|mkdiff|mkpatch)
			__pkg_diff;
			_status=$?;
			;;
		finish)
			__finish;
			_status=$?;
			;;
		almostall)
			__stage Preparing && __src_prep && \
			__stage Compiling && src_compile 2>&1 | tee ${compilelog} && \
			__stage Installing && (__prepinstalldirs && src_install && __src_postinst) 2>&1 | tee ${installlog} && \
			__stage Packaging && (__pkg_binpkg && __pkg_pkgcheck && __pkg_srcpkg && __pkg_dist) 2>&1 | tee ${pkglog}
			_status=$?;
			;;
		all)
			__stage Preparing && __src_prep && \
			__stage Compiling && src_compile 2>&1 | tee ${compilelog} && \
			__stage Installing && (__prepinstalldirs && src_install && __src_postinst) 2>&1 | tee ${installlog} && \
			__stage Packaging && (__pkg_binpkg && __pkg_pkgcheck && __pkg_srcpkg && __pkg_dist) 2>&1 | tee ${pkglog} && \
			__finish;
			_status=$?;
			;;
		help)
			__show_help;
			exit 0;
			;;
		version)
			__show_version;
			exit 0;
			;;
		conf*|dist|spkg|src-package)
			# deprecated commands
			;;
		*)
			error "unknown command ${argv[${arg_n}]}";
			_status=$?;
			;;
	esac

	if (( _status != 0 ))
	then
		break;
	fi

	arg_n+=1;
done

exit ${_status};
