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