#
#                             stb225 Project
#                   ---------------------------------
#
# Copyright (C) 2009 NXP B.V.,
# All Rights Reserved.
#
# Filename: prjconfig.sh
#
#
# Rev Date        Author      Comments
#-------------------------------------------------------------------------------
#   6 20070621    batelaan    $USER does not exist on cygwin: use LOGNAME
#   7 20070712    batelaan    *_defconfig => *.config
#   8 20070802    amccurdy    Use consistent expression for nbuilddirs and builddirname
#                             Fixes problems when $PRJROOT contains _files_ named build_*
#   9 20070820    batelaan    Handle failure to find any .config file.
#  10 20070820    batelaan    Confusion prevention with multiple/renamed build_* dirs.
#  11 20070821    batelaan    Bash version 3.00.15(1) on RHEL4 compatibility
#  12 20070821    batelaan    Fix $$ slipup.
#  13 20070823    batelaan    Prevent use of shell aliases
#  14 20070917    batelaan    Add -u|--update and -c|--check options.
#  15 20071026    batelaan    Ensure all shell functions are unset
#  16 20071206    batelaan    Use -R reminder option to conf to help option -u usability.
#  17 20080306    batelaan    Prevent running if $SHLVL != PRJCONFIG_SHLVL (if set)
#  18 20080513    batelaan    Add -D option. Check -c and -u are followed with config file[s].
#  19 20080515    batelaan    Handle use of BUILDROOT_PATH_FMT in searching build dirs.
#  20 20080528    batelaan    Rm extraneous popd
#  21 20080617    batelaan    Look for etc/configs/*/*.config
#  22 20080620    batelaan    Compile etc/tools/prjconfig programs.
#  23 20080708    batelaan    Merge for CM purposes.
#  24 20080721    batelaan    Add minor reliability enhancement/msg.
#  25 20080724    batelaan    Lookup files below $DI_PRJROOT as well.
#  26 20081028    batelaan    Add -V option. Add keepconfig, syncconfig, diffconfig commands.
#  27 20081030    batelaan    Add .config.info.nfreezable etc files.
#  28 20081209    batelaan    Handle use of vars from .config file in BUILDROOT_PATH_FMT
#  29 20081215    batelaan    Lookup input file in $DI_PRJROOT also.
#  30 20090126    batelaan    Improve user feedback if prjconfig detects incorrect inputConfigFile
#  31 20090306    batelaan    Added patching capability.
#  32 20090316    batelaan    Improve handling of incorrect config files.
#  33 20090325    batelaan    Correct lookup of files below $DI_PRJROOT
#  34 20090327    batelaan    Correct handling of non-existant config files.

# Configure script to be run by or after setup.sh before doing anything like running make!
if [ ! -d $PRJROOT ]; then
  \echo >&2 "No such directory: \$PRJROOT ($PRJROOT). Please check environment and filesystem."
  return 1
fi

# Prevent running if we have run before ($PRJCONFIG_SHLVL is set)
# and it is != $SHLVL, because that may fail to correctly update
# the environment.
if [ "$1" != "-V" -a "$1" != "--verify" ]; then
  # we are NOT just verifying the setup.
  if [ -n "$PRJCONFIG_SHLVL" ]; then
    # prjconfig has run before.
    if [ "$PRJCONFIG_SHLVL" != "$SHLVL" ]; then
      \echo >&2 "**ERROR: prjconfig should not be invoked inside a sub-shell."
      \echo >&2 "(The environment may otherwise not be correctly updated.)"
      \echo >&2 "Aborting."
      return 1
    fi
  fi
fi

_tmpPath=`mktemp -t -d prjconfig.monaco.XXXXXXXX`
_prjconfigPconfig=$PRJROOT/etc/Pconfig
_prjconfigToolsDir=$PRJROOT/etc/tools/prjconfig
_prjconfigDiDefault=$DI_PRJROOT/etc/configs/default.config
_prjconfigDefault=$PRJROOT/etc/configs/default.config
# The .config file used for doing comparisons etc.
_prjconfigTmpConfig=$_tmpPath/.config

