dialog.subr revision 258420
1if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1
2#
3# Copyright (c) 2006-2013 Devin Teske
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: stable/10/usr.sbin/bsdconfig/share/dialog.subr 258420 2013-11-21 03:38:47Z dteske $
28#
29############################################################ INCLUDES
30
31BSDCFG_SHARE="/usr/share/bsdconfig"
32. $BSDCFG_SHARE/common.subr || exit 1
33f_dprintf "%s: loading includes..." dialog.subr
34f_include $BSDCFG_SHARE/strings.subr
35f_include $BSDCFG_SHARE/variable.subr
36
37BSDCFG_LIBE="/usr/libexec/bsdconfig"
38f_include_lang $BSDCFG_LIBE/include/messages.subr
39
40############################################################ CONFIGURATION
41
42#
43# Default file descriptor to link to stdout for dialog(1) passthru allowing
44# execution of dialog from within a sub-shell (so-long as its standard output
45# is explicitly redirected to this file descriptor).
46#
47: ${DIALOG_TERMINAL_PASSTHRU_FD:=${TERMINAL_STDOUT_PASSTHRU:-3}}
48
49############################################################ GLOBALS
50
51#
52# Default name of dialog(1) utility
53# NOTE: This is changed to "Xdialog" by the optional `-X' argument
54#
55DIALOG="dialog"
56
57#
58# Default dialog(1) title and backtitle text
59#
60DIALOG_TITLE="$pgm"
61DIALOG_BACKTITLE="bsdconfig"
62
63#
64# Settings used while interacting with dialog(1)
65#
66DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz"
67
68#
69# Declare that we are fully-compliant with Xdialog(1) by unset'ing all
70# compatibility settings.
71#
72unset XDIALOG_HIGH_DIALOG_COMPAT
73unset XDIALOG_FORCE_AUTOSIZE
74unset XDIALOG_INFOBOX_TIMEOUT
75
76#
77# Exit codes for [X]dialog(1)
78#
79DIALOG_OK=${SUCCESS:-0}
80DIALOG_CANCEL=${FAILURE:-1}
81DIALOG_HELP=2
82DIALOG_ITEM_HELP=2
83DIALOG_EXTRA=3
84DIALOG_ITEM_HELP=4
85export DIALOG_ERROR=254 # sh(1) can't handle the default of `-1'
86DIALOG_ESC=255
87
88#
89# Default behavior is to call f_dialog_init() automatically when loaded.
90#
91: ${DIALOG_SELF_INITIALIZE=1}
92
93#
94# Default terminal size (used if/when running without a controlling terminal)
95#
96: ${DEFAULT_TERMINAL_SIZE:=24 80}
97
98#
99# Minimum width(s) for various dialog(1) implementations (sensible global
100# default(s) for all widgets of a given variant)
101#
102: ${DIALOG_MIN_WIDTH:=24}
103: ${XDIALOG_MIN_WIDTH:=35}
104
105#
106# When manually sizing Xdialog(1) widgets such as calendar and timebox, you'll
107# need to know the size of the embedded GUI objects because the height passed
108# to Xdialog(1) for these widgets has to be tall enough to accomodate them.
109#
110# These values are helpful when manually sizing with dialog(1) too, but in a
111# different way. dialog(1) does not make you accomodate the custom items in the
112# height (but does for width) -- a height of 3 will display three lines and a
113# full calendar, for example (whereas Xdialog will truncate the calendar if
114# given a height of 3). For dialog(1), use these values for making sure that
115# the height does not exceed max_height (obtained by f_dialog_max_size()).
116#
117DIALOG_CALENDAR_HEIGHT=15
118DIALOG_TIMEBOX_HEIGHT=6
119
120############################################################ GENERIC FUNCTIONS
121
122# f_dialog_data_sanitize $var_to_edit ...
123#
124# When using dialog(1) or Xdialog(1) sometimes unintended warnings or errors
125# are generated from underlying libraries. For example, if $LANG is set to an
126# invalid or unknown locale, the warnings from the Xdialog(1) libraries will
127# clutter the output. This function helps by providing a centralied function
128# that removes spurious warnings from the dialog(1) (or Xdialog(1)) response.
129#
130# Simply pass the name of one or more variables that need to be sanitized.
131# After execution, the variables will hold their newly-sanitized data.
132#
133f_dialog_data_sanitize()
134{
135	if [ "$#" -eq 0 ]; then
136		f_dprintf "%s: called with zero arguments" \
137		          f_dialog_response_sanitize
138		return $FAILURE
139	fi
140
141	local __var_to_edit
142	for __var_to_edit in $*; do
143		# Skip warnings and trim leading/trailing whitespace
144		setvar $__var_to_edit "$( f_getvar $__var_to_edit | awk '
145			BEGIN { data = 0 }
146			{
147				if ( ! data )
148				{
149					if ( $0 ~ /^$/ ) next
150					if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
151					data = 1
152				}
153				print
154			}
155		' )"
156	done
157}
158
159# f_dialog_line_sanitize $var_to_edit ...
160#
161# When using dialog(1) or Xdialog(1) sometimes unintended warnings or errors
162# are generated from underlying libraries. For example, if $LANG is set to an
163# invalid or unknown locale, the warnings from the Xdialog(1) libraries will
164# clutter the output. This function helps by providing a centralied function
165# that removes spurious warnings from the dialog(1) (or Xdialog(1)) response.
166#
167# Simply pass the name of one or more variables that need to be sanitized.
168# After execution, the variables will hold their newly-sanitized data.
169#
170# This function, unlike f_dialog_data_sanitize(), also removes leading/trailing
171# whitespace from each line.
172#
173f_dialog_line_sanitize()
174{
175	if [ "$#" -eq 0 ]; then
176		f_dprintf "%s: called with zero arguments" \
177		          f_dialog_response_sanitize
178		return $FAILURE
179	fi
180
181	local __var_to_edit
182	for __var_to_edit in $*; do
183		# Skip warnings and trim leading/trailing whitespace
184		setvar $__var_to_edit "$( f_getvar $__var_to_edit | awk '
185			BEGIN { data = 0 }
186			{
187				if ( ! data )
188				{
189					if ( $0 ~ /^$/ ) next
190					if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
191					data = 1
192				}
193				sub(/^[[:space:]]*/, "")
194				sub(/[[:space:]]*$/, "")
195				print
196			}
197		' )"
198	done
199}
200
201############################################################ TITLE FUNCTIONS
202
203# f_dialog_title [$new_title]
204#
205# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1)
206# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first
207# argument is NULL, the current title is returned.
208#
209# Each time this function is called, a backup of the current values is made
210# allowing a one-time (single-level) restoration of the previous title using
211# the f_dialog_title_restore() function (below).
212#
213f_dialog_title()
214{
215	local new_title="$1"
216
217	if [ "${1+set}" ]; then
218		if [ "$USE_XDIALOG" ]; then
219			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
220			DIALOG_BACKTITLE="$new_title"
221		else
222			_DIALOG_TITLE="$DIALOG_TITLE"
223			DIALOG_TITLE="$new_title"
224		fi
225	else
226		if [ "$USE_XDIALOG" ]; then
227			echo "$DIALOG_BACKTITLE"
228		else
229			echo "$DIALOG_TITLE"
230		fi
231	fi
232}
233
234# f_dialog_title_restore
235#
236# Restore the previous title set by the last call to f_dialog_title().
237# Restoration is non-recursive and only works to restore the most-recent title.
238#
239f_dialog_title_restore()
240{
241	if [ "$USE_XDIALOG" ]; then
242		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
243	else
244		DIALOG_TITLE="$_DIALOG_TITLE"
245	fi
246}
247
248# f_dialog_backtitle [$new_backtitle]
249#
250# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of
251# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the
252# first argument is NULL, the current backtitle is returned.
253#
254f_dialog_backtitle()
255{
256	local new_backtitle="$1"
257
258	if [ "${1+set}" ]; then
259		if [ "$USE_XDIALOG" ]; then
260			_DIALOG_TITLE="$DIALOG_TITLE"
261			DIALOG_TITLE="$new_backtitle"
262		else
263			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
264			DIALOG_BACKTITLE="$new_backtitle"
265		fi
266	else
267		if [ "$USE_XDIALOG" ]; then
268			echo "$DIALOG_TITLE"
269		else
270			echo "$DIALOG_BACKTITLE"
271		fi
272	fi
273}
274
275# f_dialog_backtitle_restore
276#
277# Restore the previous backtitle set by the last call to f_dialog_backtitle().
278# Restoration is non-recursive and only works to restore the most-recent
279# backtitle.
280#
281f_dialog_backtitle_restore()
282{
283	if [ "$USE_XDIALOG" ]; then
284		DIALOG_TITLE="$_DIALOG_TITLE"
285	else
286		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
287	fi
288}
289
290############################################################ SIZE FUNCTIONS
291
292# f_dialog_max_size $var_height $var_width
293#
294# Get the maximum height and width for a dialog widget and store the values in
295# $var_height and $var_width (respectively).
296#
297f_dialog_max_size()
298{
299	local __var_height="$1" __var_width="$2" __max_size
300	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
301	if [ "$USE_XDIALOG" ]; then
302		__max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
303	else
304		if __max_size=$( $DIALOG --print-maxsize \
305			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD )
306		then
307			# usually "MaxSize: 24, 80"
308			__max_size="${__max_size#*: }"
309			f_replaceall "$__max_size" "," "" __max_size
310		else
311			__max_size=$( stty size 2> /dev/null )
312			# usually "24 80"
313		fi
314		: ${__max_size:=$DEFAULT_TERMINAL_SIZE}
315	fi
316	if [ "$__var_height" ]; then
317		local __height="${__max_size%%[$IFS]*}"
318		#
319		# If we're not using Xdialog(1), we should assume that $DIALOG
320		# will render --backtitle behind the widget. In such a case, we
321		# should prevent a widget from obscuring the backtitle (unless
322		# $NO_BACKTITLE is set and non-NULL, allowing a trap-door).
323		#
324		if [ ! "$USE_XDIALOG" ] && [ ! "$NO_BACKTITLE" ]; then
325			#
326			# If use_shadow (in ~/.dialogrc) is OFF, we need to
327			# subtract 4, otherwise 5. However, don't check this
328			# every time, rely on an initialization variable set
329			# by f_dialog_init().
330			#
331			local __adjust=5
332			[ "$NO_SHADOW" ] && __adjust=4
333
334			# Don't adjust height if already too small (allowing
335			# obscured backtitle for small values of __height).
336			[ ${__height:-0} -gt 11 ] &&
337				__height=$(( $__height - $__adjust ))
338		fi
339		setvar "$__var_height" "$__height"
340	fi
341	[ "$__var_width" ] && setvar "$__var_width" "${__max_size##*[$IFS]}"
342}
343
344# f_dialog_size_constrain $var_height $var_width [$min_height [$min_width]]
345#
346# Modify $var_height to be no-less-than $min_height (if given; zero otherwise)
347# and no-greater-than terminal height (or screen height if $USE_XDIALOG is
348# set).
349#
350# Also modify $var_width to be no-less-than $XDIALOG_MIN_WIDTH (or
351# $XDIALOG_MIN_WIDTH if $_USE_XDIALOG is set) and no-greater-than terminal
352# or screen width. The use of $[X]DIALOG_MIN_WIDTH can be overridden by
353# passing $min_width.
354#
355# Return status is success unless one of the passed arguments is invalid
356# or all of the $var_* arguments are either NULL or missing.
357#
358f_dialog_size_constrain()
359{
360	local __var_height="$1" __var_width="$2"
361	local __min_height="$3" __min_width="$4"
362	local __retval=$SUCCESS
363
364	# Return failure unless at least one var_* argument is passed
365	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
366
367	#
368	# Print debug warnings if any given (non-NULL) argument are invalid
369	# NOTE: Don't change the name of $__{var,min,}{height,width}
370	#
371	local __height __width
372	local __arg __cp __fname=f_dialog_size_constrain 
373	for __arg in height width; do
374		debug= f_getvar __var_$__arg __cp
375		[ "$__cp" ] || continue
376		if ! debug= f_getvar "$__cp" __$__arg; then
377			f_dprintf "%s: var_%s variable \`%s' not set" \
378			          $__fname $__arg "$__cp"
379			__retval=$FAILURE
380		elif ! eval f_isinteger \$__$__arg; then
381			f_dprintf "%s: var_%s variable value not a number" \
382			          $__fname $__arg
383			__retval=$FAILURE
384		fi
385	done
386	for __arg in height width; do
387		debug= f_getvar __min_$__arg __cp
388		[ "$__cp" ] || continue
389		f_isinteger "$__cp" && continue
390		f_dprintf "%s: min_%s value not a number" $__fname $__arg
391		__retval=$FAILURE
392		setvar __min_$__arg ""
393	done
394
395	# Obtain maximum height and width values
396	# NOTE: Function name appended to prevent __var_{height,width} values
397	#       from becoming local (and thus preventing setvar from working).
398	local __max_height_size_constain __max_width_size_constrain
399	f_dialog_max_size \
400		__max_height_size_constrain __max_width_size_constrain
401
402	# Adjust height if desired
403	if [ "$__var_height" ]; then
404		if [ $__height -lt ${__min_height:-0} ]; then
405			setvar "$__var_height" $__min_height
406		elif [ $__height -gt $__max_height_size_constrain ]; then
407			setvar "$__var_height" $__max_height_size_constrain
408		fi
409	fi
410
411	# Adjust width if desired
412	if [ "$__var_width" ]; then
413		if [ "$USE_XDIALOG" ]; then
414			: ${__min_width:=${XDIALOG_MIN_WIDTH:-35}}
415		else
416			: ${__min_width:=${DIALOG_MIN_WIDTH:-24}}
417		fi
418		if [ $__width -lt $__min_width ]; then
419			setvar "$__var_width" $__min_width
420		elif [ $__width -gt $__max_width_size_constrain ]; then
421			setvar "$__var_width" $__max_width_size_constrain
422		fi
423	fi
424
425	if [ "$debug" ]; then
426		# Print final constrained values to debugging
427		[ "$__var_height" ] && f_quietly f_getvar "$__var_height"
428		[ "$__var_width"  ] && f_quietly f_getvar "$__var_width"
429	fi
430
431	return $__retval # success if no debug warnings were printed
432}
433
434# f_dialog_menu_constrain $var_height $var_width $var_rows "$prompt" \
435#                         [$min_height [$min_width [$min_rows]]]
436#
437# Modify $var_height to be no-less-than $min_height (if given; zero otherwise)
438# and no-greater-than terminal height (or screen height if $USE_XDIALOG is
439# set).
440#
441# Also modify $var_width to be no-less-than $XDIALOG_MIN_WIDTH (or
442# $XDIALOG_MIN_WIDTH if $_USE_XDIALOG is set) and no-greater-than terminal
443# or screen width. The use of $[X]DIALOG_MIN_WIDTH can be overridden by
444# passing $min_width.
445#
446# Last, modify $var_rows to be no-less-than $min_rows (if specified; zero
447# otherwise) and no-greater-than (max_height - 8) where max_height is the
448# terminal height (or screen height if $USE_XDIALOG is set). If $prompt is NULL
449# or missing, dialog(1) allows $var_rows to be (max_height - 7), maximizing the
450# number of visible rows.
451#
452# Return status is success unless one of the passed arguments is invalid
453# or all of the $var_* arguments are either NULL or missing.
454#
455f_dialog_menu_constrain()
456{
457	local __var_height="$1" __var_width="$2" __var_rows="$3" __prompt="$4"
458	local __min_height="$5" __min_width="$6" __min_rows="$7"
459
460	# Return failure unless at least one var_* argument is passed
461	[ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
462		return $FAILURE
463
464	#
465	# Print debug warnings if any given (non-NULL) argument are invalid
466	# NOTE: Don't change the name of $__{var,min,}{height,width,rows}
467	#
468	local __height __width __rows
469	local __arg __cp __fname=f_dialog_menu_constrain 
470	for __arg in height width rows; do
471		debug= f_getvar __var_$__arg __cp
472		[ "$__cp" ] || continue
473		if ! debug= f_getvar "$__cp" __$__arg; then
474			f_dprintf "%s: var_%s variable \`%s' not set" \
475			          $__fname $__arg "$__cp"
476			__retval=$FAILURE
477		elif ! eval f_isinteger \$__$__arg; then
478			f_dprintf "%s: var_%s variable value not a number" \
479			          $__fname $__arg
480			__retval=$FAILURE
481		fi
482	done
483	for __arg in height width rows; do
484		debug= f_getvar __min_$__arg __cp
485		[ "$__cp" ] || continue
486		f_isinteger "$__cp" && continue
487		f_dprintf "%s: min_%s value not a number" $__fname $__arg
488		__retval=$FAILURE
489		setvar __min_$__arg ""
490	done
491
492	# Obtain maximum height and width values
493	# NOTE: Function name appended to prevent __var_{height,width} values
494	#       from becoming local (and thus preventing setvar from working).
495	local __max_height_menu_constrain __max_width_menu_constrain
496	f_dialog_max_size \
497		__max_height_menu_constrain __max_width_menu_constrain
498
499	# Adjust height if desired
500	if [ "$__var_height" ]; then
501		if [ $__height -lt ${__min_height:-0} ]; then
502			setvar "$__var_height" $__min_height
503		elif [ $__height -gt $__max_height_menu_constrain ]; then
504			setvar "$__var_height" $__max_height_menu_constrain
505		fi
506	fi
507
508	# Adjust width if desired
509	if [ "$__var_width" ]; then
510		if [ "$USE_XDIALOG" ]; then
511			: ${__min_width:=${XDIALOG_MIN_WIDTH:-35}}
512		else
513			: ${__min_width:=${DIALOG_MIN_WIDTH:-24}}
514		fi
515		if [ $__width -lt $__min_width ]; then
516			setvar "$__var_width" $__min_width
517		elif [ $__width -gt $__max_width_menu_constrain ]; then
518			setvar "$__var_width" $__max_width_menu_constrain
519		fi
520	fi
521
522	# Adjust rows if desired
523	if [ "$__var_rows" ]; then
524		if [ "$USE_XDIALOG" ]; then
525			: ${__min_rows:=1}
526		else
527			: ${__min_rows:=0}
528		fi
529
530		local __max_rows=$(( $__max_height_menu_constrain - 7 ))
531		# If prompt_len is zero (no prompt), bump the max-rows by 1
532		# Default assumption is (if no argument) that there's no prompt
533		[ ${__prompt_len:-0} -gt 0 ] ||
534			__max_rows=$(( $__max_rows + 1 ))
535
536		if [ $__rows -lt $__min_rows ]; then
537			setvar "$__var_rows" $__min_rows
538		elif [ $__rows -gt $__max_rows ]; then
539			setvar "$__var_rows" $__max_rows
540		fi
541	fi
542
543	if [ "$debug" ]; then
544		# Print final constrained values to debugging
545		[ "$__var_height" ] && f_quietly f_getvar "$__var_height"
546		[ "$__var_width"  ] && f_quietly f_getvar "$__var_width"
547		[ "$__var_rows"   ] && f_quietly f_getvar "$__var_rows"
548	fi
549
550	return $__retval # success if no debug warnings were printed
551}
552
553# f_dialog_infobox_size [-n] $var_height $var_width \
554#                       $title $backtitle $prompt [$hline]
555#
556# Not all versions of dialog(1) perform auto-sizing of the width and height of
557# `--infobox' boxes sensibly.
558#
559# This function helps solve this issue by taking two sets of sequential
560# arguments. The first set of arguments are the variable names to use when
561# storing the calculated height and width. The second set of arguments are the
562# title, backtitle, prompt, and [optionally] hline. The optimal height and
563# width for the described widget (not exceeding the actual terminal height or
564# width) is stored in $var_height and $var_width (respectively).
565#
566# If the first argument is `-n', the calculated sizes ($var_height and
567# $var_width) are not constrained to minimum/maximum values.
568#
569# Newline character sequences (``\n'') in $prompt are expanded as-is done by
570# dialog(1).
571#
572f_dialog_infobox_size()
573{
574	local __constrain=1
575	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
576	local __var_height="$1" __var_width="$2"
577	local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
578
579	# Return unless at least one size aspect has been requested
580	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
581
582	# Default height/width of zero for auto-sizing
583	local __height=0 __width=0 __n
584
585	# Adjust height if desired
586	if [ "$__var_height" ]; then
587		#
588		# Set height based on number of rows in prompt
589		#
590		__n=$( echo -n "$__prompt" | f_number_of_lines )
591		__n=$(( $__n + 2 ))
592		[ $__n -gt $__height ] && __height=$__n
593
594		#
595		# For Xdialog(1) bump height if backtitle is enabled (displayed
596		# in the X11 window with a separator line between the backtitle
597		# and msg text).
598		#
599		if [ "$USE_XDIALOG" -a "$__btitle" ]; then
600			__n=$( echo "$__btitle" | f_number_of_lines )
601			__height=$(( $__height + $__n + 2 ))
602		fi
603
604		setvar "$__var_height" $__height
605	fi
606
607	# Adjust width if desired
608	if [ "$__var_width" ]; then
609		#
610		# Bump width for long titles
611		#
612		__n=$(( ${#__title} + 4 ))
613		[ $__n -gt $__width ] && __width=$__n
614
615		#
616		# If using Xdialog(1), bump width for long backtitles (which
617		# appear within the window).
618		#
619		if [ "$USE_XDIALOG" ]; then
620			__n=$(( ${#__btitle} + 4 ))
621			[ $__n -gt $__width ] && __width=$__n
622		fi
623
624		#
625		# Bump width for long prompts
626		#
627		__n=$( echo "$__prompt" | f_longest_line_length )
628		__n=$(( $__n + 4 )) # add width for border
629		[ $__n -gt $__width ] && __width=$__n
630
631		#
632		# Bump width for long hlines. Xdialog(1) supports `--hline' but
633		# it's currently not used (so don't do anything here if using
634		# Xdialog(1)).
635		#
636		if [ ! "$USE_XDIALOG" ]; then
637			__n=$(( ${#__hline} + 10 ))
638			[ $__n -gt $__width ] && __width=$__n
639		fi
640
641		# Bump width by 16.6% if using Xdialog(1)
642		[ "$USE_XDIALOG" ] && __width=$(( $__width + $__width / 6 ))
643
644		setvar "$__var_width" $__width
645	fi
646
647	# Constrain values to sensible minimums/maximums unless `-n' was passed
648	# Return success if no-constrain, else return status from constrain
649	[ ! "$__constrain" ] ||
650		f_dialog_size_constrain "$__var_height" "$__var_width"
651}
652
653# f_dialog_buttonbox_size [-n] $var_height $var_width \
654#                         $title $backtitle $prompt [$hline]
655#
656# Not all versions of dialog(1) perform auto-sizing of the width and height of
657# `--msgbox' and `--yesno' boxes sensibly.
658#
659# This function helps solve this issue by taking two sets of sequential
660# arguments. The first set of arguments are the variable names to use when
661# storing the calculated height and width. The second set of arguments are the
662# title, backtitle, prompt, and [optionally] hline. The optimal height and
663# width for the described widget (not exceeding the actual terminal height or
664# width) is stored in $var_height and $var_width (respectively).
665#
666# If the first argument is `-n', the calculated sizes ($var_height and
667# $var_width) are not constrained to minimum/maximum values.
668#
669# Newline character sequences (``\n'') in $prompt are expanded as-is done by
670# dialog(1).
671#
672f_dialog_buttonbox_size()
673{
674	local __constrain=1
675	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
676	local __var_height="$1" __var_width="$2"
677	local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
678
679	# Return unless at least one size aspect has been requested
680	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
681
682	# Calculate height/width of infobox (adjusted/constrained below)
683	# NOTE: Function name appended to prevent __var_{height,width} values
684	#       from becoming local (and thus preventing setvar from working).
685	local __height_bbox_size __width_bbox_size
686	f_dialog_infobox_size -n \
687		"${__var_height:+__height_bbox_size}" \
688		"${__var_width:+__width_bbox_size}" \
689		"$__title" "$__btitle" "$__prompt" "$__hline"
690
691	# Adjust height if desired
692	if [ "$__var_height" ]; then
693		# Add height to accomodate the buttons
694		__height_bbox_size=$(( $__height_bbox_size + 2 ))
695
696		# Adjust for clipping with Xdialog(1) on Linux/GTK2
697		[ "$USE_XDIALOG" ] &&
698			__height_bbox_size=$(( $__height_bbox_size + 3 ))
699
700		setvar "$__var_height" $__height_bbox_size
701	fi
702
703	# No adjustemnts to width, just pass-thru the infobox width
704	if [ "$__var_width" ]; then
705		setvar "$__var_width" $__width_bbox_size
706	fi
707
708	# Constrain values to sensible minimums/maximums unless `-n' was passed
709	# Return success if no-constrain, else return status from constrain
710	[ ! "$__constrain" ] ||
711		f_dialog_size_constrain "$__var_height" "$__var_width"
712}
713
714# f_dialog_inputbox_size [-n] $var_height $var_width \
715#                        $title $backtitle $prompt $init [$hline]
716#
717# Not all versions of dialog(1) perform auto-sizing of the width and height of
718# `--inputbox' boxes sensibly.
719#
720# This function helps solve this issue by taking two sets of sequential
721# arguments. The first set of arguments are the variable names to use when
722# storing the calculated height and width. The second set of arguments are the
723# title, backtitle, prompt, and [optionally] hline. The optimal height and
724# width for the described widget (not exceeding the actual terminal height or
725# width) is stored in $var_height and $var_width (respectively).
726#
727# If the first argument is `-n', the calculated sizes ($var_height and
728# $var_width) are not constrained to minimum/maximum values.
729#
730# Newline character sequences (``\n'') in $prompt are expanded as-is done by
731# dialog(1).
732#
733f_dialog_inputbox_size()
734{
735	local __constrain=1
736	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
737	local __var_height="$1" __var_width="$2"
738	local __title="$3" __btitle="$4" __prompt="$5" __init="$6" __hline="$7"
739
740	# Return unless at least one size aspect has been requested
741	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
742
743	# Calculate height/width of buttonbox (adjusted/constrained below)
744	# NOTE: Function name appended to prevent __var_{height,width} values
745	#       from becoming local (and thus preventing setvar from working).
746	local __height_ibox_size __width_ibox_size
747	f_dialog_buttonbox_size -n \
748		"${__var_height:+__height_ibox_size}" \
749		"${__var_width:+__width_ibox_size}" \
750		"$__title" "$__btitle" "$__prompt" "$__hline"
751
752	# Adjust height if desired
753	if [ "$__var_height" ]; then
754		# Add height for input box (not needed for Xdialog(1))
755		[ ! "$USE_XDIALOG" ] &&
756			__height_ibox_size=$(( $__height_ibox_size + 3 ))
757
758		setvar "$__var_height" $__height_ibox_size
759	fi
760
761	# Adjust width if desired
762	if [ "$__var_width" ]; then
763		# Bump width for initial text (something neither dialog(1) nor
764		# Xdialog(1) do, but worth it!; add 16.6% if using Xdialog(1))
765		local __n=$(( ${#__init} + 7 ))
766		[ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 ))
767		[ $__n -gt $__width_ibox_size ] && __width_ibox_size=$__n
768
769		setvar "$__var_width" $__width_ibox_size
770	fi
771
772	# Constrain values to sensible minimums/maximums unless `-n' was passed
773	# Return success if no-constrain, else return status from constrain
774	[ ! "$__constrain" ] ||
775		f_dialog_size_constrain "$__var_height" "$__var_width"
776}
777
778# f_xdialog_2inputsbox_size [-n] $var_height $var_width \
779#                           $title $backtitle $prompt \
780#                           $label1 $init1 $label2 $init2
781#
782# Xdialog(1) does not perform auto-sizing of the width and height of
783# `--2inputsbox' boxes sensibly.
784#
785# This function helps solve this issue by taking two sets of sequential
786# arguments. The first set of arguments are the variable names to use when
787# storing the calculated height and width. The second set of arguments are the
788# title, backtitle, prompt, label for the first field, initial text for said
789# field, label for the second field, and initial text for said field. The
790# optimal height and width for the described widget (not exceeding the actual
791# terminal height or width) is stored in $var_height and $var_width
792# (respectively).
793#
794# If the first argument is `-n', the calculated sizes ($var_height and
795# $var_width) are not constrained to minimum/maximum values.
796#
797# Newline character sequences (``\n'') in $prompt are expanded as-is done by
798# Xdialog(1).
799#
800f_xdialog_2inputsbox_size()
801{
802	local __constrain=1
803	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
804	local __var_height="$1" __var_width="$2"
805	local __title="$3" __btitle="$4" __prompt="$5"
806	local __label1="$6" __init1="$7" __label2="$8" __init2="$9"
807
808	# Return unless at least one size aspect has been requested
809	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
810
811	# Calculate height/width of inputbox (adjusted/constrained below)
812	# NOTE: Function name appended to prevent __var_{height,width} values
813	#       from becoming local (and thus preventing setvar from working).
814	local __height_2ibox_size __width_2ibox_size
815	f_dialog_inputbox_size -n \
816		"${__var_height:+__height_2ibox_size}" \
817		"${__var_width:+__width_2ibox_size}" \
818		"$__title" "$__btitle" "$__prompt" "$__hline" "$__init1"
819	
820	# Adjust height if desired
821	if [ "$__var_height" ]; then
822		# Add height for 1st label, 2nd label, and 2nd input box
823		__height_2ibox_size=$(( $__height_2ibox_size + 2 + 2 + 2  ))
824		setvar "$__var_height" $__height_2ibox_size
825	fi
826
827	# Adjust width if desired
828	if [ "$__var_width" ]; then
829		local __n
830
831		# Bump width for first label text (+16.6% since Xdialog(1))
832		__n=$(( ${#__label1} + 7 ))
833		__n=$(( $__n + $__n / 6 ))
834		[ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
835
836		# Bump width for second label text (+16.6% since Xdialog(1))
837		__n=$(( ${#__label2} + 7 ))
838		__n=$(( $__n + $__n / 6 ))
839		[ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
840
841		# Bump width for 2nd initial text (something neither dialog(1)
842		# nor Xdialog(1) do, but worth it!; +16.6% since Xdialog(1))
843		__n=$(( ${#__init2} + 7 ))
844		__n=$(( $__n + $__n / 6 ))
845		[ $__n -gt $__width_2ibox_size ] && __width_2ibox_size=$__n
846
847		setvar "$__var_width" $__width_2ibox_size
848	fi
849
850	# Constrain values to sensible minimums/maximums unless `-n' was passed
851	# Return success if no-constrain, else return status from constrain
852	[ ! "$__constrain" ] ||
853		f_dialog_size_constrain "$__var_height" "$__var_width"
854}
855
856# f_dialog_menu_size [-n] $var_height $var_width $var_rows \
857#                    $title $backtitle $prompt $hline \
858#                    $tag1 $item1 $tag2 $item2 ...
859#
860# Not all versions of dialog(1) perform auto-sizing of the width and height of
861# `--menu' boxes sensibly.
862#
863# This function helps solve this issue by taking three sets of sequential
864# arguments. The first set of arguments are the variable names to use when
865# storing the calculated height, width, and rows. The second set of arguments
866# are the title, backtitle, prompt, and hline. The [optional] third set of
867# arguments are the menu list itself (comprised of tag/item couplets). The
868# optimal height, width, and rows for the described widget (not exceeding the
869# actual terminal height or width) is stored in $var_height, $var_width, and
870# $var_rows (respectively).
871#
872# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
873# and $var_rows) are not constrained to minimum/maximum values.
874#
875f_dialog_menu_size()
876{
877	local __constrain=1
878	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
879	local __var_height="$1" __var_width="$2" __var_rows="$3"
880	local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
881	shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
882
883	# Return unless at least one size aspect has been requested
884	[ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
885		return $FAILURE
886
887	# Calculate height/width of infobox (adjusted/constrained below)
888	# NOTE: Function name appended to prevent __var_{height,width} values
889	#       from becoming local (and thus preventing setvar from working).
890	local __height_menu_size __width_menu_size
891	f_dialog_infobox_size -n \
892		"${__var_height:+__height_menu_size}" \
893		"${__var_width:+__width_menu_size}" \
894		"$__title" "$__btitle" "$__prompt" "$__hline"
895
896	#
897	# Always process the menu-item arguments to get the longest tag-length,
898	# longest item-length (both used to bump the width), and the number of
899	# rows (used to bump the height).
900	#
901	local __longest_tag=0 __longest_item=0 __rows=0
902	while [ $# -ge 2 ]; do
903		local __tag="$1" __item="$2"
904		shift 2 # tag/item
905		[ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
906		[ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
907		__rows=$(( $__rows + 1 ))
908	done
909
910	# Adjust rows early (for up-comning height calculation)
911	if [ "$__var_height" -o "$__var_rows" ]; then
912		# Add a row for visual aid if using Xdialog(1)
913		[ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
914	fi
915
916	# Adjust height if desired
917	if [ "$__var_height" ]; then
918		# Add rows to height
919		if [ "$USE_XDIALOG" ]; then
920			__height_menu_size=$((
921				$__height_menu_size + $__rows + 7 ))
922		else
923			__height_menu_size=$((
924				$__height_menu_size + $__rows + 4 ))
925		fi
926		setvar "$__var_height" $__height_menu_size
927	fi
928
929	# Adjust width if desired
930	if [ "$__var_width" ]; then
931		# The sum total between the longest tag-length and the
932		# longest item-length should be used to bump menu width
933		local __n=$(( $__longest_tag + $__longest_item + 10 ))
934		[ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
935		[ $__n -gt $__width_menu_size ] && __width_menu_size=$__n
936
937		setvar "$__var_width" $__width_menu_size
938	fi
939
940	# Store adjusted rows if desired
941	[ "$__var_rows" ] && setvar "$__var_rows" $__rows
942
943	# Constrain height, width, and rows to sensible minimum/maximum values
944	# Return success if no-constrain, else return status from constrain
945	[ ! "$__constrain" ] || f_dialog_menu_constrain \
946		"$__var_height" "$__var_width" "$__var_rows" "$__prompt"
947}
948
949# f_dialog_menu_with_help_size [-n] $var_height $var_width $var_rows \
950#                              $title $backtitle $prompt $hline \
951#                              $tag1 $item1 $help1 $tag2 $item2 $help2 ...
952#
953# Not all versions of dialog(1) perform auto-sizing of the width and height of
954# `--menu' boxes sensibly.
955#
956# This function helps solve this issue by taking three sets of sequential
957# arguments. The first set of arguments are the variable names to use when
958# storing the calculated height, width, and rows. The second set of arguments
959# are the title, backtitle, prompt, and hline. The [optional] third set of
960# arguments are the menu list itself (comprised of tag/item/help triplets). The
961# optimal height, width, and rows for the described widget (not exceeding the
962# actual terminal height or width) is stored in $var_height, $var_width, and
963# $var_rows (respectively).
964#
965# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
966# and $var_rows) are not constrained to minimum/maximum values.
967#
968f_dialog_menu_with_help_size()
969{
970	local __constrain=1
971	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
972	local __var_height="$1" __var_width="$2" __var_rows="$3"
973	local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
974	shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
975
976	# Return unless at least one size aspect has been requested
977	[ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
978		return $FAILURE
979
980	# Calculate height/width of infobox (adjusted/constrained below)
981	# NOTE: Function name appended to prevent __var_{height,width} values
982	#       from becoming local (and thus preventing setvar from working).
983	local __height_menu_with_help_size __width_menu_with_help_size
984	f_dialog_infobox_size -n \
985		"${__var_height:+__height_menu_with_help_size}" \
986		"${__var_width:+__width_menu_with_help_size}" \
987		"$__title" "$__btitle" "$__prompt" "$__hline"
988
989	#
990	# Always process the menu-item arguments to get the longest tag-length,
991	# longest item-length, longest help-length (help-length only considered
992	# if using Xdialog(1), as it places the help string in the widget) --
993	# all used to bump the width -- and the number of rows (used to bump
994	# the height).
995	#
996	local __longest_tag=0 __longest_item=0 __longest_help=0 __rows=0
997	while [ $# -ge 3 ]; do
998		local __tag="$1" __item="$2" __help="$3"
999		shift 3 # tag/item/help
1000		[ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
1001		[ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
1002		[ ${#__help} -gt $__longest_help ] && __longest_help=${#__help}
1003		__rows=$(( $__rows + 1 ))
1004	done
1005
1006	# Adjust rows early (for up-coming height calculation)
1007	if [ "$__var_height" -o "$__var_rows" ]; then
1008		# Add a row for visual aid if using Xdialog(1)
1009		[ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
1010	fi
1011
1012	# Adjust height if desired
1013	if [ "$__var_height" ]; then
1014		# Add rows to height
1015		if [ "$USE_XDIALOG" ]; then
1016			__height_menu_with_help_size=$((
1017				$__height_menu_with_help_size + $__rows + 8 ))
1018		else
1019			__height_menu_with_help_size=$((
1020				$__height_menu_with_help_size + $__rows + 4 ))
1021		fi
1022		setvar "$__var_height" $__height_menu_with_help_size
1023	fi
1024
1025	# Adjust width if desired
1026	if [ "$__var_width" ]; then
1027		# The sum total between the longest tag-length and the
1028		# longest item-length should be used to bump menu width
1029		local __n=$(( $__longest_tag + $__longest_item + 10 ))
1030		[ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
1031		[ $__n -gt $__width_menu_with_help_size ] &&
1032			__width_menu_with_help_size=$__n
1033
1034		# Update width for help text if using Xdialog(1)
1035		if [ "$USE_XDIALOG" ]; then
1036			__n=$(( $__longest_help + 10 ))
1037			__n=$(( $__n + $__n / 6 )) # plus 16.6%
1038			[ $__n -gt $__width_menu_with_help_size ] &&
1039				__width_menu_with_help_size=$__n
1040		fi
1041
1042		setvar "$__var_width" $__width_menu_with_help_size
1043	fi
1044
1045	# Store adjusted rows if desired
1046	[ "$__var_rows" ] && setvar "$__var_rows" $__rows
1047
1048	# Constrain height, width, and rows to sensible minimum/maximum values
1049	# Return success if no-constrain, else return status from constrain
1050	[ ! "$__constrain" ] || f_dialog_menu_constrain \
1051		"$__var_height" "$__var_width" "$__var_rows" "$__prompt"
1052}
1053
1054# f_dialog_radiolist_size [-n] $var_height $var_width $var_rows \
1055#                         $title $backtitle $prompt $hline \
1056#                         $tag1 $item1 $status1 $tag2 $item2 $status2 ...
1057#
1058# Not all versions of dialog(1) perform auto-sizing of the width and height of
1059# `--radiolist' boxes sensibly.
1060#
1061# This function helps solve this issue by taking three sets of sequential
1062# arguments. The first set of arguments are the variable names to use when
1063# storing the calculated height, width, and rows. The second set of arguments
1064# are the title, backtitle, prompt, and hline. The [optional] third set of
1065# arguments are the radio list itself (comprised of tag/item/status triplets).
1066# The optimal height, width, and rows for the described widget (not exceeding
1067# the actual terminal height or width) is stored in $var_height, $var_width,
1068# and $var_rows (respectively).
1069#
1070# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
1071# and $var_rows) are not constrained to minimum/maximum values.
1072#
1073f_dialog_radiolist_size()
1074{
1075	local __constrain=1
1076	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
1077	local __var_height="$1" __var_width="$2" __var_rows="$3"
1078	local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
1079	shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
1080
1081	# Return unless at least one size aspect has been requested
1082	[ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
1083		return $FAILURE
1084
1085	# Calculate height/width of infobox (adjusted/constrained below)
1086	# NOTE: Function name appended to prevent __var_{height,width} values
1087	#       from becoming local (and thus preventing setvar from working).
1088	local __height_rlist_size __width_rlist_size
1089	f_dialog_infobox_size -n \
1090		"${__var_height:+__height_rlist_size}" \
1091		"${__var_width:+__width_rlist_size}" \
1092		"$__title" "$__btitle" "$__prompt" "$__hline"
1093
1094	#
1095	# Always process the menu-item arguments to get the longest tag-length,
1096	# longest item-length (both used to bump the width), and the number of
1097	# rows (used to bump the height).
1098	#
1099	local __longest_tag=0 __longest_item=0 __rows=0
1100	while [ $# -ge 3 ]; do
1101		local __tag="$1" __item="$2"
1102		shift 3 # tag/item/status
1103		[ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
1104		[ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
1105		__rows=$(( $__rows + 1 ))
1106	done
1107
1108	# Adjust rows early (for up-coming height calculation)
1109	if [ "$__var_height" -o "$__var_rows" ]; then
1110		# Add a row for visual aid if using Xdialog(1)
1111		[ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
1112	fi
1113
1114	# Adjust height if desired
1115	if [ "$__var_height" ]; then
1116		# Add rows to height
1117		if [ "$USE_XDIALOG" ]; then
1118			__height_rlist_size=$((
1119				$__height_rlist_size + $__rows + 7 ))
1120		else
1121			__height_rlist_size=$((
1122				$__height_rlist_size + $__rows + 4 ))
1123		fi
1124		setvar "$__var_height" $__height_rlist_size
1125	fi
1126
1127	# Adjust width if desired
1128	if [ "$__var_width" ]; then
1129		# Sum total between longest tag-length, longest item-length,
1130		# and radio-button width should be used to bump menu width
1131		local __n=$(( $__longest_tag + $__longest_item + 13 ))
1132		[ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
1133		[ $__n -gt $__width_rlist_size ] && __width_rlist_size=$__n
1134
1135		setvar "$__var_width" $__width_rlist_size
1136	fi
1137
1138	# Store adjusted rows if desired
1139	[ "$__var_rows" ] && setvar "$__var_rows" $__rows
1140
1141	# Constrain height, width, and rows to sensible minimum/maximum values
1142	# Return success if no-constrain, else return status from constrain
1143	[ ! "$__constrain" ] || f_dialog_menu_constrain \
1144		"$__var_height" "$__var_width" "$__var_rows" "$__prompt"
1145}
1146
1147# f_dialog_checklist_size [-n] $var_height $var_width $var_rows \
1148#                         $title $backtitle $prompt $hline \
1149#                         $tag1 $item1 $status1 $tag2 $item2 $status2 ...
1150#
1151# Not all versions of dialog(1) perform auto-sizing of the width and height of
1152# `--checklist' boxes sensibly.
1153#
1154# This function helps solve this issue by taking three sets of sequential
1155# arguments. The first set of arguments are the variable names to use when
1156# storing the calculated height, width, and rows. The second set of arguments
1157# are the title, backtitle, prompt, and hline. The [optional] third set of
1158# arguments are the check list itself (comprised of tag/item/status triplets).
1159# The optimal height, width, and rows for the described widget (not exceeding
1160# the actual terminal height or width) is stored in $var_height, $var_width,
1161# and $var_rows (respectively). 
1162#
1163# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
1164# and $var_rows) are not constrained to minimum/maximum values.
1165#
1166f_dialog_checklist_size()
1167{
1168	f_dialog_radiolist_size "$@"
1169}
1170
1171# f_dialog_radiolist_with_help_size [-n] $var_height $var_width $var_rows \
1172#                                   $title $backtitle $prompt $hline \
1173#                                   $tag1 $item1 $status1 $help1 \
1174#                                   $tag2 $item2 $status2 $help2 ...
1175#
1176# Not all versions of dialog(1) perform auto-sizing of the width and height of
1177# `--radiolist' boxes sensibly.
1178#
1179# This function helps solve this issue by taking three sets of sequential
1180# arguments. The first set of arguments are the variable names to use when
1181# storing the calculated height, width, and rows. The second set of arguments
1182# are the title, backtitle, prompt, and hline. The [optional] third set of
1183# arguments are the radio list itself (comprised of tag/item/status/help
1184# quadruplets). The optimal height, width, and rows for the described widget
1185# (not exceeding the actual terminal height or width) is stored in $var_height,
1186# $var_width, and $var_rows (respectively).
1187#
1188# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
1189# and $var_rows) are not constrained to minimum/maximum values.
1190#
1191f_dialog_radiolist_with_help_size()
1192{
1193	local __constrain=1
1194	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
1195	local __var_height="$1" __var_width="$2" __var_rows="$3"
1196	local __title="$4" __btitle="$5" __prompt="$6" __hline="$7"
1197	shift 7 # var_height/var_width/var_rows/title/btitle/prompt/hline
1198
1199	# Return unless at least one size aspect has been requested
1200	[ "$__var_height" -o "$__var_width" -o "$__var_rows" ] ||
1201		return $FAILURE
1202
1203	# Calculate height/width of infobox (adjusted/constrained below)
1204	# NOTE: Function name appended to prevent __var_{height,width} values
1205	#       from becoming local (and thus preventing setvar from working).
1206	local __height_rlist_with_help_size __width_rlist_with_help_size
1207	f_dialog_infobox_size -n \
1208		"${__var_height:+__height_rlist_with_help_size}" \
1209		"${__var_width:+__width_rlist_with_help_size}" \
1210		"$__title" "$__btitle" "$__prompt" "$__hline"
1211
1212	#
1213	# Always process the menu-item arguments to get the longest tag-length,
1214	# longest item-length, longest help-length (help-length only considered
1215	# if using Xdialog(1), as it places the help string in the widget) --
1216	# all used to bump the width -- and the number of rows (used to bump
1217	# the height).
1218	#
1219	local __longest_tag=0 __longest_item=0 __longest_help=0 __rows=0
1220	while [ $# -ge 4 ]; do
1221		local __tag="$1" __item="$2" __status="$3" __help="$4"
1222		shift 4 # tag/item/status/help
1223		[ ${#__tag} -gt $__longest_tag ] && __longest_tag=${#__tag}
1224		[ ${#__item} -gt $__longest_item ] && __longest_item=${#__item}
1225		[ ${#__help} -gt $__longest_help ] && __longest_help=${#__help}
1226		__rows=$(( $__rows + 1 ))
1227	done
1228
1229	# Adjust rows early (for up-coming height calculation)
1230	if [ "$__var_height" -o "$__var_rows" ]; then
1231		# Add a row for visual aid if using Xdialog(1)
1232		[ "$USE_XDIALOG" ] && __rows=$(( $__rows + 1 ))
1233	fi
1234
1235	# Adjust height if desired
1236	if [ "$__var_height" ]; then
1237		# Add rows to height
1238		if [ "$USE_XDIALOG" ]; then
1239			__height_rlist_with_help_size=$((
1240				$__height_rlist_with_help_size + $__rows + 7 ))
1241		else
1242			__height_rlist_with_help_size=$((
1243				$__height_rlist_with_help_size + $__rows + 4 ))
1244		fi
1245		setvar "$__var_height" $__height
1246	fi
1247
1248	# Adjust width if desired
1249	if [ "$__var_width" ]; then
1250		# Sum total between longest tag-length, longest item-length,
1251		# and radio-button width should be used to bump menu width
1252		local __n=$(( $__longest_tag + $__longest_item + 13 ))
1253		[ "$USE_XDIALOG" ] && __n=$(( $__n + $__n / 6 )) # plus 16.6%
1254		[ $__n -gt $__width_rlist_with_help_size ] &&
1255			__width_rlist_with_help_size=$__n
1256
1257		# Update width for help text if using Xdialog(1)
1258		if [ "$USE_XDIALOG" ]; then
1259			__n=$(( $__longest_help + 10 ))
1260			__n=$(( $__n + $__n / 6 )) # plus 16.6%
1261			[ $__n -gt $__width_rlist_with_help_size ] &&
1262				__width_rlist_with_help_size=$__n
1263		fi
1264
1265		setvar "$__var_width" $__width_rlist_with_help_size
1266	fi
1267
1268	# Store adjusted rows if desired
1269	[ "$__var_rows" ] && setvar "$__var_rows" $__rows
1270
1271	# Constrain height, width, and rows to sensible minimum/maximum values
1272	# Return success if no-constrain, else return status from constrain
1273	[ ! "$__constrain" ] || f_dialog_menu_constrain \
1274		"$__var_height" "$__var_width" "$__var_rows" "$__prompt"
1275}
1276
1277# f_dialog_checklist_with_help_size [-n] $var_height $var_width $var_rows \
1278#                                   $title $backtitle $prompt $hline \
1279#                                   $tag1 $item1 $status1 $help1 \
1280#                                   $tag2 $item2 $status2 $help2 ...
1281#
1282# Not all versions of dialog(1) perform auto-sizing of the width and height of
1283# `--checklist' boxes sensibly.
1284#
1285# This function helps solve this issue by taking three sets of sequential
1286# arguments. The first set of arguments are the variable names to use when
1287# storing the calculated height, width, and rows. The second set of arguments
1288# are the title, backtitle, prompt, and hline. The [optional] third set of
1289# arguments are the check list itself (comprised of tag/item/status/help
1290# quadruplets). The optimal height, width, and rows for the described widget
1291# (not exceeding the actual terminal height or width) is stored in $var_height,
1292# $var_width, and $var_rows (respectively).
1293#
1294# If the first argument is `-n', the calculated sizes ($var_height, $var_width,
1295# and $var_rows) are not constrained to minimum/maximum values.
1296#
1297f_dialog_checklist_with_help_size()
1298{
1299	f_dialog_radiolist_with_help_size "$@"
1300}
1301
1302# f_dialog_calendar_size [-n] $var_height $var_width \
1303#                        $title $backtitle $prompt [$hline]
1304#
1305# Not all versions of dialog(1) perform auto-sizing of the width and height of
1306# `--calendar' boxes sensibly.
1307#
1308# This function helps solve this issue by taking two sets of sequential
1309# arguments. The first set of arguments are the variable names to use when
1310# storing the calculated height and width. The second set of arguments are the
1311# title, backtitle, prompt, and [optionally] hline. The optimal height and
1312# width for the described widget (not exceeding the actual terminal height or
1313# width) is stored in $var_height and $var_width (respectively).
1314#
1315# If the first argument is `-n', the calculated sizes ($var_height and
1316# $var_width) are not constrained to minimum/maximum values.
1317#
1318# Newline character sequences (``\n'') in $prompt are expanded as-is done by
1319# dialog(1).
1320#
1321f_dialog_calendar_size()
1322{
1323	local __constrain=1
1324	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
1325	local __var_height="$1" __var_width="$2"
1326	local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
1327
1328	# Return unless at least one size aspect has been requested
1329	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
1330
1331	#
1332	# Obtain/Adjust minimum and maximum thresholds
1333	# NOTE: Function name appended to prevent __var_{height,width} values
1334	#       from becoming local (and thus preventing setvar from working).
1335	#
1336	local __max_height_cal_size __max_width_cal_size
1337	f_dialog_max_size __max_height_cal_size __max_width_cal_size
1338	__max_width_cal_size=$(( $__max_width_cal_size - 2 ))
1339		# the calendar box will refuse to display if too wide
1340	local __min_width
1341	if [ "$USE_XDIALOG" ]; then
1342		__min_width=55
1343	else
1344		__min_width=40
1345		__max_height_cal_size=$((
1346			$__max_height_cal_size - $DIALOG_CALENDAR_HEIGHT ))
1347		# When using dialog(1), we can't predict whether the user has
1348		# disabled shadow's in their `$HOME/.dialogrc' file, so we'll
1349		# subtract one for the potential shadow around the widget
1350		__max_height_cal_size=$(( $__max_height_cal_size - 1 ))
1351	fi
1352
1353	# Calculate height if desired
1354	if [ "$__var_height" ]; then
1355		local __height
1356		__height=$( echo "$__prompt" | f_number_of_lines )
1357
1358		if [ "$USE_XDIALOG" ]; then
1359			# Add height to accomodate for embedded calendar widget
1360			__height=$(( $__height + $DIALOG_CALENDAR_HEIGHT - 1 ))
1361
1362			# Also, bump height if backtitle is enabled
1363			if [ "$__btitle" ]; then
1364				local __n
1365				__n=$( echo "$__btitle" | f_number_of_lines )
1366				__height=$(( $__height + $__n + 2 ))
1367			fi
1368		else
1369			[ "$__prompt" ] && __height=$(( $__height + 1 ))
1370		fi
1371
1372		# Enforce maximum height, unless `-n' was passed
1373		[ "$__constrain" -a $__height -gt $__max_height_cal_size ] &&
1374			__height=$__max_height_cal_size
1375
1376		setvar "$__var_height" $__height
1377	fi
1378
1379	# Calculate width if desired
1380	if [ "$__var_width" ]; then
1381		# NOTE: Function name appended to prevent __var_{height,width}
1382		#       values from becoming local (and thus preventing setvar
1383		#       from working).
1384		local __width_cal_size
1385		f_dialog_infobox_size -n "" __width_cal_size \
1386			"$__title" "$__btitle" "$__prompt" "$__hline"
1387
1388		# Enforce minimum/maximum width, unless `-n' was passed
1389		if [ "$__constrain" ]; then
1390			if [ $__width_cal_size -lt $__min_width ]; then
1391				__width_cal_size=$__min_width
1392			elif [ $__width_cal_size -gt $__max_width_cal_size ]
1393			then
1394				__width_cal_size=$__max_width_size
1395			fi
1396		fi
1397
1398		setvar "$__var_width" $__width_cal_size
1399	fi
1400
1401	return $SUCCESS
1402}
1403
1404# f_dialog_timebox_size [-n] $var_height $var_width \
1405#                       $title $backtitle $prompt [$hline]
1406#
1407# Not all versions of dialog(1) perform auto-sizing of the width and height of
1408# `--timebox' boxes sensibly.
1409#
1410# This function helps solve this issue by taking two sets of sequential
1411# arguments. The first set of arguments are the variable names to use when
1412# storing the calculated height and width. The second set of arguments are the
1413# title, backtitle, prompt, and [optionally] hline. The optional height and
1414# width for the described widget (not exceeding the actual terminal height or
1415# width) is stored in $var_height and $var_width (respectively).
1416#
1417# If the first argument is `-n', the calculated sizes ($var_height and
1418# $var_width) are not constrained to minimum/maximum values.
1419#
1420# Newline character sequences (``\n'') in $prompt are expanded as-is done by
1421# dialog(1).
1422#
1423f_dialog_timebox_size()
1424{
1425	local __constrain=1
1426	[ "$1" = "-n" ] && __constrain= && shift 1 # -n
1427	local __var_height="$1" __var_width="$2"
1428	local __title="$3" __btitle="$4" __prompt="$5" __hline="$6"
1429
1430	# Return unless at least one size aspect has been requested
1431	[ "$__var_height" -o "$__var_width" ] || return $FAILURE
1432
1433	#
1434	# Obtain/Adjust minimum and maximum thresholds
1435	# NOTE: Function name appended to prevent __var_{height,width} values
1436	#       from becoming local (and thus preventing setvar from working).
1437	#
1438	local __max_height_tbox_size __max_width_tbox_size
1439	f_dialog_max_size __max_height_tbox_size __max_width_tbox_size
1440	__max_width_tbox_size=$(( $__max_width_tbox_size - 2 ))
1441		# the timebox widget refuses to display if too wide
1442	local __min_width
1443	if [ "$USE_XDIALOG" ]; then
1444		__min_width=40
1445	else
1446		__min_width=20
1447		__max_height_tbox_size=$(( \
1448			$__max_height_tbox_size - $DIALOG_TIMEBOX_HEIGHT ))
1449		# When using dialog(1), we can't predict whether the user has
1450		# disabled shadow's in their `$HOME/.dialogrc' file, so we'll
1451		# subtract one for the potential shadow around the widget
1452		__max_height_tbox_size=$(( $__max_height_tbox_size - 1 ))
1453	fi
1454
1455	# Calculate height if desired
1456	if [ "$__var_height" -a "$USE_XDIALOG" ]; then
1457		# When using Xdialog(1), the height seems to have
1458		# no effect. All values provide the same results.
1459		setvar "$__var_height" 0 # autosize
1460	elif [ "$__var_height" ]; then
1461		local __height
1462		__height=$( echo "$__prompt" | f_number_of_lines )
1463		__height=$(( $__height ${__prompt:++1} + 1 ))
1464
1465		# Enforce maximum height, unless `-n' was passed
1466		[ "$__constrain" -a $__height -gt $__max_height_tbox_size ] &&
1467			__height=$__max_height_tbox_size
1468
1469		setvar "$__var_height" $__height
1470	fi
1471
1472	# Calculate width if desired
1473	if [ "$__var_width" ]; then
1474		# NOTE: Function name appended to prevent __var_{height,width}
1475		#       values from becoming local (and thus preventing setvar
1476		#       from working).
1477		local __width_tbox_size
1478		f_dialog_infobox_size -n "" __width_tbox_size \
1479			"$__title" "$__btitle" "$__prompt" "$__hline"
1480
1481		# Enforce the minimum width for displaying the timebox
1482		if [ "$__constrain" ]; then
1483			if [ $__width_tbox_size -lt $__min_width ]; then
1484				__width_tbox_size=$__min_width
1485			elif [ $__width_tbox_size -ge $__max_width_tbox_size ]
1486			then
1487				__width_tbox_size=$__max_width_tbox_size
1488			fi
1489		fi
1490
1491		setvar "$__var_width" $__width_tbox_size
1492	fi
1493
1494	return $SUCCESS
1495}
1496
1497############################################################ CLEAR FUNCTIONS
1498
1499# f_dialog_clear
1500#
1501# Clears any/all previous dialog(1) displays.
1502#
1503f_dialog_clear()
1504{
1505	$DIALOG --clear
1506}
1507
1508############################################################ INFO FUNCTIONS
1509
1510# f_dialog_info $info_text ...
1511#
1512# Throw up a dialog(1) infobox. The infobox remains until another dialog is
1513# displayed or `dialog --clear' (or f_dialog_clear) is called.
1514#
1515f_dialog_info()
1516{
1517	local info_text="$*" height width
1518	f_dialog_infobox_size height width \
1519		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$info_text"
1520	$DIALOG \
1521		--title "$DIALOG_TITLE"         \
1522		--backtitle "$DIALOG_BACKTITLE" \
1523		${USE_XDIALOG:+--ignore-eof}    \
1524		${USE_XDIALOG:+--no-buttons}    \
1525		--infobox "$info_text" $height $width
1526}
1527
1528# f_xdialog_info $info_text ...
1529#
1530# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces
1531# EOF. This implies that you must execute this either as an rvalue to a pipe,
1532# lvalue to indirection or in a sub-shell that provides data on stdin.
1533#
1534f_xdialog_info()
1535{
1536	local info_text="$*" height width
1537	f_dialog_infobox_size height width \
1538		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$info_text"
1539	$DIALOG \
1540		--title "$DIALOG_TITLE"               \
1541		--backtitle "$DIALOG_BACKTITLE"       \
1542		--no-close --no-buttons               \
1543		--infobox "$info_text" $height $width \
1544		-1 # timeout of -1 means abort when EOF on stdin
1545}
1546
1547############################################################ MSGBOX FUNCTIONS
1548
1549# f_dialog_msgbox $msg_text [$hline]
1550#
1551# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER
1552# or ESC, acknowledging the modal dialog.
1553#
1554# If the user presses ENTER, the exit status is zero (success), otherwise if
1555# the user presses ESC the exit status is 255.
1556#
1557f_dialog_msgbox()
1558{
1559	local msg_text="$1" hline="$2" height width
1560	f_dialog_buttonbox_size height width \
1561		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
1562	$DIALOG \
1563		--title "$DIALOG_TITLE"         \
1564		--backtitle "$DIALOG_BACKTITLE" \
1565		--hline "$hline"                \
1566		--ok-label "$msg_ok"            \
1567		--msgbox "$msg_text" $height $width
1568}
1569
1570############################################################ TEXTBOX FUNCTIONS
1571
1572# f_dialog_textbox $file
1573#
1574# Display the contents of $file (or an error if $file does not exist, etc.) in
1575# a dialog(1) textbox (which has a scrollable region for the text). The textbox
1576# remains until the user presses ENTER or ESC, acknowledging the modal dialog.
1577#
1578# If the user presses ENTER, the exit status is zero (success), otherwise if
1579# the user presses ESC the exit status is 255.
1580#
1581f_dialog_textbox()
1582{
1583	local file="$1"
1584	local contents height width retval
1585
1586	contents=$( cat "$file" 2>&1 )
1587	retval=$?
1588
1589	f_dialog_buttonbox_size height width \
1590		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$contents"
1591
1592	if [ $retval -eq $SUCCESS ]; then
1593		$DIALOG \
1594			--title "$DIALOG_TITLE"         \
1595			--backtitle "$DIALOG_BACKTITLE" \
1596			--exit-label "$msg_ok"          \
1597			--no-cancel                     \
1598			--textbox "$file" $height $width
1599	else
1600		$DIALOG \
1601			--title "$DIALOG_TITLE"         \
1602			--backtitle "$DIALOG_BACKTITLE" \
1603			--ok-label "$msg_ok"            \
1604			--msgbox "$contents" $height $width
1605	fi
1606}
1607
1608############################################################ YESNO FUNCTIONS
1609
1610# f_dialog_yesno $msg_text [$hline]
1611#
1612# Display a dialog(1) Yes/No prompt to allow the user to make some decision.
1613# The yesno prompt remains until the user presses ENTER or ESC, acknowledging
1614# the modal dialog.
1615#
1616# If the user chooses YES the exit status is zero, or chooses NO the exit
1617# status is one, or presses ESC the exit status is 255.
1618#
1619f_dialog_yesno()
1620{
1621	local msg_text="$1" height width
1622	local hline="${2-$hline_arrows_tab_enter}"
1623
1624	f_interactive || return 0 # If non-interactive, return YES all the time
1625
1626	f_dialog_buttonbox_size height width \
1627		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
1628
1629	if [ "$USE_XDIALOG" ]; then
1630		$DIALOG \
1631			--title "$DIALOG_TITLE"         \
1632			--backtitle "$DIALOG_BACKTITLE" \
1633			--hline "$hline"                \
1634			--ok-label "$msg_yes"           \
1635			--cancel-label "$msg_no"        \
1636			--yesno "$msg_text" $height $width
1637	else
1638		$DIALOG \
1639			--title "$DIALOG_TITLE"         \
1640			--backtitle "$DIALOG_BACKTITLE" \
1641			--hline "$hline"                \
1642			--yes-label "$msg_yes"          \
1643			--no-label "$msg_no"            \
1644			--yesno "$msg_text" $height $width
1645	fi
1646}
1647
1648# f_dialog_noyes $msg_text [$hline]
1649#
1650# Display a dialog(1) No/Yes prompt to allow the user to make some decision.
1651# The noyes prompt remains until the user presses ENTER or ESC, acknowledging
1652# the modal dialog.
1653#
1654# If the user chooses YES the exit status is zero, or chooses NO the exit
1655# status is one, or presses ESC the exit status is 255.
1656#
1657# NOTE: This is just like the f_dialog_yesno function except "No" is default.
1658#
1659f_dialog_noyes()
1660{
1661	local msg_text="$1" height width
1662	local hline="${2-$hline_arrows_tab_enter}"
1663
1664	f_interactive || return 1 # If non-interactive, return NO all the time
1665
1666	f_dialog_buttonbox_size height width \
1667		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$msg_text" "$hline"
1668
1669	if [ "$USE_XDIALOG" ]; then
1670		$DIALOG \
1671			--title "$DIALOG_TITLE"         \
1672			--backtitle "$DIALOG_BACKTITLE" \
1673			--hline "$hline"                \
1674			--default-no                    \
1675			--ok-label "$msg_yes"           \
1676			--cancel-label "$msg_no"        \
1677			--yesno "$msg_text" $height $width
1678	else
1679		$DIALOG \
1680			--title "$DIALOG_TITLE"         \
1681			--backtitle "$DIALOG_BACKTITLE" \
1682			--hline "$hline"                \
1683			--defaultno                     \
1684			--yes-label "$msg_yes"          \
1685			--no-label "$msg_no"            \
1686			--yesno "$msg_text" $height $width
1687	fi
1688}
1689
1690############################################################ INPUT FUNCTIONS
1691
1692# f_dialog_inputstr_store [-s] $text
1693#
1694# Store some text from a dialog(1) inputbox to be retrieved later by
1695# f_dialog_inputstr_fetch(). If the first argument is `-s', the text is
1696# sanitized before being stored.
1697#
1698f_dialog_inputstr_store()
1699{
1700	local sanitize=
1701	[ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
1702	local text="$1"
1703
1704	# Sanitize the line before storing it if desired
1705	[ "$sanitize" ] && f_dialog_line_sanitize text
1706
1707	setvar DIALOG_INPUTBOX_$$ "$text"
1708}
1709
1710# f_dialog_inputstr_fetch [$var_to_set]
1711#
1712# Obtain the inputstr entered by the user from the most recently displayed
1713# dialog(1) inputbox (previously stored with f_dialog_inputstr_store() above).
1714# If $var_to_set is NULL or missing, output is printed to stdout (which is less
1715# recommended due to performance degradation; in a loop for example).
1716#
1717f_dialog_inputstr_fetch()
1718{
1719	local __var_to_set="$1" __cp
1720
1721	debug= f_getvar DIALOG_INPUTBOX_$$ "${__var_to_set:-__cp}" # get data
1722	setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive
1723
1724	# Return the line on standard-out if desired
1725	[ "$__var_to_set" ] || echo "$__cp"
1726
1727	return $SUCCESS
1728}
1729
1730# f_dialog_input $var_to_set $prompt [$init [$hline]]
1731#
1732# Prompt the user with a dialog(1) inputbox to enter some value. The inputbox
1733# remains until the the user presses ENTER or ESC, or otherwise ends the
1734# editing session (by selecting `Cancel' for example).
1735#
1736# If the user presses ENTER, the exit status is zero (success), otherwise if
1737# the user presses ESC the exit status is 255, or if the user chose Cancel, the
1738# exit status is instead 1.
1739#
1740# NOTE: The hline should correspond to the type of data you want from the user.
1741# NOTE: Should not be used to edit multiline values.
1742#
1743f_dialog_input()
1744{
1745	local __var_to_set="$1" __prompt="$2" __init="$3" __hline="$4"
1746
1747	# NOTE: Function name appended to prevent __var_{height,width} values
1748	#       from becoming local (and thus preventing setvar from working).
1749	local __height_input __width_input
1750	f_dialog_inputbox_size __height_input __width_input \
1751		"$DIALOG_TITLE" "$DIALOG_BACKTITLE" \
1752		"$__prompt" "$__init" "$__hline"
1753
1754	local __opterm="--"
1755	[ "$USE_XDIALOG" ] && __opterm=
1756
1757	local __dialog_input
1758	__dialog_input=$(
1759		$DIALOG \
1760			--title "$DIALOG_TITLE"         \
1761			--backtitle "$DIALOG_BACKTITLE" \
1762			--hline "$__hline"              \
1763			--ok-label "$msg_ok"            \
1764			--cancel-label "$msg_cancel"    \
1765			--inputbox "$__prompt"          \
1766			$__height_input $__width_input  \
1767			$__opterm "$__init"             \
1768			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1769	)
1770	local __retval=$?
1771
1772	# Remove warnings and leading/trailing whitespace from user input
1773	f_dialog_line_sanitize __dialog_input
1774
1775	setvar "$__var_to_set" "$__dialog_input"
1776	return $__retval
1777}
1778
1779############################################################ MENU FUNCTIONS
1780
1781# f_dialog_menutag_store [-s] $text
1782#
1783# Store some text from a dialog(1) menu to be retrieved later by
1784# f_dialog_menutag_fetch(). If the first argument is `-s', the text is
1785# sanitized before being stored.
1786#
1787f_dialog_menutag_store()
1788{
1789	local sanitize=
1790	[ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
1791	local text="$1"
1792
1793	# Sanitize the menutag before storing it if desired
1794	[ "$sanitize" ] && f_dialog_data_sanitize text
1795
1796	setvar DIALOG_MENU_$$ "$text"
1797}
1798
1799# f_dialog_menutag_fetch [$var_to_set]
1800#
1801# Obtain the menutag chosen by the user from the most recently displayed
1802# dialog(1) menu (previously stored with f_dialog_menutag_store() above). If
1803# $var_to_set is NULL or missing, output is printed to stdout (which is less
1804# recommended due to performance degradation; in a loop for example).
1805#
1806f_dialog_menutag_fetch()
1807{
1808	local __var_to_set="$1" __cp
1809
1810	debug= f_getvar DIALOG_MENU_$$ "${__var_to_set:-__cp}" # get the data
1811	setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive
1812
1813	# Return the data on standard-out if desired
1814	[ "$__var_to_set" ] || echo "$__cp"
1815
1816	return $SUCCESS
1817}
1818
1819# f_dialog_menuitem_store [-s] $text
1820#
1821# Store the item from a dialog(1) menu (see f_dialog_menutag2item()) to be
1822# retrieved later by f_dialog_menuitem_fetch(). If the first argument is `-s',
1823# the text is sanitized before being stored.
1824#
1825f_dialog_menuitem_store()
1826{
1827	local sanitize=
1828	[ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
1829	local text="$1"
1830
1831	# Sanitize the menuitem before storing it if desired
1832	[ "$sanitize" ] && f_dialog_data_sanitize text
1833
1834	setvar DIALOG_MENUITEM_$$ "$text"
1835}
1836
1837# f_dialog_menuitem_fetch [$var_to_set]
1838#
1839# Obtain the menuitem chosen by the user from the most recently displayed
1840# dialog(1) menu (previously stored with f_dialog_menuitem_store() above). If
1841# $var_to_set is NULL or missing, output is printed to stdout (which is less
1842# recommended due to performance degradation; in a loop for example).
1843#
1844f_dialog_menuitem_fetch()
1845{
1846	local __var_to_set="$1" __cp
1847
1848	debug= f_getvar DIALOG_MENUITEM_$$ "${__var_to_set:-__cp}" # get data
1849	setvar DIALOG_MENUITEM_$$ "" # scrub memory in case data was sensitive
1850
1851	# Return the data on standard-out if desired
1852	[ "$__var_to_set" ] || echo "$__cp"
1853
1854	return $SUCCESS
1855}
1856
1857# f_dialog_default_store [-s] $text
1858#
1859# Store some text to be used later as the --default-item argument to dialog(1)
1860# (or Xdialog(1)) for --menu, --checklist, and --radiolist widgets. Retrieve
1861# the text later with f_dialog_menutag_fetch(). If the first argument is `-s',
1862# the text is sanitized before being stored.
1863#
1864f_dialog_default_store()
1865{
1866	local sanitize=
1867	[ "$1" = "-s" ] && sanitize=1 && shift 1 # -s
1868	local text="$1"
1869
1870	# Sanitize the defaulitem before storing it if desired
1871	[ "$sanitize" ] && f_dialog_data_sanitize text
1872
1873	setvar DEFAULTITEM_$$ "$text"
1874}
1875
1876# f_dialog_default_fetch [$var_to_set]
1877#
1878# Obtain text to be used with the --default-item argument of dialog(1) (or
1879# Xdialog(1)) (previously stored with f_dialog_default_store() above). If
1880# $var_to_set is NULL or missing, output is printed to stdout (which is less
1881# recommended due to performance degradation; in a loop for example).
1882#
1883f_dialog_default_fetch()
1884{
1885	local __var_to_set="$1" __cp
1886
1887	debug= f_getvar DEFAULTITEM_$$ "${__var_to_set:-__cp}" # get the data
1888	setvar DEFAULTITEM_$$ "" # scrub memory in case data was sensitive
1889
1890	# Return the data on standard-out if desired
1891	[ "$__var_to_set" ] || echo "$__cp"
1892
1893	return $SUCCESS
1894}
1895
1896# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ...
1897#
1898# To use the `--menu' option of dialog(1) you must pass an ordered list of
1899# tag/item pairs on the command-line. When the user selects a menu option the
1900# tag for that item is printed to stderr.
1901#
1902# This function allows you to dereference the tag chosen by the user back into
1903# the item associated with said tag.
1904#
1905# Pass the tag chosen by the user as the first argument, followed by the
1906# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1907# passed to dialog(1) for consistency).
1908#
1909# If the tag cannot be found, NULL is returned.
1910#
1911f_dialog_menutag2item()
1912{
1913	local tag="$1" tagn item
1914	shift 1 # tag
1915
1916	while [ $# -gt 0 ]; do
1917		tagn="$1"
1918		item="$2"
1919		shift 2 # tagn/item
1920
1921		if [ "$tag" = "$tagn" ]; then
1922			echo "$item"
1923			return $SUCCESS
1924		fi
1925	done
1926	return $FAILURE
1927}
1928
1929# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \
1930#                                             $tag2 $item2 $help2 ...
1931#
1932# To use the `--menu' option of dialog(1) with the `--item-help' option, you
1933# must pass an ordered list of tag/item/help triplets on the command-line. When
1934# the user selects a menu option the tag for that item is printed to stderr.
1935#
1936# This function allows you to dereference the tag chosen by the user back into
1937# the item associated with said tag (help is discarded/ignored).
1938#
1939# Pass the tag chosen by the user as the first argument, followed by the
1940# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
1941# as was passed to dialog(1) for consistency).
1942#
1943# If the tag cannot be found, NULL is returned.
1944#
1945f_dialog_menutag2item_with_help()
1946{
1947	local tag="$1" tagn item
1948	shift 1 # tag
1949
1950	while [ $# -gt 0 ]; do
1951		tagn="$1"
1952		item="$2"
1953		shift 3 # tagn/item/help
1954
1955		if [ "$tag" = "$tagn" ]; then
1956			echo "$item"
1957			return $SUCCESS
1958		fi
1959	done
1960	return $FAILURE
1961}
1962
1963# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ...
1964#
1965# To use the `--menu' option of dialog(1) you must pass an ordered list of
1966# tag/item pairs on the command-line. When the user selects a menu option the
1967# tag for that item is printed to stderr.
1968#
1969# This function allows you to dereference the tag chosen by the user back into
1970# the index associated with said tag. The index is the one-based tag/item pair
1971# array position within the ordered list of tag/item pairs passed to dialog(1).
1972#
1973# Pass the tag chosen by the user as the first argument, followed by the
1974# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1975# passed to dialog(1) for consistency).
1976#
1977# If the tag cannot be found, NULL is returned.
1978#
1979f_dialog_menutag2index()
1980{
1981	local tag="$1" tagn n=1
1982	shift 1 # tag
1983
1984	while [ $# -gt 0 ]; do
1985		tagn="$1"
1986		shift 2 # tagn/item
1987
1988		if [ "$tag" = "$tagn" ]; then
1989			echo $n
1990			return $SUCCESS
1991		fi
1992		n=$(( $n + 1 ))
1993	done
1994	return $FAILURE
1995}
1996
1997# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \
1998#                                              $tag2 $item2 $help2 ...
1999#
2000# To use the `--menu' option of dialog(1) with the `--item-help' option, you
2001# must pass an ordered list of tag/item/help triplets on the command-line. When
2002# the user selects a menu option the tag for that item is printed to stderr.
2003#
2004# This function allows you to dereference the tag chosen by the user back into
2005# the index associated with said tag. The index is the one-based tag/item/help
2006# triplet array position within the ordered list of tag/item/help triplets
2007# passed to dialog(1).
2008#
2009# Pass the tag chosen by the user as the first argument, followed by the
2010# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
2011# as was passed to dialog(1) for consistency).
2012#
2013# If the tag cannot be found, NULL is returned.
2014#
2015f_dialog_menutag2index_with_help()
2016{
2017	local tag="$1" tagn n=1
2018	shift 1 # tag
2019
2020	while [ $# -gt 0 ]; do
2021		tagn="$1"
2022		shift 3 # tagn/item/help
2023
2024		if [ "$tag" = "$tagn" ]; then
2025			echo $n
2026			return $SUCCESS
2027		fi
2028		n=$(( $n + 1 ))
2029	done
2030	return $FAILURE
2031}
2032
2033############################################################ INIT FUNCTIONS
2034
2035# f_dialog_init
2036#
2037# Initialize (or re-initialize) the dialog module after setting/changing any
2038# of the following environment variables:
2039#
2040# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
2041# 	              that Xdialog(1) should be used instead of dialog(1).
2042#
2043# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
2044# 	              that (while running as root) sudo(8) authentication is
2045# 	              required to proceed.
2046#
2047# Also reads ~/.dialogrc for the following information:
2048#
2049# 	NO_SHADOW     Either NULL or Non-NULL. If use_shadow is OFF (case-
2050# 	              insensitive) in ~/.dialogrc this is set to "1" (otherwise
2051# 	              unset).
2052#
2053f_dialog_init()
2054{
2055	DIALOG_SELF_INITIALIZE=
2056	USE_DIALOG=1
2057
2058	#
2059	# Clone terminal stdout so we can redirect to it from within sub-shells
2060	#
2061	eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1
2062
2063	#
2064	# Add `-S' and `-X' to the list of standard arguments supported by all
2065	#
2066	case "$GETOPTS_STDARGS" in
2067	*SX*) : good ;; # already present
2068	   *) GETOPTS_STDARGS="${GETOPTS_STDARGS}SX"
2069	esac
2070
2071	#
2072	# Process stored command-line arguments
2073	#
2074	f_dprintf "f_dialog_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \
2075	          "$ARGV" "$GETOPTS_STDARGS"
2076	SECURE=$( set -- $ARGV
2077		while getopts \
2078			"$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" \
2079		flag > /dev/null; do
2080			case "$flag" in
2081			S) echo 1 ;;
2082			esac
2083		done
2084	)
2085	USE_XDIALOG=$( set -- $ARGV
2086		while getopts \
2087			"$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" \
2088		flag > /dev/null; do
2089			case "$flag" in
2090			S|X) echo 1 ;;
2091			esac
2092		done
2093	)
2094	f_dprintf "f_dialog_init: SECURE=[%s] USE_XDIALOG=[%s]" \
2095	          "$SECURE" "$USE_XDIALOG"
2096
2097	#
2098	# Process `-X' command-line option
2099	#
2100	[ "$USE_XDIALOG" ] && DIALOG=Xdialog USE_DIALOG=
2101
2102	#
2103	# Sanity check, or die gracefully
2104	#
2105	if ! f_have $DIALOG; then
2106		unset USE_XDIALOG
2107		local failed_dialog="$DIALOG"
2108		DIALOG=dialog
2109		f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
2110	fi
2111
2112	#
2113	# Read ~/.dialogrc (unless using Xdialog(1)) for properties
2114	#
2115	if [ -f ~/.dialogrc -a ! "$USE_XDIALOG" ]; then
2116		eval "$(
2117			awk -v param=use_shadow -v expect=OFF \
2118			    -v set="NO_SHADOW=1" '
2119			!/^[[:space:]]*(#|$)/ && \
2120			tolower($1) ~ "^"param"(=|$)" && \
2121			/[^#]*=/ {
2122				sub(/^[^=]*=[[:space:]]*/, "")
2123				if ( toupper($1) == expect ) print set";"
2124			}' ~/.dialogrc
2125		)"
2126	fi
2127
2128	#
2129	# If we're already running as root but we got there by way of sudo(8)
2130	# and we have X11, we should merge the xauth(1) credentials from our
2131	# original user.
2132	#
2133	if [ "$USE_XDIALOG" ] &&
2134	   [ "$( id -u )" = "0" ] &&
2135	   [ "$SUDO_USER" -a "$DISPLAY" ]
2136	then
2137		if ! f_have xauth; then
2138			# Die gracefully, as we [likely] can't use Xdialog(1)
2139			unset USE_XDIALOG
2140			DIALOG=dialog
2141			f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth"
2142		fi
2143		HOSTNAME=$(hostname)
2144		local displaynum="${DISPLAY#*:}"
2145		eval xauth -if \~$SUDO_USER/.Xauthority extract - \
2146			\"\$HOSTNAME/unix:\$displaynum\" \
2147			\"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \
2148			~root/.Xauthority merge - > /dev/null 2>&1'
2149	fi
2150
2151	#
2152	# Probe Xdialog(1) for maximum height/width constraints, or die
2153	# gracefully
2154	#
2155	if [ "$USE_XDIALOG" ]; then
2156		local maxsize
2157		if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 )
2158		then
2159			# Xdialog(1) failed, fall back to dialog(1)
2160			unset USE_XDIALOG
2161
2162			# Display the error message produced by Xdialog(1)
2163			local height width
2164			f_dialog_buttonbox_size height width \
2165				"$DIALOG_TITLE" "$DIALOG_BACKTITLE" "$maxsize"
2166			dialog \
2167				--title "$DIALOG_TITLE"         \
2168				--backtitle "$DIALOG_BACKTITLE" \
2169				--ok-label "$msg_ok"            \
2170				--msgbox "$maxsize" $height $width
2171			exit $FAILURE
2172		fi
2173
2174		XDIALOG_MAXSIZE=$(
2175			set -- ${maxsize##*:}
2176
2177			height=${1%,}
2178			width=$2
2179
2180			echo $height $width
2181		)
2182	fi
2183
2184	#
2185	# If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE.
2186	# The reason for this is because many dialog(1) applications use
2187	# --backtitle for the program name (which is better suited as
2188	# --title with Xdialog(1)).
2189	#
2190	if [ "$USE_XDIALOG" ]; then
2191		local _DIALOG_TITLE="$DIALOG_TITLE"
2192		DIALOG_TITLE="$DIALOG_BACKTITLE"
2193		DIALOG_BACKTITLE="$_DIALOG_TITLE"
2194	fi
2195
2196	f_dprintf "f_dialog_init: dialog(1) API initialized."
2197}
2198
2199############################################################ MAIN
2200
2201#
2202# Self-initialize unless requested otherwise
2203#
2204f_dprintf "%s: DIALOG_SELF_INITIALIZE=[%s]" \
2205          dialog.subr "$DIALOG_SELF_INITIALIZE"
2206case "$DIALOG_SELF_INITIALIZE" in
2207""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
2208*) f_dialog_init
2209esac
2210
2211f_dprintf "%s: Successfully loaded." dialog.subr
2212
2213fi # ! $_DIALOG_SUBR
2214