#! /bin/sh
#
#   Copyright (c) 2015-2017 Nat! - Mulle kybernetiK
#   All rights reserved.
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:
#
#   Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
#   Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
#   Neither the name of Mulle kybernetiK nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
#   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#   POSSIBILITY OF SUCH DAMAGE.
MULLE_BOOTSTRAP_VERSION_MAJOR=3
MULLE_BOOTSTRAP_VERSION_MINOR=0
MULLE_BOOTSTRAP_VERSION_PATCH=0

MULLE_BOOTSTRAP_VERSION="${MULLE_BOOTSTRAP_VERSION_MAJOR}.${MULLE_BOOTSTRAP_VERSION_MINOR}.${MULLE_BOOTSTRAP_VERSION_PATCH}"

#
# This is the main user interface to mulle-bootstrap
# sorta like git
#

trap_fail()
{
   exit 1
}


bootstrap_technical_option_usage()
{
   if [ ! -z "${MULLE_TRACE}" ]
   then
      cat <<EOF
   -ld       : additional debug output
   -le       : external command execution log output
   -lm       : extended dependency analysis output
   -ls       : extended settings log output
   -t        : enable shell trace
   -tpwd     : emit shortened PWD during trace
   -tr       : also trace resolver
   -ts       : also trace settings
   -s        : be silent

   [Check source for more options]
EOF
   fi
}


mulle_brew_usage()
{
   cat <<EOF
usage: mulle-brew [flags] [command] [options]
 Flags:
   -n        : do nothing creative or destructive
   -v        : -v to be more verbose (-vv or -vvv for more verbosity)
EOF

   bootstrap_technical_option_usage

   cat <<EOF

 Commands:
   install   : fetches brews
   update    : update brew
   upgrade   : upgrade brew fomulae
   init      : initializes a bootstrap project

 Options are command specific. Use mulle-brew <command> -h for help.
EOF

   if [ "${UNAME}" = 'darwin' ]
   then
      cat <<EOF
   xcode     : setup xcodeproj settings
EOF
   fi

   exit 1
}


mulle_bootstrap_usage()
{
   cat <<EOF
usage: mulle-bootstrap [flags] [command] [options]
 Flags:
   -a -y     : default answer to questions (scripts wont be checked)
               -a (clone preferred) -y (local copy/symlink preferred)
   -f        : force operation
   -n        : do nothing creative or destructive
   -v        : -v to be more verbose (-vv or -vvv for more verbosity)
   -V        : verbose build
   -DKEY=VAL : define local key/value C style
EOF

   bootstrap_technical_option_usage

   cat <<EOF

 Commands:
   bootstrap     : does install and build recursively [default]
   build         : builds fetched repositories
   clean         : cleans mulle-bootstrap produced files.
   init          : initializes a bootstrap project
   install       : fetches required repositories recursively. Use it after
                   updating the repositories and embedded_repositories files.
   update        : updates repositories (fetch).
   upgrade       : updates and checks out repositories (pull).
   show          : show repositories and brews

 Options are command specific. Use mulle-bootstrap <command> -h for help.
EOF

   if [ "${UNAME}" = 'darwin' ]
   then
      cat <<EOF
   xcode         : sets up xcodeproj settings
EOF
   fi

   if [ !  -z "${MULLE_TRACE}" ]
   then
      cat <<EOF

   systeminstall : install dependencies as system headers
   git           : run git commands on fetched repositories
   status        : show status of repositories and brews
   tag           : tag fetched repositories
EOF
   fi

   exit 1
}


ensure_consistency()
{
   if [ -f "${REPOS_DIR}/.bootstrap_fetch_started" ]
   then
      log_error "A previous fetch or update was incomplete.
Suggested resolution (in $PWD):
    ${C_RESET_BOLD}mulle-bootstrap clean dist${C_ERROR}
    ${C_RESET_BOLD}mulle-bootstrap${C_ERROR}

Or do you feel lucky ? Then try again with
   ${C_RESET_BOLD}mulle-bootstrap -f ${MULLE_BOOTSTRAP_ARGUMENTS}${C_ERROR}
But you've gotta ask yourself one question: Do I feel lucky ?
Well, do ya, punk?"
      exit 1
   fi
}


