#! /bin/sh
#
# This script is used to configure the linux kernel.
#
# It was inspired by a desire to not have to hit <enter> 9 million times
# or startup the X server just to change a single kernel parameter.  
#
# This script attempts to parse the configuration files, which are
# scattered throughout the kernel source tree, and creates a temporary
# set of mini scripts which are in turn used to create nested menus and
# radiolists.
#
# It uses a very modified/mutilated version of the "dialog" utility
# written by Savio Lam (lam836@cs.cuhk.hk). Savio is not responsible
# for this script or the version of dialog used by this script.
# Please do not contact him with questions. The official version of 
# dialog is available at sunsite.unc.edu or a sunsite mirror.
#
# Portions of this script were borrowed from the original Configure
# script.
#
# Please send comments / questions / bug fixes to roadcapw@cfw.com
#
#----------------------------------------------------------------------------


#
# Change this to TRUE if you prefer all kernel options listed
# in a single menu rather than the standard menu hierarchy.
#
single_menu_mode=

#
# Make sure we're really running bash.
#
[ -z "$BASH" ] && { echo "Menuconfig requires bash" 1>&2; exit 1; }

#
# Cache function definitions
#
set -h

#
# Extract available help for an option from Configure.help
# and send it to standard output.
#
# Most of this function was borrowed from the original kernel
# Configure script.
#
function extract_help () {
  if [ -f Documentation/Configure.help ]
  then
     #first escape regexp special characters in the argument:
     var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
     #now pick out the right help text:
     text=$(sed -n "/^$var[ 	]*\$/,\${
                        /^$var[ 	]*\$/d
                        /^#.*/d
			/^[ 	]*\$/q
                        p
                    }" Documentation/Configure.help)

     if [ -z "$text" ]
     then
	  echo "There is no help available for this kernel option."
     else
	  echo "$text"
     fi
  else
	 echo "There is no help available for this kernel option."
  fi
}

#
# Activate a help dialog.
#
function help () {
	if extract_help $1 >help.out
	then
		$DIALOG	--backtitle "$backtitle" --title "$2"\
			--textbox help.out 20 75
	else
		$DIALOG	--backtitle "$backtitle" \
			--textbox help.out 20 75
	fi
	rm help.out
}

#
# Show the README file.
#
function show_readme() {
	$DIALOG --backtitle "$backtitle" \
		--textbox scripts/README.Menuconfig 21 75
}

#
# Begin building the dialog menu command and Initialize the 
# Radiolist function file.
#
function menu_name () {
	echo -ne "$DIALOG --title '$1'\
			--backtitle '$backtitle' \
			--menu '$menu_instructions' \
			21 75 11 '$default' " >MCmenu
	>MCradiolists
}

#
# Additional comments
#
function comment () {
	comment_ctr=$[ comment_ctr + 1 ]
	echo -ne "': $comment_ctr' '--- $1' " >>MCmenu
}

#
# Don't need this yet, but we don't want to puke either.
#
function define_bool () {
	:	
}

#
# Add a submenu option to the menu currently under construction.
#
function submenu () {
	echo -ne "'activate_menu $2' '$1  --->' " >>MCmenu
}

#
# Create a menu entry to handle the traditional sound configuration.
#
function soundcfg () {
	echo -ne "'l_soundcfg' "\
		 "'Old configuration script "\
		 "(For: SM Wave, PSS & AudioTrix Pro) -->' " >>MCmenu
}

#
# Startup the traditional sound configuration program.
#
function l_soundcfg () {
	clear
	$MAKE -C drivers/sound config
}


#
# Create a boolean (Yes/No) function for our current menu
# which calls our local bool function.
#
function bool () {
	eval $2=\${$2:-'n'}  x=\$$2

	case $x in
	y|m) yes='ON' no='OFF' flag="*"
	   ;;
	n) yes='OFF' no='ON' flag=" "
	   ;;
	esac

	echo -ne "'$2' '($flag) $1' " >>MCmenu

	echo -e "function $2 () { l_bool '$1' '$yes' '$no' '$2' }\n" \
		>>MCradiolists
}

#
# Handle a boolean (Yes/No) option.
#
function l_bool () {
	while true
	do
		$DIALOG --title "$1" \
			--backtitle "$backtitle" \
			--radiolist "$radiolist_instructions" 12 70 2 \
			'y' 'Yes' $2 'n' 'No' $3 2>MCdialog.out

		case "$?" in
		0) eval $4=`cat MCdialog.out`
		   break ;;

		1) help "$4" "$1" ;;

		*) break ;;
		esac
	done
}

