#!/bin/bash
#
# kconfig-new-symbols.sh
#
# Non-interactive "what would oldconfig have prompted me for" report.
# Never touches .config — it's a true dry run.
#
# What it does:
#   1. Runs `make listnewconfig` (the read-only Kconfig target that lists
#      exactly the symbols oldconfig would ask you about — auto-resolvable
#      ones as CONFIG_X=value, genuinely undecided ones as bare CONFIG_X —
#      without writing .config). Prints the count and the symbol list.
#   2. For each new symbol, looks it up in a reference .config
#      (--ref-config) and prints its value there (or NOT SET), then
#      greps the Kconfig files in this tree for that symbol's help text.
#      The value is colorized when stdout is a terminal: red = NOT SET,
#      light blue = m, bright green = y (see --color to force on/off).
#
# Must be run from the root of a kernel source tree, with whatever
# ARCH / CROSS_COMPILE / O environment you'd normally build with already
# exported (this script just calls `make listnewconfig`, it inherits your
# env exactly like a normal build would).
#
# Example:
#  ./kconfig-new-symbols.sh --ref-config /tmp/ref.config > new-options.txt
#
set -euo pipefail

CONFIG=".config"
REF_CONFIG=""
COLOR_MODE="auto"

usage() {
    cat <<'EOF'
Usage: kconfig-new-symbols.sh [options]

  --ref-config PATH   Reference .config to cross-check new symbols against.
                      Optional: without it, only step 1 (count + list) runs.
  --config PATH       Path to the working .config (default: ./.config)
  --color WHEN        Colorize the reference-config value: auto (default,
                      on when stdout is a terminal), always, or never.
  -h, --help          Show this help
EOF
    exit 1
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --ref-config) REF_CONFIG="$2"; shift 2 ;;
        --config) CONFIG="$2"; shift 2 ;;
        --color) COLOR_MODE="$2"; shift 2 ;;
        -h|--help) usage ;;
        *) echo "Unknown argument: $1" >&2; usage ;;
    esac
done

case "$COLOR_MODE" in
    auto)   [[ -t 1 ]] && USE_COLOR=1 || USE_COLOR=0 ;;
    always) USE_COLOR=1 ;;
    never)  USE_COLOR=0 ;;
    *) echo "error: --color must be auto, always, or never" >&2; exit 1 ;;
esac

if [[ "$USE_COLOR" -eq 1 ]]; then
    C_RED=$'\033[0;31m'      # NOT SET
    C_LBLUE=$'\033[0;94m'    # m
    C_GREEN=$'\033[0;92m'    # y
    C_RESET=$'\033[0m'
else
    C_RED='' C_LBLUE='' C_GREEN='' C_RESET=''
fi

[[ -f "$CONFIG" ]] || { echo "error: config not found: $CONFIG" >&2; exit 1; }
[[ -f "Kconfig" && -f "Makefile" ]] || {
    echo "error: run this from the root of a kernel source tree" >&2
    exit 1
}
if [[ -n "$REF_CONFIG" && ! -f "$REF_CONFIG" ]]; then
    echo "error: reference config not found: $REF_CONFIG" >&2
    exit 1
fi

# --- Step 1: list the new symbols (read-only, .config is never written) -

echo "Running: make listnewconfig ..." >&2
LISTNEWCONFIG_OUT=$(make listnewconfig 2>&1)

# listnewconfig emits "CONFIG_FOO=y" for symbols it can auto-resolve and
# bare "CONFIG_FOO" for symbols that genuinely need a decision, interleaved
# with the usual Kconfig warnings on stray/obsolete .config lines — filter
# down to just the CONFIG_ lines and drop any trailing "=value". No sort:
# listnewconfig walks the same menu tree in the same order oldconfig
# prompts you in, so preserving that order (just deduped) is the point.
NEW_ONLY=$(printf '%s\n' "$LISTNEWCONFIG_OUT" \
    | grep -oE '^CONFIG_[A-Za-z0-9_]+' \
    | awk '!seen[$0]++' || true)

COUNT=$(printf '%s\n' "$NEW_ONLY" | grep -c . || true)

echo "New CONFIG options introduced: $COUNT"
echo

if [[ "$COUNT" -eq 0 ]]; then
    exit 0
fi

printf '%s\n' "$NEW_ONLY"
echo

if [[ -z "$REF_CONFIG" ]]; then
    echo "(pass --ref-config to also see reference values and Kconfig help)"
    exit 0
fi

# --- Step 2: cross-reference against --ref-config, plus help text -------

# Collect the Kconfig file list once, not once per symbol.
mapfile -t KCONFIG_FILES < <(find . -name 'Kconfig*' -not -path './.git/*')

get_ref_value() {
    local sym="$1" line value
    line=$(grep -E "^${sym}=|^# ${sym} is not set" "$REF_CONFIG" || true)
    if [[ -z "$line" ]]; then
        value="NOT SET"
    elif [[ "$line" == "#"* ]]; then
        value="NOT SET"
    else
        value="${line#${sym}=}"
    fi

    case "$value" in
        "NOT SET") printf '%s%s%s' "$C_RED" "$value" "$C_RESET" ;;
        y)         printf '%s%s%s' "$C_GREEN" "$value" "$C_RESET" ;;
        m)         printf '%s%s%s' "$C_LBLUE" "$value" "$C_RESET" ;;
        *)         printf '%s' "$value" ;;
    esac
}

# Pull the help text for a bare symbol name (no CONFIG_ prefix) out of the
# Kconfig tree. Relies on standard kernel Kconfig indentation conventions:
# "config FOO" at column 0, properties indented one tab, help text at
# tab + further whitespace.
get_help() {
    local sym="$1"
    awk -v sym="$sym" '
        $1 ~ /^(menuconfig|config)$/ && $2 == sym {
            capture=1; inhelp=0
            next
        }
        capture && /^(config|menuconfig|choice|endchoice|menu|endmenu|if|endif|comment|source)([ \t]|$)/ {
            capture=0; inhelp=0
        }
        capture && /^\t(help|---help---)$/ {
            inhelp=1
            next
        }
        capture && inhelp {
            if ($0 ~ /^\t[ \t]/) {
                line=$0
                sub(/^[ \t]+/, "", line)
                print line
            } else if ($0 == "") {
                print ""
            } else {
                inhelp=0
            }
        }
    ' "${KCONFIG_FILES[@]}" 2>/dev/null
}

for sym in $NEW_ONLY; do
    bare="${sym#CONFIG_}"
    echo "=== $sym ==="
    echo "Reference config: $(get_ref_value "$sym")"
    echo "--- Help ---"
    help_text=$(get_help "$bare")
    if [[ -z "$help_text" ]]; then
        echo "(no help text found in this tree)"
    else
        echo "$help_text"
    fi
    echo
done
