#!/bin/sh
#
# Copyright (c) 2022-2023, Jesús Daniel Colmenares Oviedo <DtxdF@disroot.org>
# 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 the copyright holder 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.

lib_load "${LIBDIR}/check_func"
lib_load "${LIBDIR}/jail"
lib_load "${LIBDIR}/mksum"
lib_load "${LIBDIR}/random"
lib_load "${LIBDIR}/replace"

# Additional options used by the `appjail quick` command.

# The root directory of the first Makejail
MAKEJAIL_ROOTDIR=

# Include
MAKEJAIL_AFTER_INCLUDE=
MAKEJAIL_AFTER_INCLUDED=
MAKEJAIL_BEFORE_INCLUDE=
MAKEJAIL_BEFORE_INCLUDED=

# Build args
MAKEJAIL_BUILD_ARGS=

# Default makejail filename
DEFAULT_MAKEJAIL_FILE="Makejail"

# Apply
MAKEJAIL_APPLY=0

# Errors
MAKEJAIL_ERRORS=1

# Verbose
MAKEJAIL_VERBOSE=0

# Stages dir
MAKEJAIL_STAGESDIR="stages"

# Current stage
MAKEJAIL_CURRENT_STAGE="build"

# Stages
MAKEJAIL_STAGE_APPLY="apply"
MAKEJAIL_STAGE_BUILD="build"
MAKEJAIL_STAGE_PRECREATE="precreate"
MAKEJAIL_STAGE_CREATE="create"
MAKEJAIL_STAGE_POSTCREATE="postcreate"
MAKEJAIL_STAGE_PRESTART="prestart"
MAKEJAIL_STAGE_START="start"
MAKEJAIL_STAGE_POSTSTART="poststart"
MAKEJAIL_STAGE_PRECMD="precmd"
MAKEJAIL_STAGE_CMD="cmd"
MAKEJAIL_STAGE_POSTCMD="postcmd"
MAKEJAIL_STAGE_PRESTOP="prestop"
MAKEJAIL_STAGE_STOP="stop"
MAKEJAIL_STAGE_POSTSTOP="poststop"

# Init stages
MAKEJAIL_INIT_STAGES="\
		${MAKEJAIL_STAGE_APPLY} \
		${MAKEJAIL_STAGE_PRECREATE} \
		${MAKEJAIL_STAGE_CREATE} \
		${MAKEJAIL_STAGE_POSTCREATE} \
		${MAKEJAIL_STAGE_PRESTART} \
		${MAKEJAIL_STAGE_START} \
		${MAKEJAIL_STAGE_POSTSTART} \
		${MAKEJAIL_STAGE_PRECMD} \
		${MAKEJAIL_STAGE_CMD} \
		${MAKEJAIL_STAGE_POSTCMD} \
		${MAKEJAIL_STAGE_PRESTOP} \
		${MAKEJAIL_STAGE_STOP} \
		${MAKEJAIL_STAGE_POSTSTOP}"

# All stages
MAKEJAIL_STAGES="${MAKEJAIL_STAGE_BUILD} ${MAKEJAIL_INIT_STAGES}"

# Commands
MAKEJAIL_CMD_ARG="ARG"
MAKEJAIL_CMD_FROM="FROM"
MAKEJAIL_CMD_INCLUDE="INCLUDE"
MAKEJAIL_CMD_OPTION="OPTION"
MAKEJAIL_CMD_STAGE="STAGE"

# Files
MAKEJAIL_OPTIONS=
MAKEJAIL_TEMPDIR=

makejail_desc="Build a jail using a Makejail file."

makejail_main()
{
	local _o
	# commands
	local opt_create=1 # default
	local opt_delete=0
	local opt_list=0
	local opt_update=0
	# options
	local opt_exact_match=0
	local makejail_file="${DEFAULT_MAKEJAIL_FILE}"
	local repoid=
	# environment
	local environment=

	while getopts ":AcEelva:B:b:d:f:j:o:u:V:" _o; do
		case "${_o}" in
			a|B|b|d|f|j|o|u|V)
				if lib_check_empty "${OPTARG}"; then
					makejail_usage
					exit ${EX_USAGE}
				fi
				;;
		esac

		case "${_o}" in
			A)
				MAKEJAIL_APPLY=1
				;;
			c)
				opt_create=1
				;;
			E)
				opt_exact_match=1
				;;
			e)
				MAKEJAIL_ERRORS=0
				;;
			l)
				opt_list=1
				;;
			v)
				MAKEJAIL_VERBOSE=1
				;;
			a)
				local arg="${OPTARG}"
				arg=`lib_escape_string "${arg}" "" '\"' "-"`

				MAKEJAIL_AFTER_INCLUDE="${MAKEJAIL_AFTER_INCLUDE} \"${arg}\""
				;;
			B)
				local arg="${OPTARG}"
				arg=`lib_escape_string "${arg}" "" '\"' "-"`

				MAKEJAIL_BEFORE_INCLUDE="${MAKEJAIL_BEFORE_INCLUDE} \"${arg}\""
				;;
			b)
				local arg="${OPTARG}"
				arg=`lib_escape_string "${arg}" "" '\"' "-"`

				MAKEJAIL_BUILD_ARGS="${MAKEJAIL_BUILD_ARGS} \"${arg}\""
				;;
			d)
				opt_delete=1
				repoid="${OPTARG}"
				;;
			f)
				makejail_file="${OPTARG}"
				;;
			j)
				MAKEJAIL_JAILNAME="${OPTARG}"
				;;
			o)
				if [ -z "${MAKEJAIL_OPTIONS}" ]; then
					MAKEJAIL_OPTIONS="`lib_generate_tempfile`" || exit $?

					local escape_mkoptions
					escape_mkoptions=`lib_escape_string "${MAKEJAIL_OPTIONS}"`

					lib_atexit_add "rm -f \"${escape_mkoptions}\""
				fi

				local option="${OPTARG}"
				option=`lib_escape_string "${option}"`

				echo "\"${option}\"" >> "${MAKEJAIL_OPTIONS}"
				;;
			u)
				opt_update=1
				repoid="${OPTARG}"
				;;
			V)
				local env="${OPTARG}"
				if ! lib_check_var "${env}"; then
					lib_err ${EX_DATAERR} -- "${env}: Invalid environment variable."
				fi

				local env_name=`lib_jailparam_name "${env}" "="`
				local env_value=`lib_jailparam_value "${env}" "="`

				if lib_check_empty "${env_value}"; then
					lib_err ${EX_DATAERR} -- "${env_name}: this environment variable requires a value."
				fi

				env_value=`"${SCRIPTSDIR}/escape-env-val.sh" "${env_value}" "internal"`

				env="${env_name}=${env_value}"

				if [ -z "${environment}" ]; then
					environment="\\\"${env}\\\""
				else
					environment="${environment} \\\"${env}\\\""
				fi
				;;
			*)
				makejail_usage
				exit ${EX_USAGE}
				;;
		esac
	done
	shift $((OPTIND-1))

	if [ ${opt_delete} -eq 1 ]; then
		local Eflag=
		if [ ${opt_exact_match} -eq 1 ]; then
			Eflag="-E"
		fi

		makejail_delete ${Eflag} "${repoid}"
	elif [ ${opt_list} -eq 1 ]; then
		makejail_list
	elif [ ${opt_update} -eq 1 ]; then
		local Eflag=
		if [ ${opt_exact_match} -eq 1 ]; then
			Eflag="-E"
		fi

		makejail_update ${Eflag} "${repoid}"
	else
		if [ -n "${environment}" ]; then
			_create_tempdir

			local envdir="${MAKEJAIL_TEMPDIR}/env"
			if ! mkdir -p "${envdir}"; then
				lib_err ${EX_IOERR} "Error creating ${envdir}"
			fi

			local stage
			if [ ${MAKEJAIL_APPLY} -eq 1 ]; then
				stage="${MAKEJAIL_STAGE_APPLY}"
			else
				stage="${MAKEJAIL_STAGE_BUILD}"
			fi

			printf "%s" "${environment}" >> "${envdir}/${stage}.tmp"
		fi

		makejail_run "${makejail_file}" "$@"
	fi
}

