#! /bin/ksh
#
# ident "@(#)sc_common.ksh 1.9     01/06/28 SMI"
#
# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
# All rights reserved.
#

####################################################
#
# sc_print_title() "text"
#
#	text	- the title
#
#	Print the given "text" as the first title
#	on the page.  This title is both preceeded
#	and followed by a newline.
#
#	This function always returns zero.
#
####################################################
sc_print_title()
{
	set -f
	typeset text="$(echo $*)"
	set +f

	clear
	echo
	printf "  %s\n" "${text}"
	echo

	return 0
}

####################################################
#
# sc_print_prompt() text [space]
#
#	text	- the prompt
#	space	- preserve whitespace
#
#	Print the given "text" as a prompt.
#
#	This function always returns zero.
#
####################################################
sc_print_prompt()
{
	set -f
	typeset text="${1}"
	typeset space="${2}"

	if [[ -z "${space}" ]]; then
		text="$(echo ${text})"
	fi
	set +f

	printf "    %s  " "${text}"

	return 0
}

####################################################
#
# sc_print_para() text
#
#	text	- the text of the paragraph
#
#	Print the given "text" as a formatted paragraph to stdout.
#
#	This function always returns zero.
#
####################################################
sc_print_para()
{
	set -f
	typeset text="$(echo $*)"

	echo "${text}" | fmt -w 70 | sed 's/^/    /'
	echo

	return 0
}

####################################################
#
# sc_print_ctrld_message() [menuname]
#
#	menuname	- if not given, "Main Menu" is used in the message
#
#	Print the "Control-d" message.
#
#	This function always returns zero.
#
####################################################
sc_print_ctrld_message()
{
	typeset menuname=$1

	typeset -r sctxt_ctrld_message="$(gettext '
		Press Ctrl-d at any time to return to the %s.
	')"

	if [[ -z "${menuname}" ]]; then
		menuname="$(gettext 'Main Menu')"
	fi

	sc_print_para "$(printf "${sctxt_ctrld_message}" "${menuname}")"
}

####################################################
#
# sc_prompt() "prompt" [default [nonl [space]]]
#
#	prompt	- the prompt string
#	default	- the default value
#	nonl	- no extra newline after answer
#	space	- preserve whitespace
#
#	Display the prompt and return the user's answer.
#
#	If the trailing character of the prompt is ? or :, and
#	there is a "default", the trailer is re-positioned after
#	the "default".
#
#	The prompt is printed on file descriptor 4, and the answer
#	is printed to stdout.  File descriptor 4 should be
#	dupped to the original stdout before this function is called.
#
#	Return values:
#		0	- proceed
#		1	- ^D was typed
#
####################################################
sc_prompt()
{
	set -f
	typeset prompt="${1}"
	typeset default="${2}"
	typeset nonl="${3}"
	typeset space="${4}"

	if [[ -z "${space}" ]]; then
		prompt="$(echo ${prompt})"
	fi
	set +f

	typeset answer=
	typeset trailer
	typeset foo

	integer i

	# 
	# The caller of this function should have already opened
	# descriptor 4 as a dup of the original stdout.
	#
	# Dup this function's stdout to descriptor 5.
	# Then, re-direct stdout to descriptor 4.
	#
	# So, the default stdout from this function will go to
	# the original stdout (probably the tty).  Descriptor 5
	# has the answer printed to it.
	#
	exec 5>&1
	exec 1>&4

	# if there is a default value, shift the terminating ? or : character
	if [[ -n "${default}" ]]; then
		trailer=
		if [[ "${prompt}" = *\? ]]; then
			trailer="?"
		elif [[ "${prompt}" = *: ]]; then
			trailer=":"
		fi
		if [[ -n "${trailer}" ]]; then
			prompt="${prompt%${trailer}}"
			prompt="${prompt} [${default}]${trailer}"
		else
			prompt="${prompt} [${default}]"
		fi
	fi

	#
	# Display the prompt and get the user's response
	# Loop until an answer is given.   Or, if there is a
	# default, the user need not supply an answer.
	#
	# It is not legal for the user to supply more than one value.
	#
	let i=0
	while true
	do
		# If this is not the first time through, beep
		[[ ${i} -gt 0 ]] && echo "\a\c"
		let i=1

		# Prompt and get response
		sc_print_prompt "${prompt}" "${space}"
		read answer foo

		# Return 1 on EOF
		if [[ $? -eq 1 ]]; then
			echo
			return 1
		fi

		# If more than one arg, repeat prompt
		if [[ -n "${foo}" ]]; then
			continue
		fi

		# If no answer and default, default is the answer
		if [[ -z "${answer}" ]] && [[ -n "${default}" ]]; then
			answer="${default}"
		fi

		# If still no answer, repeat prompt
		if [[ -z "${answer}" ]]; then
			continue
		fi

		# Okay
		break
	done

	# Unless "nonl" was given, print extra newline
	if [[ -z "${nonl}" ]]; then
		echo
	fi

	echo "${answer}" >&5

	return 0
}

