#!/usr/local/bin/cbsd
#v11.1.0
CBSDMODULE="build"
MYARG="filelist dstdir basedir"
MYOPTARG="prunelist verbose chaselibs excludedir"
MYDESC="Copy bin/lib files by filelist{.xz} from basedir to dstdir"
ADDHELP="filelist - source with file list to copy\n\
dstdir - destination dir\n\
basedir - copy file from this dir\n\
prunelist - source with file list to exclude\n\
excludedir - skip manage files from this directories list (use pipe as delimer, e.g: /dev|/root\n"

. ${subr}

verbose=0
chaselibs=0

init $*

[ -n "${excludedir}" ] && excludedir=$( echo ${excludedir} |/usr/bin/tr '|' ' ' )

BASE_DIR="${basedir}"
FILES="${filelist}"
DST_DIR="${dstdir}"

make_mtree()
{
	[ -f ${BASE_DIR}/etc/mtree/BSD.root.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.root.dist -p ${DST_DIR} >/dev/null
	[ -f ${BASE_DIR}/etc/mtree/BSD.usr.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.usr.dist -p ${DST_DIR}/usr >/dev/null
	[ -f ${BASE_DIR}/etc/mtree/BSD.var.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.var.dist -p ${DST_DIR}/var >/dev/null
	[ -f ${BASE_DIR}/etc/mtree/BIND.chroot.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BIND.chroot.dist -p ${DST_DIR}/var/named >/dev/null
	[ -f ${BASE_DIR}/etc/mtree/BSD.sendmail.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.sendmail.dist -p ${DST_DIR} >/dev/null
	[ -f ${BASE_DIR}/etc/mtree/BSD.include.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.include.dist -p ${DST_DIR}/usr/include >/dev/null
	[ ! -d "${DST_DIR}/usr/tests" ] && /bin/mkdir -p "${DST_DIR}/usr/tests"
	[ -f ${BASE_DIR}/etc/mtree/BSD.tests.dist ] && /usr/sbin/mtree -deU -f ${BASE_DIR}/etc/mtree/BSD.tests.dist -p ${DST_DIR}/usr/tests >/dev/null
}

make_libmap()
{
	A=$( /usr/bin/mktemp /tmp/libtxt.XXX )
	B=$( /usr/bin/mktemp /tmp/libtxtsort.XXX )
	TRAP="${TRAP} /bin/rm -f ${A} ${B};"
	trap "${TRAP}" HUP INT ABRT BUS TERM EXIT

	/usr/bin/xzcat ${FILES} |while read line; do
		[ -z "${line}" ] && continue
		case ":${line}" in
			:#*)
				continue
				;;
		esac
		[ -r "${BASE_DIR}${line}" ] && /usr/bin/ldd -f "%p\n" ${BASE_DIR}${line} >> $A 2>/dev/null
	done
	/usr/bin/sort -u ${A} > ${B}
	/bin/rm -f ${A}
}

copy_binlib()
{
	local _dotnum=0
	local _prefix
	local _strlen
	local _skip

	# pass one: copy files
	/usr/bin/xzcat ${FILES}| while read line; do
		[ -z "${line}" ] && continue

		_prefix=$( substr --pos=0 --len=1 --str="${line}" )
		_hard_link=0

		case "${_prefix}" in
			"#")
				continue
				;;
			"%")
				_all_files=
				_hard_link=1
				# collect all hard-links in variables
				for i in ${line}; do
					[ "${i}" = "%" ] && continue
					_all_files="${_all_files} ${i}"
				done
				;;
			*)
				_all_files="${line}"
				;;
		esac

		_skip=0

		for i in ${_all_files}; do

			if [ -n "${excludedir}" ]; then
				for skipdir in ${excludedir}; do
					_strlen=$( strlen ${skipdir} )
					_test_for_skip=$( substr --pos=0 --len=${_strlen} --str="${i}" )
					if [ "${skipdir}" = "${_test_for_skip}" ]; then
						[ ${verbose} -eq 1 ] && echo "** skip list dir: ${skipdir}"
						_skip=1
						continue
					fi
				done
			fi

			if [ ! -r "${BASE_DIR}${i}" ]; then
				[ ${verbose} -eq 1 ] && ${ECHO} "\n${MAGENTA}Notice: Exist in index, but not found in ${GREEN}${BASE_DIR}: ${MAGENTA}${i}${NORMAL}\n"
				continue
			fi

			[ ${verbose} -eq 1 ] && echo "copying ${BASE_DIR}${i} to ${DST_DIR}${D}"
		done

		[ ${_skip} -eq 1 ] && continue

		if [ ${_hard_link} -eq 0 ]; then
			D=$( /usr/sbin/chroot ${BASE_DIR} dirname ${line} )
			[ ! -d "{DST_DIR}${D}" ] && /bin/mkdir -p ${DST_DIR}${D}
			if [ -r ${BASE_DIR}${line} -o -h ${BASE_DIR}${line} ]; then
				/bin/cp -a ${BASE_DIR}${line} ${DST_DIR}${D}
			else
				${ECHO} "${MAGENTA}No such file from index in source, skipp: ${GREEN}${BASE_DIR}${line}${NORMAL}"
			fi
		else
			_hard_count=0
			_hard_dst=		# store destination for hard links

			for i in ${_all_files}; do
				D=$( /usr/sbin/chroot ${BASE_DIR} dirname ${i} )
				[ ! -d "{DST_DIR}${D}" ] && /bin/mkdir -p ${DST_DIR}${D}

				if [ ${_hard_count} -eq 0 ]; then
					_hard_dst="${DST_DIR}${i}"
					/bin/cp -a ${BASE_DIR}${i} ${DST_DIR}${D}
					_hard_count=1
				else
					_hard_src="${DST_DIR}${i}"
					cd ${DST_DIR}${D} && /bin/ln -f ${_hard_dst} ${_hard_src}
				fi
			done
		fi

		_dotnum=$(( _dotnum + 1 ))
		if [ ${_dotnum} -gt 100 -a ${verbose} -eq 0 ]; then
			printf "."
			_dotnum=0
		fi
	done

	[ ${chaselibs} -eq 0 ] && return 0
	_dotnum=0

	# necessary libs
	/bin/cat ${B}| while read line; do
		[ -z "${line}" ] && continue
		[ -f "${DST_DIR}${line}" ] && continue
		[ ! -r "${BASE_DIR}${line}" -a ${verbose} -eq 1 ] && ${ECHO} "\n${MAGENTA}Notice: exist in index, but not found in ${GREEN}${BASE_DIR}: ${MAGENTA}${line}${NORMAL}\n" && continue
		D=$( /usr/sbin/chroot ${BASE_DIR} dirname ${line} )
		[ ! -d "${DST_DIR}${D}" ] && /bin/mkdir -p ${DST_DIR}${D}
		[ ${verbose} -eq 1 ] && echo "/bin/cp -a ${BASE_DIR}${line} ${DST_DIR}${D}"
		/usr/local/bin/rsync -a --hard-links --acls --xattrs --devices --numeric-ids --recursive --partial ${BASE_DIR}${line} ${DST_DIR}${D}
		_dotnum=$(( _dotnum + 1 ))
		if [ ${_dotnum} -gt 100 -a ${verbose} -eq 0 ]; then
			printf "."
			_dotnum=0
		fi

		_dotnum=$(( _dotnum + 1 ))

	done

	/bin/rm -f ${B}
}



prunelist()
{
	[ ! -f "${prunelist}" -o -z "${prunelist}" ] && return 0 # no prune
	[ -z "${1}" ] && return 0 # sanity

	${ECHO} "${MAGENTA}Prune file by list: ${GREEN}${prunelist}${NORMAL}"

	for FILE in $( /bin/cat ${prunelist} ); do
		[ -z "${FILE}" ] && continue
		case ":${FILE}" in
			:#* | :)
				continue
				;;
		esac
		/bin/rm -rf ${1}/${FILE} 2>/dev/null
	done
}

[ ! -d "${BASE_DIR}" ] && err 1 "No such ${BASE_DIR}
[ ! -r "${FILES}" ] && err 1 "No such ${FILES}

if [ -d "${DST_DIR}" ]; then
	/bin/chflags -R noschg ${DST_DIR}
	/bin/rm -rf ${DST_DIR}
fi

/bin/mkdir -p ${DST_DIR}

make_mtree
make_libmap
copy_binlib
# CRLF after dot:
[ ${verbose} -eq 0 ] && echo
prunelist
preparebase dst=${DST_DIR}
