# This script checks the version of the configuration file and either
# alerts the user about the need to run the upgrade, or attempts to
# perform such an upgrade.

CFGFILE="${1}"

. "${CT_LIB_DIR}/scripts/functions"
. "${CFGFILE}"

# If an old config does not define a version, assume it is 0. This is used
# if we run this script on an old full .config file, not restored from a
# defconfig.
CT_CONFIG_VERSION="${CT_CONFIG_VERSION:-0}"
if [ "${CT_CONFIG_VERSION_CURRENT}" == "${CT_CONFIG_VERSION}" ]; then
    # Nothing to do
    exit 0
fi

if [ -z "${CT_UPGRADECONFIG}" ]; then
    if [ "${CT_CONFIG_VERSION}" != "0" ]; then
        oldversion="is version ${CT_CONFIG_VERSION}"
    else
        oldversion="has no version"
    fi
    cat 2>&1 <<EOF

Configuration file was generated by an older version of crosstool-NG;
configuration file ${oldversion}; crosstool-NG currently expects
version ${CT_CONFIG_VERSION_CURRENT}. If this configuration file was generated by a crosstool-NG
version 1.23.0 or later, you can run 'ct-ng upgradeconfig'.
Compatibility with previous releases is not guaranteed. In any case,
verify the resulting configuration.

EOF
    if [ "${CT_VCHECK}" = "strict" ]; then
        exit 1
    else
        exit 0
    fi
fi

# From now on, we're running actual upgrade, not just a check. Preserve the CT_xxx
# variables that we need using a different prefix so that we can unset the stale
# values at each iteration.
MY_LIB_DIR="${CT_LIB_DIR}"
MY_CONFIG_VERSION_CURRENT="${CT_CONFIG_VERSION_CURRENT}"

is_set()
{
    if [ "x${val+set}" = "xset" ]; then
        return 0
    else
        return 1
    fi
}

info()
{
    # $opt comes from the caller
    echo "INFO ${opt:+:: ${opt} }:: $1" >&2
}

warning()
{
    # $opt comes from the caller
    echo "WARN ${opt:+:: ${opt} }:: $1" >&2
}

error()
{
     # $opt comes from the caller
    echo " ERR ${opt:+:: ${opt} }:: $1" >&2
    exit 1
}

warning_if_set()
{
    if is_set; then
        warning "$@"
    fi
}

# When a symbol is replaced with a newer version. If it is a choice and
# the replacement existed in the old version as well, add a replacement_for
# handler for the other symbol to avoid kconfig warnings.
replace()
{
    local newopt="${1}"

    if is_set; then
        info "No longer supported; replacing with '${newopt}'".
        opt="${newopt}"
    else
        # Wasn't set; just drop it silently
        unset opt
    fi
}

# Avoid multiple definitions for a symbol when multiple old symbols are folded into one
# in a new version. If any of the variable names passed as arguments are set, skip
# emitting this variable (which, presumably, is "not set").
replacement_for()
{
    while [ -n "${1}" ]; do
        if [ -n "${!1}" ]; then
            unset opt
            return
        fi
        shift
    done
}

# Helper: takes ${ln} in the caller's environment and sets ${opt} and ${val}
# accordingly. Returns 0 if this line was an option, 1 otherwise.
set_opt_and_val()
{
    case "${ln}" in
        CT_*=*)
            opt=${ln%%=*}
            val=${ln#*=}
            case "${val}" in
            \"*\")
                val="${val%\"}"
                val="${val#\"}"
                q=\"
                ;;
            esac
            return 0
            ;;
        "# CT_"*" is not set")
            opt=${ln#* }
            opt=${opt%% *}
            return 0
            ;;
        *)
            ;;
    esac
    return 1
}


# Main upgrade driver. One version at a time, read line by line, interpret
# the options and replace anything that needs replacing.
input="${CFGFILE}"
while :; do
    # Purge any possibly stale values
    unset "${!CT_@}"
    # Reload the next input so that the upgrade function can rely on other CT_xxx variables,
    # not just the currently processed variable.
    . "${input}"
    v=${CT_CONFIG_VERSION:-0}
    if [ "${v}" -ge "${MY_CONFIG_VERSION_CURRENT}" ]; then
        break
    fi

    vn=$[ v + 1 ]
    info "Upgrading v${v} to v${vn}"
    if [ ! -r "${MY_LIB_DIR}/scripts/upgrade/v${v}" ]; then
        error "Missing upgrade script for v${v} of the config file"
    fi
    unset upgrade
    . "${MY_LIB_DIR}/scripts/upgrade/v${v}"
    # First pass: read in the whole file and mark the options mentioned;
    # it may not be possible to upgrade a defconfig if some non-trivial
    # option dependencies need to be resolved. We are not interested in
    # values yet.
    unset selected_opts
    declare -A selected_opts
    while read ln; do
        if set_opt_and_val; then
            selected_opts[${opt}]=1
        fi
    done < "${input}"

    # Second pass: actually upgrade the options.
    {
        while read ln; do
            unset opt
            unset val
            q=
            if set_opt_and_val; then
                case "${opt}" in
                    CT_CONFIG_VERSION_CURRENT|CT_CONFIG_VERSION)
                        continue
                        ;;
                esac
            else
                echo "${ln}"
            fi
            upgrade
            # Emit the option(s)
            if [ x${opt+set} = x ]; then
                continue
            elif [ x${val+set} = x ]; then
                echo "# ${opt} is not set"
            else
                echo "${opt}=${q}${val}${q}"
            fi
        done
        echo "CT_CONFIG_VERSION=\"${vn}\""
        echo "CT_CONFIG_VERSION_CURRENT=\"${CT_CONFIG_VERSION_CURRENT}\""
    } < "${input}" > "${CFGFILE}.${vn}"
    unset opt
    v=${vn}
    rm -f "${input}"
    input="${CFGFILE}.${vn}"
    # Ideally, we'd do 'ct-ng olddefconfig' after each step with the appropriate
    # Kconfig so that the next step would be able to use auto-set values from the
    # previous step. However, that would require us to keep archived config/ trees
    # from every config file version, which is not practical. So, I decided to defer
    # this until it is actually needed. Even then, it is probably sufficient to only
    # keep the versions where there is such a dependency.
done
mv "${CFGFILE}.${MY_CONFIG_VERSION_CURRENT}" "${CFGFILE}"