####################################################
#
# sc_prompt_yesno() "prompt" [default]
#
#	prompt	- the prompt string
#	default	- the default value
#
#	Display the yes/no prompt and return the user's answer.
#
#	The user may y, yes, Y, YES, n, no, N, or NO.
#	Function will always print "yes" or "no" on stdout.
#
#	The prompt is printed on file descriptor 4, and the answer
#	is printed to stdout.  File descriptor 4 should be
#	dupped to the original stdout before this function is called.
#
#	Return values:
#		0	- proceed
#		1	- ^D was typed
#
####################################################
sc_prompt_yesno()
{
	typeset prompt="${1}"
	typeset default="${2}"

	typeset answer=

	integer i

	# if there is a terminating ? and ${YES} is yes and ${NO} is no,
	# add (yes/no) and shift the terminating "?"
	if [[ "${YES}" = "yes" ]] &&
	    [[ "${NO}" = "no" ]] &&
	    [[ "${prompt}" = *\? ]]; then
		prompt="${prompt%\?}"
		prompt="${prompt} (yes/no)?"
	fi

	let i=0
	while true
	do
		# If this is not the first time through, beep
		[[ ${i} -gt 0 ]] && echo "\a\c" >&4
		let i=1

		# Prompt and get response
		answer=$(sc_prompt "${prompt}" "${default}" "nonl")

		# Return 1 on EOF
		if [[ $? -eq 1 ]]; then
			echo >&4
			return 1
		fi

		# I18N sensative answer always returns "yes" or "no" string
		case ${answer} in
		${YES} | yes | y | YES | Y)
			answer="yes"
			break
			;;

		${NO} | no | n | NO | N)
			answer="no"
			break
			;;

		*)
			answer=
			;;
		esac
	done
	echo >&4

	echo "${answer}"

	return 0
}

####################################################
#
# sc_prompt_pause()
#
#	Print message asking user to type the ENTER key,
#	then wait for a response from the keyboard.
#
#	Return values:
#		0	- proceed
#		1	- ^D was typed
#
####################################################
sc_prompt_pause()
{
	# Pause until they hit ENTER
	sc_print_prompt "\n$(gettext 'Hit ENTER to continue:')"
	read
	if [[ $? -ne 0 ]]; then
       		echo
		return 1
	fi
	echo

	return 0
}

