#!/bin/bash -efu
# SPDX-License-Identifier: GPL-3.0-or-later

. shell-error
. shell-quote

FEATUREFILES=()
LOCALFILES=()
FILES=()
DIRS=()

append_uniq()
{
	local arr i sz v
	eval "sz=\${#$1[*]}"
	for (( i=0; $i < $sz; i++ )); do
		eval "v=\"\${$1[$i]}\""
		[ "$v" != "$2" ] ||
			return 0
	done
	eval "$1+=(\"\$2\")"
}

in_runtimedir()
{
	local f="${1#$LOCALBUILDDIR}"
	[ -z "${f##$RUNTIMEDIR/*}" ]
}

in_features_bindir()
{
	local f="${1#$LOCALBUILDDIR}"
	[ -z "${f##$BIN_FEATURESDIR/*}" ]
}

append_progs()
{
	local n f d SEARCH_PATH=''

	[ -z "$LOCALBUILDDIR" ] ||
		SEARCH_PATH="$LOCALBUILDDIR$RUNTIMEDIR/sbin:$LOCALBUILDDIR$RUNTIMEDIR/bin"

	for n in $ALL_ACTIVE_FEATURES; do
		for d in "$LOCALBUILDDIR$BIN_FEATURESDIR/$n"/{bin,sbin} "$BIN_FEATURESDIR/$n"/{bin,sbin}; do
			[ ! -d "$d" ] ||
				SEARCH_PATH="${SEARCH_PATH:+$SEARCH_PATH:}$d"
		done
	done

	SEARCH_PATH="${SEARCH_PATH:+$SEARCH_PATH:}$BUSYBOX_PATH:$SYSTEM_PATH"

	for n; do
		[ -n "$n" ] ||
			continue
		if [ -z "${n##/*}" ]; then
			message "WARNING: it doesn't make sense to search in PATH for a utility with absolute path: $n"
			append_uniq FILES "$n"
			continue
		elif [ -z "${n##*/*}" ]; then
			message "WARNING: it doesn't make sense to search in PATH for a utility with subpath: $n"
			n="${n##*/}"
		fi
		found=
		while read -d: -r path; do
			[ -n "$path" ] ||
				continue
			f="$path/$n"
			if [ -x "$f" ]; then
				found=1
				break
			fi
		done <<<"$SEARCH_PATH"

		[ -n "$found" ] ||
			fatal "Not found utility: $n"

		# We ignore the files from RUNTIMEDIR because this entire
		# directory will be copied later.
		! in_runtimedir "$f" ||
			continue

		# We put these utilities on a separate list because we need to
		# remove a prefix from them.
		if in_features_bindir "$f"; then
			append_uniq FEATUREFILES "$f"
			continue
		fi

		append_uniq FILES "$f"
	done
}

append_libs()
{
	local n fn SEARCH_PATH optional

	optional="$1"
	shift

	SEARCH_PATH="$(ldconfig -vNX 2>/dev/null | grep ^/ | cut -d: -f1)"

	for n; do
		[ -n "$n" ] ||
			continue

		if [ -z "${n##/*}" ]; then
			append_uniq FILES "$n"
			continue
		elif [ -z "${n##*/*}" ]; then
			message "WARNING: it doesn't make sense to search in PATH for a library with subpath: $n"
			n="${n##*/}"
		fi

		local fn="$n"
		names=("$fn")
		[[ "$fn" == *".so"* ]] || { fn="$fn.so"; names+=("$fn"); }
		[[ "$fn" == "lib"* ]]  || { fn="lib$fn"; names+=("$fn");}

		found=
		for path in $SEARCH_PATH; do
			[ -n "$path" ] ||
				continue

			for fn in "${names[@]}"; do
				f="$path/$fn"
				if [ -f "$f" ] || [ -h "$f" ]; then
					found=1
					break 2
				fi
			done
		done

		if [ -z "$found" ]; then
			[ "$optional" = optional ] ||
				fatal "Not found library: $n"
			continue
		fi

		append_uniq FILES "$f"

	done
}

for n in \
	"$RUNTIMEDIR" "$BASEDATADIR" \
	${LOCALBUILDDIR:+"${LOCALBUILDDIR-}$RUNTIMEDIR"} \
	${PUT_FEATURE_DIRS-} $PUT_DIRS;
do
	append_uniq DIRS "$n"
done

FILES+=(
	"$UDEVD"
	"$UDEVADM"
	/lib/udev/ata_id
	/lib/udev/cdrom_id
	/lib/udev/scsi_id
)

for n in \
	/lib/udev/{edd_id,vol_id,path_id,usb_id,firmware} \
	$(find-terminfo linux) \
	/var/resolv \
	"$KERNEL_MODULES_DIR"/modules.{builtin,order,builtin.modinfo} \
	/etc/modprobe.conf;
do
	[ ! -e "$n" ] ||
		append_uniq FILES "$n"
done

append_progs \
	blkid cat chmod chroot cp depmod dmesg findmnt grep kill killall5 ln \
	logger ls lsmod mkdir mknod mktemp modprobe mount mountpoint mv \
	readlink rm rmdir setsid sh sleep start-stop-daemon switch_root \
	touch umount which ${PUT_FEATURE_PROGS-} ${PUT_PROGS-}

[ -z "${PUT_FEATURE_LIBS-}" ] ||
        append_libs required ${PUT_FEATURE_LIBS-}

