#!/bin/bash

#############################################################################
# Copyright (c) 2003-2009 Novell, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program 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 this program; if not, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com
#############################################################################

#########################################################
# dirty scroll region tricks ...

use_region=false
if test -f scripts/kconfig/Makefile && \
   grep -q syncconfig scripts/kconfig/Makefile; then
    syncconfig="syncconfig"
else
    syncconfig="silentoldconfig"
fi

function _region_init_ () {
    echo -ne '\x1b[H\033[J'	# clear screen
    echo -ne '\x1b[4;0r'	# setup scroll region
    echo -ne '\x1b[4;0H'	# move cursor
}

function _region_fini_ () {
    echo -ne '\x1b7'		# save cursor
    echo -ne '\x1b[0;0r'		# del scroll region
    echo -ne '\x1b8'		# restore cursor
}

function _region_msg_ () {
    local msg="$*"
    if $silent; then
	    return
    fi
    if $use_region; then
	echo -ne '\x1b7'	# save cursor
	echo -ne '\x1b[0;0H'	# move cursor
	echo -e "##\x1b[K"	# message
	echo -e "## $msg\x1b[K"	# message
	echo -e "##\x1b[K"	# message
	echo -ne '\x1b8'	# restore cursor
    else
	echo -e "##"
	echo -e "## $msg"
	echo -e "##"
    fi
}

info()
{
	if $silent; then
		return
	fi
	echo "$@"
}

set_var()
{
	local name=$1 val=$2 config config_files

	name="${name%%=*}"
	case "$name" in
		CONFIG_*) ;;
		*) name="CONFIG_$name" ;;
	esac
	config_files=$(${scripts}/guards $CONFIG_SYMBOLS < ${prefix}config.conf)
	if [ -n "$set_flavor" ] ; then
		info "appending $name=$val to all -$set_flavor config files listed in config.conf"
		config_files=$(printf "%s\n" $config_files | grep "/$set_flavor\$")
	else
		info "appending $name=$val to all config files listed in config.conf"
	fi
	for config in $config_files; do
		if test -L "${prefix}config/$config"; then
			continue
		fi
		# do not change trimmed configs unless requested
		if test -z "$set_flavor" && ! \
			grep -q '^CONFIG_MMU=' "${prefix}config/$config"; then
			continue
		fi
		sed -i "/\\<$name[ =]/d" "${prefix}config/$config"
		case "$val" in
		n) echo "# $name is not set" ;;
		*) echo "$name=$val" ;;
		esac >> ${prefix}config/$config
	done
}


function _cleanup_() {
	test -d "$TMPDIR" && rm -rf $TMPDIR
	if $use_region; then
		_region_fini_
	fi
}
TMPDIR=
trap _cleanup_ EXIT

#########################################################
# main

cpu_arch=
mode=oldconfig
option=
value=
silent=false
check=false
current=false
until [ "$#" = "0" ] ; do
	case "$1" in
	y|-y|--yes|o|-o|--olddefconfig)
		mode=yes
		shift
		;;
	--mod)
		mode=allmodconfig
		shift
		;;
	a|-a|--arch)
		cpu_arch=$2
		shift 2
		;;
	m|-m|--menuconfig)
		mode=menuconfig
		shift
		;;
	-nco-y|--new-config-option-yes)
		mode=single
		option=$2
		value=y
		shift 2
		;;
	-nco-m|--new-config-option-mod)
		mode=single
		option=$2
		value=m
		shift 2
		;;
	-nco-n|--new-config-option-no|-dco|--disable-config-option)
		mode=single
		option=$2
		value=n
		shift 2
		;;
	-nco|--new-config-option)
		mode=single
		option="${2%%=*}"
		value="${2#*=}"
		shift 2
		;;
	--flavor)
		set_flavor="$2"
		shift 2
		;;
	--vanilla)
		set_flavor="vanilla"
		shift
		;;
	--check)
		check=true
		shift
		;;
	-c|--current)
		current=true
		shift
		;;
	-s|--silent)
		silent=true
		shift
		;;
	-h|--help)
		cat <<EOF

