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