#
# Same as bool() except options are (Module/No)
#
function mod_bool () {
	eval $2=\${$2:-'n'}  x=\$$2

	case $x in
	m) module='ON'  no='OFF' flag='M'
	   ;;
	*) module='OFF' no='ON'  flag=' '
	   ;;
	esac

	echo -ne "'$2' '($flag) $1' " >>MCmenu

	echo -e "function $2 () { l_mod_bool '$1' '$module' '$no' '$2' }\n" \
		>>MCradiolists
}

#
# Same as l_bool() except options are (Module/No)
#
function l_mod_bool() {
	while true
	do
		$DIALOG --title "$1" \
			--backtitle "$backtitle" \
			--radiolist "$radiolist_instructions" 12 70 2 \
			'm' 'Module' $2 'n' 'No' $3 2>MCdialog.out

		case "$?" in
		0) eval $4=`cat MCdialog.out`
		   break ;;

		1) help "$4" "$1" ;;

		*) break ;;
		esac
			
	done
}

#
# Create a tristate (Yes/No/Module) radiolist function
# which calls our local tristate function.
#
# Collapses to a boolean (Yes/No) if module support is disabled.
#
function tristate () {
	if [ "$CONFIG_MODULES" != "y" ]
	then
		bool "$1" "$2"
	else
		eval $2=\${$2:-'n'}  x=\$$2
	
		case $x in
		y) yes='ON'  no='OFF' module='OFF' flag="*"
   		;;
		m) yes='OFF' no='OFF' module='ON' flag="M"
   		;;
		*) yes='OFF' no='ON'  module='OFF' flag=" "
   		;;
		esac
	
		echo -ne "'$2' '($flag) $1' " >>MCmenu
	
		echo -e "
		function $2 () { \
			l_tristate '$1' '$yes' '$no' '$module' '$2'
		}"  >>MCradiolists
	fi
}

#
# Handle a tristate (Yes/No/Module) option.
#
function l_tristate () {
	while true
	do
		$DIALOG --title "$1" \
			--backtitle "$backtitle" \
			--radiolist "$radiolist_instructions" 13 70 3 \
			'y' 'Yes' $2 'n' 'No' $3 'm' 'Module' $4 \
			2>MCdialog.out

		case "$?" in
		0) eval $5=`cat MCdialog.out`
		   break ;;

		1) help "$5" "$1" ;;

		*) break ;;
		esac
	done
}

#
# Create a tristate radiolist function which is dependent on
# another kernel configuration option.
#
# Quote from the original configure script:
#
#       If the option we depend upon is a module,
#       then the only allowable options are M or N.  If Y, then
#       this is a normal tristate.  This is used in cases where modules
#       are nested, and one module requires the presence of something
#       else in the kernel.
#
function dep_tristate () {
	if [ "$CONFIG_MODULES" != "y" ]
	then
		bool "$1" "$2"
	else
		if  eval [ "_$3" != "_m" ]
		then
			tristate "$1" "$2" $3
		else
			mod_bool "$1" "$2"
		fi
	fi
}

#
# Add a menu item which will call our local int function.
# 
function int () {
	eval $2=\${$2:-"$3"} x=\$$2

	echo -ne "'$2' '($x) $1' " >>MCmenu

	echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' }\n" >>MCradiolists
}

#
# Create a dialog for entering an integer into a kernel option.
#
function l_int () {
	while true
	do
		if $DIALOG --title "$1" \
			--backtitle "$backtitle" \
			--inputbox "$inputbox_instructions_int" \
			15 55 "$4" 2>MCdialog.out
		then
			answer="`cat MCdialog.out`"
			answer="${answer:-$3}"

			if expr $answer : '0$\|-?[1-9][0-9]*$' >/dev/null
			then
				eval $2="$answer"
			else
				eval $2="$3"
				echo -en "\007"
				${DIALOG} --backtitle "$backtitle" \
					--infobox "You have made an invalid entry." 3 43
				sleep 2
			fi

			break
		fi

		help "$2" "$1"
	done
}


