#!/bin/bash
# $Id: 656ddef1bdbd2f87413e64d19eab8e037a8b8821 $

isTrue() {
	case "$1" in
		[Tt][Rr][Uu][Ee])
			return 0
		;;
		[Tt])
			return 0
		;;
		[Yy][Ee][Ss])
			return 0
		;;
		[Yy])
			return 0
		;;
		1)
			return 0
		;;
	esac
	return 1
}

setColorVars() {
if isTrue ${USECOLOR}
then
	GOOD=$'\e[32;01m'
	WARN=$'\e[33;01m'
	BAD=$'\e[31;01m'
	NORMAL=$'\e[0m'
	BOLD=$'\e[0;01m'
	UNDER=$'\e[4m'
else
	GOOD=''
	WARN=''
	BAD=''
	NORMAL=''
	BOLD=''
	UNDER=''
fi
}
setColorVars

dump_debugcache() {
	TODEBUGCACHE=0
	echo "${DEBUGCACHE}" >> ${LOGFILE}
}

# print_info(loglevel, print [, newline [, prefixline [, forcefile ] ] ])
print_info() {
	local NEWLINE=1
	local FORCEFILE=0
	local PREFIXLINE=1
	local SCRPRINT=0
	local STR=''

	# NOT ENOUGH ARGS
	if [ "$#" -lt '2' ] ; then return 1; fi

	# IF 3 OR MORE ARGS, CHECK IF WE WANT A NEWLINE AFTER PRINT
	if [ "$#" -gt '2' ]
	then
		if isTrue "$3"
		then
			NEWLINE='1';
		else
			NEWLINE='0';
		fi
	fi

	# IF 4 OR MORE ARGS, CHECK IF WE WANT TO PREFIX WITH A *
	if [ "$#" -gt '3' ]
	then
		if isTrue "$4"
		then
			PREFIXLINE='1'
		else
			PREFIXLINE='0'
		fi
	fi

	# IF 5 OR MORE ARGS, CHECK IF WE WANT TO FORCE OUTPUT TO DEBUG
	# FILE EVEN IF IT DOESN'T MEET THE MINIMUM DEBUG REQS
	if [ "$#" -gt '4' ]
	then
		if isTrue "$5"
		then
			FORCEFILE='1'
		else
			FORCEFILE='0'
		fi
	fi

	# PRINT TO SCREEN ONLY IF PASSED LOGLEVEL IS HIGHER THAN
	# OR EQUAL TO SET DEBUG LEVEL
	if [ "$1" -lt "${LOGLEVEL}" -o "$1" = "${LOGLEVEL}" ]
	then
		SCRPRINT='1'
	fi

	# RETURN IF NOT OUTPUTTING ANYWHERE
	if [ "${SCRPRINT}" != '1' -a "${FORCEFILE}" != '1' ]
	then
		return 0
	fi

	# STRUCTURE DATA TO BE OUTPUT TO SCREEN, AND OUTPUT IT
	if [ "${SCRPRINT}" = '1' ]
	then
		if [ "${PREFIXLINE}" = '1' ]
		then
			STR="${GOOD}*${NORMAL} ${2}"
		else
			STR="${2}"
		fi

		printf "%b" "${STR}"
		if [ "${NEWLINE}" != '0' ]; then
			echo
		fi
	fi

	# STRUCTURE DATA TO BE OUTPUT TO FILE, AND OUTPUT IT
	if [ "${SCRPRINT}" = '1' -o "${FORCEFILE}" = '1' ]
	then
		STRR=${2//${WARN}/}
		STRR=${STRR//${BAD}/}
		STRR=${STRR//${BOLD}/}
		STRR=${STRR//${NORMAL}/}

		if [ "${PREFIXLINE}" = '1' ]
		then
			STR="* ${STRR}"
		else
			STR="${STRR}"
		fi

		if [ "${TODEBUGCACHE}" = '1' ]; then
			DEBUGCACHE="${DEBUGCACHE}${STR}"
		else
			printf "%b" "${STR}" >> ${LOGFILE}
		fi

		if [ "${NEWLINE}" != '0' ]; then
			if [ "${TODEBUGCACHE}" = '1' ]; then
				DEBUGCACHE="${DEBUGCACHE}"$'\n'
			else
				echo >> ${LOGFILE}
			fi
		fi
	fi

	return 0
}

print_error()
{
	GOOD=${BAD} print_info "$@"
}

print_warning()
{
	GOOD=${WARN} print_info "$@"
}

# var_replace(var_name, var_value, string)
# $1 = variable name
# $2 = variable value
# $3 = string

var_replace()
{
  # Escape '\' and '.' in $2 to make it safe to use
  # in the later sed expression
  local SAFE_VAR
  SAFE_VAR=`echo "${2}" | sed -e 's/\([\/\.]\)/\\\\\\1/g'`

  echo "${3}" | sed -e "s/%%${1}%%/${SAFE_VAR}/g" -
}

arch_replace() {
  var_replace "ARCH" "${ARCH}" "${1}"
}

cache_replace() {
  var_replace "CACHE" "${CACHE_DIR}/${GK_V}" "${1}"
}

clear_log() {
    if [ -f "${LOGFILE}" ]
    then
	(echo > "${LOGFILE}") 2>/dev/null || small_die "Genkernel: Could not write to ${LOGFILE}."
    fi
}

gen_die() {
	dump_debugcache

	if [ "$#" -gt '0' ]
	then
		print_error 1 "ERROR: ${1}"
	fi
	print_error 1 ''
	print_error 1 "-- Grepping log... --"
	print_error 1 ''

	if isTrue ${USECOLOR}
	then
		GREP_COLOR='1' grep -B5 -E --colour=always "([Ww][Aa][Rr][Nn][Ii][Nn][Gg]|[Ee][Rr][Rr][Oo][Rr][ :,!]|[Ff][Aa][Ii][Ll][Ee]?[Dd]?)" ${LOGFILE} \
				| sed -s "s|^\(*\)\?|${BAD}*${NORMAL}|"
	else
		grep -B5 -E "([Ww][Aa][Rr][Nn][Ii][Nn][Gg]|[Ee][Rr][Rr][Oo][Rr][ :,!]|[Ff][Aa][Ii][Ll][Ee]?[Dd]?)" ${LOGFILE}
	fi
	print_error 1 ''
	print_error 1 "-- End log... --"
	print_error 1 ''
	print_error 1 "Please consult ${LOGFILE} for more information and any"
	print_error 1 "errors that were reported above."
	print_error 1 ''
	print_error 1 "Report any genkernel bugs to bugs.gentoo.org and"
	print_error 1 "assign your bug to genkernel@gentoo.org. Please include"
	print_error 1 "as much information as you can in your bug report; attaching"
	print_error 1 "${LOGFILE} so that your issue can be dealt with effectively."
	print_error 1 ''
	print_error 1 'Please do *not* report compilation failures as genkernel bugs!'
	print_error 1 ''

	# Cleanup temp dirs and caches if requested
	isTrue "${CMD_DEBUGCLEANUP}"  && cleanup
  	exit 1
}

isBootRO()
{
	return $(awk '( $2 == "'${BOOTDIR}'" && $4 ~ /(^|,)ro(,|$)/){ I=1; exit }END{print !I }' /proc/mounts);
}

setup_cache_dir()
{

[ ! -d "${CACHE_DIR}/${GK_V}" ] && mkdir -p "${CACHE_DIR}/${GK_V}"

if [ "${CLEAR_CACHE_DIR}" == 'yes' ]
then
	print_info 1 "Clearing cache dir contents from ${CACHE_DIR}"
	while read i
	do
		print_info 1 "	 >> removing ${i}"
		rm "${i}"
	done < <(find "${CACHE_DIR}" -maxdepth 2 -type f -name '*.tar.*' -o -name '*.bz2')
fi

}

clear_tmpdir()
{
if isTrue ${CMD_INSTALL}
then
	TMPDIR_CONTENTS=`ls ${TMPDIR}`
	print_info 1 "Removing tmp dir contents"
	for i in ${TMPDIR_CONTENTS}
	do
		print_info 1 "	 >> removing ${i}"
		rm -r ${TMPDIR}/${i}
	done
fi
}

#
# Function to copy various kernel boot image products to the boot directory,
# preserve a generation of old images (just like the manual kernel build's
# "make install" does), and maintain the symlinks (if enabled).
#
# Arguments:
#     $1  Symlink name.  Symlink on the boot directory. Path not included.
#     $2  Source image.  Fully qualified path name of the source image.
#     $3  Dest image.    Name of the destination image in the boot directory,
#         no path included.  This script pushd's into ${BOOTDIR} in order to
#         create relative symlinks just like the manual kernel build.
#
# - JRG
#
copy_image_with_preserve() {
	local symlinkName=$1
	local newSrceImage=$2
	local fullDestName=$3

	local currDestImage
	local prevDestImage
	local currDestImageExists=0
	local prevDestImageExists=0

 	print_info 4 "Copying new ${symlinkName} image, " 0

	# Old product might be a different version.  If so, we need to read
	# the symlink to see what it's name is, if there are symlinks.
	cd ${KERNEL_OUTPUTDIR}
	if [ "${SYMLINK}" = '1' ]
	then
 		print_info 4 "automatically managing symlinks and old images." 1 0
		if [ -e "${BOOTDIR}/${symlinkName}" ]
		then
			# JRG: Do I need a special case here for when the standard symlink
			# name is, in fact, not a symlink?
			currDestImage=`readlink --no-newline ${BOOTDIR}/${symlinkName}`
			print_info 5 "  Current ${symlinkName} symlink exists:"
			print_info 5 "    ${currDestImage}"
		else
			currDestImage="${fullDestName}"
			print_info 5 "  Current ${symlinkName} symlink did not exist."
			print_info 5 "    Defaulted to: ${currDestImage}"
		fi
		if [ -e "${BOOTDIR}/${currDestImage}" ]
		then
			currDestImageExists=1
 			print_info 5 "  Actual image file exists."
		fi

		if [ -e "${BOOTDIR}/${symlinkName}.old" ]
		then
			# JRG: Do I need a special case here for when the standard symlink
			# name is, in fact, not a symlink?
			prevDestImage=`readlink --no-newline ${BOOTDIR}/${symlinkName}.old`
			print_info 5 "  Old ${symlinkName} symlink exists:"
			print_info 5 "    ${prevDestImage}"
		else
			prevDestImage="${fullDestName}.old"
			print_info 5 "  Old ${symlinkName} symlink did not exist."
			print_info 5 "    Defaulted to: ${prevDestImage}"
		fi
		if [ -e "${BOOTDIR}/${prevDestImage}" ]
		then
			prevDestImageExists=1
 			print_info 5 "  Actual old image file exists."
		fi
	else
		print_info 4 "symlinks not being handled by genkernel." 1 0
		currDestImage="${fullDestName}"
		prevDestImage="${fullDestName}.old"
	fi

	# When symlinks are not being managed by genkernel, old symlinks might
    # still be useful.  Leave 'em alone unless managed.
	if [ "${SYMLINK}" = '1' ]
	then
		print_info 5 "  Deleting old symlinks, if any."
		rm -f "${BOOTDIR}/${symlinkName}"
		rm -f "${BOOTDIR}/${symlinkName}.old"
	fi

	# We only erase the .old image when it is the exact same version as the
	# current and new images.  Different version .old (and current) images are
	# left behind.  This is consistent with how "make install" of the manual
	# kernel build works.
	if [ "${currDestImage}" == "${fullDestName}" ]
	then
		#
		# Case for new and currrent of the same base version.
		#
 		print_info 5 "  Same base version.  May have to delete old image to make room."

		if [ "${currDestImageExists}" = '1' ]
		then
			if [ -e "${BOOTDIR}/${currDestImage}.old" ]
			then
				print_info 5 "  Deleting old identical version ${symlinkName}."
				rm -f "${BOOTDIR}/${currDestImage}.old"
			fi
			print_info 5 "  Moving ${BOOTDIR}/${currDestImage}"
			print_info 5 "    to ${BOOTDIR}/${currDestImage}.old"
			mv "${BOOTDIR}/${currDestImage}" "${BOOTDIR}/${currDestImage}.old" ||
			    gen_die "Could not rename the old ${symlinkName} image!"
			prevDestImage="${currDestImage}.old"
			prevDestImageExists=1
		fi
	else
		#
		# Case for new / current not of the same base version.
		#
 		print_info 5 "  Different base version.  Do not delete old images."
		prevDestImage="${currDestImage}"
		currDestImage="${fullDestName}"
	fi

	print_info 5 "  Copying ${symlinkName}: ${newSrceImage}"
	print_info 5 "    to ${BOOTDIR}/${currDestImage}"
	cp "${newSrceImage}" "${BOOTDIR}/${currDestImage}" ||
	    gen_die "Could not copy the ${symlinkName} image to ${BOOTDIR}!"

	if [ "${SYMLINK}" = '1' ]
	then
		print_info 5 "  Make new symlink(s) (from ${BOOTDIR}):"
		print_info 5 "    ${symlinkName} -> ${currDestImage}"
		pushd ${BOOTDIR} >/dev/null
		ln -s "${currDestImage}" "${symlinkName}" || 
		    gen_die "Could not create the ${symlinkName} symlink!"
		if [ "${prevDestImageExists}" = '1' ]
		then
			print_info 5 "    ${symlinkName}.old -> ${prevDestImage}"
			ln -s "${prevDestImage}" "${symlinkName}.old" ||
			    gen_die "Could not create the ${symlinkName}.old symlink!"
		fi
		popd >/dev/null
	fi
}

#
# Helper function to allow command line arguments to override configuration
# file specified values and to apply defaults.
#
# Arguments:
#     $1  Argument type:
#           1  Switch type arguments (e.g., --color / --no-color).
#           2  Value type arguments (e.g., --debuglevel=5).
#     $2  Config file variable name.
#     $3  Command line variable name.
#     $4  Default.  If both the config file variable and the command line
#         option are not present, then the config file variable is set to
#         this default value.  Optional.
#
# The order of priority of these three sources (highest first) is:
#     Command line, which overrides
#     Config file (/etc/genkernel.conf), which overrides
#     Default.
#
# Arguments $2 and $3 are variable *names*, not *values*.  This function uses
# various forms of indirection to access the values.
#
# For switch type arguments, all forms of "True" are converted to a numeric 1
# and all forms of "False" (everything else, really) to a numeric 0.
#
# - JRG
#
set_config_with_override() {
	local VarType=$1
	local CfgVar=$2
	local OverrideVar=$3
	local Default=$4
	local Result

	#
	# Syntax check the function arguments.
	#
	case "$VarType" in
		BOOL|STRING)
			;;
		*)
			gen_die "Illegal variable type \"$VarType\" passed to set_config_with_override()."
			;;
	esac

	if [ -n "${!OverrideVar}" ]
	then
		Result=${!OverrideVar}
		if [ -n "${!CfgVar}" ]
		then
			print_info 5 "  $CfgVar overridden on command line to \"$Result\"."
		else
			print_info 5 "  $CfgVar set on command line to \"$Result\"."
		fi
	else
		if [ -n "${!CfgVar}" ]
		then
			Result=${!CfgVar}
			# we need to set the CMD_* according to configfile...
			eval ${OverrideVar}=\"${Result}\"
			print_info 5 "  $CfgVar set in config file to \"${Result}\"."
		else
			if [ -n "$Default" ]
			then
				Result=${Default}
				# set OverrideVar to Result, otherwise CMD_* may not be initialized...
				eval ${OverrideVar}=\"${Result}\"
				print_info 5 "  $CfgVar defaulted to \"${Result}\"."
			else
				print_info 5 "  $CfgVar not set."
			fi
		fi
	fi

	if [ "${VarType}" = BOOL ]
	then
		if isTrue "${Result}"
		then
			Result=1
		else
			Result=0
		fi
	fi

	eval ${CfgVar}=\"${Result}\"
}

rootfs_type_is() {
	local fstype=$1

	# It is possible that the awk will return MULTIPLE lines, depending on your
	# initramfs setup (one of the entries will be 'rootfs').
	if awk '($2=="/"){print $3}' /proc/mounts | grep -sq --line-regexp "$fstype" ;
	then
		echo yes
	else
		echo no
	fi
}

check_distfiles() {
	for i in $BUSYBOX_SRCTAR $MULTIPATH_SRCTAR $LVM_SRCTAR $DMRAID_SRCTAR $ISCSI_SRCTAR $GPG_SRCTAR
	do
		if [ ! -f "${i}" ]
		then
			small_die "Could not find source tarball ${i}. Please refetch."
		fi
	done
}

find_kernel_binary() {
	local kernel_binary=$*
	local curdir=$(pwd)

	cd "${KERNEL_OUTPUTDIR}"
	for i in ${kernel_binary}
	do
		if [ -e "${i}" ]
		then
			tmp_kernel_binary=$i
			break
		fi
	done
#	if [ -z "${tmp_kernel_binary}" ]
#	then
#		gen_die "Cannot locate kernel binary!"
#	fi
	cd "${curdir}"
	echo "${tmp_kernel_binary}"
}

function kconfig_get_opt() {
	kconfig="$1"
	optname="$2"
	sed -n "${kconfig}" \
		-e "/^#\? \?${optname}[ =].*/{ s/.*${optname}[ =]//g; s/is not set\| +//g; p; q }"
}

function kconfig_set_opt() {
	kconfig="$1"
	optname="$2"
	optval="$3"
	sed -i "${kconfig}" \
		-e "s/^#\? \?${optname}[ =].*/${optname}=${optval}/g" \
	|| gen_die "Failed to set ${optname}=${optval} in $kconfig"
}