[ -z "${PUT_FEATURE_OPTIONAL_LIBS-}" ] ||
        append_libs optional ${PUT_FEATURE_OPTIONAL_LIBS-}

while read -d: -r n; do
	[ -n "$n" ] ||
		continue
	(set +f; shopt -s nullglob dotglob; printf '%s\n' "$n"/*) > "$TEMPDIR/list"
	while read -r bin; do
		[ -n "$bin" ] ||
			continue
		for pattern in '*/shell-*' ${PUT_FEATURE_PROGS_WILDCARD-}; do
			[ -z "${bin##$pattern}" ] ||
				continue
			if [ -n "$LOCALBUILDDIR" ] && [ -z "${bin##$LOCALBUILDDIR/*}" ]; then
				append_uniq LOCALFILES "$bin"
				continue
			fi
			[ -n "${bin##$RUNTIMEDIR/*}" ] ||
				continue
			append_uniq FILES "$bin"
		done
	done < "$TEMPDIR/list"
done <<<"$BUILDDIR_PATH:$BUSYBOX_PATH:$SYSTEM_PATH"
rm -f -- "$TEMPDIR/list"

for n in ${PUT_FEATURE_FILES-} $PUT_FILES; do
	append_uniq FILES "$n"
done

cd "$ROOTDIR"

mkdir_save_symlinks()
{
	local dir sub_dir follow_dir canon_path

	for dir in "$@"; do
		[ -n "$dir" ] ||
			continue

		sub_dir="$(dirname "$dir")"

		if [ ! -e "$sub_dir" ] && [ ! -h "$sub_dir" ]; then
			mkdir_save_symlinks "$sub_dir"
		fi

		if [ ! -h "/$dir" ]; then
			mkdir $verbose -p -- "./$dir"
		else
			follow_dir=".$(readlink -f "/$dir")"
			mkdir_save_symlinks "$follow_dir"

			canon_path="$(realpath --relative-to="$sub_dir" "$follow_dir")"
			ln -s "$canon_path" "./$dir"
		fi
	done
}

mkdir -p -- {,home/}root

mkdir_save_symlinks \
	{dev,mnt,run,proc,sys,tmp,{,usr/}{,s}bin,{,usr/}{share,lib{,32,x32,64}},var/{cache,lock/subsys,log,run}} \
	lib/{modules,udev,initrd/{all,kmodules,post,pre}} \
	etc/{{initrd,modprobe.d,sysconfig},udev/rules.d} \
	.initrd/{killall,uevent/events,uevent/queues/udev/.tmp} \
	"${KERNEL_MODULES_DIR#/}" \
	#

initrd-release

mkfifo ./.initrd/telinit
mkfifo ./.initrd/rdshell

ln_if_missing()
{
	[ -h "$2" ] || [ -e "$2" ] || ln $verbose -s -- "$1" "$2"
}

BASH="$(find /bin/ /sbin/ /usr/bin/ /usr/sbin/ /usr/local/bin/ /usr/local/sbin/ -name bash -print -quit)"

[ -n "$BASH" ] ||
	fatal "bash not found"
put-file . "$BASH"

ln_if_missing "$BASH" ./bin/sh
ln_if_missing "$BASH" ./bin/bash

if [ -h /bin/sh ]; then
	f="$(readlink -f /bin/sh)"
	mkdir -p -- ".${f%/*}"
	ln_if_missing "$BASH" ".$f"
fi

for f in "${FEATUREFILES[@]}"; do
	n="${f#$LOCALBUILDDIR}"
	n="${n#$BIN_FEATURESDIR/*/}"
	p="${f%/$n}"
	put-file -r "$p" . "$f"
done

printf '%s\n' "${DIRS[@]}"       |xargs -r put-tree .
printf '%s\n' "${FILES[@]}"      |xargs -r put-file .
printf '%s\n' "${LOCALFILES[@]}" |xargs -r put-file -r "$LOCALBUILDDIR" .

ln_if_missing "$UDEVD"   ./sbin/udevd
ln_if_missing "$UDEVADM" ./sbin/udevadm

for i in rc0 rc1 rc2 rc3 rc4 rc5 rc6 init; do
	mkdir -p "./etc/rc.d/$i.d"
	ln -s -f "rc.d/$i.d" "./etc/$i.d"
done

for n in group gshadow passwd shadow fstab; do
	touch "./etc/$n"
done

ln -s /proc/self/mounts ./etc/mtab

n_file=0
for filename in ${SYSCONFIG_FILES-}; do
	n_file=$(($n_file + 1))
	eval "vars=\"\${SYSCONFIG_NAMES_${n_file}-}\""

	n_var=0
	for varname in $vars; do
		n_var=$(($n_var + 1))
		eval "value=\"\${SYSCONFIG_${n_file}_${n_var}-}\""

		quote_shell_variable value "$value"
		printf '%s="%s"\n' "$varname" "$value"
	done >> "./etc/sysconfig/$filename"
done

for d in lib etc; do
	if [ -d "/$d/modprobe.d" ]; then
		verbose "Copying files from $d/modprobe.d ..."
		find -L "/$d/modprobe.d" \
				-name '*.conf' \
			-exec cp -aLt ./etc/modprobe.d -- '{}' '+'
	fi
done

for n in $ALL_ACTIVE_FEATURES; do
	mkdir -p "./.initrd/features/$n"
done
