################################################################################
#
# src_prep.cygpart - cygport source unpacking and preparation functions
#
# Copyright (C) 2006-2015 Yaakov Selkowitz
# Provided by the Cygwin Ports project <http://sourceware.org/cygwinports/>
#
# 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/>.
#
################################################################################

#****** Chapter 6/Preparation
#****

__cpio_gz_extract() {
	check_prog_req cpio;
	gzip -dc ${1} | cpio -midu --quiet;
}

__gem_extract() {
	local gem=${1}
	local gemfile=${gem##*/}
	local gemsubdir=${gemfile%.gem}

	check_prog_req gem ruby
	gem unpack ${gem} --quiet
	gem spec ${gem} --ruby > ${gemsubdir}/${gemfile%-[0-9]*}.gemspec
}

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

	pushd ${T}

	if check_prog rpm2tar
	then
		rpm2tar ${rpmpath};
		tar xf ${tarfile};
		srcfiles="$(tar tf ${tarfile})";
	elif check_prog rpm2cpio cpio
	then
		rpm2cpio ${rpmpath} > ${cpiofile};
		cpio -i --quiet < ${cpiofile};
		srcfiles="$(cpio -t --quiet < ${cpiofile})";
	else
		error "${rpmfile} requires rpm2targz or rpm to unpack";
	fi

	popd

	for srcfile in ${srcfiles##./}
	do
		unpack ${T}/${srcfile};
	done
}

# unpacks archives
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.lrz)
				check_prog_req lrzuntar lrzip
				unpack_cmd="lrzuntar"
				;;
			*.tar|*.tar.*|*.tbz|*.tbz2|*.tgz|*.tlz|*.tzo)
				unpack_cmd="tar xf" ;;
			*.bz2)
				unpack_cmd="bunzip2 -c";
				unpack_out="${unpack_file_name%.bz2}";
				;;
			*.cpio.gz)
				if check_prog bsdtar
				then
					unpack_cmd="bsdtar zxf";
				else
					unpack_cmd="__cpio_gz_extract";
				fi
				;;
			*.gz)
				unpack_cmd="gunzip -c";
				unpack_out="${unpack_file_name%.gz}";
				;;
			*.gem)
				unpack_cmd="__gem_extract";
				;;
			*.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";
				;;
			*.xo|*.zip|*.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 ! check_prog gpg
	then
		# display notice only once
		if ! defined _gpg_not_found_
		then
			inform "gnupg must be installed in order to check signatures.";
			_gpg_not_found_=1
		fi

		return 0;
	fi

	if [ -f ${_file}.${_sigext} ]
	then
		inform "${_filetype} signature follows:";
		gpg --verify ${_file}.${_sigext} ${_file} || true;
	fi
}

__mkdirs() {
	cd ${top};
	mkdir -p ${srcdir} ${origsrcdir} ${B} ${D} ${T} ${configdir} ${logdir} ${distdir} ${patchdir} ${spkgdir};
}

cygpatch() {
	local patchfile_name;
	local patchfile_path;
	local -i patchlevel;
	local -i max_patchlevel=5;
	local srcdir_depth;

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

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

		patchlevel=0;

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

		while (( patchlevel <= max_patchlevel ))
		do
			if patch -N -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
			then
				inform "applying patch ${patchfile_name}:";
				patch -N -p${patchlevel} --no-backup-if-mismatch -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} --no-backup-if-mismatch -i ${patchfile_path} || error "patch ${patchfile_name} failed"
				break;
			elif patch -R -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
			then
				warning "patch ${patchfile_name} already applied; skipping";
				break;
			else
				if (( patchlevel == max_patchlevel ))
				then
					error "patch ${patchfile_name} will not apply";
				else
					patchlevel+=1;
					continue;
				fi
			fi
		done
	done
}

