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