#!/bin/bash

LANG=C
LC_ALL=POSIX

declare -a PTXDIST_ARGS_FULL
declare -a PTXDIST_ARGS_SECOND PTX_MAKE_ARGS
PTXDIST_ARGS_FULL=("${@}")
PTXDIST_LOG_PROMPT="ptxdist: "

#
# defaults
#
PTXDIST_PTXCONFIG_DEFAULT='${PTXDIST_WORKSPACE}/selected_ptxconfig'
PTXDIST_PLATFORMCONFIG_DEFAULT='${PTXDIST_WORKSPACE}/selected_platformconfig'
PTXDIST_COLLECTIONCONFIG_DEFAULT='${PTXDIST_WORKSPACE}/selected_collectionconfig'
PTXDIST_TOOLCHAIN_DEFAULT='${PTXDIST_WORKSPACE}/selected_toolchain'
PTXDIST_PTXRC_DEFAULT='${HOME}/.ptxdist/ptxdistrc-${PTXDIST_VERSION_PTXRC}'


#
# menu_select
#
# start file chooser and actual "select function"
#
# ${1}	specifies what to select {ptxconfig,platformconfig,collectionconfig}
#
menu_select() {
	local config="${1}"
	local "${config}"

	check_nonstandard "${config}" &&
	ptxd_dialog_fselect "${config}" &&

	do_select "${config}" "${!config}"
}


#
# le menu
#
menu() {
	local ptxconfig="$(readlink -f "${PTXDIST_PTXCONFIG}")"
	ptxconfig="${ptxconfig#${PTXDIST_WORKSPACE}/}"

	local -a menu
	if ! ptxd_get_ptxconf PTXCONF_NO_PLATFORM > /dev/null; then
		menu=( "${menu[@]}"	"platformconfig"	"Configure Hardware Platform" )
	fi

	local kernel_version="$(ptxd_get_ptxconf PTXCONF_KERNEL_VERSION)"
	if [ -n "${kernel_version}" ]; then
		menu=( "${menu[@]}"	"kernel"		"Configure Kernel (${kernel_version})" )
	fi

	local barebox_version="$(ptxd_get_ptxconf PTXCONF_BAREBOX_VERSION)"
	if [ -n "${barebox_version}" ]; then
		menu=( "${menu[@]}"	"barebox"		"Configure barebox (${barebox_version})" )
	fi

	local platform="$(ptxd_get_ptxconf PTXCONF_PLATFORM)"
	if [ -z "${platform}" -a -e "${PTXDIST_PLATFORMCONFIG}" ]; then
		platform="$(readlink -f "${PTXDIST_PLATFORMCONFIG}")"
		platform="${platform#${PTXDIST_WORKSPACE}/}"
	fi

	exec 3>&1
	exec 4>&1
	local cmd
	cmd="$(${PTX_DIALOG} \
		--clear \
		--title "PTXdist Main Menu" \
		--output-fd 3 \
		--default-item "${_ptxdist_menu_cmd}" \
		--cancel-label "Exit" \
		--menu "" 0 0 0 \
		-- \
		"menuconfig"		"Configure Software Platform" \
		"${menu[@]}" \
		"--------------"	"--------------------------------------------" \
		"select"		"Select Software Platform (${ptxconfig})" \
		"platform"		"Select Hardware Platform (${platform})" \
		"--------------"	"--------------------------------------------" \
		"boardsetup"		"Configure Board Properties" \
		"setup"			"Configure User Properties" \
		"localsetup"		"Configure BSP Properties" \
		"--------------"	"--------------------------------------------" \
		"go"			"Build the project" \
		"images"		"Build all images" \
		3>&1 1>&4 \
		)" || return
	exec 4>&-
	exec 3>&-
	_ptxdist_menu_cmd="${cmd}"

	case "${cmd}" in
	menuconfig)	cmd=ptx ;;
	platformconfig)	cmd=platform ;;
	select)		cmd=ptxconfig ;;
	platform)	cmd=platformconfig ;;
	boardsetup)	cmd=board ;;
	setup)		cmd=setup ;;
	localsetup)	cmd=localsetup ;;
	esac

	PTXDIST_QUIET=1

	case "${cmd}" in
	platformconfig|ptxconfig)
		menu_select "${cmd}"
		;;

	ptx|platform|kernel|barebox|board|setup|localsetup)
		do_config menuconfig "${cmd}"
		# FIXME: reread user config file
		;;
	-*)
		# ignore the "--------------"
		;;
	go|images)
		do_${cmd}
		read
		;;
	*)
		echo "${cmd}"
		read
		;;
	esac

	return 0
}



#
# check if user has used --ptxconfig, --platformconfig, --collectionconfig, --toolchain
#
check_nonstandard() {
	local nonstandard_set="PTX_${1}_SET"

	if [ "${!nonstandard_set}" = "true" ]; then
		ptxd_dialog_msgbox "error: cannot select ${1} when using the '--${1}' feature"
		return 1
	elif [ -z "${!nonstandard_set}" ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}error: invalid use of '${FUNCNAME} ${*}'"
		echo
		exit 1
	fi
}



#
# abort if ptxdist is run as root
#
check_uid() {
	if [ ${UID} -eq 0 ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}error: refusing to run PTXdist as root"
		echo
		exit 1
	fi
}



#
# remove "." from the PATH
#
check_path() {
	PATH="$(echo "${PATH}" | sed -e "s/\(:\.\)\+:/:/g" -e "s/\(^\.:\|:\.$\)//g")"
}



#
# check ptxdist version against configfile version
#
# $1: config file version
#
# return:
# 0: ptxdist and config file are compatible
# 1: ptxdist and config file are incompatible
#
check_version() {
	#
	# if config file and ptxdist match return success
	# (or if being forced)
	#
	if [ "${1}" = "${PTXDIST_VERSION_FULL}" -o \
	    -n "${PTXDIST_FORCE}" ]; then
		return 0
	fi

	local ifs_old="${IFS}"
	local IFS=".-"
	set -- ${1}
	IFS="${ifs_old}"

	local config_year="${1}"
	local config_month="${2}"
	local config_bugfix="${3}"
	local config_scm="${4}"

	if ptxd_get_ptxconf PTXCONF_BUILD_TOOLCHAIN >/dev/null; then
		#
		# let ptxdist build 1.99.x toolchain projects, too
		#
		if [ "${config_year}.${config_month}" = "1.99" -a \
		    "${config_bugfix}" != "svn" ]; then
			return 0
		fi
	fi

	#
	# If both ptxdist and config file are not using a release
	# (i.e. scm is set), we say it's compatible, too.
	#
	# If not the developer has to fix it :P
	#
	if [ -n "${PTXDIST_VERSION_SCM}" -a -n "${config_scm}" ]; then
		return 0
	fi

	#
	# we're doing timed releases now, so year and month _must_
	# match.
	#
	# the "bugfix" level of ptxdist must be greater or equal to
	# the "bugfix" level of the config file
	#
	if [ "${PTXDIST_VERSION_YEAR}.${PTXDIST_VERSION_MONTH}${PTXDIST_VERSION_SCM:+-git}" != \
		"${config_year}.${config_month}" ]; then
		return 1
	elif [ ${PTXDIST_VERSION_BUGFIX} -ge ${config_bugfix} ] >/dev/null 2>&1; then
		return 0
	fi

	return 1
}



_get_config_ptx() {
	if [ ! -e "${PTXDIST_PTXCONFIG}" ]; then
		ptxd_dialog_msgbox \
			"error:	'${PTXDIST_PTXCONFIG#${PTXDIST_WORKSPACE}/}' file is missing\n" \
			"	try 'ptxdist select <ptxconfig>'\n"
		return 1
	fi


	configfile_version="$(ptxd_get_ptxconf PTXCONF_CONFIGFILE_VERSION)"
	if [ $? -ne 0 -a -z "${PTXDIST_FORCE}" ]; then
		ptxd_dialog_msgbox \
			"error:	the config file '${PTXDIST_PTXCONFIG#${PTXDIST_WORKSPACE}/}'\n" \
			"	is missing the symbol 'PTXCONF_CONFIGFILE_VERSION',\n" \
			"	which means it is broken.\n" \
			"\n" \
			"	Try 'ptxdist select <ptxconfig>'\n"
		return 1
	fi
}

#
# check the ptxconfig file
#
_check_config_ptx() {
	local configfile_version

	_get_config_ptx || return

	check_version "${configfile_version}" || {
		ptxd_dialog_msgbox \
			"error:	The ptxconfig file version and ptxdist version do not match:\n" \
			"\n" \
			"	configfile version: ${configfile_version}\n" \
			"	ptxdist version:    ${PTXDIST_VERSION_FULL}\n" \
			"\n" \
			"	You can either migrate from an older ptxdist release with:\n" \
			"	'ptxdist migrate'\n" \
			"\n" \
			"	or, to ignore this error, add '--force'\n" \
			"	to ptxdist's parameters, e.g.:\n" \
			"	'ptxdist --force ${PTXDIST_ARGS_FULL[*]}'"
		return 1
	}
}


#
# check the platformconfig file
#
_check_config_platform() {
	local configfile_version
	#
	# don't check platform if e.g. toolchain defines 'PTXCONF_NO_PLATFORM'
	#
	ptxd_get_ptxconf PTXCONF_NO_PLATFORM >/dev/null && return || true

	if [ ! -e "${PTXDIST_PLATFORMCONFIG}" ]; then
		ptxd_dialog_msgbox \
			"error:	'${PTXDIST_PLATFORMCONFIG#${PTXDIST_WORKSPACE}/}' is missing\n" \
			"	try 'ptxdist platform <platformconfig>'"
		return 1
	fi

	if [ -n "${PTXDIST_FORCE}" ]; then
		return
	fi

	configfile_version="$(ptxd_get_ptxconf PTXCONF_PLATFORMCONFIG_VERSION)" || {
		ptxd_dialog_msgbox \
			"error:	The config file '${PTXDIST_PLATFORMCONFIG#${PTXDIST_WORKSPACE}/}'\n" \
			"	is missing the symbol: 'PTXCONF_PLATFORMCONFIG_VERSION',\n" \
			"	which means it is broken.\n"
		return 1
	}

	check_version "${configfile_version}" || {
		ptxd_dialog_msgbox \
			"error:	The platformconfig file version and ptxdist version do not match:\n" \
			"\n" \
			"	configfile version: ${configfile_version}\n" \
			"	ptxdist version:    ${PTXDIST_VERSION_FULL}\n" \
			"\n" \
			"	You can either migrate from an older ptxdist release with:\n" \
			"	'ptxdist migrate'\n" \
			"\n" \
			"	or, to ignore this error, add '--force'\n" \
			"	to ptxdist's parameters, e.g.:\n" \
			"	'ptxdist --force ${PTXDIST_ARGS_FULL[*]}'"
		return 1
	}
}


_check_config_collection() {
	if [ ! -e "${PTXDIST_COLLECTIONCONFIG}" -a \
	    \( -L "${PTXDIST_COLLECTIONCONFIG}" -o "${PTX_collectionconfig_SET}" == "true" \) ]; then
		ptxd_dialog_msgbox \
			"error:	'${PTXDIST_COLLECTIONCONFIG#${PTXDIST_WORKSPACE}/}' is missing\n" \
			"	try 'ptxdist collection <collectionconfig>'"
		return 1
	fi
}

