#! /bin/sh
#
# This script is used to configure BusyBox.
#
# It was inspired by the challenge in the original Configure script
# to ``do something better'', combined with the actual need to ``do
# something better'' because the old configure script wasn't flexible
# enough.
#
# Raymond Chen was the original author of Configure.
# Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
#
# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
# with an empty IFS.
#
# 030995 (storner@osiris.ping.dk) - added support for tri-state answers,
# for selecting modules to compile.
#
# 180995 Bernhard Kaindl (bkaindl@ping.at) - added dummy functions for
# use with a config.in modified for make menuconfig.
#
# 301195 (boldt@math.ucsb.edu) - added help text support
#
# 281295 Paul Gortmaker - make tri_state functions collapse to boolean
# if module support is not enabled.
#
# 010296 Aaron Ucko (ucko@vax1.rockhurst.edu) - fix int and hex to accept
# arbitrary ranges
#
# 150296 Dick Streefland (dicks@tasking.nl) - report new configuration
# items and ask for a value even when doing a "make oldconfig"
#
# 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is
# chosen for an item, define the macro <option_name>_MODULE
#
# 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
# expressions for GNU expr since version 1.15 and up use \? and \+.
#
# 300397 Phil Blundell (pjb27@cam.ac.uk) - added support for min/max 
# arguments to "int", allow dep_tristate to take a list of dependencies
# rather than just one.
#
# 090398 Axel Boldt (boldt@math.ucsb.edu) - allow for empty lines in help
# texts.
#
# 102598 Michael Chastain (mec@shout.net) - put temporary files in
# current directory, not in /tmp.
#
# 24 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
# - Improve the exit message (Jeff Ronne).
#
# 7 October 2000, Ghozlane Toumi, <gtoumi@messel.emse.fr>
# added switches for "random" , "all yes" and "all modules"
#

#
# Make sure we're really running bash.
#
# I would really have preferred to write this script in a language with
# better string handling, but alas, bash is the only scripting language
# that I can be reasonable sure everybody has on their linux machine.
#
[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }

# Disable filename globbing once and for all.
# Enable function cacheing.
set -f -h

#
# Dummy functions for use with a config.in modified for menuconf
#
function mainmenu_option () {
	:
}
function mainmenu_name () {
	:
}
function endmenu () {
	:
}

#
# returns a random number between 1 and $1
#
function rnd () {
	rnd=$[ $RANDOM  %  $1 + 1 ]
}

#
# randomly chose a number in a config list (LIST_CONFIG_NAME)
# or in a range ( MIN_CONFIG_NAME MAX_CONFIG_NAME )
# ONLY if there is no forced default (and we are in an "auto" mode)
# we are limited by the range of values taken by  "$RANDOM"
#
#       rndval CONFIG_NAME
#