makejail_delete()
{
	local _o
	local opt_exact_match=0

	if [ $# -eq 0 ]; then
		lib_err ${EX_USAGE} "usage: makejail_delete [-E] repoid"
	fi

	_makejail_check_git # check

	while getopts ":E" _o; do
		case "${_o}" in
			E)
				opt_exact_match=1
				;;
			*)
				makejail_delete # usage
				;;
		esac
	done
	shift $((OPTIND-1))

	local repoid
	repoid="$1"
	if [ -z "${repoid}" ]; then
		makejail_delete # usage
	fi

	if ! lib_check_hexnumber "${repoid}"; then
		lib_err ${EX_DATAERR} -- "${repoid}: invalid ID."
	fi
	
	local use_glob="*"
	if [ ${opt_exact_match} -eq 1 ]; then
		use_glob=
	fi

	mkdir -p -- "${GLOBAL_GIT_CACHEDIR}"

	local match
	match=`find "${GLOBAL_GIT_CACHEDIR}" -name "${repoid}${use_glob}" -mindepth 1 -maxdepth 1 -exec basename {} \;`

	local match_n
	match_n=`echo "${match}" | wc -w`

	if [ ${match_n} -eq 0 ]; then
		lib_err ${EX_NOINPUT} -- "${repoid}: repository not found."
	elif [ ${match_n} -eq 1 ]; then
		lib_debug "Destroying ${match} ..."

		rm -rf -- "${GLOBAL_GIT_CACHEDIR}/${match}"
	else
		lib_err - -- "${repoid}: too many repositories to delete:"

		for repoid in ${match}; do
			local repourl
			repourl=`_makejail_git_geturl "${repoid}"`

			lib_err - "    - ${repoid}: ${repourl}"
		done
		exit ${EX_NOPERM}
	fi
}

makejail_list()
{
	_makejail_check_git # check

	{
		printf "%s\t%s\n" "ID" "URL"

		mkdir -p -- "${GLOBAL_GIT_CACHEDIR}"

		ls -- "${GLOBAL_GIT_CACHEDIR}" | while read -r repoid
		do
			url=`_makejail_git_geturl "${repoid}"`

			printf "%s\t%s\n" "${repoid}" "${url}"
		done 
	} | column -ts $'\t'
}

makejail_update()
{
	local _o
	local opt_exact_match=0
	local opt_match_all=0

	if [ $# -eq 0 ]; then
		lib_err ${EX_USAGE} "usage: makejail_update [-E] [repoid|*]"
	fi

	_makejail_check_git # check

	while getopts ":E" _o; do
		case "${_o}" in
			E)
				opt_exact_match=1
				;;
			*)
				makejail_delete # usage
				;;
		esac
	done
	shift $((OPTIND-1))

	local repoid
	repoid="$1"
	if [ -z "${repoid}" ]; then
		makejail_delete # usage
	fi

	if [ "${repoid}" = "*" ]; then
		opt_match_all=1
		repoid=
	fi

	if [ ${opt_match_all} -eq 0 ] && ! lib_check_hexnumber "${repoid}"; then
		lib_err ${EX_DATAERR} -- "${repoid}: invalid ID."
	fi

	local use_glob="*"
	if [ "${opt_match_all}" -eq 0 ] && [ ${opt_exact_match} -eq 1 ]; then
		use_glob=
	fi

	mkdir -p -- "${GLOBAL_GIT_CACHEDIR}"

	local match
	match=`find "${GLOBAL_GIT_CACHEDIR}" -name "${repoid}${use_glob}" -mindepth 1 -maxdepth 1 -exec basename {} \;`

	local match_n
	match_n=`echo "${match}" | wc -w`

	if [ ${match_n} -eq 0 ]; then
		if [ ${opt_match_all} -eq 0 ]; then
			lib_err ${EX_NOINPUT} -- "${repoid}: repository not found."
		else
			lib_err ${EX_NOINPUT} -- "There is no repositories yet."
		fi
	else
		local current_commit url
		for repoid in ${match}; do
			current_commit=`_makejail_git_getlastcommit "${repoid}"`
			url=`_makejail_git_geturl "${repoid}"`

			lib_set_logprefix " [`random_color`${repoid}${COLOR_DEFAULT}]"

			lib_info "Updating (url = ${url}) ..."

			if ! _makejail_git_update "${GLOBAL_GIT_CACHEDIR}/${repoid}"; then
				lib_err $? "An error occurred while updating."
			fi

			if [ "${current_commit}" = `_makejail_git_getlastcommit "${repoid}"` ]; then
				lib_info "Already up to date."
			else
				lib_info "Updated."
			fi
		done

		lib_info "Done."
	fi
}