#
# Add a menu item which will call our local int function.
# 
function hex () {
	eval $2=\${$2:-"$3"} x=\${$2##*[x,X]}

	echo -ne "'$2' '($x) $1' " >>MCmenu

	echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' }\n" >>MCradiolists
}

#
# Create a dialog for entering a hexidecimal into a kernel option.
#
function l_hex () {
	while true
	do
		if $DIALOG --title "$1" \
			--backtitle "$backtitle" \
			--inputbox "$inputbox_instructions_hex" \
			15 55 "$4" 2>MCdialog.out
		then
			answer="`cat MCdialog.out`"
			answer="${answer:-$3}"
			answer="${answer##*[x,X]}"

			if expr $answer : '[0-9a-fA-F]+$' >/dev/null
			then
				eval $2="$answer"
			else
				eval $2="$3"
				echo -en "\007"
				${DIALOG} --backtitle "$backtitle" \
					--infobox "You have made an invalid entry." 3 43
				sleep 2
			fi

			break
		fi

		help "$2" "$1"
	done
}

#
# Add a menu item which will call our local One-of-Many choice list.
#
function choice () {
	#
	# Need to remember params cause the're gonna get reset.
	#
	title=$1
	choices=$2
	default=$3
	current=

	#
	# Find out if one of the choices is already set.
	# If it's not then make it the default.
	#
	set -- $choices
	firstchoice=$2

	while [ -n "$2" ]
	do
		if eval [ "_\$$2" = "_y" ]
		then
			current=$1
			break
		fi
		shift ; shift
	done

	: ${current:=$default}

	echo -ne "'$firstchoice' '($current) $title' " >>MCmenu

	echo -e "
	function $firstchoice () {
		l_choice '$title' \"$choices\" $current
	}\n" >>MCradiolists
}


function l_choice () {
	#
	# Need to remember params cause the're gonna get reset.
	#
	title="$1"
	choices="$2"
	current="$3"

	#
	# Scan current value of choices and set radiolist switches.
	#
	list=
	set -- $choices
	firstchoice=$2
	while [ -n "$2" ]
	do
		case "$1" in
		"$current")	list="$list $2 $1 ON "  ;;
		*)		list="$list $2 $1 OFF " ;;
		esac
			
		shift ; shift
	done

	while true
	do
		if $DIALOG --title "$title" \
			--backtitle "$backtitle" \
			--radiolist "$radiolist_instructions" \
			22 70 11 $list 2>MCdialog.out
		then
			choice=`cat MCdialog.out`
			break
		fi

		help "$firstchoice" "$title"
	done

	#
	# Now set the boolean value of each option base on
	# the selection made from the radiolist.
	#
	set -- $choices
	while [ -n "$2" ]
	do
		if [ "$2" = "$choice" ]
		then
			eval $2="y"
		else
			eval $2="n"
		fi
		
		shift ; shift
	done
}


#
# A faster awk based recursive parser. (I hope)
#
function parser1 () {
awk '
BEGIN {
	menu_no = 0
	comment_is_option = 0
	parser("'$CONFIG_IN'","MCmenu0")
}

function parser(ifile,menu) {

	while (getline <ifile) {
		if ($1 == "mainmenu_option") {
			comment_is_option = "1"
		}
		else if ($1 == "comment" && comment_is_option == "1") {
			comment_is_option= "0"
			sub($1,"",$0)
			++menu_no

			printf("submenu %s MCmenu%s\n", $0, menu_no) >>menu

			printf( "function MCmenu%s () {\n"\
				"default=$1\n"\
				"menu_name %s\n",\
				 menu_no, $0) >"MCmenu"menu_no

			parser(ifile, "MCmenu"menu_no)
		}
		else if ($1 ~ "endmenu") {
			printf("}\n") >>menu
			return
		} 
		else if ($0 ~ /^#|$MAKE|mainmenu_name/) {
			printf("") >>menu
		}
		else if ($1 == "source") {
			# Yuk!  Blah!  Phooey!
			if ($2 ~ "drivers/sound") {
				printf("soundcfg\n") >>menu
			}

			parser($2,menu)
		}
		else {
			print >>menu
		}
	}
}'
}

#
# Secondary parser for single menu mode.
#
function parser2 () {
awk '
BEGIN {
	parser("'$CONFIG_IN'","MCmenu0")
}

function parser(ifile,menu) {

	while (getline <ifile) {
		if ($1 ~ /mainmenu_option|endmenu/) {
			printf("") >>menu
		} 
		else if ($0 ~ /^#|$MAKE|mainmenu_name/) {
			printf("") >>menu
		}
		else if ($1 == "source") {
			if ($2 ~ "drivers/sound") {
				printf("soundcfg\n") >>menu
			}
			parser($2,menu)
		}
		else {
			print >>menu
		}
	}
}'
}