#
# Check for existence of the config files
# check_config()
#
check_config() {
	if [ -z "${PTX_SLEDGEHAMMER}" ]; then
		_check_config_ptx &&
		_check_config_platform &&
		_check_config_collection
	fi &&
	setup_logfile &&
	if [ ! -e "${PTXDIST_PLATFORMDIR}/selected_ptxconfig" -o -L "${PTXDIST_PLATFORMDIR}/selected_ptxconfig" ]; then
		local ptxconfig="$(readlink -f "${PTXDIST_PTXCONFIG}")"
		rm -f "${PTXDIST_PLATFORMDIR}/selected_ptxconfig" &&
		ln -s "${ptxconfig}" "${PTXDIST_PLATFORMDIR}/selected_ptxconfig"
	fi &&
	if [ ! -e "${PTXDIST_PLATFORMDIR}/selected_platformconfig" -o -L "${PTXDIST_PLATFORMDIR}/selected_platformconfig" ]; then
		local platformconfig="$(readlink -f "${PTXDIST_PLATFORMCONFIG}")"
		rm -f "${PTXDIST_PLATFORMDIR}/selected_platformconfig" &&
		ln -s "${platformconfig}" "${PTXDIST_PLATFORMDIR}/selected_platformconfig"
	fi &&
	if [ -e "${PTXDIST_COLLECTIONCONFIG}" -a \( ! -e "${PTXDIST_PLATFORMDIR}/selected_collectionconfig" -o \
		-L "${PTXDIST_PLATFORMDIR}/selected_collectionconfig" \) ]; then
		local collectionconfig="$(readlink -f "${PTXDIST_COLLECTIONCONFIG}")"
		rm -f "${PTXDIST_PLATFORMDIR}/selected_collectionconfig" &&
		ln -s "${collectionconfig}" "${PTXDIST_PLATFORMDIR}/selected_collectionconfig"
	fi
}



#
# first create and then run some tests on given directory
#
# $1:	the directory to create
#
check_dirs_mkdir() {
	local dir="${1}"

	# check for spaces
	if echo "${dir}" | grep -q " "; then
		echo
		echo "error: some important dir ('${dir}')"
		echo "	     contains spaces, this will probably not work, sorry"
		echo
		exit 1
	fi

	# create dir if not exiting
	if [ ! -d "${dir}" ]; then
		if ! install -m755 -d "${dir}" 2> /dev/null; then
			echo
			echo "error: '${dir}'"
			echo "	     does not exist and cannot be created!"
			echo "	     Please create that dir with write permissions for you."
			echo
			read -t 5 -p "press enter to let sudo do that job!"
			if [ ${?} -ne 0 ]; then
				echo
				exit 1
			fi
			echo
			echo sudo install -m755 -d "${dir}"
			sudo install -m755 -d "${dir}"
			echo sudo chown "${UID}" "${dir}"
			sudo chown "${UID}" "${dir}"
		fi
	fi

	# test r/w
	local testfile="${dir}/.secret-world-domination-project"
	if ! touch "${testfile}" 2> /dev/null; then
		echo
		echo "error: '${dir}'"
		echo "	     does exist, but is not writable."
		echo "	     Change the permissions and try again."
		echo
		read -t 5 -p "press enter to let sudo do the job!"
		if [ ${?} -ne 0 ]; then
			echo
			exit 1
		fi
		echo
		echo sudo chown "${UID}" "${dir}"
		sudo chown "${UID}" "${dir}"
		echo sudo chmod u+w "${dir}"
		sudo chmod u+w "${dir}"

		if ! touch "${testfile}" 2> /dev/null; then
			echo
			echo "error: cannot make '${dir}' writable, giving up"
			echo
			exit 1
		fi
	fi

	rm -- "${testfile}"
	chmod ug-s "${dir}"
}


#
# Most install stages think that some standard directories are there,
# so they are created here.
#
check_dirs() {
	local ptxconf_sysroot_target ptxconf_sysroot_host ptxconf_sysroot_cross

	ptxconf_sysroot_host="$(ptxd_get_ptxconf PTXCONF_SYSROOT_HOST)" &&
	ptxconf_sysroot_cross="$(ptxd_get_ptxconf PTXCONF_SYSROOT_CROSS)" &&
	ptxconf_sysroot_target="$(ptxd_get_ptxconf PTXCONF_SYSROOT_TARGET)" || return

	# check for r/w and create standard directory layout
	local dir link
	for dir in \
		"${PTXDIST_PLATFORMDIR}" \
		"${ptxconf_sysroot_host}" \
		"${ptxconf_sysroot_cross}" \
		"${ptxconf_sysroot_target}" \
		; do
		check_dirs_mkdir "${dir}"
	done
	if ! df -l "${PTXDIST_PLATFORMDIR}" >/dev/null 2>&1; then
		if [ "${PTXCONF_SETUP_DISABLE_LOCAL_CHECK}" != "y" ]; then
			echo
			echo "$(ptxd_print_path "${PTXDIST_PLATFORMDIR}") must be on a local disk!"
			echo "Disable this check with 'ptxdist setup' (Developer Options) to continue anyways."
			echo
			exit 1
		else
			ptxd_warning "$(ptxd_print_path "${PTXDIST_PLATFORMDIR}") is not on a local disk."
		fi
	fi

	# create standard directory layout
	for dir in \
		"${ptxconf_sysroot_host}" \
		"${ptxconf_sysroot_cross}" \
		"${ptxconf_sysroot_target}" \
		; do
		install -m755 -d "${dir}"/{etc,usr/{lib,{,s}bin,include,{,share/}man/man{1,2,3,4,5,6,7,8,9}}}
	done
	for dir in \
		"${ptxconf_sysroot_host}" \
		"${ptxconf_sysroot_cross}" \
		; do
		for link in sbin bin lib; do
			ptxd_replace_link "usr/${link}" "${dir}/${link}"
		done
	done

	# create build and output dirs
	for dir in \
		"${BUILDDIR}" \
		"${CROSS_BUILDDIR}" \
		"${HOST_BUILDDIR}" \
		"${STATEDIR}" \
		"${IMAGEDIR}" \
		"${ROOTDIR}" \
		"${PTXDIST_SRCDIR}" \
	        "${PTXDIST_GEN_CONFIG_DIR}" \
		; do
		if [ ! -d "${dir}" ]; then
			install -m755 -d "${dir}" || ptxd_bailout "cannot create dir: '${dir}'"
		fi
	done

	# check for case sensitive file system
	for dir in \
		"${BUILDDIR}" \
		"${CROSS_BUILDDIR}" \
		"${HOST_BUILDDIR}" \
		; do
		local testfile_lower="${dir}/.secret-world-domination-project"
		local testfile_upper="${dir}/.Secret-World-Domination-Project"

		echo lower > "${testfile_lower}"
		echo upper > "${testfile_upper}"

		if [ "$(<"${testfile_lower}")" != "lower" -o \
		    "$(<"${testfile_upper}")" != "upper" ]; then
			echo
			echo "error: '${dir}'"
			echo "	     is not a case sensitive filesystem."
			echo "	     Please move your project to a case sensitive one"
			echo
			exit 1
		fi

		rm -- "${testfile_lower}" "${testfile_upper}"
	done

	# make sure that the BSP git dir is ignored
	if git -C "${PTXDIST_PLATFORMDIR}" rev-parse HEAD &> /dev/null; then
		touch "${PTXDIST_PLATFORMDIR}/.git"
	fi
}


#
# Check for defined compiler
# This only should be done when we build userland (chicken egg problem)
#
check_compiler() {
    local wrapper_dir sysroot_host

    sysroot_host="$(ptxd_get_ptxconf PTXCONF_SYSROOT_HOST)"
    wrapper_dir="${sysroot_host}/usr/lib/wrapper"

    ptxd_lib_setup_host_wrapper &&

    if ! ptxd_get_ptxconf PTXCONF_BUILD_TOOLCHAIN >/dev/null; then
	local toolchain compiler_prefix

	toolchain="$(readlink -f "${PTXDIST_TOOLCHAIN}")"
	compiler_prefix="$(ptxd_get_ptxconf PTXCONF_COMPILER_PREFIX)"

	ptxd_lib_setup_toolchain &&
	ptxd_lib_setup_target_wrapper &&
	ptxd_lib_setup_target_icecc
    fi &&

    # ptxd_lib_setup_target_wrapper provides the real/clang symlink needed here
    ptxd_lib_setup_host_icecc &&

    PATH="${wrapper_dir}:${PATH}"
}


#
# checks the umask and modifies it if it is save to do so
#
check_umask() {
	local mask="$(umask)"
	if [ "${mask}" = "0022" ]; then
		return
	fi
	if [ "$((mask & 0755))" -eq 0 ]; then
		# setting a stricter umask is ok
		umask 0022
	else
		ptxd_bailout "PTXdist does not work correctly with the current umask (${mask})!" \
			"A less restrictive umask, e.g. 0022 or 0002, is required."
	fi

}

#
# checks if the dependencies are alright (make for the poor)
#
check_deps() {
	ptxd_dgen || ptxd_bailout "error in dgen"
}


#
# checks if the given PTXCONF_ option is actually selected
#
check_if_selected_single() {
	local configvar="PTXCONF_$(ptxd_name_to_NAME "${1}")"	# FIXME
	ptxd_get_ptxconf "${configvar}" > /dev/null || {
		if [ ${?} -eq 2 ]; then
			ptxd_dialog_msgbox \
				"${PTXDIST_LOG_PROMPT}error: '${1}' is not a valid package name"
			return 1
		else
			ptxd_dialog_msgbox \
				"${PTXDIST_LOG_PROMPT}error: '${1}' is not selected in\n" \
				"	${PTXDIST_PTXCONFIG}"
			return 1
		fi
	}
}