define_expansion()
{
   local keyvalue

   keyvalue="$1"

   is_bootstrap_project || fail "This is not a mulle-bootstrap project"

   if [ -z "${keyvalue}" ]
   then
      fail "Missing key, directly after -D"
      mulle_bootstrap_usage
   fi

   [ -z "${MULLE_BOOTSTRAP_FUNCTIONS_SH}" ] && . mulle-bootstrap-functions.sh

   local key
   local value

   key="`echo "${keyvalue}" | cut -d= -f1 | tr '[a-z]' '[A-Z]'`"
   if [ -z "${key}" ]
   then
      key="${keyvalue}"
      value="YES"
   else
      value="`echo "${keyvalue}" | cut -d= -f2-`"
   fi

   local path

   path="${BOOTSTRAP_DIR}.local/${key}"
   mkdir_if_missing "`dirname -- "${path}"`"
   redirect_exekutor "${path}" echo "# commandline argument -D${keyvalue}
${value}"
}


_bootstrap_main()
{
   local command

   log_debug "::: bootstrap begin :::"

   . mulle-bootstrap-fetch.sh
   . mulle-bootstrap-build.sh
   . mulle-bootstrap-warn-scripts.sh

   # used for option handling only
   MULLE_BOOTSTRAP_WILL_BUILD="YES"

   warn_scripts_main "${BOOTSTRAP_DIR}" || exit 1

   if fetch_needed
   then
      MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap fetch"
      fetch_main "$@" || exit 1
      # used for option handling only
      MULLE_BOOTSTRAP_DID_FETCH="YES"
   else
      log_verbose "No fetch needed"
   fi

   if build_needed
   then
      MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap build"
      build_main "$@" || exit 1
   else
      log_verbose "No build needed"
   fi

   log_debug "::: bootstrap end :::"
}


bootstrap_libexec_path()
{
   local exedir
   local exedirpath
   local prefix
   local libexecpath

   exedir="`dirname "$1"`"
   exedirpath="`( cd "${exedir}" ; pwd -P )`" || fail "failed to get pwd"
   prefix="`dirname "${exedirpath}"`"
   libexecpath="${prefix}/libexec/mulle-bootstrap"

   if [ ! -x "${libexecpath}/mulle-bootstrap-functions.sh" ]
   then
      libexecpath="${exedirpath}/src"
   fi

   if [ -x "${libexecpath}/mulle-bootstrap-functions.sh" ]
   then
      echo "${libexecpath}"
   fi
}


bootstrap_dump_env()
{
   log_trace "FULL trace started"
   log_trace "ARGS:${C_TRACE2} ${MULLE_BOOTSTRAP_ARGUMENTS}"
   log_trace "PWD :${C_TRACE2} `pwd -P`"
   log_trace "ENV :${C_TRACE2} `env | sort`"
   log_trace "LS  :${C_TRACE2} `ls -a1F`"
}


bootstrap_init()
{
   local libexecpath

   libexecpath="`bootstrap_libexec_path "$0"`"
   if [ -z "${libexecpath}" ]
   then
      echo "could not find libexec for mulle-bootstrap" >&2
      exit 1
   fi

   PATH="${libexecpath}:$PATH"
   export PATH

   if [ ! -z "${MULLE_BOOTSTRAP_LIBEXEC_TRACE}" ]
   then
      echo "PATH=$PATH" >&2
   fi

   #
   # shell export commands with minimal
   # trap setup
   #
   case "${1}" in
      library-path)
         echo "${libexecpath}"
         exit 0
      ;;

      version)
         echo "${MULLE_BOOTSTRAP_VERSION}"
         exit 0
      ;;
   esac

   #  set -e # more pain then gain in the end
   #  set -u # doesn't work with my style

   # now include this first to get
   # logging and UNAME

   . mulle-bootstrap-logging.sh
   . mulle-bootstrap-local-environment.sh || fail "not loaded"

   trap trap_fail INT

   # source_environment
}


#
# variables called flag. because they are indirectly set by flags
#
bootstrap_setup_trace()
{
   case "${1}" in
      VERBOSE)
         MULLE_FLAG_LOG_VERBOSE="YES"
      ;;

      FLUFF)
         MULLE_FLAG_LOG_FLUFF="YES"
         MULLE_FLAG_LOG_VERBOSE="YES"
         MULLE_FLAG_LOG_EXECUTOR="YES"
      ;;

      TRACE)
         MULLE_FLAG_LOG_SETTINGS="YES"
         MULLE_FLAG_LOG_EXECUTOR="YES"
         MULLE_FLAG_LOG_FLUFF="YES"
         MULLE_FLAG_LOG_VERBOSE="YES"
         bootstrap_dump_env
      ;;

      1848)
         MULLE_FLAG_LOG_SETTINGS="YES"
         MULLE_FLAG_LOG_FLUFF="YES"
         MULLE_FLAG_LOG_VERBOSE="YES"
         MULLE_FLAG_VERBOSE_BUILD="YES"

         bootstrap_dump_env

         if [ "${MULLE_TRACE_POSTPONE}" = "NO" ]
         then
            log_trace "1848 trace (set -x) started"
            set -x
            PS4="+ ${ps4string} + "
         fi
      ;;
   esac
}