#
# Parse all the config.in files into mini scripts.
#
function parse_config_files () {
	rm -f MCmenu*

	echo "function MCmenu0 () {" >MCmenu0
	echo 'default=$1' >>MCmenu0
	echo "menu_name 'Main Menu'" >>MCmenu0


	if [ "_$single_menu_mode" = "_TRUE" ]
	then
		parser2
	else
		parser1
	fi

	echo "}" >>MCmenu0

	#
	# These mini scripts must be sourced into the current
	# environment in order for all of this to work.  Leaving
	# them on the disk as executables screws up the recursion
	# in activate_menu(), among other things.  Once they are
	# sourced we can disgard them.
	#
	for i in MCmenu*
	do
		source $i
	done

	rm -f MCmenu*
}

#
# This is the menu tree's bootstrap.
#
# Executes the parsed menus on demand and creates a set of functions,
# one per configuration option.  These functions will in turn execute
# dialog commands or recursively call other menus.
#
function activate_menu () {

	while true
	do
		comment_ctr=0
		$1 "$default"		#Create the radiolists and dialog cmd
		. MCradiolists		#Read in the dialog functions.

		. MCmenu 2>MCdialog.out	#Activate this menu

		case "$?" in
		0)
			defaults="`cat MCdialog.out`$defaults"  #psuedo stack
			. MCdialog.out
			default="${defaults%%*}" defaults="${defaults#*}"
			;;
		2)	
			echo >>MCdialog.out
			read selection <MCdialog.out
			default="${selection%% *}"

			case "$selection" in
			*"-->"*) show_readme ;;
			*)	 eval help $selection ;;
			esac
			;;
		255|1)
			break
			;;
		esac
	done
}

#
# Just what it says.
#
save_configuration () {
	${DIALOG} --backtitle "$backtitle" \
		  --infobox "Saving your new kernel configuration..."  3 43

	#
	# Now, let's redefine the configuration functions for final
	# output to the config files.
	#
	function bool () {
		eval define_bool "$2" "\${$2:-n}"
	}

	function tristate () {
		eval define_bool "$2" "\${$2:-n}"
	}

	function dep_tristate () {
		eval x=\${$2:-n}

		if eval [ "_$3" = "_m" ]
		then
			if [ "$x" = "y" ]
			then
				x="m"
			fi
		fi

		define_bool "$2" "$x"
	}

	function int () {
		eval x=\${$2:-"$3"}
		echo "$2=$x" 		>>$CONFIG
		echo "#define $2 ($x)"	>>$CONFIG_H
	}

	function hex () {
		eval x=\${$2:-"$3"}
		echo "$2=$x" 			 >>$CONFIG
		echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H
	}

	function define_bool () {
		eval $1="$2"

   		case "$2" in
         	y)
                	echo "$1=y" 		>>$CONFIG
                	echo "#define $1 1"	>>$CONFIG_H
                	;;

         	m)
			if [ "$CONFIG_MODULES" = "y" ]
			then
                		echo "$1=m"		>>$CONFIG
                		echo "#undef  $1"	>>$CONFIG_H
			else
                		echo "$1=y" 		>>$CONFIG
                		echo "#define $1 1"	>>$CONFIG_H
			fi
                	;;

         	n)
                	echo "# $1 is not set"	>>$CONFIG
                	echo "#undef  $1"	>>$CONFIG_H
                	;;
        	esac
	}

	function choice () {
		#
		# Find the first choice that's already set to 'y'
		#
		choices="$2"
		default="$3"
		current=

		set -- $choices
		while [ -n "$2" ]
		do
			if eval [ "_\$$2" = "_y" ]
			then
				current=$1
				break
			fi
			shift ; shift
		done

		#
		# Use the default if none were set.  
		#
		: ${current:=$default}

		#
		# Then extract the actual option from the list of choices.
		#
		current=${choices#*$current} ; set $current

		define_bool "$1" "y"
	}

	function mainmenu_name () {
		:
	}

	function mainmenu_option () {
		comment_is_option=TRUE
	}

	function endmenu () {
		:
	}

	function comment () {
		if [ "$comment_is_option" ]
		then
			comment_is_option=
			echo        >>$CONFIG
			echo "#"    >>$CONFIG
			echo "# $1" >>$CONFIG
			echo "#"    >>$CONFIG

			echo         >>$CONFIG_H
			echo "/*"    >>$CONFIG_H
			echo " * $1" >>$CONFIG_H
			echo " */"   >>$CONFIG_H
		fi
	}

	CONFIG=.tmpconfig
	CONFIG_H=.tmpconfig.h

	echo "#" >$CONFIG
	echo "# Automatically generated by make menuconfig: don't edit" >>$CONFIG
	echo "#" >>$CONFIG

	echo "/*" >$CONFIG_H
	echo " * Automatically generated by make menuconfig: don't edit" >>$CONFIG_H
	echo " */" >>$CONFIG_H

	MAKE=:	#To prevent sound Makefile from running.
	
	if . $CONFIG_IN >>.menuconfig.log 2>&1
	then
		#
		# Create the sound driver's config files for cards
		# Which are compatible with the new config method.
		#
		if [ "_$CONFIG_TRIX"   != "_y" -a\
		     "_$CONFIG_PSS"    != "_y" -a\
		     "_$CONFIG_SMWAVE" != "_y"    ]
		then
			make -C drivers/sound kernelconfig >>.menuconfig.log 2>&1
		fi

		if [ -f .config ]
		then
			rm -f .config.old
			mv .config .config.old
		fi
		mv .tmpconfig .config
		mv .tmpconfig.h include/linux/autoconf.h
		return 0
	else
		return 1
	fi
}