#
# checks if all the given PTXCONF_ option are actually selected
#
check_if_selected() {
	if [ -z "${1}" ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}error: please specify a target"
		echo
		exit 1
	fi

	while [ ${#} -ne 0 ]; do
	    check_if_selected_single "${1}" || return
	    shift
	done
}


#
# check if the given packages can be targetinstalled
#
# (host and cross packaged
#
check_targetinstall_pkgs() {
	while [ ${#} -ne 0 ]; do
	    case "${1}" in
		host-*|cross-*)
		    ptxd_dialog_msgbox \
			"error: 'host' or 'cross' packages cannot be targetinstalled!\n" \
			"\n" \
			"       You probably want to do: 'ptxdist install ${1}'"
		    return 1
		    ;;
	    esac
	    shift
	done
}


#
# runs the standard tests before calling into make:
#
check_premake()
{
	check_config &&
	check_dirs &&
	check_umask &&
	check_deps
}

check_virtualenv()
{
	if [ -n "${PTXDIST_VIRTUALENV}" ]; then
		if [ ! -e "${PTXDIST_VIRTUALENV}/bin/activate" ]; then
			ptxd_bailout "Virtual Env '${PTXDIST_VIRTUALENV}' not found"
		fi
		. "${PTXDIST_VIRTUALENV}/bin/activate"
	fi
}

#
# runs the standard tests before calling into make + compiler check
#
check_premake_compiler()
{
	check_premake &&
	check_compiler &&
	check_virtualenv
}


#
# usage()
#
usage() {
cat << EOF

PTXdist $(printf "%-24s" ${PTXDIST_VERSION_FULL}) Build System for Embedded Linux Systems

  ptxdist <action [args]> [options]

Setup and Project Actions:

  menu				enter main control menu

  setup				setup per-user preferences
  localsetup			setup per-bsp preferences
  boardsetup			setup per-board preferences

  nconfig
  menuconfig			configure the project's filesystem

  menuconfig kernel
  kernelconfig			configure the kernel

  menuconfig platform
  platformconfig		configure the platform

  menuconfig collection		configure the collection

  menuconfig barebox		configure the bootloader Barebox

  oldconfig			run 'make oldconfig' on ptxconfig file
  allmodconfig			run 'make allmodconfig' on ptxconfig file
  allyesconfig			run 'make allyesconfig' on ptxconfig file
  allnoconfig			run 'make allnoconfig' on ptxconfig file
  alldefconfig			run 'make alldefconfig' on ptxconfig file
  randconfig			run 'make randconfig' on ptxconfig file

  migrate			migrate config files from a previous
				ptxdist release

  toolchain [<path>]		if path is omitted the toolchain is guessed,
				    (guessing works only if platformconfig is
				     already selected)
				otherwise select this toolchain
				    (<path> to binaries)
  select <config>		if there is no selected_ptxconfig file you can
				select one of several project configs to be used
  platform <config>		if there is no selected_platform file you can
				select one of several platform
				configs to be used
  collection <config>		if there is no selected_collection file you can
				select one of several collection
				configs to be used
Build Actions:

  go				start building the current project
  get				get all package sources
  getdev			get all pre-built archives (if enabled)
  urlcheck			check package URL for all packages

  get <package>			get package sources
  extract <package>		extract package
  prepare <package>		run configure stages for package
  compile <package>		compile the sources
  install <package>		install host side components into sysroot/
  targetinstall <package>	install files for target into root/
  clean <package>		cleanup package
  drop <package>.<stage>	mark a stage of a package as unbuilt
  tags <package>		try to build tags for the package
  urlcheck <package>		check if the package URL still works
  licensecheck <package>	check md5sums of the package license files

  images			build images for target system
  image <image>			build the specified image

Clean Actions:

  clean				cleanup build-host and build-cross dirs
  clean root			cleanup root directory for target
  clean target			cleanup all target packages
  distclean			cleanup everything

Misc:

  version			print out ptxdist version
  test <testname>		run tests
  newpackage <type>		create a new package Makefile in a rules dir
				use 'newpackage help' for a longer description
  nfsroot			run a userspace NFS server and export the nfsroot
  gdb				run cross gdb with configured sysroot etc.
  cgdb				run cross gdb with configured sysroot etc.
  				with cgdb as frontend
  bsp-info			print some basic information about the BSP
  package-info <package>	print some basic information about the package
  fast-bsp-report		generate a yaml file that describes the BSP and
				all packages. Less data but works without
				building packages.
  full-bsp-report		generate a yaml file that describes the BSP and
				all packages. More data but will build all
				packages if necessary.
  print <var>			print the contents of a variable, in the way
				it is known by "make"
  printnext <var>		assumes that the contents of <var> is another
				variable and print the contents of this variable
  licensecheck			check md5sums of license files for all packages
  lint				run some basic checks for the bsp and PTXdist
  list-packages			print a list of all selected packages
  local-src <pkg> [<directory>]	overwrite a package source with a locally provided
				directory containing the sourcecode.
				Not specifying the directory will undo the change.
  bash				enter a ptxdist environment bash shell
  bash <cmd> [args...]		execute <cmd> in ptxdist environment
  docs-html			generate HTML documentation
  docs-latex			generate LaTeX / PDF documentation
  export-src <target dir>	export all source archives needed for this
				project to <target dir>
  cargosync <pkg>		create or update the rule file for the cargo package
				dependencies

Overwrite defaults:

  --ptxconfig=<config>		use specified ptxconfig
  --platformconfig=<config>	use specified platformconfig
  --collectionconfig=<config>	use specified collectionconfig
  --toolchain=<toolchain>	use specified toolchain
  --force, -f			select config even if MOJO is missing
  --force-download		allow downloading, even if disabled by setup

Options:

  --debug, -d			print out additional info (like make decisions)
  --quiet, -q			suppress output, show only stderr
  --silent, -s			alias for --quiet
  --verbose, -v			be more verbose, print command before execution
  --output-sync			Improve output readability for parallel building.
				Disabled by default except for quiet builds.
  --no-output-sync		Disable output-sync if enabled by default.
  --progress			Show progress information. This is only available
				in quiet mode.

  --j-intern=<n>, -ji<n>	set number of parallel builds in packages
				(default = 2*CPUs)
  --j-extern=<n>, -je<n>	set number of packages built in parallel
				(default = 1). Deprecated, use -j instead.
  -j[<n>]			build packages in parallel with a total number
				of <n> jobs in parallel. If -j is used without
				argument then <n> defaults to 2*CPUs.

  --load-average=<n>, -l<n>	try to limit load to <n>

  --nice=<n>, -n<n>		run with reduced scheduling priority (i.e. nice)
				(default = 10)

  --dirty			avoid rebuilding packages to speed up development

  --keep-going, -k		keep going. Continue as much as possible
				after an error.

  --git				use git to apply patches

  --auto-version		automatically switch to the correct PTXdist version

  --virtualenv=<dir>		Python virtual environment directory. It must
				contain a 'bin/activate' shell script.
  --no-container		Override the setup/localsetup configuration and
				explicitly run PTXdist without a container.
EOF
}

clean() {
	local dir bdir

	# we want to clean all target packages
	if [ "${1}" = "target" ]; then
		if [ ! -n "${PTXDIST_FORCE}${PTXDIST_QUIET}${PTXCONF_SETUP_DIRECT_CLEAN}" ]; then
			read -e -p "really clean all target packages? [y/N] " r
			case "${r}" in
				y|Y) ;;
				*) exit 1 ;;
			esac
		fi
		check_premake_compiler &&
		pkgs=($(ptxd_make "/print-PACKAGES /print-EXTRA_PACKAGES /print-LAZY_PACKAGES")) &&
		ptxd_make_log "${pkgs[@]/%/_clean}"
		set -- root
	fi

	# we want to clean the root dir
	if [ "${1}" = "root" ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}cleaning image packages..."
		rm -fr -- "${STATEDIR}"/image-*
		echo "${PTXDIST_LOG_PROMPT}cleaning image directory..."
		rm -fr -- "${IMAGEDIR}"
		echo "${PTXDIST_LOG_PROMPT}cleaning root directory..."
		rm -fr -- "${ROOTDIR}"
		rm -fr -- "${PTXDIST_PLATFORMDIR}/root-debug"
		rm -fr -- "${PTXDIST_PLATFORMDIR}/nfsroot"
		echo "${PTXDIST_LOG_PROMPT}cleaning packages..."
		rm -fr -- "${PKGDIR}"/*.ipk
		echo "${PTXDIST_LOG_PROMPT}cleaning targetinstall stages..."
		rm -f -- \
		    "${STATEDIR}"/*.cmds \
		    "${STATEDIR}"/*.perms \
		    "${STATEDIR}"/*.targetinstall* \
		    "${STATEDIR}"/*.xpkg.map
		echo "${PTXDIST_LOG_PROMPT}done."
		echo
		return
	fi

	# we want to clean a single package
	if [ -n "${1}" ]; then
		check_if_selected "${@}" &&
		check_premake_compiler &&
		ptxd_make_log "${@/%/_clean}"
		return
	fi

	if [ ! -n "${PTXDIST_FORCE}${PTXDIST_QUIET}${PTXCONF_SETUP_DIRECT_CLEAN}" ]; then
		read -e -p "really clean all? [y/N] " r
		case "${r}" in
			y|Y) ;;
			*) exit 1 ;;
		esac
	fi

	echo
	echo "${PTXDIST_LOG_PROMPT}removing build directories..."
	for dir in "${BUILDDIR}" "${CROSS_BUILDDIR}" "${HOST_BUILDDIR}"; do
		if [ ! -d "${dir}" ]; then
			continue
		fi
		for bdir in $(find "${dir}" -maxdepth 1 -mindepth 1 -type l); do
			# run 'make clean' for linked source directories
			pushd "${bdir}" > /dev/null
			echo -n "${PTXDIST_LOG_PROMPT}running 'make clean' in '${bdir#${dir}/}'... "
			make clean 1> /dev/null 2>&1
			echo "done"
			popd > /dev/null
		done
		rm -fr -- "${dir}"
	done

	if [ -f "${PTXDIST_PTXCONFIG}" ]; then
		echo "${PTXDIST_LOG_PROMPT}removing sysroot directories..."
		local ptxconf_sysroot_target="$(ptxd_get_ptxconf PTXCONF_SYSROOT_TARGET)"
		local ptxconf_sysroot_host="$(ptxd_get_ptxconf PTXCONF_SYSROOT_HOST)"
		local ptxconf_sysroot_cross="$(ptxd_get_ptxconf PTXCONF_SYSROOT_CROSS)"
		local ptxconf_gnu_target="$(ptxd_get_ptxconf PTXCONF_GNU_TARGET)"

		rm -rf -- "${ptxconf_sysroot_target}" "${ptxconf_sysroot_host}" "${ptxconf_sysroot_cross}"

		# this is for ptxdist-1 backward compatibility
		dir="${PTXDIST_WORKSPACE}/local/${ptxconf_gnu_target}"
		if [ -n "${ptxconf_gnu_target}" -a -d "${dir}" ]; then
			rm -rf "${dir}" || true
			rmdir "${PTXDIST_WORKSPACE}/local" >/dev/null 2>&1 || true
		fi
	fi

	echo "${PTXDIST_LOG_PROMPT}removing deps..."
	rm -f -- "${PTXDIST_PLATFORMDIR}/"{deptree-a4.ps,deptree.ps}
	rm -f -- "${STATEDIR}/depend.out"
	echo "${PTXDIST_LOG_PROMPT}removing imagedir..."
	rm -fr -- "${IMAGEDIR}"
	echo "${PTXDIST_LOG_PROMPT}removing root..."
	rm -fr -- "${ROOTDIR}"
	rm -fr -- "${PTXDIST_PLATFORMDIR}/root-debug"
	rm -fr -- "${PTXDIST_PLATFORMDIR}/nfsroot"
	echo "${PTXDIST_LOG_PROMPT}removing report..."
	rm -fr -- "${REPORTDIR}"
	echo "${PTXDIST_LOG_PROMPT}removing state..."
	rm -fr -- "${STATEDIR}"
	echo "${PTXDIST_LOG_PROMPT}removing logfile..."
	rm -f -- "${PTX_LOGFILE}"
	echo "${PTXDIST_LOG_PROMPT}removing test logfile..."
	rm -f -- "${PTXDIST_PLATFORMDIR}/test.log"
	echo "${PTXDIST_LOG_PROMPT}removing packages dir..."
	rm -fr -- "${PKGDIR}"
	for cfg in \
		"${PTXDIST_PTXCONFIG}" \
		"${PTXDIST_PLATFORMCONFIG}" \
		"${PTXDIST_COLLECTIONCONFIG}" \
		; do
		local cfg="$(readlink -f "${cfg}")"
		if [ ${?} -ne 0 -a -e "${cfg}.old" ]; then
			rm -f -- "${cfg}.old"
		fi
	done

	# remove the remaining PTXDIST_PLATFORMDIR (if empty)
	rmdir -- "${PTXDIST_PLATFORMDIR}" >/dev/null 2>&1

	echo "${PTXDIST_LOG_PROMPT}done."
	echo
}

drop() {
	local statefile

	if [ -z "${1}" ]; then
		echo "Usage: drop <package>.<stage>"
		exit 1
	fi

	if [ -z "${2}" ]; then
		statefile="${1}"
	else
		statefile="${1}.${2}"
	fi

	echo
	if [ -e "${STATEDIR}/${statefile}" -o -n "${PTXDIST_FORCE}" ] ; then
		rm -f -- "${STATEDIR}/${statefile}"
		echo "dropping ${statefile}"
		echo
		exit
	else
		echo "stage '${statefile}' isn't built, so we cannot drop it"
		echo "maybe you want use clean: 'ptxdist clean ${statefile/.*/}'"
		echo
		exit 1
	fi
}