# Utility functions
function _prjconfigUsage {
  \echo >&2 "$@"
  /bin/cat >&2 <<'EOT'
Usage#1: prjconfig [ m | o | d | k | s | -V | [-D] <inputConfigFile>]
  m|menuconfig
             - run menuconfig (the default if no arguments given).
  o|oldconfig
             - use existing (old) $BUILDROOT/.config
               (asks for missing values only).
  d|diffconfig
             - do a diff between the original <inputConfigFile>
               and the current $BUILDROOT/.config file.
  k|keepconfig
             - keep current $BUILDROOT/.config file, but reset any error
               indicators.
  s|syncconfig
             - update to new contents of <inputConfigFile>.
  [-D] <inputConfigFile> [FILE_OPTS]
             - uses <inputConfigFile> and prompts for missing values
               (with -D uses defaults for missing values).
  -V         - verify current $BUILDROOT/.config file. Errors are:
               - the original <inputConfigFile> has changed.
               - it is not up to date compared with the prjconfig option definitions.

When no explicit <inputConfigFile> is specified, it searches for a
configuration file to use using the following steps:
- if $BUILDROOT is defined, and file $BUILDROOT/.config exists, use it.
- if there is exactly one build_*/.config file match, use it.
- if there are two or more build_*/.config file matches, then fail with
a message asking you to specify an explicit config file to use.
- if $DI_PRJROOT is define, then:
  - if $DI_PRJROOT/etc/configs/default.config exists, use it.
  - if there is exactly one *.config file below $DI_PRJROOT/etc/configs, use it.
- if $PRJROOT/etc/configs/default.config exists, use it.
- if there is exactly one *.config file below $PRJROOT/etc/configs, use it.
- otherwise fail with a suitable message.

(Note: replace build_* with $BUILDROOT_PATH_FMT if it is defined)

FILE_OPTS are:
  *.pl       - Perl file to be run on <inputConfigFile>, to patch it.

Usage#2: prjconfig -c|-u <configfile>...
  -c <configfile>... - check if config files are up-to-date
  -u <configfile>... - bring config files up-to-date
EOT
  return 1
}

# Run a tool ($1) from $_prjconfigToolsDir. Compile the program first if needed.
function _prjconfigToolRun {
  local pgm=$1
  shift
  if make -C $_prjconfigToolsDir -s DESTDIR=$PRJROOT/.prjconfig.TMP $pgm >&2
  then
    local exe=
    if [[ "$OS" = *Windows* ]]; then
      exe=.exe
    fi
    /bin/cp -f $_prjconfigTmpConfig $_prjconfigTmpConfig.old
    $PRJROOT/.prjconfig.TMP/$pgm$exe "$@" || return 1
    if [ $pgm = conf ]; then
      changed=0
    fi
  else
    \echo >&2 "**FATAL: could not make $pgm in $_prjconfigToolsDir. Aborting."
    return 1
  fi
}