_makejail_git_getlastcommit()
{
	local repoid

	repoid="$1"
	if [ -z "${repoid}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_git_getlastcommit repoid"
	fi

	local repopath
	repopath="${GLOBAL_GIT_CACHEDIR}/${repoid}"

	git -C "${repopath}" rev-parse HEAD
}

_makejail_git_geturl()
{
	local repoid

	repoid="$1"
	if [ -z "${repoid}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_git_geturl repoid"
	fi

	local repopath
	repopath="${GLOBAL_GIT_CACHEDIR}/${repoid}"

	git -C "${repopath}" remote get-url origin
}

makejail_run()
{
	local errlevel=0
	local makejail_file

	makejail_file="$1"; shift
	if [ -z "${makejail_file}" ]; then
		lib_err ${EX_USAGE} "usage: makejail_run makejail_file"
	fi

	if [ ${MAKEJAIL_APPLY} -eq 1 ]; then
		if [ -z "${MAKEJAIL_JAILNAME}" ]; then
			makejail_usage
			exit ${EX_USAGE}
		fi

		if ! lib_check_jailname "${MAKEJAIL_JAILNAME}"; then
			lib_err ${EX_DATAERR} "Invalid jail name \"${MAKEJAIL_JAILNAME}\""
		fi

		local jail_path
		jail_path="${JAILDIR}/${MAKEJAIL_JAILNAME}"
		if [ ! -d "${jail_path}/jail" ]; then
			lib_err ${EX_NOINPUT} "Cannot find the jail \`${MAKEJAIL_JAILNAME}\`"
		fi

		if lib_jail_exists "${MAKEJAIL_JAILNAME}"; then
			if ! lib_jail_created_by_appjail "${MAKEJAIL_JAILNAME}"; then
				lib_warn -- "${MAKEJAIL_JAILNAME} was not been created by appjail."
				exit 0
			fi
		fi

		lib_set_logprefix " [`random_color`${MAKEJAIL_JAILNAME}${COLOR_DEFAULT}]"

		lib_info "Applying ${makejail_file} ..."

		_create_tempdir

		makejail_write "${makejail_file}"
	else
		if [ -z "${MAKEJAIL_JAILNAME}" ]; then
			MAKEJAIL_JAILNAME=`random_hexstring 11 0 15 | tr -d '\n'`
		fi

		lib_set_logprefix " [`random_color`${MAKEJAIL_JAILNAME}${COLOR_DEFAULT}]"

		lib_info "Building ..."

		lib_debug "Main Makejail: ${makejail_file}"

		local output
		output="`lib_generate_tempfile`"

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		local escape_output
		escape_output=`lib_escape_string "${output}"`

		lib_atexit_add "rm -f \"${escape_output}\""

		_create_tempdir

		makejail_write "${makejail_file}" > "${output}"
		
		# Print the result
		lib_debug "Buildscript generated:"
		lib_debug_read "${output}"

		local current_pwd
		current_pwd=`pwd -P`

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		local makejail_rootdir
		makejail_rootdir=`head -1 -- "${MAKEJAIL_ROOTDIR}"`

		(cd -- "${makejail_rootdir}"; env \
			APPJAIL_CONFIG="${CONFIG}" \
			APPJAIL_JAILNAME="${MAKEJAIL_JAILNAME}" \
			APPJAIL_JAILDIR="${JAILDIR}/${MAKEJAIL_JAILNAME}/jail" \
			APPJAIL_PWD="${current_pwd}" \
			APPJAIL_ROOTDIR="${JAILDIR}/${MAKEJAIL_JAILNAME}" \
			APPJAIL_SCRIPT="${APPJAIL_PROGRAM}" \
				sh -- "${output}" "$@")
		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi
	fi

	if [ -d "${JAILDIR}/${MAKEJAIL_JAILNAME}" ]; then
		# build initscript
		local initscript
		initscript=`makejail_makeinitscript`

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		# Print the result
		lib_debug "initscript generated:"
		lib_debug_read "${initscript}"

		if [ ${MAKEJAIL_APPLY} -eq 1 ]; then
			local makejail_rootdir
			makejail_rootdir=`head -1 -- "${MAKEJAIL_ROOTDIR}"`

			# To be (run), or not to be (run), that is the question...
			(cd -- "${makejail_rootdir}"; env \
				APPJAIL_CONFIG="${CONFIG}" \
				APPJAIL_JAILDIR="${JAILDIR}/${MAKEJAIL_JAILNAME}/jail" \
				APPJAIL_JAILNAME="${MAKEJAIL_JAILNAME}" \
				APPJAIL_ROOTDIR="${JAILDIR}/${MAKEJAIL_JAILNAME}" \
				APPJAIL_SCRIPT="${APPJAIL_PROGRAM}" \
					"${SCRIPTSDIR}/run_init.sh" -A -f "${CONFIG}" "${initscript}" "$@")

			errlevel=$?
			if [ ${errlevel} -ne 0 ]; then
				exit ${errlevel}
			fi
		else
			local outname="${JAILDIR}/${MAKEJAIL_JAILNAME}/init"

			if ! mv -- "${initscript}" "${outname}"; then
				lib_err ${EX_IOERR} "Error moving ${initscript} as ${outname}"
			fi
		fi
	fi
}

_create_tempdir()
{
	if [ -z "${MAKEJAIL_TEMPDIR}" ]; then
		MAKEJAIL_TEMPDIR=`lib_generate_tempdir` || exit $?

		local escape_mk_tempdir
		escape_mk_tempdir=`lib_escape_string "${MAKEJAIL_TEMPDIR}"`

		lib_atexit_add "rm -rf \"${escape_mk_tempdir}\" > /dev/null 2>&1"
	fi
}

makejail_write()
{
	local makejail_file

	makejail_file="$1"
	if [ -z "${makejail_file}" ]; then
		lib_err ${EX_USAGE} "usage: makejail_write makejail_file"
	fi

	local temp_makejail
	temp_makejail="`lib_generate_tempfile`"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_temp_makejail
	escape_temp_makejail=`lib_escape_string "${temp_makejail}"`

	lib_atexit_add "rm -f \"${escape_temp_makejail}\""

	MAKEJAIL_ROOTDIR="`lib_generate_tempfile`"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_mkrootdir
	escape_mkrootdir=`lib_escape_string "${MAKEJAIL_ROOTDIR}"`

	lib_atexit_add "rm -f \"${escape_mkrootdir}\""

	# Compile all include's files
	(makejail_include "${makejail_file}") > "${temp_makejail}"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	# Replace build args
	if [ -n "${MAKEJAIL_BUILD_ARGS}" ]; then
		makejail_build_args "${temp_makejail}" "${MAKEJAIL_BUILD_ARGS}"
	fi

	# Replace default args and detects if there are required args.
	lib_replace_macrovars "${temp_makejail}"

	# Replace all escaped %%{...} with %{...}
	lib_replace_escaped_macrovars "${temp_makejail}"

	# Print the result
	lib_debug "Makejail generated:"
	lib_debug_read "${temp_makejail}"

	local priority=0
	local line
	while IFS= read -r line; do
		local cmd=`lib_jailparam_name "${line}" " "`
		local parameters=`lib_jailparam_value "${line}" " "`

		if [ "${cmd}" = "${MAKEJAIL_CMD_STAGE}" ]; then
			makejail_change_stage "${parameters}"
			continue
		fi
		
		# Machinery depending on whether this Makejail is to be applied or not.
		if [ ${MAKEJAIL_APPLY} -eq 1 ]; then
			# Ignore if we aren't in the apply stage.
			if [ "${MAKEJAIL_CURRENT_STAGE}" != "${MAKEJAIL_STAGE_APPLY}" ]; then
				continue
			fi
		else
			# Ignore if we are in the apply stage.
			if [ "${MAKEJAIL_CURRENT_STAGE}" = "${MAKEJAIL_STAGE_APPLY}" ]; then
				continue
			fi
		fi

		local _cmd
		_cmd=`makejail_check_cmd "${cmd}"` || exit $?

		local stagedir
		stagedir="${MAKEJAIL_TEMPDIR}/${MAKEJAIL_STAGESDIR}/${MAKEJAIL_CURRENT_STAGE}"

		if ! mkdir -p "${stagedir}"; then
			lib_err ${EX_IOERR} "Error creating ${stagedir}"
		fi

		makejail_run_cmd "${_cmd}" "${parameters}" > "${stagedir}/${priority}.${cmd}"

		priority=$((priority+1))
	done < "${temp_makejail}"

	# Build stage should be ignored.
	if [ ${MAKEJAIL_APPLY} -eq 1 ]; then
		return 0
	fi
	
	# Configuration and functions
	_makejail_config

	# Errors
	if [ ${MAKEJAIL_ERRORS} -eq 1 ]; then
		echo "set -e"
	fi
	
	# Verbose and debugging
	if [ ${MAKEJAIL_VERBOSE} -eq 1 ]; then
		echo "set -xv"
	fi

	# ARG
	makejail_write_cmd - "${MAKEJAIL_STAGE_BUILD}" "${MAKEJAIL_CMD_ARG}"
	
	# FROM
	makejail_write_cmd - "${MAKEJAIL_STAGE_BUILD}" "${MAKEJAIL_CMD_FROM}"

	local builddir="${MAKEJAIL_TEMPDIR}/${MAKEJAIL_STAGESDIR}/${MAKEJAIL_STAGE_BUILD}"

	mkdir -p -- "${builddir}" || exit $?

	# Additional options provided by the user.
	if [ -n "${MAKEJAIL_OPTIONS}" ]; then
		local option
		while IFS= read -r option; do
			echo "${option}" > "${builddir}/${priority}.${MAKEJAIL_CMD_OPTION}"
			priority=$((priority+1))
		done < "${MAKEJAIL_OPTIONS}"
	else
		echo > "${builddir}/${priority}.${MAKEJAIL_CMD_OPTION}"
		priority=$((priority+1))
	fi
	# OPTION
	makejail_write_cmd - "${MAKEJAIL_STAGE_BUILD}" "${MAKEJAIL_CMD_OPTION}"
	
	# build commands
	ls -A -- "${MAKEJAIL_TEMPDIR}/${MAKEJAIL_STAGESDIR}/${MAKEJAIL_STAGE_BUILD}" | sort -n | while IFS= read -r cmd
	do
		priority=`echo "${cmd}" | cut -d. -f1`
		cmd=`echo "${cmd}" | cut -d. -f2`

		makejail_write_cmd "${priority}" "${MAKEJAIL_STAGE_BUILD}" "${cmd}"

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			return ${errlevel}
		fi
	done

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi
}

makejail_build_args()
{
	local makejail_file="$1" build_args="$2"

	local errlevel

	local tempdir=`lib_generate_tempdir`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_tempdir
	escape_tempdir=`lib_escape_string "${tempdir}"`

	lib_atexit_add "rm -rf \"${escape_tempdir}\" > /dev/null 2>&1"

	local build_args_list
	build_args_list=`lib_split_jailparams "${build_args}"` || exit $?

	printf "%s\n" "${build_args_list}" | while IFS= read -r build_arg
	do
		name=`lib_jailparam_name "${build_arg}" "="`
		if [ -f "${tempdir}/${name}" ]; then
			lib_err ${EX_DATAERR} -- "${name}: cannot use a duplicate build argument."
		else
			touch -- "${tempdir}/${name}"
		fi

		value=`lib_jailparam_value "${build_arg}" "="`

		lib_replace_macrovar "${makejail_file}" "${name}" "${value}"
	done

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi
}

makejail_makeinitscript()
{
	local errlevel

	local initscript
	initscript="`lib_generate_tempfile`"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_initscript
	escape_initscript=`lib_escape_string "${initscript}"`

	lib_atexit_add "rm -f \"${escape_initscript}\""

	chmod +x "${initscript}"

	# Configuration and functions
	_makejail_config >> "${initscript}"

	for stage in ${MAKEJAIL_INIT_STAGES}; do
		local stagedir
		stagedir="${MAKEJAIL_TEMPDIR}/${MAKEJAIL_STAGESDIR}/${stage}"

		if [ ! -d "${stagedir}" ]; then
			continue
		fi

		lib_debug "Writing ${stage} stage ..."

		echo "${stage}() {"
		
		# ARG
		makejail_write_cmd - "${stage}" "${MAKEJAIL_CMD_ARG}"
		
		ls -A -- "${stagedir}" | sort -n | while IFS= read -r cmd
		do
			priority=`echo "${cmd}" | cut -d. -f1`
			cmd=`echo "${cmd}" | cut -d. -f2`

			makejail_write_cmd "${priority}" "${stage}" "${cmd}"

			errlevel=$?
			if [ ${errlevel} -ne 0 ]; then
				return ${errlevel}
			fi
		done

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		echo "}"
	done >> "${initscript}"

	echo "${initscript}"
}

_makejail_config()
{
	# Configuration and functions
	cat << EOF
set -T

. "\${APPJAIL_CONFIG}"
. "\${LIBDIR}/load"

lib_load "\${LIBDIR}/sysexits"
lib_load "\${LIBDIR}/atexit"
lib_load "\${LIBDIR}/log"
lib_load "\${LIBDIR}/check_func"

lib_atexit_init

trap '' SIGINT
EOF
}

makejail_write_cmd()
{
	local priority="$1" stage="$2" cmd="$3" input="$4"
	if [ -z "${stage}" -o -z "${cmd}" ]; then
		lib_err ${EX_USAGE} "usage: makejail_write_cmd [- | priority] stage cmd [input]"
	fi
	
	local errlevel
	errlevel=0

	local stagedir
	stagedir="${MAKEJAIL_TEMPDIR}/${MAKEJAIL_STAGESDIR}/${stage}"

	if [ "${priority}" = "-" ]; then
		local _cmd=`ls "${stagedir}/"*.${cmd} 2> /dev/null | head -1`
		if lib_check_empty "${_cmd}"; then
			return 0
		fi

		local args_file
		args_file="`lib_generate_tempfile`"

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		local escape_args_file
		escape_args_file=`lib_escape_string "${args_file}"`

		lib_atexit_add "rm -f \"${escape_args_file}\""

		# Order by priority.
		ls "${stagedir}" | sort -n | while IFS= read -r cmd_file
		do
			cmd_name=`echo "${cmd_file}" | cut -d. -f2`

			if [ "${cmd_name}" != "${cmd}" ]; then
				continue
			fi

			cat -- "${stagedir}/${cmd_file}"
		done >> "${args_file}"

		# Priority is not necessary in this case.
		makejail_write_cmd -1 "${stage}" "${cmd}" "${args_file}"

		rm -f -- "${stagedir}/"*.${cmd}
		
		return 0
	fi

	if [ -z "${input}" ]; then
		input="${stagedir}/${priority}.${cmd}"
	fi

	if [ `wc -c "${input}" | awk '{print $1}'` -eq 0 ]; then
		# ignore
		return 0
	fi

	local script
	if [ -x "${MAKEJAIL_WCOMMANDS}/${stage}/${cmd}" ]; then
		script="${MAKEJAIL_WCOMMANDS}/${stage}/${cmd}"
	elif [ -x "${MAKEJAIL_WCOMMANDS}/all/${cmd}" ]; then
		script="${MAKEJAIL_WCOMMANDS}/all/${cmd}"
	else
		lib_err ${EX_NOINPUT} -- "${cmd}: command not found."
	fi

	lib_debug "Running makejail command (write): ${script} (input:${input})"

	env \
		AJ_CONFIG="${CONFIG}" \
		MAKEJAIL_CURRENT_STAGE="${MAKEJAIL_CURRENT_STAGE}" \
		MAKEJAIL_TEMPDIR="${MAKEJAIL_TEMPDIR}" \
		MAKEJAIL_STAGESDIR="${MAKEJAIL_STAGESDIR}" \
			"${script}" "${input}"
	
	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	rm -f -- "${input}"
}

makejail_run_cmd()
{
	local cmd="$1" parameters="$2"

	if lib_check_empty "${cmd}"; then
		lib_err ${EX_DATAERR} "usage: makejail_run_cmd cmd [parameters]"
	fi

	local errlevel
	errlevel=0

	lib_debug "Running makejail command (cmd): ${cmd} (args:${parameters})"

	env \
		AJ_CONFIG="${CONFIG}" \
		MAKEJAIL_CURRENT_STAGE="${MAKEJAIL_CURRENT_STAGE}" \
		MAKEJAIL_TEMPDIR="${MAKEJAIL_TEMPDIR}" \
		MAKEJAIL_STAGESDIR="${MAKEJAIL_STAGESDIR}" \
			"${cmd}" "${parameters}"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi
}

makejail_check_cmd()
{
	local cmd="$1"
	if lib_check_empty "${cmd}"; then
		lib_err ${EX_DATAERR} "Makejail needs a command to run!"
	fi

	local script

	if lib_check_ispath "${cmd}"; then
		lib_err ${EX_DATAERR} "Invalid command: ${cmd}"
	fi

	if [ -x "${MAKEJAIL_COMMANDS}/${MAKEJAIL_CURRENT_STAGE}/${cmd}" ]; then
		script="${MAKEJAIL_COMMANDS}/${MAKEJAIL_CURRENT_STAGE}/${cmd}"
	elif [ -x "${MAKEJAIL_COMMANDS}/all/${cmd}" ]; then
		script="${MAKEJAIL_COMMANDS}/all/${cmd}"
	else
		lib_err ${EX_NOINPUT} -- "${cmd}: command not found."
	fi

	echo "${script}"
}

makejail_include()
{
	local makejail_file
	makejail_file="$1"

	if [ -z "${makejail_file}" ]; then
		lib_err ${EX_USAGE} "usage: makejail_include makejail_file"
	fi

	local errlevel

	makejail_file=`makejail_getinclude "${makejail_file}"`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	makejail_file=`realpath -- "${makejail_file}"`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	# Avoid possible loops
	_makejail_check "${makejail_file}"
	_makejail_add "${makejail_file}"

	local rootdir
	rootdir=`dirname -- "${makejail_file}"`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local _rootdir="${rootdir}"

	# The root directory of the first Makejail that will be used later when
	# running the buildscript.
	if [ -z "${MAKEJAIL_WRITE_ROOTDIR}" ]; then
		MAKEJAIL_WRITE_ROOTDIR=1
		
		printf "%s\n" "${_rootdir}" > "${MAKEJAIL_ROOTDIR}"
	fi

	local current_pwd
	current_pwd=`pwd -P`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	if [ "${current_pwd}" != "${_rootdir}" ]; then
		cd -- "${_rootdir}"
		if [ "${MAKEJAIL_CURRENT_STAGE}" = "${MAKEJAIL_STAGE_BUILD}" ]; then
			# For macrovars.
			rootdir=`lib_replace_escape_macrovar "${rootdir}"`
			rootdir=`lib_escape_string "${rootdir}"`
		
			printf "%s\n" "RAW cd -- \"${rootdir}\" # Makejail: ${makejail_file}"
		fi
	fi

	lib_debug "Including ${makejail_file} ..."

	local temp_makejail
	temp_makejail=`lib_generate_tempfile` || exit $?

	local escape_temp_makejail
	escape_temp_makejail=`lib_escape_string "${temp_makejail}"`

	lib_atexit_add "rm -f \"${escape_temp_makejail}\""

	# Removes Carriege Feed to avoid problems when parsing. See issue #4.
	cat -- "${makejail_file}" | sed -Ee 's/\r$//' > "${temp_makejail}"

	# Before include
	if [ -n "${MAKEJAIL_BEFORE_INCLUDE}" -a -z "${MAKEJAIL_BEFORE_INCLUDED}" ]; then
		local temp_include_file
		temp_include_file=`lib_generate_tempfile` || exit $?

		local escape_temp_include_file
		escape_temp_include_file=`lib_escape_string "${temp_include_file}"`

		lib_atexit_add "rm -f \"${escape_temp_include_file}\""

		lib_split_jailparams "${MAKEJAIL_BEFORE_INCLUDE}" | while IFS= read -r include_file; do
			printf "INCLUDE %s\n" "${include_file}"
		done >> "${temp_include_file}"

		cat -- "${temp_makejail}" >> "${temp_include_file}"

		temp_makejail="${temp_include_file}"

		MAKEJAIL_BEFORE_INCLUDED=1
	fi

	# After include
	if [ -n "${MAKEJAIL_AFTER_INCLUDE}" -a -z "${MAKEJAIL_AFTER_INCLUDED}" ]; then
		lib_split_jailparams "${MAKEJAIL_AFTER_INCLUDE}" | while IFS= read -r include_file; do
			printf "INCLUDE %s\n" "${include_file}"
		done >> "${temp_makejail}"

		MAKEJAIL_AFTER_INCLUDED=1
	fi

	local line lines=
	while IFS= read -r line; do
		if lib_check_empty "${line}"; then
			continue
		fi

		if lib_check_comment "${line}"; then
			continue
		fi

		# Remove tabs and spaces.
		line=`printf "%s" "${line}" | sed -Ee 's/^[\t ]*//'`

		if printf "%s" "${line}" | grep -qEe '\\\\[[:space:]]*$'; then # Escape \.
			line=`printf "%s" "${line}" | sed -Ee 's/\\\\[[:space:]]*$//'`

			if [ -n "${lines}" ]; then
				line="${lines}${line}"
			fi

			lines=
		elif printf "%s" "${line}" | grep -qEe '\\[[:space:]]*$'; then # line-continuation.
			line=`printf "%s" "${line}" | sed -Ee 's/\\\\[[:space:]]*$//'`
			if [ -z "${lines}" ]; then
				lines="${line}"
			else
				lines="${lines}${line}"
			fi
			continue
		else # concat all previous lines.
			if [ -n "${lines}" ]; then
				line="${lines}${line}"
			fi

			lines=
		fi

		local cmd=`lib_jailparam_name "${line}" " "`

		if [ "${cmd}" = "${MAKEJAIL_CMD_INCLUDE}" ]; then
			local current_stage="${MAKEJAIL_CURRENT_STAGE}"

			local parameters=`lib_jailparam_value "${line}" " "`
			makejail_include "${parameters}"

			if [ "${current_stage}" != "${MAKEJAIL_CURRENT_STAGE}" ]; then
				makejail_change_stage "${current_stage}"
				printf "%s\n" "STAGE ${current_stage}"
			fi

			current_pwd=`pwd -P`

			errlevel=$?
			if [ ${errlevel} -ne 0 ]; then
				exit ${errlevel}
			fi

			if [ "${current_pwd}" != "${_rootdir}" ]; then
				cd -- "${_rootdir}"
				if [ "${current_stage}" = "${MAKEJAIL_STAGE_BUILD}" ]; then
					printf "%s\n" "RAW cd -- \"${rootdir}\" # Makejail: ${makejail_file}"
				fi
			fi
		elif [ "${cmd}" = "${MAKEJAIL_CMD_STAGE}" ]; then
			local parameters=`lib_jailparam_value "${line}" " "`

			makejail_change_stage "${parameters}"

			printf "%s\n" "${line}"

			if [ "${MAKEJAIL_CURRENT_STAGE}" = "${MAKEJAIL_STAGE_BUILD}" ]; then
				printf "%s\n" "RAW cd -- \"${rootdir}\" # Makejail: ${makejail_file}"
			fi
		else
			printf "%s\n" "${line}"
		fi
	done < "${temp_makejail}"
}

_makejail_check()
{
	local makejail_file="$1"
	if [ -z "${makejail_file}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_check makejail_file"
	fi

	local errlevel

	if [ -z "${MAKEJAIL_CHK_LIST}" ]; then
		MAKEJAIL_CHK_LIST="`lib_generate_tempfile`"

		errlevel=$?
		if [ ${errlevel} -ne 0 ]; then
			exit ${errlevel}
		fi

		local escape_mk_chk_lst
		escape_mk_chk_lst=`lib_escape_string "${MAKEJAIL_CHK_LIST}"`

		lib_atexit_add "rm -f \"${escape_mk_chk_lst}\""
	fi

	local match
	while IFS= read -r match; do
		if [ "${match}" = "${makejail_file}" ]; then
			lib_err ${EX_DATAERR} -- "${makejail_file}: Makejail is already included."
		fi
	done < "${MAKEJAIL_CHK_LIST}"
}

_makejail_add()
{
	local makejail_file="$1"
	if [ -z "${makejail_file}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_add makejail_file"
	fi

	printf "%s\n" "${makejail_file}" >> "${MAKEJAIL_CHK_LIST}"
}

makejail_getinclude()
{
	local include_file="$1"
	if lib_check_empty "${include_file}"; then
		lib_err ${EX_USAGE} "usage: INCLUDE [<method>+]<path> [<args> ...]"
	fi

	local method=`lib_jailparam_name "${include_file}" "+"`
	parameters=`lib_jailparam_value "${include_file}" "+"`

	if lib_check_empty "${parameters}"; then
		parameters="${method}"
		method="file"
	fi

	case "${method}" in
		cmd|git|fetch|file)
			;;
		gh|github)
			method="github"
			;;
		gh-ssh|github-ssh)
			method="github_ssh"
			;;
		gl|gitlab)
			method="gitlab"
			;;
		gl-ssh|gitlab-ssh)
			method="gitlab_ssh"
			;;
		*)
			lib_err ${EX_DATAERR} -- "${method}: Invalid method."
			;;
	esac

	lib_debug "Using method:${method} (args:${parameters}) from ${include_file}."

	local errlevel
	local makejail_file

	makejail_file=`makejail_getinclude_${method} "${parameters}"`

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	printf "%s\n" "${makejail_file}"
}