#
# Remove temporary files
#
cleanup () {
	cleanup1
	cleanup2
}

cleanup1 () {
	rm -f MCmenu* MCradiolists MCdialog.out help.out
}

cleanup2 () {
	rm -f .tmpconfig .tmpconfig.h
}

menu_instructions="\
Arrow keys navigate the menu.  \
Highlighted letters are hotkeys.  \
Select an item with <Space Bar> or <Enter>.  \
When finished press <Esc><Esc> or <X>.  \
A (*) marks an option to be compiled into the kernel.  \
An (M) marks an option to be compiled as a module."

radiolist_instructions="\
Use the arrow keys to navigate this window or \
press the hotkey of the item you wish to select \
followed by the <SPACE BAR>.
Press <H> for additional information about this option."

inputbox_instructions_int="\
Please enter a decimal value between 1 and 9999. \
Fractions will not be accepted.  \
Use the <TAB> key to move from the input field to buttons below it."

inputbox_instructions_hex="\
Please enter a hexidecimal value. \
Use the <TAB> key to move from the input field to buttons below it."

backtitle="Linux Kernel Configuration"

DIALOG="./scripts/lxdialog/lxdialog"

kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}"

trap "cleanup ; rm -f .menuconfig ; exit 1" 1 2 15

#
# Locate default files.
#
DEFAULT=""
if [ "$1" = "-d" ] ; then
	DEFAULT="-d"
	shift
fi

CONFIG_IN=./config.in
if [ "$1" != "" ] ; then
	CONFIG_IN=$1
fi

DEFAULTS=arch/$ARCH/defconfig
if [ -f .config ]; then
  DEFAULTS=.config
fi

if [ -f $DEFAULTS ]; then
  echo "#"
  echo "# Using defaults found in" $DEFAULTS
  echo "#"
  . $DEFAULTS
else
  echo "#"
  echo "# No defaults found"
  echo "#"
fi

# Fresh new log.
>.menuconfig.log


$DIALOG	--backtitle "$backtitle" \
	--infobox "Preparing configuration scripts..." 3 40

#
# Check kernel version of previous menuconfig build.
# If it's different then we should tell the sound driver
# to rebuild it's Config.in file.
#
rebuildsound=TRUE
if [ -e .menuconfig ]
then
	read x <.menuconfig
	if [ "$x" = "# $kernel_version" ]
	then
		rebuildsound=
	fi
fi

if [ "$rebuildsound" ]
then
	# Activate the Linux compatible sound configuration.
	# This may not work for all sound cards.  (See sound docs)
	#
	make -C drivers/sound mkscript >>.menuconfig.log 2>&1

	echo "# $kernel_version" >.menuconfig
fi

#
# Read config.in files and parse them into one shell function per menu.
#
parse_config_files $CONFIG_IN

#
# Start the ball rolling from the top.
#
activate_menu MCmenu0

#
# All done!
#
cleanup1

#
# Confirm and Save
#
if $DIALOG --backtitle "$backtitle" \
	   --yesno "Do you wish to save your new kernel configuration?" 5 60
	   
then
	save_configuration

	clear
	cat <<EOM


The linux kernel is now hopefully configured for your setup.
Check the top-level Makefile for additional configuration,
and do a 'make dep ; make clean' if you want to be sure all
the files are correctly re-made.

EOM
else
	clear
	echo -e "Your kernel configuration changes were NOT saved.\n"
fi


exit 0