__src_prep() {
	local sigext;
	local sig_exts="asc sig sign";
	local src_orig_patch;
	local src_patch;
	local src_pkg;
	local tar_patch;
	local n=1;

	cd ${top};

	__mkdirs;

	# FIXME: do something with this; e.g. check that cygport
	# wasn't upgraded since prep
	__config_set cygport_version ${_version}

	if [ -f ${top}/${cygportfile}.sig ]
	then
		__gpg_verify ${top}/${cygportfile} "CYGPORT SCRIPT";
	fi

	for src_pkg in ${_src_orig_pkgs}
	do
		if [ -f ${DISTDIR}/${src_pkg} -a ! -f ${top}/${src_pkg} ]
		then
			src_pkg=${DISTDIR}/${src_pkg};
		elif [ -f ${top}/${src_pkg##*/} -a ! -f ${top}/${src_pkg} ]
		then
			src_pkg=${src_pkg##*/};
		fi
		for sigext in ${sig_exts}
		do
			if [ -f ${src_pkg}.${sigext} ]
			then
				__gpg_verify ${src_pkg} "SOURCE $((n++))" ${sigext};
			fi
		done
	done

	for src_patch in ${_src_orig_patches}
	do
		if [ -f ${DISTDIR}/${src_patch} -a ! -f ${top}/${src_patch} ]
		then
			src_patch=${DISTDIR}/${src_patch};
		elif [ -f ${top}/${src_patch##*/} -a ! -f ${src_patch} ]
		then
			src_patch=${src_patch##*/};
		fi
		for sigext in ${sig_exts}
		do
			if [ -f ${src_patch}.${sigext} ]
			then
				__gpg_verify ${src_patch} "PATCH $((n++))" ${sigext};
			fi
		done
	done

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

	if [ -f ${top}/${src_patchfile}.sig ]
	then
		__gpg_verify ${top}/${src_patchfile} "SOURCE PATCH";
	fi

	cd ${origsrcdir};

	for src_pkg in ${_src_orig_pkgs}
	do
		if [ -f ${DISTDIR}/${src_pkg} -a ! -f ${top}/${src_pkg} ]
		then
			src_pkg=${DISTDIR}/${src_pkg};
		elif [ -f ${top}/${src_pkg##*/} -a ! -f ${top}/${src_pkg} ]
		then
			src_pkg=${top}/${src_pkg##*/};
		else
			src_pkg=${top}/${src_pkg};
		fi
		unpack ${src_pkg};
	done

#****v* Preparation/SRC_DIR
#  DESCRIPTION
#  The top-level directory of the primary source tarball.  Defaults to
#  PN-PV.
#  NOTE
#  If the source tarball does not have a single directory under which all
#  files are unpacked, use SRC_DIR=".".
#****

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

	# cd will fail if not executable (e.g. dot2tex)
	chmod +x ${origsrcdir}/${SRC_DIR};
	cd ${origsrcdir}/${SRC_DIR};

#****v* Preparation/DISTCLEANFILES
#  DESCRIPTION
#  A list of files to be deleted immediately upon unpacking sources,
#  relative to $S.  This is intended to be used with buildsystem-generated
#  files which are incorrectly included in the source tarball.
#****

	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
		if [ -f ${DISTDIR}/${src_patch} -a ! -f ${top}/${src_patch} ]
		then
			src_patch=${DISTDIR}/${src_patch};
		elif [ -f ${top}/${src_patch##*/} -a ! -f ${top}/${src_patch} ]
		then
			src_patch=${top}/${src_patch##*/};
		else
			src_patch=${top}/${src_patch};
		fi
		case ${src_patch} in
			*.tar.gz|*.tgz|*.tar.bz2|*.tbz2)
				pushd ${T};
				unpack ${src_patch};
				popd;

				for tar_patch in $(tar tf ${src_patch} | sort | grep -E '(diff|patch)$')
				do
					cygpatch ${T}/${tar_patch};
				done
				;;
			*)
				cygpatch ${src_patch};
				;;
		esac
	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} ]
	then
		cygpatch ${top}/${cygwin_patchfile};
	fi

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

readonly -f __cpio_gz_extract __gem_extract __srpm_extract unpack \
            __gpg_verify __mkdirs cygpatch __src_prep