${0##*/} does either:
 * run make oldconfig to clean up the .config files
 * modify kernel .config files in the GIT tree

run it with no options in your SCRATCH_AREA $SCRATCH_AREA, like
	patches/scripts/${0##*/}
possible options in this mode:
	called with no option will run just make oldconfig interactive
	y|-y|--yes         to run 'yes "" | make oldconfig' - equivalent to
                           'make olddefconfig' on newer kernels
	o|-o|--olddefconfig  same as '--yes'
	--mod              to set all new options to 'm' (booleans to 'y')
	a|-a|--arch        to run make oldconfig only for the given arch
	m|-m|--menuconfig  to run make menuconfig instead of oldconfig
	--flavor <flavor>  to run only for configs of specified flavor
	--vanilla          an alias for "--flavor vanilla"
	--check            just check if configs are up to date
	-c|--current       uset tmp/current for checks

run it with one of the following options to modify all .config files listed
in config.conf:
	-nco-y|--new-config-option-yes   compile something into the kernel
	-nco-m|--new-config-option-mod   compile something as a module
	-nco-n|--new-config-option-no    disable a kernel .config option
	-dco|--disable-config-option     alias for -nco-n
each of them takes a second argument, which can be either
FOO
FOO=X
CONFIG_FOO
CONFIG_FOO=X
run with the following to modify option taking a value:
	-nco|--new-config-option OPTION=VALUE
as above, OPTION can be used with or without the CONFIG_ prefix; for string
options requiring double quotes, these must be used in argument (and shell
escaped)

Run with -s|--silent in both modes to suppress most output
EOF
		exit 1
		;;
	*)
		echo ugh
		exit 1
		;;
	esac
done

if [ -f patches/scripts/arch-symbols ] ; then
	prefix=patches/
elif [ -f scripts/arch-symbols ] ; then
	prefix=
else
	echo "no arch-symbols found"
	exit 1
fi

if $current; then
	prefix=../../$prefix
	cd tmp/current
fi

scripts="${prefix}scripts"

if test -e "${prefix}rpm/config.sh"; then
	source "$_"
fi
if test -z "$set_flavor" && test "$VANILLA_ONLY" = 1 -o -e .is_vanilla; then
	set_flavor=vanilla
fi

if [ -z "$cpu_arch" ]; then
    CONFIG_SYMBOLS=$(
        for arch in $(${scripts}/arch-symbols --list); do
            ${scripts}/arch-symbols $arch
        done
    )
else
    CONFIG_SYMBOLS=$(${scripts}/arch-symbols $cpu_arch)
fi

case "$mode" in
single)
	set_var "$option" "$value"
	exit 0
	;;
menuconfig)
	;;
*)
	if test "$set_flavor" = "vanilla" -a -z "$VANILLA_ONLY" -a \
			! -e .is_vanilla; then
		echo "run_oldconfig.sh --vanilla only works in a tree created with" >&2
		echo -n "sequence-patch.sh --vanilla. Do you really want to continue? [yN] " >&2
		read
		case "$REPLY" in
		"" | [Nn]*)
			exit 1
		esac
	fi

	case "$TERM" in
	linux* | xterm* | screen*)
		if tty -s && ! $silent; then
			use_region=true
			_region_init_
		fi
	esac
esac

config_files=$(${scripts}/guards $CONFIG_SYMBOLS < ${prefix}config.conf)

if [ -z "$set_flavor" ] ; then
    config_files=$(printf "%s\n" $config_files | grep -v vanilla)
else
    config_files=$(printf "%s\n" $config_files | grep "/$set_flavor\$")
fi