####################################################
#
# sc_get_menuoption() "menuitem" [...]
#
#	menuitem	- a menu item
#
#	Print the menu described by the list of menu
#	items, and return the selected option.
#
#	Each menuitem has the following format:
#
#	    "<type>+<printstar>+<optionletter>+<text>"
#
#		<type>:		T1/T2	for title
#				N	for non-selectable menu option
#				S	for selectable menu option
#				R	for newline
#
#		<printstar>:	0 to NOT print *
#				1 to print *
#
#	The menu is printed on file descriptor 4, and the selected
#	option is printed to stdout.  File descriptor 4 should be
#	dupped to the original stdout before this function is called.
#
#	This function always returns zero.
#
####################################################
sc_get_menuoption()
{
	typeset type
	typeset printstar
	typeset optionletter
	typeset text

	typeset star=
	typeset selectable=
	typeset answer=
	typeset option=

	# 
	# The caller of this function should have already opened
	# descriptor 4 as a dup of the original stdout.
	#
	# Dup this function's stdout to descriptor 5.
	# Then, re-direct stdout to descriptor 4.
	#
	# So, the default stdout from this function will go to
	# the original stdout (probably the tty).  Descriptor 5
	# has the option letter printed to it.
	#
	exec 5>&1
	exec 1>&4

	# Process each menu item in the arg list
	while [[ $# -ne 0 ]]
	do
		# Each menu item has 4 fields;  the field seperator is "+"
		type="$(IFS=+ ; set -- ${1};  echo ${1})"
		printstar="$(IFS=+ ; set -- ${1};  echo ${2})"
		optionletter="$(IFS=+ ; set -- ${1};  echo ${3})"
		text="$(IFS=+ ; set -- ${1};  echo ${4})"

		# Switch on item type
		case ${type} in
		'T1')	# Title 1
			sc_print_title "${text}"
			;;

		'T2')	# Title 2
			sc_print_para "${text}"
			;;

		'N')	# Non-selectble menu item
			printf "      %1.1s%2.2s) %s\n" "" "${optionletter}" "${text}"
			;;

		'S')	# Selectble menu item
			star=
			if [[ "${printstar}" = 1 ]];  then
				star="*"
			fi
			printf "      %1.1s%2.2s) %s\n" "${star}" "${optionletter}" "${text}"
			selectable="${selectable} ${optionletter}"
			;;

		'R')	# Newline
			echo
			;;
		esac
		shift
	done

	# Get the selected menu option
	answer=
	if [[ -n "${selectable}" ]]; then
		echo
		while [[ -z "${answer}" ]]
		do
			answer=$(sc_prompt "$(gettext 'Option:')")
			for option in ${selectable}
			do
				if [[ "${answer}" = "${option}" ]]; then
					break 2
				fi
			done
			answer=

			# beep
			echo "\a\c"
		done
	fi

	echo "${answer}" >&5

	return 0
}