function rndval () {
	[ "$AUTO" != "yes" -o -n "$old" ] && return
	def_list=$(eval echo "\${LIST_$1}")
	def_min=$(eval echo "\${MIN_$1}")
	def_max=$(eval echo "\${MAX_$1}")

	if [ -n "$def_list" ]; then
	  set -- $(echo $def_list | sed 's/,/ /g')
	  rnd $#
	  while [ $rnd -le $# ] ; do
	    def=$1
	    shift
	  done
	  return
	fi
	if [ -n "$def_min" -a -n "$def_max" ]; then
	  rnd $[ $def_max - $def_min ]
	  def=$[ $def_min + $rnd ]
	fi
}

#
# help prints the corresponding help text from Configure.help to stdout
#
#       help variable
#
function help () {
  if [ -f docs/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[ 	]*\$/c\\
${var}:\\

			/^#/b
			/^[^ 	]/q
			/<file:\\([^>]*\\)>/s//\\1/g
			p
		    }" docs/Configure.help)
     if [ -z "$text" ]
     then
	  echo; echo "  Sorry, no help available for this option yet.";echo
     else
	  (echo; echo "$text") | ${PAGER:-more}
     fi
  else
     echo;
     echo "  Can't access the file docs/Configure.help which"
     echo "  should contain the help texts."
     echo
  fi
}


#
# readln reads a line into $ans.
#
#	readln prompt default oldval
#
function readln () {
	if [ "$AUTO" = "yes" ]; then 
		echo -n "$1"
		ans=$2
		echo $ans
	elif [ "$DEFAULT" = "-d" -a -n "$3" ]; then
		echo "$1"
		ans=$2
	else
		echo -n "$1"
		[ -z "$3" ] && echo -n "(NEW) "
		IFS='@' read ans || exit 1
		[ -z "$ans" ] && ans=$2
	fi
}

#
# comment does some pretty-printing
#
#	comment 'xxx'
# 
function comment () {
	echo "*"; echo "* $1" ; echo "*"
	(echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
	(echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
}

#
# define_bool sets the value of a boolean argument
#
#	define_bool define value
#
function define_bool () {
	define_tristate $1 $2
}

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

	 "m")
		echo "$1=m" >>$CONFIG
		echo "#undef  $1" >>$CONFIG_H
		echo "#define $1_MODULE 1" >>$CONFIG_H
		;;

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

#
# bool processes a boolean argument
#
#	bool question define
#
function bool () {
	old=$(eval echo "\${$2}")
	def=${old:-'n'}
	if [ "$AUTO" = "yes" -a -z "$old" ]; then
	  if [ "$RND" = "-r" ]; then
	    rnd 2
	    case $rnd in
	      "1") def="y" ;;
	      "2") def="n" ;;
	    esac
	  else
	    def=$DEF_ANS;
	  fi
	fi
	case "$def" in
	 "y" | "m") defprompt="Y/n/?"
	      def="y"
	      ;;
	 "n") defprompt="N/y/?"
	      ;;
	esac
	while :; do
	  readln "$1 ($2) [$defprompt] " "$def" "$old"
	  case "$ans" in
	    [yY] | [yY]es ) define_bool "$2" "y"
			    break;;
	    [nN] | [nN]o )  define_bool "$2" "n"
			    break;;
	    * )             help "$2"
			    ;;
	  esac
	done
}

#
# tristate processes a tristate argument
#
#	tristate question define
#
function tristate () {
	if [ "$CONFIG_MODULES" != "y" ]; then
	  bool "$1" "$2"
	else 
	  old=$(eval echo "\${$2}")
	  def=${old:-'n'}
	  if [ "$AUTO" = "yes" -a -z "$old" ]; then
	     if [ "$RND" = "-r" ]; then 
	      rnd 3
	      case $rnd in
	        "1") def="y" ;;
	        "2") def="n" ;;
	        "3") def="m" ;;
	      esac
	    else
	      def=$DEF_ANS
	    fi
	  fi
	  case "$def" in
	   "y") defprompt="Y/m/n/?"
		;;
	   "m") defprompt="M/n/y/?"
		;;
	   "n") defprompt="N/y/m/?"
		;;
	  esac
	  while :; do
	    readln "$1 ($2) [$defprompt] " "$def" "$old"
	    case "$ans" in
	      [yY] | [yY]es ) define_tristate "$2" "y"
			      break ;;
	      [nN] | [nN]o )  define_tristate "$2" "n"
			      break ;;
	      [mM] )          define_tristate "$2" "m"
			      break ;;
	      * )             help "$2"
			      ;;
	    esac
	  done
	fi
}

#
# dep_tristate processes a tristate argument that depends upon
# another option or options.  If any of the options we depend upon is a
# module, then the only allowable options are M or N.  If all are 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.
#
#	dep_tristate question define default ...
#
function dep_tristate () {
	old=$(eval echo "\${$2}")
	def=${old:-'n'}
	ques=$1
	var=$2
	need_module=0
	shift 2
	while [ $# -gt 0 ]; do
	  case "$1" in
 	    n)
	      define_tristate "$var" "n"
	      return
	      ;;
	    m)
	      need_module=1
	      ;;
	  esac
	  shift
	done

	if [ $need_module = 1 ]; then
	   if [ "$CONFIG_MODULES" = "y" ]; then
		if [ "$AUTO" = "yes" -a -z "$old" ]; then
		   if [ "$RND" = "-r" ]; then
		      rnd 2
		      case $rnd in
			"1") def="m" ;;
			"2") def="n" ;;
		      esac
		   else
		      def=$DEF_ANS
		   fi
		fi
		case "$def" in
		 "y" | "m") defprompt="M/n/?"
		      def="m"
		      ;;
		 "n") defprompt="N/m/?"
		      ;;
		esac
		while :; do
		  readln "$ques ($var) [$defprompt] " "$def" "$old"
		  case "$ans" in
		      [nN] | [nN]o )  define_tristate "$var" "n"
				      break ;;
		      [mM] )          define_tristate "$var" "m"
				      break ;;
		      [yY] | [yY]es ) echo 
   echo "  This answer is not allowed, because it is not consistent with"
   echo "  your other choices."
   echo "  This driver depends on another one which you chose to compile"
   echo "  as a module. This means that you can either compile this one"
   echo "  as a module as well (with M) or leave it out altogether (N)."
				      echo
				      ;;
		      * )             help "$var"
				      ;;
		  esac
		done
	   fi
	else
	   tristate "$ques" "$var"
	fi
}