create_docs() {
	local builder srcdir outdir dir entry
	local -a args

	builder="${1}"
	outdir=${PTXDIST_WORKSPACE}/Documentation

	if [ "${PTXDIST_TOPDIR}" -ef "${PTXDIST_WORKSPACE}" ]; then
		srcdir="${PTXDIST_TEMPDIR}/docs"
		args=( "-t" "ptxdistonly" )
	else
		srcdir="${STATEDIR}/docs"
		rm -rf "${srcdir}" &&
		ptxd_source_kconfig "${PTXDIST_PTXCONFIG}" &&
		ptxd_source_kconfig "${PTXDIST_PLATFORMCONFIG}"
	fi &&

	mkdir -p "${srcdir}" &&
	ptxd_in_path PTXDIST_PATH doc &&
	for dir in "${ptxd_reply[@]}"; do
		for entry in $(cd "${dir}"; ls); do
			cp -an "${dir}/${entry}" "${srcdir}/" || return
		done
	done &&
	check_virtualenv &&
	sphinx-build -b "${builder}" -d "${outdir}/.doctrees" "${args[@]}" "${srcdir}" \
		"${outdir}/${builder}" &&
	if [ "${builder}" = "latex" ]; then
		sed -i s/pdflatex/xelatex/ ${outdir}/${builder}/Makefile
		make -C "${outdir}/${builder}"
	fi
}


##################################################################
################ "minusminus" option parser ######################
##################################################################

parse_first()
{
	local arg

	#
	# sane defaults
	#
	PTXDIST_TOOLCHAIN=
	PTXDIST_PLATFORMCONFIG=
	PTXDIST_COLLECTIONCONFIG=
	PTXDIST_PTXCONFIG=
	PTXDIST_VERBOSE=0

	#
	# init these ones
	#
	PTX_ptxconfig_SET="false"
	PTX_platformconfig_SET="false"
	PTX_collectionconfig_SET="false"
	PTX_toolchain_SET="false"

	set -- "${PTXDIST_ARGS_FULL[@]}"
	while [ ${#} -ne 0 ]; do
		arg="${1}"
		shift

		case "${arg}" in
		-d|--debug)
			PTX_MAKE_ARGS[${#PTX_MAKE_ARGS[@]}]="--debug=make"
			PTX_DEBUG=true
			;;
		-v|--verbose)
			PTXDIST_VERBOSE=1
			;;
		--dirty)
			PTXDIST_DIRTY=true
			;;
		-k|--keep-going)
			PTX_MAKE_ARGS[${#PTX_MAKE_ARGS[@]}]="-k"
			;;
		-p|--pedantic)
			PTXDIST_PEDANTIC=true
			;;
		-f|--force)
			PTXDIST_FORCE=true
			;;
		--force-download)
			PTXDIST_FORCE_DOWNLOAD=true
			;;
		--sledgehammer)
			PTX_SLEDGEHAMMER=true
			;;
		--ptxconfig=*)
			PTXDIST_PTXCONFIG="${arg#*=}" &&
			PTX_ptxconfig_SET=true
			;;
		--platformconfig=*)
			PTXDIST_PLATFORMCONFIG="${arg#*=}" &&
			PTX_platformconfig_SET=true
			;;
		--collectionconfig=*)
			PTXDIST_COLLECTIONCONFIG="${arg#*=}" &&
			PTX_collectionconfig_SET=true
			;;
		--toolchain=*)
			PTXDIST_TOOLCHAIN="$(ptxd_abspath "${arg#*=}")" &&
			PTX_toolchain_SET=true
			;;
		--j-intern=*)
			PTXDIST_PARALLELMFLAGS_INTERN="-j${arg#*=}"
			;;
		-ji*)
			PTXDIST_PARALLELMFLAGS_INTERN="-j${arg#*i}"
			;;
		--j-extern=*)
			PTXDIST_PARALLELMFLAGS_EXTERN="-j${arg#*=}"
			;;
		-je*)
			PTXDIST_PARALLELMFLAGS_EXTERN="-j${arg#*e}"
			;;
		-j)
			PTXDIST_PARALLELMFLAGS="auto"
			;;
		-j[1-9]*)
			PTXDIST_PARALLELMFLAGS="-j${arg#*j}"
			;;
		--jobserver-auth=*)
			PTXDIST_JOBSERVER_AUTH="${arg#--jobserver-auth=}"
			;;
		-l*)
			PTXDIST_LOADMFLAGS="-l${arg#*l}"
			;;
		--load-average=*)
			PTXDIST_LOADMFLAGS="-l${arg#*=}"
			;;
		-n*)
			PTX_NICE="${arg#*n}"
			PTX_NICE="${PTX_NICE:-10}"
			;;
		--nice=*)
			PTX_NICE="${arg#*=}"
			PTX_NICE="${PTX_NICE:-10}"
			;;
		--output-sync)
			PTXDIST_OUTPUT_SYNC=1
			;;
		--no-output-sync)
			PTXDIST_OUTPUT_SYNC=0
			;;
		--progress)
			PTXDIST_PROGRESS=1
			;;
		-q|--quiet|-s|--silent)
			PTXDIST_QUIET=1
			;;
		--git)
			# overwrite default from ptxdistrc
			export PTXCONF_SETUP_PATCHIN_GIT=y
			;;
		--update-md5)
			# overwrite default from ptxdistrc
			export PTXCONF_SETUP_CHECK="update"
			;;
		--auto-version)
			PTXDIST_AUTOVERSION=1
			;;
		--virtualenv=*)
			PTXDIST_VIRTUALENV="${arg#*=}"
			;;
		--no-container)
			unset PTXCONF_SETUP_CONTAINER_ENABLE
			;;
		--)
			while [ ${#} -ne 0 ]; do
				arg="${1}"
				shift
				PTXDIST_ARGS_SECOND[${#PTXDIST_ARGS_SECOND[@]}]="${arg}"
			done
			;;
		*)
			PTXDIST_ARGS_SECOND[${#PTXDIST_ARGS_SECOND[@]}]="${arg}"
			;;
		esac || return
	done
}

setup_layers()
{
	export -a PTXDIST_LAYERS
	local layer="${PTXDIST_WORKSPACE}"
	local -a cfgs

	while [ -e "${layer}" -o -h "${layer}" ]; do
		if [ ! -d "${layer}" ]; then
			if [ -n "${PTXDIST_AUTOVERSION}" ]; then
				break
			fi
			echo
			echo "${PTXDIST_LOG_PROMPT}error: Layer '${layer}' is not a directory!"
			echo
			exit 1
		fi
		if [ -z "${PTXDIST_AUTOVERSION}" -a -e "${layer}/bin/ptxdist" -a \
				"$(realpath "${layer}")" != "${PTXDIST_TOPDIR}" ]; then
			echo
			echo "${PTXDIST_LOG_PROMPT}error: PTXdist layer '${layer}' does not match the current PTXDIST_TOPDIR='${PTXDIST_TOPDIR}'"
			echo
			exit 1
		fi
		if [ -n "${PTXDIST_AUTOVERSION}" -a -e "${layer}/bin/ptxdist" ]; then
			PTXDIST_AUTOVERSION="$(realpath "${layer}")/bin/ptxdist"
		fi
		if [ "$(realpath "${layer}")" = "${PTXDIST_TOPDIR}" ]; then
			break
		fi
		PTXDIST_LAYERS[${#PTXDIST_LAYERS[@]}]="${layer}"
		layer="${layer}/base"
	done
	PTXDIST_LAYERS[${#PTXDIST_LAYERS[@]}]="${PTXDIST_TOPDIR}"

	if [ "${PTX_ptxconfig_SET}" = "false" ]; then
		cfgs=( "selected_ptxconfig" "configs/ptxconfig" )
	elif ! [[ "${PTXDIST_PTXCONFIG}" =~ ^/.* ]]; then
		cfgs=( "${PTXDIST_PTXCONFIG}" )
	else
		cfgs=()
	fi
	if [ "${#cfgs[*]}" -gt 0 ]; then
		PTXDIST_PTXCONFIG="${PTXDIST_LAYERS[0]}/${cfgs[0]}"
		for layer in "${PTXDIST_LAYERS[@]}"; do
			local -a ptxd_reply
			ptxd_get_path_filtered "${cfgs[@]/#/${layer}/}" || continue
			PTXDIST_PTXCONFIG="${ptxd_reply[0]}"
			break
		done
	fi

	if [ "${PTX_platformconfig_SET}" = "false" ]; then
		cfgs=( "selected_platformconfig" "configs/*/platformconfig" )
	elif ! [[ "${PTXDIST_PLATFORMCONFIG}" =~ ^/.* ]]; then
		cfgs=( "${PTXDIST_PLATFORMCONFIG}" )
	else
		cfgs=()
	fi
	if [ "${#cfgs[*]}" -gt 0 ]; then
		PTXDIST_PLATFORMCONFIG="${PTXDIST_LAYERS[0]}/${cfgs[0]}"
		for layer in "${PTXDIST_LAYERS[@]}"; do
			local tmp=( ${cfgs[@]/#/${layer}/} )
			if [ "${#tmp[@]}" -gt 2 ]; then
				tmp=( "${tmp[0]}" )
				cfgs=( "${cfgs[0]}" )
			fi
			local -a ptxd_reply
			ptxd_get_path_filtered "${tmp[@]}" || continue
			PTXDIST_PLATFORMCONFIG="${ptxd_reply[0]}"
			break
		done
	fi

	if [ "${PTX_collectionconfig_SET}" = "false" ]; then
		cfgs=( "selected_collectionconfig" )
	elif ! [[ "${PTXDIST_COLLECTIONCONFIG}" =~ ^/.* ]]; then
		cfgs=( "${PTXDIST_COLLECTIONCONFIG}" )
	else
		cfgs=()
	fi
	if [ "${#cfgs[*]}" -gt 0 ]; then
		for layer in "${PTXDIST_LAYERS[@]}"; do
			local -a ptxd_reply
			ptxd_get_path_filtered "${cfgs[@]/#/${layer}/}" || continue
			PTXDIST_COLLECTIONCONFIG="${ptxd_reply[0]}"
			break
		done
	fi

	if [ "${PTX_toolchain_SET}" = "false" ]; then
		for layer in "${PTXDIST_LAYERS[@]}"; do
			local -a ptxd_reply
			ptxd_get_path_filtered "${layer}/selected_toolchain" || continue
			PTXDIST_TOOLCHAIN="${ptxd_reply[0]}"
			break
		done
		if [ -z "${PTXDIST_TOOLCHAIN}" ]; then
			local -a toolchain
			if do_select_toolchain_guess 2>/dev/null; then
				PTXDIST_TOOLCHAIN="${toolchain}"
			fi
		fi
	fi
}


##################################################################
################ do_* ############################################
##################################################################

#
# calls menu/old config on several components
#
# $1	what kind of config ("menuconfig", "oldconfig", "all*config", "randconfig")
# $2	what to "config"
# $...	optional parameters
#
do_config()
{
	local config="${1}"
	local part="${2}"

	if [ "${part}" = "platform" ] && ptxd_get_ptxconf PTXCONF_NO_PLATFORM >/dev/null; then
		return
	elif [ "${part}" != "setup" -a "${part}" != "localsetup" ]; then
		check_config || return
	fi

	case "${config}" in
	menuconfig|nconfig|oldconfig|all*config|randconfig)
		;;
	*)
		echo
		echo "${PTXDIST_LOG_PROMPT}error: invalid use of '${FUNCNAME} ${*}'"
		echo
		exit 1
		;;
	esac

	case "${part}" in
	"ptx"|"ptxdist"|"")
		PTXDIST_DEP_TARGET="all" ptxd_kconfig "${config}" "ptx"
		;;
	"platform"|"board"|"setup"|"localsetup")
		PTXDIST_DEP_TARGET="all" ptxd_kconfig "${config}" "${part}"
		;;
	"collection")
		local file_dotconfig="${3}"

		if [ -n "${file_dotconfig}" ]; then
			file_dotconfig="$(ptxd_abspath "${file_dotconfig}")" || exit 1
		else
			file_dotconfig="${PTXDIST_COLLECTIONCONFIG}"
			if [ ! -e "${file_dotconfig}" ]; then
				ptxd_dialog_msgbox \
					"error: no collectionconfig selected, please try\n" \
					"	'ptxdist collection <collectionconfig>' first, or\n" \
					"	'ptxdist menuconfig collection <collectionconfig>' to edit a specific collection."
				return 1;
			fi
		fi

		check_premake_compiler &&
		ptxd_kconfig "${config}" "${part}" "${file_dotconfig}"
		;;
	*)
		if [ "${config}" != "oldconfig" -o "${part}" != "all" ]; then
			check_if_selected "${part}"
		fi &&

		ptxd_dialog_infobox "${PTXDIST_LOG_PROMPT}Checking dependencies. This may take some seconds." &&

		check_premake_compiler &&
		ptxd_make "${part}_${config}"
		;;
	esac

	local retval=${?}
	if [ ${retval} -ne 0 ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}'${config}${part:+ }${part}' returned with an error"
		if [ -z "${PTXDIST_FORCE}" ]; then
			echo "${PTXDIST_LOG_PROMPT}for warnings, --force can be used to continue anyways"
		fi
		echo
		if [ -n "${PTX_MENU}" ]; then
			read
		fi
	fi

	return ${retval}
}


#
# do_select:
#
# select a configfile, do sanity checks, etc
#
# ${1}: type of config file {ptxconfig,platformconfig,collectionconfig}
# ${2}: the actual config file
#
do_select()
{
	local type="${1}"
	local file="${2}"
	local dest_ptr="PTXDIST_$(echo "${type}" | tr 'a-z' 'A-Z')_DEFAULT"
	local dest_file="$(readlink -f "${!dest_ptr}")"
	local magic="PTXCONF__${type}_MAGIC__=y"

	check_nonstandard "${type}" || return

	if [ ! -f "${file}" ]; then
		ptxd_dialog_msgbox \
			"error: cannot select\n" \
			"	'${file}'\n" \
			"	does not exist, or is not a file."
		return 1
	fi

	# check if magic is present in config file
	if ! grep -E -q "^${magic}$" "${file}" && [ -z "${PTXDIST_FORCE}" ]; then
		ptxd_dialog_msgbox \
			"error: Couldn't verify that\n" \
			"	'${file}'\n" \
			"	is a valid ${type} file.\n" \
			"	If you are absolutely sure, please add '--force'\n" \
			"	to ptxdist's parameters, e.g.:\n" \
			"\n" \
			"	'ptxdist --force ${PTXDIST_ARGS_FULL[*]}'"
		return 1
	fi

	if [ -e "${!dest_ptr}" -a ! -L "${!dest_ptr}" ]; then
		ptxd_dialog_msgbox \
			"error: '${!dest_ptr}'\n" \
			"	is not a link. This is mostly not critical, as it usually\n" \
			"	means that ${type} is not selectable in this project."
		return 1
	fi

	ln -sf "${file}" "${!dest_ptr}"
	ptxd_dialog_msgbox \
		"info: selected ${type}:\n" \
		"      '${file}'"

	#
	# update PTXDIST_*CONFIG
	#
	case "${type}" in
	ptxconfig)
		PTXDIST_PTXCONFIG="${!dest_ptr}"
		;;
	platformconfig)
		PTXDIST_PLATFORMCONFIG="${!dest_ptr}"
		if [ "${PTX_toolchain_SET}" != "true" ]; then
			# try to guess toolchain
			do_select_toolchain
		fi
		;;
	collectionconfig)
		PTXDIST_COLLECTIONCONFIG="${!dest_ptr}"
		;;
	*)
		;;
	esac

	#
	# re-read config files and
	# export changed variables
	#
	unset PTXDIST_PLATFORMDIR
	setup_platform &&
	setup_path &&
	setup_export
}