####################################################
#
# sc_get_scrolling_menuoptions() title header1 header2 minchoices
#    maxchoices "option" [...]
#
#	title		- if not NULL, print a menu title
#	header1		- if not NULL, print 1st column header
#	header2		- if not NULL, print 2nd column header
#	minchoices	- if not NULL, cannot quit until min options selected
#	maxchoices	- if not NULL, maximum number of choices
#	option		- text of menu option item
#
#	Print a menu using the list of "choices".  If the menu length
#	exceeds a certain value, the user may select "next" (and "previous").
#
#	The quit argument may take the format "<letter>:<text>", where
#	<letter> is the option letter to use;   or, it may simpley
#	contain <text>.
#
#	Any one of the "option" arguments may be given as ":<filename>",
#	where the <filename> contains one option per line.
#
#	If "minchoices" is given, the user may not quit the menu until
#	at list "minchoices" options have been selected.   The "quit"
#	prompt will not be displayed until "minchoices" options have been
#	selected.
#
#	If "maxchoices" is given, the menu automatically quits as soon
#	as "maxchoices" options have been selected.   If "maxchoices"
#	is not given or is given as 0, the user may select an unlimited
#	number of options.   The caller should always set "maxchoices"
#	to 1, if only one option is allowed.
#
#	The menu is printed on file descriptor 4, and the selected
#	choice(s) is printed to stdout.  File descriptor 4 should be
#	dupped to the original stdout before this function is called.
#
#	This function always returns zero.
#
####################################################
sc_get_scrolling_menuoptions()
{
	typeset title="${1}"
	typeset header1="${2}"
	typeset header2="${3}"
	integer minchoices="${4}"
	integer maxchoices="${5}"

	# We expect at least four arguments
	if [[ $# -lt 5 ]]; then
		return 0
	fi
	shift 5

	typeset option_array
	typeset -l choice			# lower case
	typeset chosen_list
	typeset -l chosen			# lower case
	typeset foo
	typeset q="q"
	typeset n="n"
	typeset p="p"
	typeset filename
	typeset prompt

	integer linemax=10
	integer line
	integer list_count
	integer num_options
	integer i
	integer j
	integer first
	integer err
	integer nl
	integer q_okay=0
	integer n_okay=0
	integer p_okay=0

	# 
	# The caller of this function should have already opened
	# descriptor 4 as a dup of the original stdout.
	#
	# Dup this function's stdout to descriptor 5.
	# Then, re-direct stdout to descriptor 4.
	#
	# So, the default stdout from this function will go to
	# the original stdout (probably the tty).  Descriptor 5
	# has the option letter printed to it.
	#
	exec 5>&1
	exec 1>&4

	# construct the array of options
	set -A option_array
	let num_options=0
	while [[ $# -ne 0 ]]
	do
		if [[ "${1}" == :* ]]; then
			filename=$(IFS=: ; set -- ${1};  echo $*)
			filename=$(echo ${filename})
			if [[ -n "${filename}" ]] &&
			    [[ -r ${filename} ]]; then
				while read option_array[num_options]
				do
					((num_options += 1))
				done <${filename}
			fi
		else
			option_array[num_options]="${1}"
			((num_options += 1))
		fi
		shift
	done
	if [[ ${num_options} -eq 0 ]]; then
		return 0
	fi

	# check min and max
	if [[ ${maxchoices} -gt 0 ]] &&
	    [[ ${minchoices} -gt ${maxchoices} ]]; then
		return 0
	fi
	if [[ ${num_options} -lt ${minchoices} ]] ||
	    [[ ${num_options} -lt ${maxchoices} ]]; then
		return 0
	fi

	# If there is only one option, we are done when they select it.
	if [[ ${num_options} -eq 1 ]]; then
		let maxchoices=1
	fi

	# Set the options prompt
	if [[ ${maxchoices} -eq 1 ]]; then
		prompt="$(gettext 'Option')"
	else
		prompt="$(gettext 'Option(s)')"
	fi

	# loop until the option or options are selected
	choice_list=
	let list_count=0
	let i=0
	let first=0
	while true
	do
		# Reset the line count
		let line=0

		# Initialize
		let q_okay=0
		let n_okay=0
		let p_okay=0
		let nl=0

		# Blank line
		echo

		# Print the title, if there is one
		if [[ -n "${title}" ]];  then
			printf "%4.4s%s\n" "" "${title}"
			echo
		fi

		# Print the headers, if specified
		if [[ -n "${header1}" ]];  then
			printf "%11.11s%s\n" "" "${header1}"
			if [[ -n "${header2}" ]];  then
				printf "%11.11s%s\n" "" "${header2}"
			fi
			echo
		fi

		# Print "screenfull" of choices
		while [[ ${i} -lt ${num_options} ]] &&
		    [[ ${line} -lt ${linemax} ]]
		do
			printf "%7.7s%2.2s) %s\n" "" "$((i+1))" "${option_array[i]}"
			((i += 1))
			((line += 1))
		done

		# If it is okay to quit, print it
		if [[ ${minchoices} -le ${list_count} ]]; then
			echo
			let nl=1

			printf "%7.7s%2.2s) %s\n" "" "${q}" "$(gettext 'Done')"
			let q_okay=1
		fi

		# Add "Next" or "Previous", if necessary
		if [[ ${i} -eq ${num_options} ]] ||
		    [[ ${line} -eq ${linemax} ]];  then

			if [[ ${i} -gt ${linemax} ]];  then
				if [[ ${nl} -eq 0 ]]; then
					echo
					let nl=1
				fi

				printf "%7.7s%2.2s) %s\n" "" "${p}" "$(gettext 'Previous')"
				let p_okay=1
			fi
			if [[ ${i} -lt ${num_options} ]]; then
				if [[ ${nl} -eq 0 ]]; then
					echo
				fi

				printf "%7.7s%2.2s) %s\n" "" "${n}" "$(gettext 'Next')"
				let n_okay=1
			fi
		fi

		# Reset i
		((i -= ${line}))

		# Get choice or choices
		echo
		while true
		do
			# Initialize
			chosen=

			# Not the first time through?
			if [[ ${first} -gt 0 ]]; then
				
				# Print choice_list, if there is one
				if [[ ${list_count} -gt 0 ]]; then

					# Re-print the menu with done option?
					if [[ ${minchoices} -le ${list_count} ]] && [[ ${q_okay} -eq 0 ]]; then
						continue 2
					fi

					sc_print_prompt " $(gettext 'Selected:')" "space"
					let j=0
					for choice in ${choice_list}
					do
						if [[ ${j} -gt 0 ]]; then
							echo ",\c"
						fi
						echo "${choice}\c"
						((j += 1))
					done
					echo
					echo

				# Otherwise, beep
				else
					echo "\a\c"
				fi
			fi
			((first += 1))

			# Get the answer
			sc_print_prompt "${prompt}:"
			read chosen foo

			# Ctrl-D
			if [[ $? -ne 0 ]]; then
				return 0
			fi

			# If no answer, repeat
			if [[ -z "${chosen}" ]]; then
				continue 2
			fi

			# If more than one answer, make sure it is allowed
			if [[ ${maxchoices} -ne 1 ]]; then
				chosen="${chosen} ${foo}"

				# Get rid of any commas
				chosen="$(IFS=, ; echo ${chosen})"

			elif [[ -n "${foo}" ]]; then
				printf "\n$(gettext 'Please select one option.')\n\n\a"
				continue
			fi

			# Make sure that all options are valid
			for choice in ${chosen}
			do
				# Quit?
				if [[ "${choice}" == "${q}" ]] &&
				    [[ ${q_okay} -gt 0 ]]; then
					break 3
				fi

				# Next?
				if [[ "${choice}" == "${n}" ]] &&
				    [[ ${n_okay} -gt 0 ]]; then
					((i += ${line}))
					continue 3
				fi

				# Previous?
				if [[ "${choice}" == "${p}" ]] &&
				    [[ ${p_okay} -gt 0 ]]; then
					((i -= ${linemax}))
					if [[ ${i} -lt 0 ]]; then
						let i=0
					fi
					continue 3
				fi

				# Valid option?
				let err=0
				is_numeric "${choice}"
				if [[ $? -ne 0 ]]; then
					((err += 1))
				elif [[ ${choice} -lt 1 ]] ||
				    [[ ${choice} -gt ${num_options} ]]; then
					((err += 1))
				fi

				if [[ ${err} -gt 0 ]]; then
					if [[ ${maxchoices} -eq 1 ]]; then
						printf "\n$(gettext 'Unrecognized option - \"%s\".')\n\n\a" ${choice}
						continue 2
					else
						printf "$(gettext 'Ignoring unrecognized option - \"%s\".')\n\a" ${choice}
						continue
					fi
				fi

				# Make sure that it is not already given
				for foo in ${choice_list}
				do
					if [[ ${foo} == ${choice} ]]; then
						printf "$(gettext 'Ignoring duplicate option - \"%s\".')\n\a" ${choice}
						continue 2
					fi
				done

				# Add it to the list of choices
				choice_list="${choice_list} ${choice}"
				((list_count += 1))

				# If we hit the maximum, we are done
				if [[ ${list_count} -eq ${maxchoices} ]]; then
					break 3
				fi
			done
		done
	done

	# Newline
	echo

	# Print the first word of each choice from the list of choices
	let first=0
	for choice in ${choice_list}
	do
		if [[ ${first} -ne 0 ]]; then
			echo " \c"
		fi
		(set -- ${option_array[(choice - 1)]}; echo "${1}\c")
		((first += 1))
	done >&5
	echo >&5

	# Done
	return 0
}

#####################################################
#
# is_numeric() value
#
#	Return 0 if the given value is numeric.
#
#	Return values:
#		0	- the "value" is numeric
#		1	- the "value" is not numeric
#	
#####################################################
is_numeric()
{
	typeset value=${1}

	# If "value" is not given, it is not numeric
	if [[ -z "${value}" ]]; then
		return 1
	fi

	# If not numeric, return 1
	if [[ $(expr "${value}" : '[0-9]*') -ne ${#value} ]]; then
		return 1
	fi

	# Numeric
	return 0
}

#####################################################
#
# verify_isroot()
#
#	Print an error message and return non-zero
#	if the user is not root.
#
#####################################################
verify_isroot()
{
	typeset -r uid=$(expr "$(id)" : 'uid=\([0-9]*\)*')

	# make sure uid was set
	if [[ -z "${uid}" ]]; then
		printf "$(gettext '%s:  Cannot get uid')\n" ${PROG} >&2
		return 1
	fi

	# check for root
	if [[ ${uid} -ne 0 ]]; then
		printf "$(gettext '%s:  Must be root')\n" "${PROG}" >&2
		return 1
	fi

	return 0
}
