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