#
# guess and find the toolchain
#
# out: $toolchain: path to toolchain
#
do_select_toolchain_guess()
{
	if [ ! -e "${PTXDIST_PLATFORMCONFIG}" ]; then
		ptxd_dialog_msgbox \
			"error: cannot guess toolchain, no platform selected.\n" \
			"	try 'ptxdist platform <platformconfig>' first\n" \
			"	or use 'ptxdist toolchain </path/to/toolchain>'"
		return 1
	fi

	# minimal requirements
	local vendor="$(ptxd_get_ptxconf PTXCONF_CROSSCHAIN_VENDOR)"
	local target="$(ptxd_get_ptxconf PTXCONF_GNU_TARGET)"
	local gcc_version="$(ptxd_get_ptxconf PTXCONF_CROSSCHAIN_CHECK)"

	if [ -z "${vendor}" -o \
	    -z "${target}" -o \
	    -z "${gcc_version}" ]; then
		ptxd_dialog_msgbox \
			"info: insufficient information in your platformconfig file\n" \
			"      please use 'ptxdist toolchain </path/to/toolchain>' to select your toolchain"
		return 1
	fi

	# gcc
	local gcc="gcc-${gcc_version}-"

	# java
	local java="$(ptxd_get_ptxconf PTXCONF_GCCLIBS_GCJ)"
	java="${java:+java-}"

	# libc
	local glibc_version="$(ptxd_get_ptxconf PTXCONF_GLIBC_VERSION)"
	local uclibc_version="$(ptxd_get_ptxconf PTXCONF_UCLIBC_VERSION)"

	if [ -n "${glibc_version}" ]; then
		libc="glibc-${glibc_version}-"
	elif [ -n "${uclibc_version}" ]; then
		libc="u[cC]libc-${uclibc_version}-"
	else
		libc=""
	fi

	local hint="/opt/${vendor}/${target}/${gcc}${java}*${libc}*/bin"

	# let the shell expand the "*" in the hint, put it into an array
	if ! ptxd_get_path ${hint}; then
	    hint="/opt/${vendor}*/${target}/${gcc}${java}*${libc}*/bin"
	    ptxd_get_path ${hint}
	fi
	toolchain=("${ptxd_reply[@]}")

	# number of items in array == number of found toolchains
	local num="${#ptxd_reply[@]}"

	if [ ${num} -eq 0 ]; then
		ptxd_dialog_msgbox \
			"error: sorry, no toolchain found, matching\n" \
			"	${hint}" \
		        "\n" \
			"      please use 'ptxdist toolchain </path/to/toolchain>' to select your toolchain"

		return 1
	elif [ ${num} -ne 1 ]; then
		local old_ifs="${IFS}"
		IFS="
"
		local list="${toolchain[*]}"
		IFS="${old_ifs}"

		ptxd_dialog_msgbox \
			"warning: more than one toolchain found, matching\n" \
			"	'${hint}':\n\n" \
			"${list}"
	fi
	toolchain="${toolchain[$[num-1]]}"
}


do_select_toolchain() {
	local toolchain="${1}"

	check_nonstandard "toolchain" || return

	#
	# guess the toolchain if path is omitted
	#
	if [ -z "${toolchain}" ]; then
		do_select_toolchain_guess || return
	fi

	if [ ! -d "${toolchain}" ]; then
		ptxd_dialog_msgbox "error: path ${toolchain} does not exist!"
		return 1
	fi

	ptxd_dialog_msgbox \
		"found and using toolchain:\n" \
		"'${toolchain}'"

	if [ -L "${PTXDIST_TOOLCHAIN_DEFAULT}" ]; then
		rm -f -- "${PTXDIST_TOOLCHAIN_DEFAULT}"
	elif [ -e "${PTXDIST_TOOLCHAIN_DEFAULT}" ]; then
		ptxd_dialog_msgbox \
			"error: There is a '${PTXDIST_TOOLCHAIN#${PTXDIST_WORKSPACE}/}' in this directory which is no link.\n" \
			"	This should never happen, please contact the\n" \
			"	Pengutronix Department of Illegal File Removement."
		exit 1
	fi &&

	ln -sf "${toolchain}" "${PTXDIST_TOOLCHAIN_DEFAULT}"
}



#
# migrate _all_ config files, i.e. run oldconfig on them
#
do_migrate()
{
	local PTXDIST_FORCE=true

	local part
	for part in ptx platform; do
		do_config oldconfig "${part}" || return
	done
	if [ -e "${PTXDIST_COLLECTIONCONFIG}" ]; then
		do_config oldconfig collection
	fi
}



#
#
#
do_go()
{
	check_premake_compiler &&
	ptxd_make_log world
}



#
#
#
do_images()
{
	check_premake_compiler &&
	ptxd_make_log images
}

##################################################################
################ normal option parser ############################
##################################################################

parse_second()
{
	#
	# use args from first stage parser, prepared for us
	#
	set -- "${PTXDIST_ARGS_SECOND[@]}"

	if [ ${#} -eq 0 ]; then
		usage
		exit
	fi

	while [ ${#} -ne 0 ]; do
		local cmd="${1}"
		shift

		case "${cmd}" in
######## --*
		--version|version)
			echo "${PTXDIST_VERSION_FULL}"
			exit
			;;

		--help|help)
			man -l ${PTXDIST_TOPDIR}/man/ptxdist.1.gz
			exit
			;;

######## standard target, directly into make

		prepare|compile|install|targetinstall|tags|cargosync)
			PTXDIST_OPTIMIZE_IO=true
			;&  # fallthrough
		extract)
			local cmd_post
			declare -a pkgs

			check_premake_compiler &&
			check_if_selected "${@}" &&

			case "${cmd}" in
			    install)
				cmd_post=".post"
				;;
			    targetinstall)
				cmd_post=".post"
				check_${cmd}_pkgs "${@}"
				;;
			esac &&

			pkgs=( "${@/#/${STATEDIR}/}" ) &&
			ptxd_make_log "${pkgs[@]/%/.${cmd}${cmd_post}}"
			exit
			;;

######## *config, *setup, toolchain

		oldconfig)
			if [ ${#} -eq 1 -a "${1}" = "all" ]; then
				set -- ptx platform all
			fi
			if [ ${#} -eq 0 ]; then
				set -- "ptx"
			fi
			for part in "${@}"; do
				do_config "${cmd}" "${part}"
			done
			exit
			;;
		menuconfig|nconfig|allmodconfig|allyesconfig|allnoconfig|alldefconfig|randconfig)
			do_config "${cmd}" "${@}"
			exit
			;;

		platformconfig)
			do_config menuconfig platform
			exit ${?}
			;;

		kernelconfig)
			do_config menuconfig kernel
			exit
			;;

		boardsetup)
			check_config &&
			do_config menuconfig board
			exit
			;;

		setup)
			do_config menuconfig setup
			exit
			;;

		localsetup)
			do_config menuconfig localsetup
			exit
			;;


		select|ptx)
			do_select ptxconfig "${1}"
			exit
			;;

		platform)
			do_select platformconfig "${1}"
			exit ${?}
			;;

		collection)
			do_select collectionconfig "${1}"
			exit
			;;

		toolchain)
			do_select_toolchain "${1}"
			exit
			;;

		migrate)
			do_migrate
			exit
			;;