makejail_getinclude_cmd()
{
	local args="$1"
	if lib_check_empty "${args}"; then
		lib_err ${EX_USAGE} "usage: INCLUDE cmd+<command> [<args> ...]"
	fi

	local errlevel

	local output
	output="`lib_generate_tempfile`"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_output
	escape_output=`lib_escape_string "${output}"`

	lib_atexit_add "rm -f \"${escape_output}\""
	
	# Arguments
	local args_list
	local total_items
	local current_index=0

	local args_list=`lib_split_jailparams "${args}"` || exit $?
	local total_items=`printf "%s\n" "${args_list}" | wc -l`

	local cmd_args=
	while [ ${current_index} -lt ${total_items} ]; do 
		current_index=$((current_index+1))

		local arg=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
		if ! lib_check_empty "${arg}"; then
			arg=`lib_escape_string "${arg}"`
		fi

		cmd_args="${cmd_args} \"${arg}\""
	done

	lib_debug "Running (cmd): ${cmd_args}"

	sh -c "${cmd_args}" > "${output}"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		lib_err ${errlevel} "Error executing \`${cmd_args} \`."
	fi

	printf "%s\n" "${output}"
}

makejail_getinclude_fetch()
{
	local args="$1"
	if lib_check_empty "${args}"; then
		lib_err ${EX_USAGE} "usage: INCLUDE fetch+<url>"
	fi

	local url=`printf "%s\n" "${args}" | head -1 | tail -n 1`
	if lib_check_empty "${url}"; then
		makejail_getinclude_fetch # usage
	fi

	local errlevel

	local output
	output="`lib_generate_tempfile`"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		exit ${errlevel}
	fi

	local escape_output=`lib_escape_string "${output}"`
	lib_atexit_add "rm -f \"${escape_output}\""

	local escape_url=`lib_escape_string "${url}"`
	
	fetch_cmd=`lib_multi_replace "${MAKEJAIL_FETCH_CMD}" o "\"${escape_output}\"" u "\"${escape_url}\""`

	lib_debug "Running (fetch): ${fetch_cmd}"

	sh -c "${fetch_cmd}"

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		lib_err ${errlevel} "Error executing \`${fetch_cmd} \`."
	fi

	printf "%s\n" "${output}"
}