# Usage: _prjconfigMatchConfigFile CONFIG_NAME SHELL_VARIABLE_NAME
# Match config files.
# Return:
# - 0 if one config file found. Set variable SHELL_VARIABLE_NAME to the one found.
# - 1 if no config files found
# - 2 if more than 1 config files found
function _prjconfigMatchConfigFile {
  local CONFIG_NAME="$1"
  local buildrootPathFmt
  # Ensure that variable references expand to * if they are not defined.
  eval buildrootPathFmt="$(echo \"$buildrootPathFmtTemplate\" | perl -p -e 's@\$(\w+)@\${$1}@g; s@}@:-*}@g;')"
  local configfiles
  configfiles=(`shopt -s nullglob; \echo $buildrootPathFmt/.config`)
  if [ ${#configfiles[*]} -gt 1 ]; then
      return 2
  fi
  if [ ${#configfiles[*]} -eq 0 ]; then
      return 1
  fi

  eval $2='${configfiles[0]}'
  return 0
}

_prjconfigDoOldConfig() {
  local patched=false
  if ! _prjconfigToolRun conf -so $_prjconfigPconfig; then
    return 1
  fi
  _prjconfigCheckRunNoChanges "$infile" "Input config file " || return 1

  # check if we need to patch the $_prjconfigTmpConfig file
  while [ $# != 0 ]; do
      case "$1" in
          *.pl)
              local f="$(_prjconfigLookupFile $1)"
              if [ ! "$f" ]; then
                _prjconfigUsage "Incorrect argument ($1)."
                return 1
              fi
              if ! perl -w $f <.config >.config.tmp; then
                  \echo >&2 "**ERROR: config file patching error using file $1"
                  return 1
              fi
              mv -f .config.tmp .config
              patched=true
              ;;
          *)
              _prjconfigUsage "Incorrect argument ($1)."
              return 1
              ;;
    esac
    shift
  done

  if $patched && ! _prjconfigToolRun conf -so $_prjconfigPconfig; then
    return 1
  fi

  return 0
}

function _prjconfigCheckRunNoChanges() {
  if ! cmp -s $_prjconfigTmpConfig.old $_prjconfigTmpConfig; then
    \echo >&2 "**ERROR: $2$1 is invalid,"
    \echo >&2 "since running a 'conf' check on it changed it"
    \echo >&2 "  (see result in /tmp/prjconfig.$LOGNAME.$$.config)."
    \cp -f $_prjconfigTmpConfig /tmp/prjconfig.$LOGNAME.$$.config
    \diff $_prjconfigTmpConfig.old $_prjconfigTmpConfig  >/tmp/prjconfig.$LOGNAME.$$.txt
    \echo >&2 "To see the changes, run for example:"
    \echo >&2 "  less /tmp/prjconfig.$LOGNAME.$$.txt"
    \echo >&2 "  diff $1 /tmp/prjconfig.$LOGNAME.$$.config"
    \echo >&2 "  meld $1 /tmp/prjconfig.$LOGNAME.$$.config"
    \echo >&2
    \echo >&2 "This usually means the file was hand-edited, or the prjconfig options have changed."
    if [ -z "$PRJCONFIG_SHLVL" ]; then
        \echo >&2 "To update and use the file, run:"
    else
        \echo >&2 "To update the file, run:"
    fi
    \echo >&2 "  ${prjconfigCommand:-prjconfig} -u $1"
    \echo >&2 "or copy /tmp/prjconfig.$LOGNAME.$$.config to the <inputConfigFile>."
    return 1
  fi
  return 0
}

# This function tries to find a config file.
# See the usage for how it does it.
# (Note we check for env var BUILDROOT_PATH_FMT, otherwise we use build_*).
# If all these have failed then an error saying please provide a configfile to this script.
function _prjconfigEnsureConfigExists {
  local configfilename
  local buildrootPathFmtTemplate="${BUILDROOT_PATH_FMT:-$PRJROOT/build_\$CONFIG_NAME}"

  if [ -n "$BUILDROOT" -a -r $BUILDROOT/.config ]; then
    if ! /bin/cp -f $BUILDROOT/.config $_prjconfigTmpConfig; then
      \echo >&2 "Cannot copy $BUILDROOT/.config to $_prjconfigTmpConfig"
      return 1
    fi
    configfilename="$BUILDROOT/.config"
    eval `/bin/grep -F 'CONFIG_NAME=' $configfilename`
    local configfilename1
    _prjconfigMatchConfigFile "$CONFIG_NAME" configfilename1
    _status=$?
    if [ "$configfilename" != "$configfilename1" ]; then
      \echo >&2 "*** CONFIG_NAME value ($CONFIG_NAME) in $configfilename ***"
      \echo >&2 "*** does not match CONFIG_NAME in build directory ***"
      \echo >&2 "*** ${configfilename%/.config} ***"
      \echo >&2 "*** Did you rename the build directory? Please move or rename ***"
      \echo >&2 "*** build directories in such a way that they do not match ***"
      \echo >&2 "*** $buildrootPathFmtTemplate ***"
      return 1
    else
      \echo >&2 "*** Using $BUILDROOT/.config as config file. ***"
      return 0
    fi
  fi

  _prjconfigMatchConfigFile '*' configfilename
  local _status=$?

  # If we have too many $buildrootPathFmt/.config files then fail.
  if [ $_status -eq 2 ]; then
    \echo >&2 "*** More than one $buildrootPathFmtTemplate/.config file found. ***"
    \echo >&2 "*** Please specify an explicit config file you want to use. ***"
    return 1
  fi

  # If we have just one $buildrootPathFmt/.config file try and use it.
  if [ $_status -eq 0 ]; then
    eval `/bin/grep -F 'CONFIG_NAME=' $configfilename`
    local configfilename1
    _prjconfigMatchConfigFile "$CONFIG_NAME" configfilename1
    _status=$?
    if [ "$configfilename" != "$configfilename1" ]; then
      \echo >&2 "*** CONFIG_NAME value ($CONFIG_NAME) in $configfilename ***"
      \echo >&2 "*** does not match CONFIG_NAME in build directory ***"
      \echo >&2 "*** ${configfilename%/.config} ***"
      \echo >&2 "*** Did you rename the build directory? Please move or rename ***"
      \echo >&2 "*** build directories in such a way that they do not match ***"
      \echo >&2 "*** $buildrootPathFmtTemplate ***"
      return 1
    else
      if ! /bin/cp -f $configfilename $_prjconfigTmpConfig; then
	\echo >&2 "Cannot copy $configfilename to $_prjconfigTmpConfig"
	return 1
      fi
      \echo >&2 "*** Using $configfilename as config file. ***"
      return 0
    fi
  fi

  local configfiles

  # No build tree, try $DI_PRJROOT/etc/configs followed by
  # $PRJROOT/etc/configs

  if [ -n "$DI_PRJROOT" ]; then
    # $DI_PRJROOT is defined

    # Try $DI_PRJROOT/etc/configs/default.config.
    if [ -r $_prjconfigDiDefault ]; then
      if ! /bin/cp -f $_prjconfigDiDefault $_prjconfigTmpConfig; then
        \echo >&2 "Cannot copy $_prjconfigDiDefault to $_prjconfigTmpConfig"
        return 1
      fi
      \echo >&2 "*** Using $_prjconfigDiDefault as config file. ***"
      if [ -L $_prjconfigDiDefault ]; then
        \echo >&2 "*** This is a symlink to `readlink $_prjconfigDiDefault` ***"
      fi
      return 0
    fi

    # If only one config file available in $DI_PRJROOT/etc/configs... use it.
    configfiles=(`find $DI_PRJROOT/etc/configs -name '*.config' 2>/dev/null`)
    if [ ${#configfiles[*]} -eq 1 ]; then
      if ! /bin/cp -f ${configfiles[0]} $_prjconfigTmpConfig; then
        \echo >&2 "Cannot copy ${configfiles[0]} to $_prjconfigTmpConfig"
        return 1
      fi
      \echo >&2 "*** Using ${configfiles[0]} as config file. ***"
      return 0
    fi

    if [ ${#configfiles[*]} -gt 1 ]; then
      \echo >&2 "*** More than one config file available below \$DI_PRJROOT/etc/configs ***"
      return 1
    fi
  fi

  # Try $PRJROOT/etc/configs/default.config.
  if [ -r $_prjconfigDefault ]; then
    if ! /bin/cp -f $_prjconfigDefault $_prjconfigTmpConfig; then
      \echo >&2 "Cannot copy $_prjconfigDefault to $_prjconfigTmpConfig"
      return 1
    fi
    \echo >&2 "*** Using $_prjconfigDefault as config file. ***"
    if [ -L $_prjconfigDefault ]; then
      \echo >&2 "*** This is a symlink to `readlink $_prjconfigDefault` ***"
    fi
    return 0
  fi

  configfiles=(`find $PRJROOT/etc/configs -name '*.config' 2>/dev/null`)
  if [ ${#configfiles[*]} -eq 1 ]; then
    if ! /bin/cp -f ${configfiles[0]} $_prjconfigTmpConfig; then
      \echo >&2 "Cannot copy ${configfiles[0]} to $_prjconfigTmpConfig"
      return 1
    fi
    \echo >&2 "*** Using ${configfiles[0]} as config file. ***"
    return 0
  fi

  if [ ${#configfiles[*]} -gt 1 ]; then
    \echo >&2 "*** More than one config file available below \$PRJROOT/etc/configs ***"
    return 1
  fi

  if [ -n "$DI_PRJROOT" ]; then
    \echo >&2 "*** No config files available below \$DI_PRJROOT/etc/configs ***"
    \echo >&2 "*** or \$PRJROOT/etc/configs ***"
  else
    \echo >&2 "*** No config files available below \$PRJROOT/etc/configs ***"
  fi
  \echo >&2 "*** Please specify an explicit config file you want to use. ***"
  return 1
}

# Debug control:
case "x$1" in
  x-d) set -vx; opts=-d; shift
    ;;
esac

function _prjconfigUpdateIfChanged {
  if [ ! -f "$1" ]; then
    return
  fi
  if ! 'cmp' -s "$1" "$2" >& /dev/null; then
     if [ -f "$2" ]; then
       /bin/cp -fp "$2" "$2.prev"
     fi
     /bin/mv -f "$1" "$2"
  fi
}

function _prjconfigDoFiles {
  _status=0
  local action=$1 f files results i=0
  shift

  for f in $@; do
    printf "\n\nChecking $f ...\n\n"
    files[$i]=$f
    local infile="$(_prjconfigLookupFile $f)"
    if [ ! -f "$infile" ]; then
      \echo >&2 "*** Cannot look up config file: $f ***"
      _status=1
       break
    fi
    if ! /bin/cp -f "$infile" $_prjconfigTmpConfig; then
      _prjconfigUsage "Incorrect argument ($f)."
      _status=1
      break
    fi
    if [ "$action" = update ]; then
      _prjconfigToolRun conf ${conf_opts[@]} -R "Updating $infile" -o $_prjconfigPconfig || _status=1
    else
      _prjconfigToolRun conf ${conf_opts[@]} -o $_prjconfigPconfig || _status=1
    fi
    if cmp -s "$infile" $_prjconfigTmpConfig; then
      printf "Up-to-date: $f\n"
      results[$i]=ok
    else
      case $action in
	check)
	  printf "\n\n*** Differences detected: %s ***\n" $f
	  printf "Differences (<OLD >NEW):\n"
	  diff "$infile" $_prjconfigTmpConfig
	  results[$i]=Not-uptodate
	;;
	update)
	  printf "*** Differences detected (updating): %s ***\n" $f
	  if ! /bin/cp -f $_prjconfigTmpConfig "$infile"; then
	    printf "*** Cannot copy back new config file to $infile ***\n"
	    _status=1
	    break
	  fi
	  results[$i]=Updated
      esac
    fi
    let i="$i+1"
  done
  if [ "$_status" = 0 ]; then
    # Print report table
    local fmt="%-12s %s\n"
    printf "\n$fmt" Result Filename
    printf "$fmt" --------- ------------------------------------
    i=0
    while [ $i != ${#files[*]} ]; do
      local f
      local result
      eval f="\${files[$i]}"
      eval result="\${results[$i]}"
      printf "$fmt" $result $f
      let i="$i+1"
    done
  fi
  return $_status
}

function _prjconfigLookupFile {
  # Handle relative file argument
  local f="$1"
  if [[ "$f" != /* ]]; then
    if [ "$OLDPWD" != "$PRJROOT" -a -f "$OLDPWD/$1" ]; then
      # File was relative path to invocation directory.
      f="$OLDPWD/$1"
    elif [ -n "$DI_PRJROOT" -a -f "$DI_PRJROOT/$1" ]; then
      # File sits in $DI_PRJROOT
      f="$DI_PRJROOT/$1"
    elif [ -f "$PRJROOT/$1" ]; then
      # File sits in $PRJROOT
      f="$PRJROOT/$1"
    else
      \echo >&2 "**WARNING: cannot find file \"$1\" below any of these directories:"
      [ "$OLDPWD" = "$PRJROOT" ] || \echo >&2 "- Invocation directory ($OLDPWD)"
      \echo >&2 "- \$PRJROOT ($PRJROOT)"
      \echo >&2 "- \$DI_PRJROOT (${DI_PRJROOT:-<undefined>})"
      return 1
    fi
  fi
  printf "%s" "$f"
}

function _prjconfigMain {
  local command="$1"
  shift
  local infile
  local infile_args
  local changed
  case "x$command" in
    x)
      command=menuconfig
      ;;
    xh|xhelp|x-h|x--help)
      _prjconfigUsage 'Project configuration utility (similar to Linux & Buildroot).'
      return 1
      ;;
    x-D|x-c|x--check|x-u|x--update)
      # handled later
      ;;
    x-V|x--verify)
      if [ $# != 0 ]; then
        _prjconfigUsage "Incorrect number of arguments."
        return 1
      fi
      if [ ! -f $BUILDROOT/.config ]; then
        _prjconfigUsage "-V|--verify not possible without a \$BUILDROOT/.config file."
        return 1
      fi
      # Verify original <inputConfigFile> has not changed:
      if [ -f $BUILDROOT/.config.source_copy ] && source $BUILDROOT/.config.source; then
        if ! cmp -s "$infile" $BUILDROOT/.config.source_copy; then
          \echo >&2 "**ERROR: Original input config file ($infile) has changed."
          \echo >&2 "Please verify if you should update to the new input config file!!!"
          \echo >&2 "To see differences,        run: prjconfig diffconfig"
          \echo >&2 "To update to the new file, run: prjconfig syncconfig"
          \echo >&2 "To ignore the changes,     run: prjconfig keepconfig"
          if [ -f $BUILDROOT/.config.changed ]; then
            \echo >&2 "Note: it looks like prjconfig menuconfig was used."
          else
            \echo >&2 "Note: it looks like prjconfig menuconfig was not used."
          fi
          return 1
        fi
      fi
      infile=
      infile_args=
      ;;
    xd|xdiffconfig)
      if [ $# != 0 ]; then
        _prjconfigUsage "Incorrect number of arguments."
        return 1
      fi
      if [ ! -f $BUILDROOT/.config ]; then
        _prjconfigUsage "Doing a diffconfig is not possible without a \$BUILDROOT/.config file."
        return 1
      fi
      if ! source $BUILDROOT/.config.source; then
        \echo >&2 "\$BUILDROOT/.config.source could not be used, so cannot determine original input config file"
        return 1
      fi
      if [ ! -f "$infile" ]; then
        \echo >&2 "File ($infile) specified by \$BUILDROOT/.config.source"
        \echo >&2 "does not exist, so cannot determine original input config file."
        return 1
      fi
      if cmp -s "$infile" $BUILDROOT/.config; then
        \echo >&2 "No differences found between $infile and \$BUILDROOT/.config"
      else
        \echo >&2 "Lines starting with '<' are $infile"
        \echo >&2 "Lines starting with '>' are $BUILDROOT/.config"
        diff "$infile" $BUILDROOT/.config
        \echo >&2 "Differences found between $infile and \$BUILDROOT/.config"
        return 1
      fi
      return 0
      ;;
    xk|xkeepconfig)
      \echo >&2 "Forgetting changes to source config file..."
      if source $BUILDROOT/.config.source && [ -f "$infile" ]; then
        \echo >&2 "Copying $infile to \$BUILDROOT/.config.source_copy"
        /bin/cp -f $infile $BUILDROOT/.config.source_copy || _status=1
      fi
      return 0
      ;;
    x-*)
      _prjconfigUsage "Unrecognized option ($command)."
      return 1
      ;;
    *)
      # dealt with later.
      true
      ;;
  esac

  # Make sure _tmpPath exists
  /bin/rm -rf $_tmpPath
  /bin/mkdir -p $_tmpPath || return 1

  #Store current working dir.
  \pushd $_tmpPath > /dev/null || return 1
  # Until we do popd, don't return but set _status=1 on error.
  local _status=0

  case "$command" in
    m|menuconfig)
      if _prjconfigEnsureConfigExists; then
        # run mconf and conf to 'normalize' output since mconf prints
        # choice values even if the whole choice is disabled.
        if _prjconfigToolRun mconf $_prjconfigPconfig && _prjconfigToolRun conf -so $_prjconfigPconfig; then
          if ! cmp -s $_prjconfigTmpConfig $_prjconfigTmpConfig.old; then
            changed=1
          fi
        else
          _status=1
        fi
      else
        _status=1
      fi
      ;;
    o|oldconfig)
      if _prjconfigEnsureConfigExists; then
        _prjconfigToolRun conf -s $_prjconfigPconfig || _status=1
      else
        _status=1
      fi
      ;;
    s|syncconfig)
      if source $BUILDROOT/.config.source; then
        echo >&2 "*** Using $infile as config file ***"
        if /bin/cp -f "$infile" $_prjconfigTmpConfig; then
          _prjconfigDoOldConfig $infile_args || _status=1
        else
          \echo >&2 "Cannot copy $infile to $_prjconfigTmpConfig"
          _status=1
        fi
      else
        \echo >&2 'Missing $BUILDROOT/.config.source file so cannot determine'
        \echo >&2 'original <inputConfigFile> value. Aborting.'
        _status=1
      fi
      ;;
    -V|--verify)
      # Check that the config file is correct, i.e. not missing anything
      # or having incorrect values (hand-edited!)
      if /bin/cp -f "$BUILDROOT/.config" $_prjconfigTmpConfig; then
        if _prjconfigToolRun conf -D $BUILDROOT/.config $_prjconfigPconfig >/dev/null; then
          _prjconfigCheckRunNoChanges $BUILDROOT/.config || _status=1
        else
          _status=1
        fi
      else
        _status=1
      fi
      ;;
    -D)
      if [ $# != 1 ]; then
        _prjconfigUsage "Missing config file argument after $command option."
        _status=1
      else
        infile="$(_prjconfigLookupFile $1)"
        if [ ! "$infile" ]; then
          _prjconfigUsage "Incorrect argument ($1)."
          _status=1
        else
          if _prjconfigEnsureConfigExists; then
            _prjconfigToolRun conf -D $infile $_prjconfigPconfig || _status=1
          else
            _status=1
          fi
        fi
      fi
      ;;
    -c|--check)
      if [ $# = 0 ]; then
        _prjconfigUsage "*** Missing config filenames after $command option"
        _status=1
      else
        _prjconfigDoFiles check "$@"
      fi
      ;;
    -u|--update)
      if [ $# = 0 ]; then
        _prjconfigUsage "*** Missing config filenames after $command option"
        _status=1
      else
        _prjconfigDoFiles update "$@"
      fi
      ;;
    *)
      infile="$(_prjconfigLookupFile $command)"
      if [ ! "$infile" ] || ! /bin/cp -f "$infile" $_prjconfigTmpConfig; then
        _prjconfigUsage "Incorrect argument ($command)."
        _status=1
      else
        infile_args="$*"
        _prjconfigDoOldConfig "$@" || _status=1
      fi
      ;;
  esac

  # restore working dir
  \popd > /dev/null
  [ $_status = 0 ] || return $_status

  # Are we done?
  case "$command" in
    m|menuconfig)
      # If user did not save the config, there is not generated prjconfig.h file.
      [ -f $_tmpPath/prjconfig.h ] || return 0
      ;;
    -u|--update)
      # Since we advise a user to use "./setup.sh -u <inputConfigFile>" to correct and use an incorrect
      # config file we don't want to exit here if prjconfig has never been run successfully.
      if [ -n "$PRJCONFIG_SHLVL" ]; then return 0; fi
      # We will now use the updated config file (last argument) further below.
      eval infile="\$(_prjconfigLookupFile \$$#)"
      ;;
    -c|--check|-V|--verify)
      return 0
      ;;
  esac

  # We now have the output of the config program in $_tmpPath .
  # We run the perl program that generates the environment.
  # We then change the environment (using .).
  # If the $BUILDROOT/.config exists then we compare _prjconfig with it.
  # If $BUILDROOT (it may have been changed)/.config still exists back it up
  # We then move all of the files generated from /tmp to $(BUILDROOT)
  eval local `/bin/grep -F 'CONFIG_NAME=' $_prjconfigTmpConfig`
  \echo "Setting Environment (CONFIG_NAME=$CONFIG_NAME)"
  if ! 'perl' $PRJROOT/etc/prjconfig2cmds.pl $opts $PRJROOT/etc/prjconfig2cmds.def.pl $_prjconfigTmpConfig > $_tmpPath/.config.cmds; then
    \echo "etc/prjconfig2cmds.pl failed!"
    return 1
  fi
  . $_tmpPath/.config.cmds
  \echo "BUILDROOT=$BUILDROOT"

  # Move the generated files from $_tmpPath to $BUILDROOT/
  /bin/mkdir -p $BUILDROOT || _status=1
  local f
  for f in .config .prjconfig .config.cmd .config.cmds prjconfig.h .config.info.freezable .config.info.nfreezable .config.info.frozen; do
    _prjconfigUpdateIfChanged "$_tmpPath/$f" "$BUILDROOT/$f" || _status=1
  done

  if [ -n "$infile" ]; then
    # An inputConfigFile was used.
    if [ "$infile" != "$BUILDROOT/.config" ]; then
      \echo "infile=$infile" >$BUILDROOT/.config.source || _status=1
      \echo "infile_args='$infile_args'" >>$BUILDROOT/.config.source || _status=1
      /bin/cp -f "$infile" $BUILDROOT/.config.source_copy || _status=1
#    else
#      echo >&2 "Not updating \$BUILDROOT/.config.source* since <inputConfigFile> == \$BUILDROOT/.config"
    fi
  fi
  case x$changed in
    x0)
      /bin/rm -f $BUILDROOT/.config.changed
      ;;
    x1)
      if cmp -s $BUILDROOT/.config $BUILDROOT/.config.source_copy; then
        \echo >&2 "Editing has now restored the original <inputConfigFile> contents; removing \$BUILDROOT/.config.changed"
        /bin/rm -f $BUILDROOT/.config.changed
      else
        \echo 'This file indicates that prjconfig menuconfig changed the .config file.' >$BUILDROOT/.config.changed
      fi
      ;;
  esac
  return $_status
}

_prjconfigMain "$@"
_status=$?
# Cleanup; unset variables & functions
unset _prjconfigToolRun _prjconfigMatchConfigFile _prjconfigDoOldConfig _prjconfigCheckRunNoChanges _prjconfigEnsureConfigExists
unset _prjconfigUsage _prjconfigUpdateIfChanged _prjconfigDoFiles _prjconfigLookupFile _prjconfigMain
unset _prjconfigPconfig _prjconfigToolsDir _prjconfigDefault _prjconfig _prjconfigOld

/bin/rm -rf $_tmpPath
unset _tmpPath

# Record shell-level at which we run prjconfig, so we can prevent running
# it in sub-shells, because that may fail to correctly update
# the environment.
if [ $_status = 0 -a "$1" != "-V" -a "$1" != "--verify" -a -z "$PRJCONFIG_SHLVL" ]; then
  # Success, and we are NOT just verifying the setup, and first time we run prjconfig.
  export PRJCONFIG_SHLVL=$SHLVL
fi

'test' $_status = 0