function dep_bool () {
	ques=$1
	var=$2
	shift 2
	while [ $# -gt 0 ]; do
	  case "$1" in
	    m | n)
	      define_bool "$var" "n"
	      return
	      ;;
	  esac
	  shift
	done

	bool "$ques" "$var"
}

function dep_mbool () {
	ques=$1
	var=$2
	shift 2
	while [ $# -gt 0 ]; do
	  case "$1" in
	    n)
	      define_bool "$var" "n"
	      return
	      ;;
	  esac
	  shift
	done

	bool "$ques" "$var"
}

#
# define_int sets the value of a integer argument
#
#	define_int define value
#
function define_int () {
	echo "$1=$2" >>$CONFIG
	echo "#define $1 ($2)" >>$CONFIG_H
	eval "$1=$2"
}

#
# int processes an integer argument with optional limits
#
#	int question define default [min max]
#
function int () {
	old=$(eval echo "\${$2}")
	def=${old:-$3}
	if [ $# -gt 3 ]; then
	  min=$4
	else
	  min=-10000000    # !!
	fi
	if [ $# -gt 4 ]; then
	  max=$5
	else
	  max=10000000     # !!
	fi
	rndval $2
	while :; do
	  readln "$1 ($2) [$def] " "$def" "$old"
	  if expr \( \( $ans + 0 \) \>= $min \) \& \( $ans \<= $max \) >/dev/null 2>&1 ; then
            define_int "$2" "$ans"
	    break
          else
	    help "$2"
          fi
	done
}

#
# define_hex sets the value of a hexadecimal argument
#
#	define_hex define value
#
function define_hex () {
	echo "$1=$2" >>$CONFIG
	echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
	eval "$1=$2"
}

#
# hex processes an hexadecimal argument
#
#	hex question define default
#
function hex () {
	old=$(eval echo "\${$2}")
	def=${old:-$3}
	def=${def#*[x,X]}
	rndval $2
	while :; do
	  readln "$1 ($2) [$def] " "$def" "$old"
	  ans=${ans#*[x,X]}
	  if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
	    define_hex "$2" "$ans"
	    break
	  else
	    help "$2"
	  fi
	done
}

#
# define_string sets the value of a string argument
#
#	define_string define value
#
function define_string () {
	echo "$1=\"$2\"" >>$CONFIG
	echo "#define $1 \"$2\"" >>$CONFIG_H
	eval "$1=\"$2\""
}

#
# string processes a string argument
#
#	string question define default
#
function string () {
	old=$(eval echo "\${$2}")
	def=${old:-$3}
	while :; do
          if [ "$old" = "?" ]; then
             readln "$1 ($2) [$def] " "$def" ""
          else
	     readln "$1 ($2) [$def] " "$def" "$old"
          fi
	  if [ "$ans" = "?" ]; then
	    help "$2"
	  else
	    break
	  fi
	done
	define_string "$2" "$ans"
}
#
# choice processes a choice list (1-out-of-n)
#
#	choice question choice-list default
#
# The choice list has a syntax of:
#	NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
# The user may enter any unique prefix of one of the NAMEs and
# choice will define VALUE as if it were a boolean option.
# VALUE must be in all uppercase.  Normally, VALUE is of the
# form CONFIG_<something>.  Thus, if the user selects <something>,
# the CPP symbol CONFIG_<something> will be defined and the
# shell variable CONFIG_<something> will be set to "y".
#
function choice () {
	question="$1"
	choices="$2"
	old=
	def=$3

	# determine default answer:
	names=""
	set -- $choices
	firstvar=$2
	while [ -n "$2" ]; do
		if [ -n "$names" ]; then
			names="$names, $1"
		else
			names="$1"
		fi
		if [ "$(eval echo \"\${$2}\")" = "y" ]; then
			old=$1
			def=$1
		fi
		shift; shift
	done

	if [ "$RND" = "-r" -a -z "$old" ] ; then 
	  set -- $choices
	  rnd $#
	  while [ $rnd -le $# ] ; do 
	    def=$1
	    shift ; shift
	  done
	fi

	val=""
	while [ -z "$val" ]; do
		ambg=n
		readln "$question ($names) [$def] " "$def" "$old"
		ans=$(echo $ans | tr a-z A-Z)
		set -- $choices
		while [ -n "$1" ]; do
			name=$(echo $1 | tr a-z A-Z)
			case "$name" in
				"$ans"* | */"$ans"* )
					case "$name" in
						"$ans" | */"$ans"/* | \
						"$ans"/* | */"$ans" )
							val="$2"
							break # exact match
						;;
					esac
					if [ -n "$val" ]; then
						echo;echo \
		"  Sorry, \"$ans\" is ambiguous; please enter a longer string."
						echo
						val=""
						ambg=y
						break
					else
						val="$2"
					fi;;
			esac
			shift; shift
		done
		if [ "$val" = "" -a "$ambg" = "n" ]; then
			help "$firstvar"
		fi
	done
	set -- $choices
	while [ -n "$2" ]; do
		if [ "$2" = "$val" ]; then
			echo "  defined $val"
			define_bool "$2" "y"
		else
			define_bool "$2" "n"
		fi
		shift; shift
	done
}

CONFIG=.tmpconfig
CONFIG_H=.tmpconfig.h
FORCE_DEFAULT=.force_default
trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2

#
# Make sure we start out with a clean slate.
#
echo "#" > $CONFIG
echo "# Automatically generated make config: don't edit" >> $CONFIG
echo "#" >> $CONFIG

echo "/*" > $CONFIG_H
echo " * Automatically generated C config: don't edit" >> $CONFIG_H
echo " */" >> $CONFIG_H
echo "#define AUTOCONF_INCLUDED" >> $CONFIG_H

DEFAULT=""
if [ "$1" = "-d" ] ; then
	DEFAULT="-d"
	shift
fi

RND=""
DEF_ANS=""
AUTO=""
case "$1" in 
	-r) RND="-r" ; AUTO="yes" ; shift ;;
	-y) DEF_ANS="y" ; AUTO="yes" ; shift ;;
	-m) DEF_ANS="m" ; AUTO="yes" ; shift ;;
	-n) DEF_ANS="n" ; AUTO="yes" ; shift ;;