TMPDIR=$(mktemp -td ${0##*/}.XXXXXX)

EXTRA_SYMBOLS=
if [ -s extra-symbols ]; then
    EXTRA_SYMBOLS="$(cat extra-symbols)"
fi

${scripts}/guards $EXTRA_SYMBOLS < ${prefix}series.conf \
    > $TMPDIR/applied-patches

EXTRA_SYMBOLS="$(echo $EXTRA_SYMBOLS | sed -e 's# *[Rr][Tt] *##g')"

mkdir $TMPDIR/reuse

ask_reuse_config()
{
    local old=$1 new=$2

    # if the user either asked to never reuse the config or if this config
    # already reused something, do nothing
    for f in $TMPDIR/reuse/{never,all,$cpu_arch-all,all-$flavor}; do
        if test -e "$f"; then
            return
        fi
    done
    diff $old $new | awk >$TMPDIR/reuse/diff '
        /< .*CONFIG_/ { x[substr($0, 3)]--; }
        /> .*CONFIG_/ { x[substr($0, 3)]++; }
        END {
            for (l in x)
                if (x[l] > 0 && l !~ /^CONFIG_LOCALVERSION\>/)
                    print l;
        }'

    if test ! -s $TMPDIR/reuse/diff; then
        return
    fi
    while :; do
        echo
        cat $TMPDIR/reuse/diff | sed 's/^/  /'
        echo
        echo "Use these settings for other configurations?"
        read -p "[Y]es/for [A]rch $cpu_arch/for [F]lavor $flavor/[N]o/[E]dit/ne[V]er "
        case "$REPLY" in
        [Yy] | "")
            mv $TMPDIR/reuse/diff $TMPDIR/reuse/all
            break ;;
        [Aa])
            mv $TMPDIR/reuse/diff $TMPDIR/reuse/$cpu_arch-all
            break ;;
        [Ff])
            mv $TMPDIR/reuse/diff $TMPDIR/reuse/all-$flavor
            break ;;
        [Ee])
            ${VISUAL:-${EDITOR:-vi}} $TMPDIR/reuse/diff
            ;;
        [Nn])
            rm $TMPDIR/reuse/diff
            break ;;
        [Vv])
            rm $TMPDIR/reuse/diff
                touch $TMPDIR/reuse/never
            break ;;
        esac
    done
}

filter_config()
{
    sed  -e '/CONFIG_GCC_VERSION/ d' -e '/CONFIG_LD_VERSION/ d' -e '/CONFIG_CC_VERSION_TEXT/ d' -e '/CONFIG_PAHOLE_VERSION/ d' -e '/^# .* is not set$/p' -e '/^$\|^#/d' "$@" | sort
}