makejail_getinclude_file()
{
	local args="$1"
	if lib_check_empty "${args}"; then
		lib_err ${EX_USAGE} "usage: INCLUDE file+<makejail>"
	fi

	local file=`printf "%s\n" "${args}" | head -1 | tail -n 1`
	if lib_check_empty "${file}"; then
		makejail_getinclude_file # usage
	fi

	if [ ! -f "${file}" ]; then
		lib_err ${EX_NOINPUT} -- "${file} file does not exist or could not be read."
	fi

	printf "%s\n" "${file}"
}

makejail_getinclude_github()
{
	makejail_getinclude_git "$1 --baseurl https://github.com/"
}

makejail_getinclude_github_ssh()
{
	makejail_getinclude_git "$1 --baseurl git@github.com:"
}

makejail_getinclude_gitlab()
{
	makejail_getinclude_git "$1 --baseurl https://gitlab.com/"
}

makejail_getinclude_gitlab_ssh()
{
	makejail_getinclude_git "$1 --baseurl git@gitlab.com:"
}

makejail_getinclude_git()
{
	local args="$1"
	if lib_check_empty "${args}"; then
		lib_err ${EX_USAGE} "usage: INCLUDE git+<url> [--baseurl <url>] [--file <makejail>] [--global|--local [--cachedir <directory>]|--tmp]"
	fi

	# Arguments
	local args_list
	local total_items

	args_list=`lib_split_jailparams "${args}"` || exit $?
	total_items=`printf "%s\n" "${args_list}" | wc -l`
	total_items=$((total_items-1))

	local current_index=1

	local url=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
	if lib_check_empty "${url}"; then
		makejail_getinclude_git # usage
	fi

	# options
	local gitdir
	local makejail_filename="${DEFAULT_MAKEJAIL_FILE}"
	local global=1
	local local=0
	local tmp=0
	local cachedir=".makejail_local"

	while [ ${current_index} -lt ${total_items} ]; do 
		current_index=$((current_index+1))
		local arg=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
		if lib_check_empty "${arg}"; then
			continue
		fi

		local optarg=
		case "${arg}" in
			--baseurl|--cachedir|--file)
				current_index=$((current_index+1))
				if [ ${current_index} -eq ${total_items} ]; then
					makejail_getinclude_git # usage
				fi

				optarg=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
				if lib_check_empty "${optarg}"; then
					makejail_getinclude_git # usage
				fi
				;;
		esac

		case "${arg}" in
			--global)
				global=1
				;;
			--local)
				local=1
				;;
			--tmp)
				tmp=1
				;;
			--baseurl)
				url="${optarg}${url}"
				;;
			--cachedir)
				cachedir="${optarg}"
				;;
			--file)
				makejail_filename="${optarg}"
				;;
			--)
				break
				;;
			--*)
				makejail_getinclude_git # usage
				;;
			*)
				break
				;;
		esac
	done
	current_index=$((current_index-1))

	if [ ${local} -eq 1 ]; then
		gitdir="${cachedir}"

		lib_debug "Using local cache directory (git): ${gitdir}"
	elif [ ${tmp} -eq 1 ]; then
		gitdir=`lib_generate_tempdir` || exit $?

		local escape_gitdir
		escape_gitdir=`lib_escape_string "${gitdir}"`

		lib_atexit_add "rm -rf \"${escape_gitdir}\" > /dev/null 2>&1"
	else
		gitdir="${GLOBAL_GIT_CACHEDIR}"

		lib_debug "Using global cache directory (git): ${gitdir}"
	fi

	if [ ${local} -eq 1 -o ${global} -eq 1 ]; then
		if ! mkdir -p "${gitdir}"; then
			lib_err ${EX_IOERR} "Error creating ${cachedir}"
		fi
	fi

	_makejail_git_clone "${url}" "${gitdir}" "${makejail_filename}"
}

