#! /bin/bash
#
# This helper is used to update a kernel configuration with minimum
# user interventions. It uses values found in a template when a symbol
# needs to be resolved. If the value cannot be determined then it asks
# for to the user.
#
# For example, after upgrading the desktop config for x86_64
# architecture, you can use this script to update the i586 desktop
# since a lot of new configurations will be the same. You'll be
# prompted for i586 specific symbol values only.
#
USAGE="update-from <template> <configs>..."

if test $# -lt 2; then
	usage
fi

kdist__cd_kernel_topdir &&
configs_path=$(configs__get_repository) ||
exit

#
# Template is file relative to the config repository or to the current
# working directory.
#
template=$1

if ! test -f "$template"; then
	template="$configs_path/$template"
	if ! test -f "$template"; then
		die "Template not found: $1"
	fi
fi

#
# Retrieve the config files to be updated. It's a pattern inside the
# config repository.
#
# Do the sanity checkings now while at it.
#
shift
for f in $(GIT_DIR="$configs_path/.git" git ls-files "$@")
do
	#
	# version of the config file should match with the kernel one
	#
	case "$f" in
	$(config__version)/*)	;;
	*)
		warn "Skipping '$f': version mismatches with kernel one"
		continue
	esac

	f="$configs_path/$f"

	config__architecture "$f" >/dev/null || {
		warn "Skipping: '$f' has an unsupported arch '$arch'"
		continue
	}

	cmp -s "$f" "$template" && {
		warn "Skipping '$f': same as template"
		continue
	}

	targets+=" $f"
done

if ! test "$targets"; then
	die "No config files match '$@'"
fi

#
# The arch is given by:
#
#   1/ the name of the config file to update
#   2/ the content of the config file to update
#   3/ the current architecture.
#
get_arch () {
	case "$1" in
	*/x86_32/*)	echo i386;   return ;;
	*/x86_64/*)	echo x86_64; return ;;
	esac

	config__get_architecture "$1"

	#grep -q "CONFIG_X86_64=y" $1 && { echo x86_64; return; }
	#grep -q "CONFIG_X86_32=y" $1 && { echo i386; return; }
	#
	#kdist__architecture
}

#
# Ok this one is pretty crap...
#
read_conf_output () {
	while :
	do
		IFS='\' read -n1 -t0.005 -u${COPROC[0]} c 2>/dev/null
		rv=$?
		if ! test $rv -eq 0; then
			[ $rv -gt 128 ] && rv=0
			break
		fi

		case "$c" in
		'')	c="\n"
		esac
		CONF_REPLY+="$c"
	done
	return $rv
}

warn_about_wrong_guess () {
	warn "***"
	warn "*** I tried '$2' for '$1', but it seems invalid for this config."
	warn "***"
	warn "*** Please resolve this symbol manually."
	warn "***"
}


declare -i stats_resolv_auto=0
declare -i stats_resolv_user=0
declare -i stats_symbol_count=0

for config in $targets
do
	arch=$(config__get_architecture "$config")

	warn "Updating $config..."
	coproc {
		export KCONFIG_CONFIG="$config"
		export KCONFIG_NOTIMESTAMP=1

		make -s ARCH=$arch oldconfig
	}
	coproc_pid=$!

	while read_conf_output
	do
		case "$CONF_REPLY" in
		*choice\[*\]:" ")
			#
			# We have to resolve choices manually since we
			# don't know which symbol this choice
			# corresponds to.
			#
			echo -ne "$CONF_REPLY"
			read val
			echo >&${COPROC[1]} "$val"
			CONF_REPLY=""
			stats_resolv_user+=1
			;;
		*\[*\]" "\(NEW\)" ")
			#
			# Can the symbol's value be found in the
			# template ?
			#
			last_sym="$sym"
			sym=$(expr "$CONF_REPLY" : ".*(\([[:alnum:]_]*\)) \[") ||
			bug "unable to parse symbol"

			val=$(config__lookup_symbol_quotes $sym "$template") &&
			case $val in
			"")
				# symbol is not set
				val=N ;&
			\"*\")
				# strip the quotes
				val=${val#\"}
				val=${val%\"} ;&
			*)
				#
				# Try to detect if something's going wrong:
				# conf can reject our last guess. This is
				# probably something happening when different
				# architectures have different value ranges
				# for some symbols.
				#
				if test "$sym" = "$last_sym"; then
					#
					# Warn the user only once
					#
					[ -z "$notified" ] && {
						warn_about_wrong_guess $sym $val
						notified=yes
					}
					echo -ne "$CONF_REPLY"
					read val
					stats_resolv_user+=1
					stats_resolv_auto+=-1
				else
					#
					# Try to submit the value taken
					# from the template
					#
					notified=
					echo -ne "$CONF_REPLY"
					echo "$val"
					stats_resolv_auto+=1
				fi ;;
			esac || {
				# value is not in the template, ask to
				# the user to resolve this.
				echo -ne "$CONF_REPLY"
				read val
				stats_resolv_user+=1
			}
			echo >&${COPROC[1]} "$val"
			CONF_REPLY=""
			;;
		esac
	done

	if ! wait $coproc_id; then
		die "coproc exited abnormally ($?), aborting."
	fi

	echo "=========================="
	echo "Symbols resolved by kdist : $stats_resolv_auto"
	echo "Symbols resolved by user  : $stats_resolv_user"
	echo "Symbols total number      : $(grep -c ^CONFIG_ $config)"
	echo "=========================="
done