bootstrap_technical_flags()
{
   case "$1" in
      -n|--dry-run)
         MULLE_FLAG_EXECUTOR_DRY_RUN="YES"
      ;;

      -ld|--log-debug)
         MULLE_FLAG_LOG_DEBUG="YES"
      ;;

      -le|--log-execution)
         MULLE_FLAG_LOG_EXECUTOR="YES"
      ;;

      -lm|--log-merge)
         MULLE_FLAG_MERGE_LOG="YES"
      ;;

      -ls|--log-settings)
         MULLE_FLAG_LOG_SETTINGS="YES"
      ;;

      -lsc|--log-script-calls)
         MULLE_FLAG_LOG_SCRIPTS="YES"
      ;;

      -t|--trace)
         MULLE_TRACE="1848"
         COPYMOVEFLAGS="-v"
         GITOPTIONS="`concat "${GITOPTIONS}" "-v"`"
         MULLE_TRACE_PATHS_FLIP_X="YES"
         MULLE_TRACE_RESOLVER_FLIP_X="YES"
         MULLE_TRACE_SETTINGS_FLIP_X="YES"
         ps4string='${BASH_SOURCE[1]##*/}:${LINENO}'
      ;;

      -tf|--trace-filepaths)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         MULLE_TRACE_PATHS_FLIP_X="NO"
      ;;

      -tfpwd|--trace-full-pwd)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         ps4string='${BASH_SOURCE[1]##*/}:${LINENO} \"\w\"'
      ;;

      -tp|--trace-profile)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         case "${UNAME}" in
            linux)
               ps4string='$(date "+%s.%N (${BASH_SOURCE[1]##*/}:${LINENO})")'
            ;;
            *)
               ps4string='$(date "+%s (${BASH_SOURCE[1]##*/}:${LINENO})")'
            ;;
         esac
      ;;

      -tpo|--trace-postpone)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         MULLE_TRACE_POSTPONE="YES"
      ;;

      -tpwd|--trace-pwd)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         ps4string='${BASH_SOURCE[1]##*/}:${LINENO} \".../\W\"'
      ;;

      -tr|--trace-resolver)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         MULLE_TRACE_RESOLVER_FLIP_X="NO"
      ;;

      -ts|--trace-settings)
         [ "${MULLE_TRACE}" = "1848" ] || fail "option \"$1\" must be specified after -t"
         MULLE_TRACE_SETTINGS_FLIP_X="NO"
      ;;

      -v|--verbose)
        [ "${MULLE_TRACE}" = "1848" ] && log_warning "${MULLE_EXECUTABLE_FAIL_PREFIX}: -v after -t invalidates -t"

         MULLE_TRACE="VERBOSE"
         GITOPTIONS="`concat "${GITOPTIONS}" "-v"`"
      ;;

      -vv|--very-verbose)
        [ "${MULLE_TRACE}" = "1848" ] && log_warning "${MULLE_EXECUTABLE_FAIL_PREFIX}: -vv after -t invalidates -t"

         MULLE_TRACE="FLUFF"
         COPYMOVEFLAGS="-v"
         GITOPTIONS="`concat "${GITOPTIONS}" "-v"`"
      ;;

      -vvv|--very-verbose-with-settings)
        [ "${MULLE_TRACE}" = "1848" ] && log_warning "${MULLE_EXECUTABLE_FAIL_PREFIX}: -vvv after -t invalidates -t"

         MULLE_TRACE="TRACE"
         COPYMOVEFLAGS="-v"
         GITOPTIONS="`concat "${GITOPTIONS}" "-v"`"
      ;;

      -s|--silent)
         MULLE_TRACE=
         MULLE_FLAG_LOG_TERSE="YES"
         GITOPTIONS="`concat "${GITOPTIONS}" "-v"`"
      ;;

      *)
         return 1
      ;;
   esac

   return 0
}