_makejail_git_clone()
{
	local url="$1" gitdir="$2" mk_file="$3"
	if [ -z "${url}" -o -z "${gitdir}" -o -z "${mk_file}" ]; then
		lib_err ${EX_USAGE} "_makejail_git_clone url gitdir mk_file"
	fi

	_makejail_check_git # check

	local reponame
	reponame=`lib_mksum_str "${url}"`

	local repodir
	repodir="${gitdir}/${reponame}"

	if [ ! -d "${repodir}" ]; then
		lib_debug "Cloning ${url} as ${repodir} ..."

		git clone -q -o origin -- "${url}" "${repodir}" >&2
	else
		if [ "${AUTO_GIT_UPDATE}" != 0 ]; then
			lib_debug "Updating ${repodir} ..."

			_makejail_git_update "${repodir}"
		else
			lib_warn "Automatic updates are disabled (AUTO_GIT_UPDATE = 0), ${url} (id = ${reponame}) will not be updated."
		fi
	fi

	errlevel=$?
	if [ ${errlevel} -ne 0 ]; then
		lib_err ${errlevel} "Failed to get ${url} using git(1)."
	fi

	if [ ! -f "${repodir}/${mk_file}" ]; then
		lib_err ${EX_NOINPUT} -- "${mk_file} file does not exist."
	fi

	printf "%s\n" "${repodir}/${mk_file}"
}