######## the rest of it

		moo)
			local cow="555555554994D555555516X65155558664D555555511XT51D555555TT555P5P5D55555I15555515S5D5555T2PX555XT2PD5555PXXXS7IXXXT"
			echo "${cow}" | tr "PTXDIST123456789" "\\\\\ _\\n\\(\\)/|\`\'. o=M-"
			exit
			;;

		bash)
			check_premake_compiler &&
			if [ ${#} -eq 0 ]; then
				local bashrc="${PTXDIST_TEMPDIR}/bashrc"
				cat > "${bashrc}" <<EOF
. /etc/bash.bashrc
. ~/.bashrc
PS1="[ptx] \${PS1}"
PATH="${PATH}"
alias ptxsudo='sudo env PATH=$PATH'
EOF
				"${BASH}" --init-file "${bashrc}"
			else
				"${@}"
			fi
			exit
			;;
		bsp-info)
			check_config &&
			check_deps &&
			PTXDIST_ICECC="" check_compiler &&
			ptxd_make_log bsp-info
			;;
		clean)
			check_config &&
			check_deps &&
			clean "${@}"
			exit
			;;
		distclean)
			setup_logfile &&
			clean

			echo "${PTXDIST_LOG_PROMPT}removing configuration links..."

			for cfg in \
				"${PTXDIST_PTXCONFIG}" \
				"${PTXDIST_PLATFORMCONFIG}" \
				"${PTXDIST_COLLECTIONCONFIG}" \
				"${PTXDIST_TOOLCHAIN}" \
				; do

				if [ -L "${cfg}" ]; then
					rm -f -- "${cfg}"
				fi
			done

			echo
			exit
			;;
		drop)
			check_config &&
			drop "${1}" "${2}"
			exit
			;;
		docs-*)
			create_docs "${cmd#docs-}" &&
			exit
			;;
		export_src|export-src)
			if [ -z "${1}" ]; then
				echo
				echo "${PTXDIST_LOG_PROMPT}error: Please specify a target directory."
				echo
				exit 1
			fi
			if [ ! -d "${1}" ]; then
				echo
				echo "${PTXDIST_LOG_PROMPT}error: directory '${1}' does not exist!"
				echo
				exit 1
			fi
			check_premake &&
			PTXDIST_ICECC="" check_compiler &&
			ptxd_make_log export_src EXPORTDIR="${1}"
			exit
			;;
		fast-bsp-report|full-bsp-report)
			check_premake_compiler &&
			ptxd_make_log "${cmd}"
			exit
			;;
		gdb|cgdb)
			local -a prefix
			if [ "${cmd}" = "cgdb" ]; then
				if [ ! -x "$(which cgdb 2>/dev/null)" ]; then
					ptxd_bailout "cgdb is required to be installed on your host-machine."
				fi
				prefix=( cgdb -d )
			fi
			check_premake_compiler &&
			compiler_prefix="$(ptxd_get_ptxconf PTXCONF_COMPILER_PREFIX)" &&
			ptxdist_trap_exit_handler &&
			exec "${prefix[@]}" "${PTXDIST_PLATFORMDIR}/sysroot-host/lib/wrapper/${compiler_prefix}gdb" "${@}"
			;;
		get|urlcheck)
			declare -a pkgs
			check_premake_compiler &&

			if [ ${#} -eq 0 ]; then
				ptxd_make_log "${cmd}"
			else
				check_if_selected "${@}" &&
				pkgs=( "${@/#/${STATEDIR}/}" ) &&
				ptxd_make_log "${pkgs[@]/%/.${cmd}}"
			fi
			exit
			;;
		getdev)
			check_premake_compiler &&
			ptxd_make_log getdev
			exit
			;;
		go|images)
			PTXDIST_OPTIMIZE_IO=true
			do_${cmd}
			exit
			;;
		image)
			PTXDIST_OPTIMIZE_IO=true
			if [ ${#} -eq 0 ]; then
				echo "No image given."
				exit 1
			fi
			check_premake_compiler &&
			images=( "${@/#/${IMAGEDIR}/}" ) &&
			ptxd_make_log "${images[@]}"
			exit
			;;
		licensecheck)
			check_premake_compiler &&
			if [ ${#} -eq 0 ]; then
				ptxd_make_log license-check
			else
				check_if_selected "${@}" &&
				pkgs=( "${@/#/${STATEDIR}/}" ) &&
				ptxd_make_log "${pkgs[@]/%/.report}"
			fi
			exit
			;;
		lint)
			export PTXDIST_GEN_ALL=1
			check_premake_compiler &&
			ptxd_make_log ptxdist-lint &&
			if [ -e "${PTXDIST_TEMPDIR}/lint-failed" ]; then
				ptxd_bailout "lint checks failed"
			fi
			;;
		list-packages)
			check_config &&
			check_deps &&
			PTXDIST_ICECC="" check_compiler || return
			for i in $(ptxd_make "/print-PACKAGES"); do
				echo $i
			done | sort
			;;
		local-src)
			check_config &&
			ptxd_lib_local_src "${@}"
			exit
			;;
		make)
			check_premake_compiler &&
			ptxd_make_log "${@}"
			exit
			;;
		menu)
			if [ ! -x "$(which dialog 2>/dev/null)" ]; then
				echo
				echo "${PTXDIST_LOG_PROMPT}info: Sorry, menu not possible yet. Please install 'dialog'"
				echo
				exit 1
			fi
			PTX_MENU=true
			while menu; do true; done
			exit
			;;

		newpackage)
			if [ ! -d "rules" -a "${1}" != "help" ]; then
				echo
				echo "${PTXDIST_LOG_PROMPT}error: no rules/ directory found"
				echo "${PTXDIST_LOG_PROMPT}error: please call from a workspace or ptxdist directory"
				echo
				exit 1
			fi
			ptxd_template_new "${1}"
			exit
			;;
		nfsroot)
			PTXDIST_OPTIMIZE_IO=true
			check_premake_compiler &&
			ptxd_make_log ptxd_make_nfsd
			exit
			;;
		package-info)
			declare -a pkgs

			if [ ${#} -eq 0 ]; then
				echo "Usage: package-info <packages...>"
				exit 1
			fi

			check_config &&
			check_deps &&
			PTXDIST_ICECC="" check_compiler &&
			pkgs=( "${@/#/${STATEDIR}/}" ) &&
			ptxd_make_log "${pkgs[@]/%/.${cmd}}"
			exit
			;;
		printnext)
			make_vars=( "${@}" )
			set --
			;&
		print)
			local tmpfd
			if [ ${#} -eq 0 -a ${#make_vars[*]} -eq 0 ]; then
				exit 1
			fi
			exec {tmpfd}>&1
			while [ ${#} -gt 0 ]; do
				if [[ ! ( "${1}" =~ "/" ) ]] && ([ -n "${!1+set}" ]) 2>/dev/null; then
					if [ "${PTXDIST_VERBOSE}" = "1" ]; then
						echo -n "${1}="
					fi
					echo "${!1}"
				else
					make_vars[${#make_vars[*]}]="${1}"
				fi
				shift
			done
			if [ ${#make_vars[*]} -gt 0 ]; then
				local prefix
				check_config &&
				check_deps || exit 1
				# setup toolchain without icecc, but allow failures
				PTXDIST_ICECC="" check_compiler
				if [ "${cmd}" = "print" ]; then
					prefix="/print-"
				else
					prefix="/printnext-"
				fi
				ptxd_make_log "${make_vars[@]/#/${prefix}}" 2>&1 1>&${tmpfd} | \
					sed 's/^.*##\(.*\)##.*$/\1 undefined/' >&2
				check_pipe_status || exit 1
			fi
			exit
			;;
		test)
			check_config || return

			if [ -z "${1}" ]; then
			#	echo "No test given. try ptxdist test help for a list of available tests"
				echo "No test given."
				exit 1
			fi
			#if [ "${1}" = help ]; then
			#	# FIXME
			#	echo "available tests:"
			#	find ${PTXDIST_WORKSPACE}/tests -maxdepth 1 -type f -exec basename {} \;
			#	exit
			#fi

			for tst in \
				"${PTXDIST_WORKSPACE}/tests/${1}${PTXDIST_PLATFORMSUFFIX}" \
				"${PTXDIST_WORKSPACE}/tests/${1}" \
				"${PTXDIST_PLATFORMCONFIGDIR}/tests/${1}${PTXDIST_PLATFORMSUFFIX}" \
				"${PTXDIST_PLATFORMCONFIGDIR}/tests/${1}" \
				"${PTXDIST_TOPDIR}/tests/${1}${PTXDIST_PLATFORMSUFFIX}" \
				"${PTXDIST_TOPDIR}/tests/${1}" \
				; do \
				if [ -x "${tst}" ]; then
					echo -e "=====\n$(date): Starting test '${tst}'\n=====\n" >> "${PTXDIST_PLATFORMDIR}/test.log"
					if [ "${PTX_DEBUG}" != "true" ]; then
						"${tst}" >> "${PTXDIST_PLATFORMDIR}/test.log"
					else
						"${tst}" | tee -a "${PTXDIST_PLATFORMDIR}/test.log"
					fi
					exit ${?}
				fi
			done

			echo
			echo "${PTXDIST_LOG_PROMPT}error: test '${1}' not found in PTXDIST_TOPDIR and PTXDIST_WORKSPACE"
			echo
			exit 1
			;;
		*)
			echo "ptxdist: '${cmd}' is not a PTXdist command."
			echo
			echo "Run '${0}' for a list of commands and options or '${0} --help' for more details."
			echo
			exit 1
			;;
		esac

	done
}



##################################################################
################ setup stuff only ################################
##################################################################

#
# bash implementation of realpath / readlink -f
#   arg1 - filename
_realpath()
{
	local fname oldfname

	if type -p realpath > /dev/null; then
		realpath "${@}"
		return
	fi

	fname="${1%/}" # strips trailing '/'
	while [ -L "${fname}" ]; do
		oldfname="${fname}"
		fname="$(readlink "${fname}")"
		if [ "${fname}" = "." ] ; then
			fname="$(dirname "${oldfname}")"
		elif [ "${fname}" = "${fname#/}" ]; then
			fname="$(dirname "${oldfname}")/${fname}"
		fi
	done

	(cd "$(dirname "${fname}")"; echo $(pwd -P)/$(basename "${fname}"))
}

#
# figure out PTXDIST_TOPDIR
# this is where the ptxdist installation lives
#
# out: PTXDIST
#      PTXDIST_TOPDIR
#      PTXDIST_WORKSPACE
#      PTXDIST_PTXCONFIG_DEFAULT
#      PTXDIST_PLATFORMCONFIG_DEFAULT
#      PTXDIST_COLLECTIONCONFIG_DEFAULT
#      PTXDIST_TOOLCHAIN_DEFAULT
#
setup_topdir() {
	local ptxdist topdir

	ptxdist="$(_realpath "${0}")" &&
	topdir="$(cd "$(dirname "${ptxdist}")"/.. && pwd)" || return

	#
	# sanity check: is PTXdist already configured?
	#
	if [ ! -e "${topdir}/.done" ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}error: PTXdist in ${topdir} is not built."
		echo
		exit 1
	fi

	PTXDIST="${ptxdist}"
	PTXDIST_TOPDIR="${topdir}"
	PTXDIST_WORKSPACE="$(pwd)"

	eval PTXDIST_PTXCONFIG_DEFAULT="${PTXDIST_PTXCONFIG_DEFAULT}"
	eval PTXDIST_PLATFORMCONFIG_DEFAULT="${PTXDIST_PLATFORMCONFIG_DEFAULT}"
	eval PTXDIST_COLLECTIONCONFIG_DEFAULT="${PTXDIST_COLLECTIONCONFIG_DEFAULT}"
	eval PTXDIST_TOOLCHAIN_DEFAULT="${PTXDIST_TOOLCHAIN_DEFAULT}"

	# ${PTXDIST_TOPDIR}/bin contains links to sane tools found by configure
	export PATH="${PTXDIST_TOPDIR}/bin:${PATH}"
}


ensure_ptxrc() {
	local ptxrc tmpptxrc
	local rc_default="${PTXDIST_TOPDIR}/config/setup/ptxdistrc.default"
	local include

	# ptxd_source_kconfig is not defined during early bootstrap
	if [ "${1}" != "early" ]; then
		include=ptxd_source_kconfig
	else
		include=source
	fi

	#
	# source default values first, let the user overwrite them
	#
	"${include}" "${rc_default}" || return

	# if it's explicitly specified just use it without any magic
	if [ -n "${PTXDIST_PTXRC}" ]; then
		if [ -e "${PTXDIST_PTXRC}" ]; then
			"${include}" "${PTXDIST_PTXRC}"
		fi
		return
	fi

	eval ptxrc="${PTXDIST_PTXRC_DEFAULT}"

	if [ ! -e "${ptxrc}" ]; then
		local tmp_year="${PTXDIST_VERSION_YEAR}"
		local tmp_month="${PTXDIST_VERSION_MONTH}"
		while [ "${tmp_year}" -ge 2010 ]; do
			tmpptxrc="${HOME}/.ptxdist/ptxdistrc-${tmp_year}.${tmp_month}"
			if [ -e "${tmpptxrc}" ]; then
				break
			fi
			if [ "${tmp_month}" = "01" ]; then
				tmp_month=12
				tmp_year=$[tmp_year-1]
			else
				tmp_month=${tmp_month#0}
				tmp_month="$(printf "%02d" $[tmp_month-1])"
			fi
		done
		if [ ! -e "${tmpptxrc}" ]; then
			tmpptxrc="${rc_default}"
		fi
	else
		tmpptxrc="${ptxrc}"
	fi

	if [ -e "${tmpptxrc}.diff" ]; then
		"${include}" <(grep -vE '^[a-f0-9]{32}$' "${tmpptxrc}.diff")
	else
		"${include}" "${tmpptxrc}"
	fi

	PTXDIST_LOCAL_PTXRC="${PTXDIST_WORKSPACE}/.ptxdistrc"
	if [ -e "${PTXDIST_LOCAL_PTXRC}.diff" ]; then
		# explicitly unset variables that may have been set in the include above
		"${include}" <(grep -vE '^[a-f0-9]{32}$' "${PTXDIST_LOCAL_PTXRC}.diff" | sed 's/^# \(.*\) is not set$/unset \1/')
	elif [ -e "${PTXDIST_LOCAL_PTXRC}" ]; then
		"${include}" "${PTXDIST_LOCAL_PTXRC}"
	fi

	if [ "${1}" = "final" ]; then
		PTXDIST_PTXRC="${ptxrc}"
		if [ ! -e "${ptxrc}" ]; then
			mkdir -p -- "$(dirname "${ptxrc}")" &&
			cp -- "${tmpptxrc}" "${ptxrc}"
		fi
	fi
}

#
# cleanup environment
#
#
setup_env() {
	#
	# here an ugly hard coded value:
	#
	source "${PTXDIST_TOPDIR}/scripts/ptxdist_version.sh" || return

	# needed for PTXCONF_SETUP_ENV_WHITELIST
	ensure_ptxrc early || return

	# let shell split by IFS
	set -- ${PTXCONF_SETUP_ENV_WHITELIST} ${PTXDIST_ENV_WHITELIST} PTXDIST_ENV_WHITELIST
	whitelist="${*}"
	whitelist="${whitelist:+|}${whitelist// /|}"

	# Any change here should be done in ptxd_make_nested_ptxdist() and possibly run_in_container() as well
	unset $({
		export -p  | sed -n 's/^declare -x \([^=]*\).*$/\1/p'
		export -fp | sed -n 's/^declare -fx \([^=]*\).*$/\1/p'
		} | grep -E -v "^(PTXDIST_PTXRC|PTX_AUTOBUILD_DESTDIR|PTXDIST_PLATFORMDIR|CCACHE_.*|PWD|HOME|USER|PATH|TERM|COLUMNS|LINES|DISPLAY|TMPDIR|KCONFIG_ALLCONFIG|KCONFIG_SEED|http_proxy|https_proxy|ftp_proxy|no_proxy${whitelist})$")

	######## the environment is clean now ########

	export LANG=C
	export LC_ALL=POSIX
}


#
# deletes ptxdist's temporary storage
# closes logfile
#
# in: PTXDIST_TEMPDIR
# in: PTX_LOGFILE
#
ptxdist_trap_exit_handler() {
	local retval="${?}"
	local file

	if [ -n "${PTXDIST_TEMPDIR}" -a -d "${PTXDIST_TEMPDIR}" ]; then
		rm -fr -- "${PTXDIST_TEMPDIR}"
	fi

	if [ -e "${PTX_LOGFILE}" ]; then
		#
		# use these quotes to keep Enrik's editor happy
		#	   \\
		#	    VV
		echo -e "\n}""}} $(date '+%FT%T%z') ${PTXDIST} ${PTXDIST_ARGS_FULL[*]}; (exit value: ${retval})\n\n\n" >> "${PTX_LOGFILE}"
	fi
}


#
# setups trap, to delete temporary storage
#
setup_traps() {
	trap 'ptxdist_trap_exit_handler' 0 1 15
}


#
# source the scripts we need
#
# we need the PTXdist shell library
# we need the version definitions
# we need the static variable definitions
#
# out: "ptxd_*"		library calls
#      "*"		ptxdist version variables
#      "*DIR"		directory definitions (some not correct, due to missing PTXDIST_PLATFORMDIR)
#      PTXDIST_TEMPDIR	generic ptxdist temp dir
#
setup_libs_early() {
	local file abs_file

	for file in \
		scripts/ptxdist_vars.sh \
		scripts/libptxdist.sh \
	; do
		abs_file="${PTXDIST_TOPDIR}/${file}"
		if [ -e "${abs_file}" ]; then
			source "${abs_file}" || return
		else
			echo "${PTXDIST_LOG_PROMPT}FATAL didn't find ${abs_file}"
			exit 1
		fi
	done

	PTXDIST_TEMPDIR="$(mktemp -t -d ptxdist.XXXXXX)"
	if [ ${?} -ne 0 ]; then
		echo
		echo "${PTXDIST_LOG_PROMPT}error: unable to create tempdir"
		echo
		exit 1
	fi
}


#
# source the user's .ptxdistrc
# or default one
# setup PTXDIST_SRCDIR
#
# out: PTXCONF_*	user preferences
#      PTXDIST_SRCDIR
#      PTXDIST_PTXRC
#
setup_config() {
	ensure_ptxrc final || return

	#
	# enable or disable errexit
	#
	if [ -n "${PTXCONF_SETUP_CHECK_EXIT_ON_ERROR}" ]; then
		set -e
	else
		set +e
	fi


	#
	# setup SRCDIR
	#
	if [ -z "${PTXCONF_SETUP_SRCDIR}" ]; then
		PTXDIST_SRCDIR="${PTXDIST_WORKSPACE}/src"
	else
		eval PTXDIST_SRCDIR="${PTXCONF_SETUP_SRCDIR}"
	fi


	#
	# setup proxy
	#
	if [ -n "${PTXCONF_SETUP_HTTP_PROXY}" ]; then
		export http_proxy="${PTXCONF_SETUP_HTTP_PROXY}"
	fi

	if [ -n "${PTXCONF_SETUP_HTTPS_PROXY}" ]; then
		export https_proxy="${PTXCONF_SETUP_HTTPS_PROXY}"
	fi

	if [ -n "${PTXCONF_SETUP_FTP_PROXY}" ]; then
		export ftp_proxy="${PTXCONF_SETUP_FTP_PROXY}"
	fi

	if [ -n "${PTXCONF_SETUP_NO_PROXY}" ]; then
		export no_proxy="${PTXCONF_SETUP_NO_PROXY}"
	fi
}

#
# setup parallel building
#
# out: PTXDIST_PARALLELMFLAGS
#      PTXDIST_PARALLELMFLAGS_INTERN
#      PTXDIST_PARALLELMFLAGS_EXTERN
#      PTXDIST_OUTPUT_SYNC
#      PTXDIST_FD_LOGERR
#
setup_parallel() {
	# default no parallel for now
	local pmf_extern=""
	local pmf_intern=""
	local cpus
	local output_sync=""

	# this one is for Linux
	if [ -r /proc/cpuinfo ]; then
		cpus="$(grep -E '^(processor|cpu	)' /proc/cpuinfo | wc -l)"
		if [ ${cpus} -eq 0 ]; then
			cpus=1
		fi
	# and this one is tested on Darwin and should work on BSDs
	elif ! cpus="$(sysctl -n hw.ncpu)" 2> /dev/null; then
		cpus=1
	fi
	local pmf_cpus="-j$(( ${cpus} * 2 ))"

	if [ -z "${PTXDIST_PARALLELMFLAGS}" ]; then
		pmf_intern="${pmf_cpus}"
		pmf_extern="-j1"
		unset PTXDIST_JOBSERVER_AUTH
	elif [ "${PTXDIST_PARALLELMFLAGS}" == "-j1" ]; then
		unset PTXDIST_PARALLELMFLAGS
		pmf_intern="-j1"
		pmf_extern="-j1"
	elif [ "${PTXDIST_PARALLELMFLAGS}" == "auto" ]; then
		PTXDIST_PARALLELMFLAGS="${pmf_cpus}"
	fi

	PTXDIST_OUTPUT_SYNC="${PTXDIST_OUTPUT_SYNC:-${PTXDIST_QUIET}}"
	PTXDIST_FD_LOGERR=2
	if [ "${PTXDIST_OUTPUT_SYNC}" == "1" ]; then
		if "${PTXCONF_SETUP_HOST_MAKE}" -h | grep -q -- --output-sync; then
			PTXDIST_OUTPUT_SYNC="--output-sync="
		fi
		PTXDIST_FD_LOGERR=1
	fi
	if [[ "${PTXDIST_OUTPUT_SYNC}" =~ [01] ]]; then
		unset PTXDIST_OUTPUT_SYNC
	fi

	#
	# user may override these, via cmdline
	#
	PTXDIST_PARALLELMFLAGS_INTERN="${PTXDIST_PARALLELMFLAGS_INTERN:-${pmf_intern}}"
	PTXDIST_PARALLELMFLAGS_EXTERN="${PTXDIST_PARALLELMFLAGS_EXTERN:-${pmf_extern}}"
	export PTXDIST_PARALLELMFLAGS_EXTERN PTXDIST_OUTPUT_SYNC PTXDIST_FD_LOGERR
}


setup_auto_version()
{
	local configfile_version next saved_ptxconfig start_container
	local -a args

	ptxd_check_in_container

	if [ -z "${PTXDIST_AUTOVERSION}"  -a -z "${start_container}" ]; then
		return
	fi

	set -- "${PTXDIST_ARGS_FULL[@]}"
	while [ ${#} -ne 0 ]; do
		local arg="${1}"
		shift
		case "${arg}" in
		--auto-version) ;;
		*) args[${#args[@]}]="${arg}" ;;
		esac
	done

	case "${PTXDIST_AUTOVERSION}" in
	"")
		ptxd_run_in_container "ptxdist-${PTXDIST_VERSION_FULL}" "${args[@]}"
		;;
	"${PTXDIST_TOPDIR}/bin/ptxdist")
		# this is already the correct ptxdist
		if [ -z "${start_container}" ]; then
			return
		fi
		# fallthrough
		;&
	/*)
		# use ptxdist from the explicit PTXdist layer
		ptxd_run_in_container "${PTXDIST_AUTOVERSION}" "${args[@]}"
		;;
	esac

	file_dotconfig="${PTXDIST_PTXCONFIG}"
	ptxd_normalize_config
	if ! ptxd_get_path_filtered "${PTXDIST_LAYERS[@]/%//${relative_file_dotconfig}}"; then
		# no config yet, so this is either 'select' or fails later anyways
		return
	fi
	# the bottom layer decides the version
	saved_ptxconfig="${PTXDIST_PTXCONFIG}"
	PTXDIST_PTXCONFIG="${ptxd_reply[${#ptxd_reply[@]}-1]}"

	_get_config_ptx || return
	PTXDIST_PTXCONFIG="${saved_ptxconfig}"
	if PTXDIST_FORCE= check_version "${configfile_version}"; then
		# this is already the correct version
		if [ -n "${start_container}" ]; then
			ptxd_run_in_container "${0}" "${args[@]}"
		fi
		return
	fi

	if [ -z "${start_container}" ]; then
		if ! next="$(which ptxdist-${configfile_version})"; then
			ptxd_bailout "ptxdist-${configfile_version} not found!"
		fi
	else
		# assume that the correct PTXdist version is installed in the container
		next="ptxdist-${configfile_version}"
	fi
	ptxd_run_in_container "${next}" "${args[@]}"
}



#
# this function defines which args are allowed in case of a broken
# config during "setup_platform"
#
setup_platform_continue_with_broken_config()
{
	set -- "${PTXDIST_ARGS_FULL[@]}"
	while [ ${#} -ne 0 ]; do
		local arg="${1}"
		shift

		case "${arg}" in
		menuconfig|nconfig|select) return ;;
		*) return 1 ;;
		esac
	done
}



#
# setup PTXDIST_PLATFORMDIR properly
#
# out: PTXDIST_PLATFORMDIR
#      PTXDIST_PLATFORMSUFFIX
#      PTXDIST_PLATFORMCONFIGDIR
#      PTXDIST_PLATFORMCONFIG_SUBDIR
#      "*DIR"	correct directory definitions
#
setup_platform() {
	local platform platform_version project project_version old_platformdir

	# ptxd_get_ptxconf's return value is
	platform="$(ptxd_get_ptxconf PTXCONF_PLATFORM)"				|| [ ${?} -eq 1 ] &&
	project="$(ptxd_get_ptxconf PTXCONF_PROJECT)"				|| [ ${?} -eq 1 ] &&
	project_version="$(ptxd_get_ptxconf PTXCONF_PROJECT_VERSION)"		|| [ ${?} -eq 1 ] || return

	# remove space and "(" ")"
	project_version="${project_version//[ \(\)]/_}"

	old_platformdir="${PTXDIST_PLATFORMDIR}"
	unset PTXDIST_PLATFORMDIR
	if [ -n "${platform}" ]; then
		PTXDIST_PLATFORMDIR="${PTXDIST_WORKSPACE}/platform-${platform}${platform_version}"
		PTXDIST_PLATFORMSUFFIX=".${platform}"
	else
		PTXDIST_PLATFORMDIR="${PTXDIST_WORKSPACE}/platform"
		PTXDIST_PLATFORMSUFFIX=""
	fi

	if [ "${old_platformdir}" = "${PTXDIST_PLATFORMDIR}" ]; then
		ptxd_bailout "Nested PTXdist execution for the same platform is not allowed!"
	fi

	# reread vars with correct PTXDIST_PLATFORMDIR
	source "${SCRIPTSDIR}/ptxdist_vars.sh" || return

	local cfg_file tmp_dir cfg_dir rel_cfg_dir
	for cfg_file in \
		"${PTXDIST_PLATFORMCONFIG}" \
		"${PTXDIST_PTXCONFIG}"; do
		[ -e "${cfg_file}" ] || continue

		cfg_file="$(readlink -f "${cfg_file}")" || return
		tmp_dir="$(dirname "${cfg_file}")"
		rel_cfg_dir="${tmp_dir}"
		for layer in "${PTXDIST_LAYERS[@]}"; do
			local tmp="${tmp_dir/#$(readlink -f ${layer})\//}"
			# check the string length to get the shortest relative path
			# this is necessary when a base layer is the parent directory
			if [ "${tmp}" != "${tmp_dir}" -a ${#tmp} -lt ${#rel_cfg_dir} ]; then
				rel_cfg_dir="${tmp}"
				cfg_dir="${layer}/${tmp}"
			fi
		done
		break
	done

	PTXDIST_PLATFORMCONFIGDIR="${cfg_dir:-${PTXDIST_WORKSPACE}}"
	PTXDIST_PLATFORMCONFIG_SUBDIR="${rel_cfg_dir}"

	PTXDIST_BOARDSETUP="${HOME}/.ptxdist/boardsetup.${project}${project_version}.${platform}"
}



#
# source more libs
#
setup_libs() {
	local lib i
	local -a dir
        # check SCRIPTSDIR first because PTXDIST_PATH_SCRIPTS is set there
	for lib in "${SCRIPTSDIR}/lib/ptxd_lib_"*.sh; do
		source "${lib}" || ptxd_bailout "failed to source lib: ${lib}"
	done
	ptxd_in_path PTXDIST_PATH_SCRIPTS || return
	dir=( "${ptxd_reply[@]}" )
	for ((i=$((${#dir[@]}-1)); i>=0; i--)); do
		if [ "${dir[${i}]}" = "${SCRIPTSDIR}" ]; then
			continue
		fi
		ptxd_get_path "${dir[${i}]}/lib/ptxd_lib_"*.sh || continue
		for lib in "${ptxd_reply[@]}"; do
			source "${lib}" || ptxd_bailout "failed to source lib: ${lib}"
		done
	done
}



#
# add PTXDIST_TOOLCHAIN and sysroots to path
#
# out: PATH
#
setup_path() {
	# save PATH and reuse it later on
	PATH="${_ptxdist_setup_path:=${PATH}}"

	if [ -d "${PTXDIST_TOOLCHAIN}" ]; then
		PATH="${PTXDIST_TOOLCHAIN}:${PATH}"
	fi

	# dir might not be available yet, but will be created later
	local sysroot_host="$(ptxd_get_ptxconf PTXCONF_SYSROOT_HOST)"

	if [ -n "${sysroot_host}" ]; then
		PATH="${sysroot_host}/usr/bin:${sysroot_host}/usr/sbin:${PATH}"
	fi

	if [ -n "${PTXCONF_SETUP_CCACHE}" ]; then
		PTXDIST_CCACHE="$(which ccache 2>/dev/null)"
		if [ -z "${PTXDIST_CCACHE}" ]; then
			echo
			echo "${PTXDIST_LOG_PROMPT}warning: ccache has been activated, but was not found on your system"
			echo "${PTXDIST_LOG_PROMPT}warning: install ccache, disable it ('ptxdist setup' -> 'Developer Options' menu)"
			echo "${PTXDIST_LOG_PROMPT}warning: or ignore this warning."
			echo
			sleep 3
		fi
	fi
	if [ -n "${PTXCONF_SETUP_ICECC}" ]; then
		PTXDIST_ICECC="$(which icecc 2>/dev/null)"
		PTXDIST_ICECC_CREATE_ENV="$(which "${PTXCONF_SETUP_ICECC_CREATE_ENV}")"
		PTXDIST_ICERUN="$(which icerun 2>/dev/null)"
		if [ -z "${PTXDIST_ICECC}" -o -z "${PTXDIST_ICECC_CREATE_ENV}" ]; then
			echo
			echo "${PTXDIST_LOG_PROMPT}warning: icecc has been activated, but was not found on your system"
			echo "${PTXDIST_LOG_PROMPT}warning: install icecc, disable it ('ptxdist setup' -> 'Developer Options' menu)"
			echo "${PTXDIST_LOG_PROMPT}warning: or ignore this warning."
			echo "${PTXDIST_LOG_PROMPT}icecc:            '${PTXDIST_ICECC:-not found}'"
			echo "${PTXDIST_LOG_PROMPT}icecc-create-env: '${PTXDIST_ICECC_CREATE_ENV:-not found}'"
			echo
			sleep 3
		fi
	fi
}


#
# start logfile
#
setup_logfile()
{
	if [ -n "${PTX_LOGFILE}" ]; then
		# we were already here
		return
	fi
	PTX_LOGFILE="${PTXDIST_PLATFORMDIR}/logfile"
	local logdir="${PTX_LOGFILE%/*}"

	if [ -e "${logdir}" -a ! -w "${logdir}" -o \
		! -d "${logdir}" ] && ! mkdir -p "${logdir}" 2>/dev/null; then
		#
		# small hack:
		#
		# if we cannot create the logdir and it's outside the
		# workspace, it will be created later with the sudo
		# helper, so don't bailout in this case.
		#
		case "${logdir}" in
		"${PTXDIST_WORKSPACE}"*) ptxd_bailout "cannot create logdir: '${logdir}'" ;;
		*) return ;;
		esac
	fi

	if [ ! -e "${PTX_LOGFILE}" ]; then
		# let emacs outline mode be compatible to vi's fold mode
		echo "# -*- mode:outline; outline-regexp:\"{""{{\" -*-" > "${PTX_LOGFILE}"
		# help vim to recognise the fold markers and expand the first level of folding
		# which in fact is the Emacs setting above
		echo -e "# vim: set fdm=marker fdl=1:\n" >> "${PTX_LOGFILE}"
	fi

	#
	# use these quotes to keep Enrik's editor happy
	#      ||
	#      VV
	echo "{""{{ $(date '+%FT%T%z') ${PTXDIST} ${PTXDIST_ARGS_FULL[*]}" >> "${PTX_LOGFILE}"
	export PTX_LOGFILE
}


#
# export some important vars
# so that they can be used in make
#
setup_export() {
	export \
		PATH \
		\
		PTXDIST \
		PTXDIST_TOPDIR \
		PTXDIST_SRCDIR \
		PTXDIST_TEMPDIR \
		PTXDIST_LIB_DIR \
		PTXDIST_PTXCONFIG \
		PTXDIST_PLATFORMCONFIG \
		PTXDIST_COLLECTIONCONFIG \
		PTXDIST_BOARDSETUP \
		\
		PTXDIST_VERSION_YEAR \
		PTXDIST_VERSION_MONTH \
		PTXDIST_VERSION_BUGFIX \
		\
		PTXDIST_VERSION_SCM \
		PTXDIST_VERSION_FULL \
		\
		PTXDIST_WORKSPACE \
		\
		PTXDIST_PLATFORMDIR \
		PTXDIST_PLATFORMSUFFIX \
		PTXDIST_PLATFORMCONFIGDIR \
		PTXDIST_PLATFORMCONFIG_SUBDIR \
		\
		PTXDIST_PARALLELMFLAGS \
		PTXDIST_PARALLELMFLAGS_INTERN \
		PTXDIST_JOBSERVER_AUTH \
		PTXDIST_LOADMFLAGS \
		\
		PTXDIST_CCACHE \
		PTXDIST_FORCE_DOWNLOAD \
		PTXDIST_ICECC \
		PTXDIST_ICECC_CREATE_ENV \
		PTXDIST_ICERUN \
		PTXDIST_LOG_PROMPT \
		PTXDIST_OUTPUT_SYNC \
		PTXDIST_OPTIMIZE_IO \
		PTXDIST_DIRTY \
		PTXDIST_FORCE \
		PTXDIST_PEDANTIC \
		PTXDIST_QUIET \
		PTXDIST_VERBOSE
}


########################################################################
# main()
########################################################################

main() {
	if [ -n "${PTXDIST_TOPDIR}" ]; then
		ptxd_bailout "Calling PTXdist inside 'ptxdist bash' is not supported."
	fi

	check_uid &&
	check_path &&

	setup_topdir &&
	setup_env &&
	setup_traps &&
	setup_libs_early &&
	# ---  libs are available from here ---

	setup_config &&
	# --- errexit feature may be active ---

	parse_first &&

	setup_layers &&
	# --- vars to config files are setup ---

	setup_parallel &&

	setup_platform &&
	# --- platformdir and other *dirs are available from here ---
	# --- all variables are defined now ---

	setup_libs &&
	setup_path &&
	# --- path is now set ---

	setup_auto_version &&

	setup_export &&
	# -- all important vars are exported

	parse_second
}

main "${@}" || { echo  "PTXdist: fatal error ... cannot start, sorry!"; exit 1; }