bootstrap_should_defer_to_master()
{
   local command="$1"

   #
   # if we have a.bootstrap.local/is_minion file then
   # some commands can't run, and some commands are re-executed in master
   # and some commands (like fetch and clean) are executed locally AND in the
   # master
   #
   if ! is_minion_bootstrap_project
   then
      return 1
   fi

   if [ "${MULLE_BOOTSTRAP_DONT_DEFER}" = "YES" ]
   then
      return 1
   fi

   local masterpath

   . mulle-bootstrap-project.sh

   masterpath="`get_master_of_minion_bootstrap_project`"

   case "${command}" in
      git|setup-xcode|xcode|tag|version|defer|emancipate|uname|library-path)
         log_verbose "Minion executes locally"
      ;;

      refer|dist-clean)
         fail "This is a minion bootstrap project.\n \
${MULLE_BOOTSTRAP_EXECUTABLE} ${command}t is not possible."
      ;;

      *)
         log_verbose "Minion defers to master \"$masterpath\" for execution"

         cd "${masterpath}" || fail "master is missing"
         return 0  # this leads to  main deferring later on (but cd is set!)
      ;;
   esac

   return 1
}


bootstrap_main()
{
   local command
   local ps4string

   local MULLE_FLAG_ANSWER="ASK"
   local MULLE_FLAG_DIRTY_HARRY="YES"
   local MULLE_FLAG_IGNORE_GRAVEYARD="NO"
   local MULLE_FLAG_VERBOSE_BUILD="NO"

   # technical flags
   local MULLE_FLAG_EXECUTOR_DRY_RUN="NO"
   local MULLE_FLAG_FOLLOW_SYMLINKS="YES"
   local MULLE_FLAG_LOG_DEBUG="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_FLUFF="NO"
   local MULLE_FLAG_LOG_SCRIPTS="NO"
   local MULLE_FLAG_LOG_SETTINGS="NO"
   local MULLE_FLAG_LOG_VERBOSE="NO"
   local MULLE_FLAG_MERGE_LOG="NO"
   local MULLE_TRACE_PATHS_FLIP_X="NO"
   local MULLE_TRACE_POSTPONE="NO"
   local MULLE_TRACE_RESOLVER_FLIP_X="NO"
   local MULLE_TRACE_SETTINGS_FLIP_X="NO"

   #
   # simple option handling
   #
   while [ $# -ne 0 ]
   do
      if bootstrap_technical_flags "$1"
      then
         shift
         continue
      fi

      case "$1" in
         -a|--prefer-origin)
            MULLE_FLAG_ANSWER="NO"
          ;;

         -y|--prefer-local)
            MULLE_FLAG_ANSWER="YES"
         ;;

         # used persistently throughout so it's global
         --follow-symlinks)
            MULLE_FLAG_FOLLOW_SYMLINKS="YES"
         ;;

         -f|--force-fetch)
            MULLE_FLAG_DIRTY_HARRY="NO"
         ;;

         -ig|--ignore-graveyard)
            MULLE_FLAG_IGNORE_GRAVEYARD="YES"
         ;;

         -h|--help)
            mulle_bootstrap_usage
         ;;

         -D*)  # just like C
            # define key values (by putting them into .bootstrap.local)
            define_expansion "`echo "$1" | sed s'/^-D[ ]*//'`"
         ;;

         -V|--verbose-build)
            MULLE_FLAG_VERBOSE_BUILD="YES"
         ;;

         -*)
            log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown option \"$1\""
            mulle_bootstrap_usage
         ;;

         *)
            break
         ;;
      esac

      shift
   done

   bootstrap_setup_trace "${MULLE_TRACE}"

   # source in environment now

   local_environment_main

   #
   # some commands only run when we have a .bootstrap folder
   #
   command="${1:-bootstrap}"

   if [ "${MULLE_FLAG_DIRTY_HARRY}" = "NO" ]
   then
      set_fetch_needed
      set_build_needed
   fi

   if ! is_bootstrap_project
   then
      case "$1" in
         -h|--help)
         ;;

         *)
            case "${command}" in
               bootstrap|nomagic|build|dist|clean|dist-clean|fetch|install|syteminstall|upgrade|update|setup-xcode|show|status|xcode|tag)
                  fail "There is no ${BOOTSTRAP_DIR} or ${BOOTSTRAP_DIR}.local folder here, can't continue"
               ;;
            esac
         ;;
      esac
   else
      if bootstrap_should_defer_to_master "$@"
      then
         return 1
      fi

      if [ "${MULLE_FLAG_DIRTY_HARRY}" = "YES" ]
      then
         case "${command}" in
            clean|dist-clean|init|status|show|config|setting)
            ;;

            *)
               ensure_consistency
            ;;
         esac
      fi
   fi

   MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_BOOTSTRAP_EXECUTABLE} ${command}"
   [ $# -eq 0 ] || shift

   MULLE_EXECUTABLE_OPTIONS="$@"

   case "${command}" in
      bootstrap)
         _bootstrap_main "$@"
      ;;

      build)
         . mulle-bootstrap-build.sh

         build_main "$@" || exit 1
      ;;

      clean)
         . mulle-bootstrap-clean.sh

         clean_main "$@" || exit 1
      ;;

      config)
         . mulle-bootstrap-settings.sh

         config_main "$@" || exit 1
      ;;

      defer)
         . mulle-bootstrap-defer.sh

         defer_main "$@" || exit 1
      ;;

      dist)
         . mulle-bootstrap-clean.sh

         clean_main "dist" || exit 1
      ;;

      emancipate)
         . mulle-bootstrap-defer.sh

         emancipate_main "$@" || exit 1
      ;;

      expansion)
         . mulle-bootstrap-settings.sh

         expansion_main "$@" || exit 1
      ;;


      flags)
         . mulle-bootstrap-flags.sh

         flags_main "$@" || exit 1
      ;;

      git)
         . mulle-bootstrap-scm.sh

         git_main "$@" || exit 1
      ;;

      help)
         mulle_bootstrap_usage "$@" || exit 1
      ;;

      init)
         . mulle-bootstrap-init.sh

         init_main "$@" || exit 1
      ;;

      install|fetch)
         . mulle-bootstrap-fetch.sh

         DONT_ASK_AFTER_WARNING=YES fetch_main "$@" || exit 1
      ;;

      library-path)
         echo "$PATH" | tr ':' '\012' | head -1
         return 0
      ;;

      setting)
         . mulle-bootstrap-settings.sh

         setting_main "$@" || exit 1
      ;;

      systeminstall)
         . mulle-bootstrap-install.sh

         install_main "$@" || exit 1
      ;;

      show)
         . mulle-bootstrap-show.sh

         show_main "$@" || exit 1
      ;;

      status)
         . mulle-bootstrap-status.sh

         status_main "$@" || exit 1
      ;;

      tag)
         . mulle-bootstrap-tag.sh

         tag_main "$@" || exit 1
      ;;

      uname)
         echo "${UNAME}"
         exit 0
      ;;

      update)
         . mulle-bootstrap-fetch.sh

         update_main "$@" || exit 1
      ;;

      upgrade)
         . mulle-bootstrap-fetch.sh

         upgrade_main "$@" || exit 1
      ;;

      version)
         echo "${MULLE_BOOTSTRAP_VERSION}"
         return 0
      ;;

      xcode|setup-xcode)
         . mulle-bootstrap-xcode.sh

         MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap xcode"
         xcode_main "$@" || exit 1
      ;;

      *)
         log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown command \"${command}\""
         mulle_bootstrap_usage
      ;;
   esac
}