# Keep these in the -vanilla fragment even if -default has the same values.
# This allows the spec file to read them from the fragment without calling
# kconfig
precious_options=($(sed -n 's/^%define config_vars //p' "${prefix}rpm/kernel-binary.spec.in"))
err=0
for config in $config_files; do
    cpu_arch=${config%/*}
    flavor=${config#*/}

    if test -L "${prefix}config/$config"; then
        continue
    fi
    set -- kernel-$flavor $flavor $(case $flavor in (rt|rt_*) echo RT ;; esac)
    ${scripts}/guards $* $EXTRA_SYMBOLS \
	< ${prefix}series.conf > $TMPDIR/patches

    if ! diff -q $TMPDIR/applied-patches $TMPDIR/patches > /dev/null; then
	echo "Not all patches for $config are applied; skipping"
	diff -u $TMPDIR/applied-patches $TMPDIR/patches
	continue
    fi

    case $config in
    ppc*/*)
	MAKE_ARGS="ARCH=powerpc"
        ;;
    s390x/*)
        MAKE_ARGS="ARCH=s390"
        ;;
    arm64/*)
        MAKE_ARGS="ARCH=arm64"
        ;;
    armv*/*)
        MAKE_ARGS="ARCH=arm"
        ;;
    riscv*/*)
        MAKE_ARGS="ARCH=riscv"
        ;;
    */um)
        MAKE_ARGS="ARCH=um SUBARCH=$cpu_arch"
        ;;
    *)
        MAKE_ARGS="ARCH=$cpu_arch"
        ;;
    esac
    if [ -d scripts/dummy-tools ] ; then
	MAKE_ARGS="$MAKE_ARGS CROSS_COMPILE=scripts/dummy-tools/"
	if [ -e scripts/dummy-tools/pahole ]; then
		MAKE_ARGS="$MAKE_ARGS PAHOLE=scripts/dummy-tools/pahole"
	fi
	chmod 755 scripts/dummy-tools/*
	chmod 755 scripts/*
    fi
    MAKE_ARGS="$MAKE_ARGS RUSTC=/nothing/nowhere"
    if $silent; then
	MAKE_ARGS="$MAKE_ARGS -s"
    fi
    config="${prefix}config/$config"
    config_orig="config-orig"

    config_base=
    if ! grep -q CONFIG_MMU= "$config"; then
	if [ "$cpu_arch" = "i386" ]; then
	    config_base="$(dirname "$config")/pae"
	else
	    config_base="$(dirname "$config")/default"
	fi
	if ! [ -f "$config_base" ] && [ -n "$VARIANT" ] ; then
	    config_base="$(dirname "$config")/${VARIANT#-}"
	fi
	${scripts}/config-merge "$config_base" "$config" >$config_orig
    else
	cp "$config" $config_orig
    fi

    cp $config_orig .config

    for cfg in "CONFIG_LOCALVERSION=\"-$flavor\"" "CONFIG_SUSE_KERNEL=y" \
		    "CONFIG_DEBUG_INFO=y"; do
	    if ! grep -q "^$cfg\$" .config; then
		    echo "$cfg" >>.config
	    fi
    done
    for f in $TMPDIR/reuse/{all,$cpu_arch-all,all-$flavor}; do
        if test -e "$f"; then
            info "Reusing choice for ${f##*/}"
            cat "$f" >>.config
        fi
    done
    export KCONFIG_NOTIMESTAMP=1
    case "$mode" in
    menuconfig)
	make $MAKE_ARGS menuconfig
	;;
    yes)
	_region_msg_ "working on $config"
	yes '' | make $MAKE_ARGS oldconfig
	touch $TMPDIR/reuse/never
	;;
    allmodconfig)
	_region_msg_ "working on $config"
	cp .config config-old
	KCONFIG_ALLCONFIG=config-old make $MAKE_ARGS allmodconfig
	rm config-old
	touch $TMPDIR/reuse/never
	;;
    *)
	_region_msg_ "working on $config"
        if $check; then
            if ! make $MAKE_ARGS $syncconfig </dev/null; then
                echo "${config#$prefix} is out of date"
                err=1
                rm $config_orig
                continue
            fi
        else
            make $MAKE_ARGS oldconfig
        fi
    esac
    if ! $check; then
        ask_reuse_config $config_orig .config
	if [ -n "$config_base" ]; then
	    # We need to diff and re-merge to compare to the original,
	    # otherwise we'll see the differences between default
	    # and vanilla in addition to the changes made during this run.
	    ${scripts}/config-diff "$config_base" .config > config-new.diff
	    for opt in "${precious_options[@]}"; do
		    if ! grep -q -w "$opt" config-new.diff; then
			    grep -w "$opt" .config >>config-new.diff
		    fi
	    done
	    ${scripts}/config-merge "$config_base" config-new.diff > config-new
	    if ! $silent; then
		diff -U0 $config_orig config-new|grep -v ^@@
	    fi
	    mv config-new.diff "$config"
	    rm -f config-new

	else
	    if ! $silent; then
		diff -U0 $config_orig .config|grep -v ^@@
	    fi
	    cp .config "$config"
	fi
	rm -f $config_orig
        continue
    fi
    differences="$(
        diff -bU0 <(filter_config $config_orig) <(filter_config .config) | \
        grep '^[-+][^-+]'
    )"
    if echo "$differences" | grep -q '^+' ; then
        echo "Changes in ${config#$prefix} after running make oldconfig:"
        echo "$differences"
        err=1
    fi
    rm $config_orig
done

exit $err