esac

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

DEFAULTS=sysdeps/$TARGET_OS/defconfig
if [ -f .config ]; then
  DEFAULTS=.config
fi

if [ "$AUTO" != "yes" ]; then
  if [ -f $DEFAULTS ]; then
    echo "#"
    echo "# Using defaults found in" $DEFAULTS
    echo "#"
    . $DEFAULTS
    sed -e 's/# \(CONFIG_[^ ]*\) is not.*/\1=n/' <$DEFAULTS >.config-is-not.$$
    . .config-is-not.$$
    rm .config-is-not.$$
  else
    echo "#"
    echo "# No defaults found"
    echo "#"
  fi
else
  if [ -f $FORCE_DEFAULT ]; then
    echo "#"
    echo "# Forcing defaults found in $FORCE_DEFAULT"
    echo "#"
    sed -e '
s/# \(CONFIG_[^ ]*\) is not.*/\1=n/;
s/# range \(CONFIG_[^ ]*\) \([^ ][^ ]*\) \([^ ][^ ]*\)/MIN_\1=\2; MAX_\1=\3/;
s/# list \(CONFIG_[^ ]*\) \([^ ][^ ]*\)/LIST_\1=\2/
' <$FORCE_DEFAULT >.default_val.$$
    . .default_val.$$
    rm .default_val.$$
  else
    echo "#"
    echo "# No defaults found"
    echo "#"
  fi 
fi

. $CONFIG_IN

rm -f .config.old
if [ -f .config ]; then
	mv .config .config.old
fi
mv .tmpconfig .config
mv .tmpconfig.h include/config.h

echo
echo "*** End of BusyBox configuration."
echo "*** Check the top-level Makefile for additional configuration."
if [ ! -f .hdepend -o "$CONFIG_MODVERSIONS" = "y" ] ; then
    echo "*** Next, you must run 'make dep'."
else
    echo "*** Next, you may run 'make', or 'make install'."
fi
echo

exit 0