brew_main()
{
   local command
   local ps4string

   # technical flags
   local MULLE_FLAG_EXECUTOR_DRY_RUN="NO"
   local MULLE_FLAG_LOG_DEBUG="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_EXECUTOR="NO"
   local MULLE_FLAG_LOG_FLUFF="NO"
   local MULLE_FLAG_LOG_SCRIPTS="NO"
   local MULLE_FLAG_LOG_SETTINGS="NO"
   local MULLE_FLAG_LOG_VERBOSE="NO"
   local MULLE_FLAG_MERGE_LOG="NO"
   local MULLE_TRACE_PATHS_FLIP_X="NO"
   local MULLE_TRACE_POSTPONE="NO"
   local MULLE_TRACE_RESOLVER_FLIP_X="NO"
   local MULLE_TRACE_SETTINGS_FLIP_X="NO"

   #
   # simple option handling
   #
   while [ $# -ne 0 ]
   do
      if bootstrap_technical_flags "$1"
      then
         shift
         continue
      fi

      case "$1" in
         -h|--help)
            mulle_brew_usage
         ;;

         -*)
            log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown option \"$1\""
            mulle_brew_usage
         ;;

         *)
            break
         ;;
      esac

      shift
   done

   bootstrap_setup_trace "${MULLE_TRACE}"

   if [ "${MULLE_FLAG_EXECUTOR_DRY_RUN}" = "YES" ]
   then
      log_trace "Dry run is active."
   fi

   # source in environment now

   local_environment_main

   #
   # some commands only run when we have a .bootstrap folder
   #
   command="${1:-install}"
   [ $# -eq 0 ] || shift

   if [ ! -d "${BOOTSTRAP_DIR}" -a ! -d "${BOOTSTRAP_DIR}.local" ]
   then
      case "$1" in
         -h|--help)
         ;;

         *)
            case "${command}" in
               dist|clean|dist-clean|install|upgrade|update|setup-xcode|xcode)
                  fail "There is no ${BOOTSTRAP_DIR} or ${BOOTSTRAP_DIR}.local folder here, can't continue"
               ;;
            esac
         ;;
      esac
   else
      if bootstrap_should_defer_to_master "$@"
      then
         return 1
      fi
   fi

   MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_BOOTSTRAP_EXECUTABLE} ${command}"
   MULLE_EXECUTABLE_OPTIONS="$@"

   case "${command}" in
      build)
         . mulle-bootstrap-build.sh

         build_main "$@" || exit 1
      ;;

      clean)
         . mulle-bootstrap-clean.sh

         clean_main "$@" || exit 1
      ;;

      config)
         . mulle-bootstrap-settings.sh

         config_main "$@" || exit 1
      ;;

      dist)
         . mulle-bootstrap-clean.sh

         clean_main "dist" || exit 1
      ;;

      defer)
         . mulle-bootstrap-defer.sh

         defer_main "$@" || exit 1
      ;;

      emancipate)
         . mulle-bootstrap-defer.sh

         emancipate_main "$@" || exit 1
      ;;

      flags)
         . mulle-bootstrap-flags.sh

         flags_main "$@" || exit 1
      ;;

      help)
         mulle_brew_usage "$@" || exit 1
      ;;

      init)
         . mulle-bootstrap-init.sh

         init_main "$@" || exit 1
      ;;

      install|fetch)
         . mulle-bootstrap-brew.sh

         brew_install_main "$@" || exit 1
      ;;

      library-path)
         echo "$PATH" | tr ':' '\012' | head -1
         return 0
      ;;

      setting)
         . mulle-bootstrap-settings.sh

         setting_main "$@" || exit 1
      ;;

      show)
         . mulle-bootstrap-show.sh

         show_main "$@" || exit 1
      ;;

      uname)
         echo "${UNAME}"
         exit 0
      ;;

      update)
         . mulle-bootstrap-brew.sh

         brew_update_main "$@" || exit 1
      ;;

      upgrade)
         . mulle-bootstrap-brew.sh

         brew_upgrade_main "$@" || exit 1
      ;;

      version)
         echo "${MULLE_BOOTSTRAP_VERSION}"
         return 0
      ;;

      xcode|setup-xcode)
         . mulle-bootstrap-xcode.sh

         MULLE_EXECUTABLE_FAIL_PREFIX="mulle-bootstrap xcode"
         xcode_main "$@" || exit 1
      ;;

      *)
         log_error "${MULLE_EXECUTABLE_FAIL_PREFIX}: Unknown command \"${command}\""
         mulle_bootstrap_usage
      ;;
   esac
}

#
# service both names
#
MULLE_BOOTSTRAP_EXECUTABLE="`basename -- "$0"`"
MULLE_BOOTSTRAP_ARGUMENTS="$@"
MULLE_EXECUTABLE_FAIL_PREFIX="${MULLE_BOOTSTRAP_EXECUTABLE}"
MULLE_EXECUTABLE_PID="$$"
export MULLE_EXECUTABLE_PID


bootstrap_init

main()
{
   case "${MULLE_BOOTSTRAP_EXECUTABLE}" in
      "mulle-brew")
         if ! brew_main "$@"
         then
            # just do it again, but cd has been set differently
            main "$@" # is array
            exit $?
         fi
      ;;

      "mulle-bootstrap")
         if ! bootstrap_main "$@"
         then
            # just do it again, but cd has been set differently
            main "$@"
            exit $?
         fi
      ;;
   esac
}

main "$@"

trap - TERM EXIT