_makejail_check_git()
{
	if ! which -s "git"; then
		lib_err ${EX_UNAVAILABLE} "git(1) is not installed. Cannot continue ..."
	fi
}

_makejail_git_update()
{
	local repodir="$1"

	if [ -z "${repodir}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_git_update repodir"
	fi

	git -C "${repodir}" fetch -q origin >&2 &&
	git -C "${repodir}" reset -q --hard origin >&2
}

makejail_change_stage()
{
	local stage="$1"
	if lib_check_empty "${stage}"; then
		lib_err ${EX_USAGE} -- "usage: STAGE <stage>"
	fi

	if ! _makejail_check_stages "${stage}"; then
		lib_err ${EX_NOINPUT} -- "${stage}: stage not found."
	fi

	MAKEJAIL_CURRENT_STAGE="${stage}"
}

_makejail_check_stages()
{
	local stage2chk="$1"
	if [ -z "${stage2chk}" ]; then
		lib_err ${EX_USAGE} "usage: _makejail_check_stages stage2chk"
	fi

	# Check if a custom stage
	if printf "%s" "${stage2chk}" | grep -qEe '^custom:'; then
		local custom_stage
		custom_stage=`printf "%s" "${stage2chk}" | sed -Ee 's/^custom:(.+)/\1/'`

		if ! lib_check_stagename "${custom_stage}"; then
			lib_err ${EX_DATAERR} -- "${custom_stage}: Invalid custom stage."
		fi

		# Add as a new stage for the initscript.
		MAKEJAIL_INIT_STAGES="${MAKEJAIL_INIT_STAGES} ${stage2chk}"

		return 0
	else
		local stage
		for stage in ${MAKEJAIL_STAGES}; do
			if [ "${stage}" = "${stage2chk}" ]; then
				return 0
			fi
		done
	fi

	return 1
}

makejail_help()
{
	man 1 appjail-makejail
}

makejail_usage()
{
	cat << EOF
usage: makejail [-c] [-ev] [-a <makejail>] [-B <makejail>]
               [[-b <build-arg>[=<value>]] ...] [-f <makejail>] [-j <name>] [[-o <option>]
	       ...] [[-V <name>=<value>] ...] [-- <runtime-args> ...]
       makejail -A -j <name> [-c] [-a <makejail>] [-B <makejail>]
               [[-b <build-arg>[=<value>]] ...] [-f <makejail>] [[-V <name>=<value>] ...]
	       [-- <runtime-args> ...]
       makejail [-E] -d <id>
       makejail -l
       makejail [-E] -u [<id>|*]
EOF
}
