xref: /freebsd/usr.sbin/bsdconfig/share/dialog.subr (revision 5686c6c38a3e1cc78804eaf5f880bda23dcf592f)
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 (INLUDING, 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# Default behavior is to call f_dialog_init() automatically when loaded.
78#
79: ${DIALOG_SELF_INITIALIZE=1}
80
81#
82# Default terminal size (used if/when running without a controlling terminal)
83#
84: ${DEFAULT_TERMINAL_SIZE:=24 80}
85
86############################################################ GENERIC FUNCTIONS
87
88# f_dialog_title [$new_title]
89#
90# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1)
91# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first
92# argument is NULL, the current title is returned.
93#
94# Each time this function is called, a backup of the current values is made
95# allowing a one-time (single-level) restoration of the previous title using the
96# f_dialog_title_restore() function (below).
97#
98f_dialog_title()
99{
100	local new_title="$1"
101
102	if [ "${1+set}" ]; then
103		if [ "$USE_XDIALOG" ]; then
104			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
105			DIALOG_BACKTITLE="$new_title"
106		else
107			_DIALOG_TITLE="$DIALOG_TITLE"
108			DIALOG_TITLE="$new_title"
109		fi
110	else
111		if [ "$USE_XDIALOG" ]; then
112			echo "$DIALOG_BACKTITLE"
113		else
114			echo "$DIALOG_TITLE"
115		fi
116	fi
117}
118
119# f_dialog_title_restore
120#
121# Restore the previous title set by the last call to f_dialog_title().
122# Restoration is non-recursive and only works to restore the most-recent title.
123#
124f_dialog_title_restore()
125{
126	if [ "$USE_XDIALOG" ]; then
127		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
128	else
129		DIALOG_TITLE="$_DIALOG_TITLE"
130	fi
131}
132
133# f_dialog_backtitle [$new_backtitle]
134#
135# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of
136# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the
137# first argument is NULL, the current backtitle is returned.
138#
139f_dialog_backtitle()
140{
141	local new_backtitle="$1"
142
143	if [ "${1+set}" ]; then
144		if [ "$USE_XDIALOG" ]; then
145			_DIALOG_TITLE="$DIALOG_TITLE"
146			DIALOG_TITLE="$new_backtitle"
147		else
148			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
149			DIALOG_BACKTITLE="$new_backtitle"
150		fi
151	else
152		if [ "$USE_XDIALOG" ]; then
153			echo "$DIALOG_TITLE"
154		else
155			echo "$DIALOG_BACKTITLE"
156		fi
157	fi
158}
159
160# f_dialog_backtitle_restore
161#
162# Restore the previous backtitle set by the last call to f_dialog_backtitle().
163# Restoration is non-recursive and only works to restore the most-recent
164# backtitle.
165#
166f_dialog_backtitle_restore()
167{
168	if [ "$USE_XDIALOG" ]; then
169		DIALOG_TITLE="$_DIALOG_TITLE"
170	else
171		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
172	fi
173}
174
175############################################################ SIZE FUNCTIONS
176
177# f_dialog_infobox_size $title $backtitle $prompt [$hline]
178#
179# Not all versions of dialog(1) perform auto-sizing of the width and height of
180# `--infobox' boxes sensibly.
181#
182# This function helps solve this issue by taking as arguments (in order of
183# appearance) the title, backtitle, prompt, and [optionally] hline returning
184# the optimal width and height for the box (not exceeding the actual terminal
185# width or height).
186#
187# Newline character sequences (``\n'') in $prompt are expanded as-is done by
188# dialog(1).
189#
190# Output is in the format of "height width".
191#
192f_dialog_infobox_size()
193{
194	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
195	local min_width max_size
196
197	if [ "$USE_XDIALOG" ]; then
198		min_width=35
199		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
200	else
201		min_width=24
202		max_size=$( stty size 2> /dev/null ) # usually "24 80"
203		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
204	fi
205
206	local max_height="${max_size%%[$IFS]*}"
207	local max_width="${max_size##*[$IFS]}"
208	local height width=$min_width
209
210	#
211	# Bump width for long titles (but don't exceed terminal width).
212	#
213	n=$(( ${#title} + 4 ))
214	if [ $n -gt $width -a $n -gt $min_width ]; then
215		# Add 16.6% width for Xdialog(1)
216		[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
217
218		if [ $n -lt $max_width ]; then
219			width=$n
220		else
221			width=$max_width
222		fi
223	fi
224
225	#
226	# For Xdialog(1), bump width for long backtitles (which appear within
227	# the window; don't exceed maximum width).
228	#
229	if [ "$USE_XDIALOG" ]; then
230		n=$(( ${#btitle} + 4 ))
231		n=$(( $n + $n / 6 ))
232		if [ $n -gt $width -a $n -gt $min_width ]; then
233			if [ $n -lt $max_width ]; then
234				width=$n
235			else
236				width=$max_width
237			fi
238		fi
239	fi
240
241	#
242	# Bump width for long prompts (if not already at maximum width).
243	#
244	if [ $width -lt $max_width ]; then
245		n=$( echo "$prompt" | f_longest_line_length )
246		n=$(( $n + 4 ))
247
248		# Add 16.6% width for Xdialog(1)
249		[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
250
251		if [ $n -gt $width -a $n -gt $min_width ]; then
252			if [ $n -lt $max_width ]; then
253				width=$n
254			else
255				width=$max_width
256			fi
257		fi
258	fi
259
260	#
261	# Bump width for long hlines (if not already at maximum width).
262	# NOTE: Though Xdialog(1) supports `--hline', it's not currently used.
263	#
264	if [ ! "$USE_XDIALOG" ]; then
265		if [ $width -lt $max_width ]; then
266			n=$(( ${#hline} + 10 ))
267			if [ $n -gt $width -a $n -gt $min_width ]; then
268				if [ $n -lt $max_width ]; then
269					width=$n
270				else
271					width=$max_width
272				fi
273			fi
274		fi
275	fi
276
277	#
278	# Set height based on number of rows in prompt
279	#
280	height=$( echo -n "$prompt" | f_number_of_lines )
281	height=$(( $height + 2 ))
282
283	#
284	# For Xdialog(1) bump height if backtitle is enabled (displayed in the
285	# X11 window with a separator line between the backtitle and msg text)
286	#
287	if [ "$USE_XDIALOG" -a "$btitle" ]; then
288		n=$( echo "$btitle" | f_number_of_lines )
289		height=$(( $height + $n + 2 ))
290	fi
291
292	# Make sure height is less than maximum screen size
293	[ $height -le $max_height ] || height=$max_height
294
295	# Return both
296	echo "$height $width"
297}
298
299# f_dialog_buttonbox_size $title $backtitle $prompt [$hline]
300#
301# Not all versions of dialog(1) perform auto-sizing of the width and height of
302# `--msgbox' and `--yesno' boxes sensibly.
303#
304# This function helps solve this issue by taking as arguments (in order of
305# appearance) the title, backtitle, prompt, and [optionally] hline returning
306# the optimal width and height for the box (not exceeding the actual terminal
307# width or height).
308#
309# Newline character sequences (``\n'') in $prompt are expanded as-is done by
310# dialog(1).
311#
312# Output is in the format of "height width".
313#
314f_dialog_buttonbox_size()
315{
316	local title="$1" btitle="$2" prompt="$3" hline="$4"
317	local size="$( f_dialog_infobox_size \
318	               		"$title" "$btitle" "$prompt" "$hline" )"
319	local height="${size%%[$IFS]*}"
320	local width="${size##*[$IFS]}"
321
322	# Add height to accomodate the buttons
323	height=$(( $height + 2 ))
324
325	# Adjust for clipping with Xdialog(1) on Linux/GTK2
326	[ "$USE_XDIALOG" ] && height=$(( $height + 3 ))
327
328	#
329	# Enforce maximum height regardless
330	#
331	local max_size
332	if [ "$USE_XDIALOG" ]; then
333		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
334	else
335		max_size=$( stty size 2> /dev/null ) # usually "24 80"
336		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
337	fi
338	local max_height="${max_size%%[$IFS]*}"
339	[ $height -le $max_height ] || height=$max_height
340
341	# Return both
342	echo "$height $width"
343}
344
345# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline]
346#
347# Not all versions of dialog(1) perform auto-sizing of the width and height of
348# `--inputbox' boxes sensibly.
349#
350# This function helps solve this issue by taking as arguments (in order of
351# appearance) the title, backtitle, prompt, initial text, and [optionally]
352# hline returning the optimal width and height for the box (not exceeding the
353# actual terminal width and height).
354#
355# Newline character sequences (``\n'') in $prompt are expanded as-is done by
356# dialog(1).
357#
358# Output is in the format of "height width".
359#
360f_dialog_inputbox_size()
361{
362	local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n
363	local size="$( f_dialog_buttonbox_size \
364	               		"$title" "$btitle" "$prompt" "$hline" )"
365	local height="${size%%[$IFS]*}"
366	local width="${size##*[$IFS]}"
367
368	local min_width max_size
369	if [ "$USE_XDIALOG" ]; then
370		min_width=35
371		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
372	else
373		min_width=24
374		max_size=$( stty size 2> /dev/null ) # usually "24 80"
375		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
376	fi
377	local max_height="${max_size%%[$IFS]*}"
378	local max_width="${max_size##*[$IFS]}"
379
380	#
381	# Add height to accomodate the input box
382	#
383	[ ! "$USE_XDIALOG" ] && height=$(( $height + 3 ))
384	[ $height -le $max_height ] || height=$max_height
385
386	#
387	# Bump width for initial text (if not already at maximum width).
388	# NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
389	#
390	if [ $width -lt $max_width ]; then
391		n=$(( ${#init} + 7 ))
392
393		# Add 16.6% width for Xdialog(1)
394		[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
395
396		if [ $n -gt $width -a $n -gt $min_width ]; then
397			if [ $n -lt $max_width ]; then
398				width=$n
399			else
400				width=$max_width
401			fi
402		fi
403	fi
404
405	# Return both
406	echo "$height $width"
407}
408
409# f_xdialog_2inputsbox_size $title $backtitle $prompt \
410#                           $label1 $init1 $label2 $init2
411#
412# Xdialog(1) does not perform auto-sizing of the width and height of
413# `--2inputsbox' boxes sensibly.
414#
415# This function helps solve this issue by taking as arguments (in order of
416# appearance) the title, backtitle, prompt, label for the first field, initial
417# text for said field, label for the second field, and initial text for said
418# field returning the optimal width and height for the box (not exceeding the
419# actual terminal width and height).
420#
421# Newline character sequences (``\n'') in $prompt are expanded as-is done by
422# Xdialog(1).
423#
424# Output is in the format of "height width".
425#
426f_xdialog_2inputsbox_size()
427{
428	local title="$1" btitle="$2" prompt="$3"
429	local label1="$4" init1="$5" label2="$6" init2="$7" n
430	local size="$( f_dialog_inputbox_size \
431	               		"$title" "$btitle" "$prompt" "$init1" )"
432	local height="${size%%[$IFS]*}"
433	local width="${size##*[$IFS]}"
434
435	local min_width=35
436	local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
437	local max_height="${max_size%%[$IFS]*}"
438	local max_width="${max_size##*[$IFS]}"
439
440	# Add height for first label
441	height=$(( $height + 2 ))
442
443	#
444	# Bump width for first label text (if not already at maximum width).
445	#
446	if [ $width -lt $max_width ]; then
447		n=$(( ${#label1} + 7 ))
448
449		# Add 16.6% width for Xdialog(1)
450		n=$(( $n + $n / 6 ))
451
452		if [ $n -gt $width -a $n -gt $min_width ]; then
453			if [ $n -lt $max_width ]; then
454				width=$n
455			else
456				width=$max_width
457			fi
458		fi
459	fi
460
461	# Add height for second label
462	height=$(( $height + 2 ))
463
464	#
465	# Bump width for second label text (if not already at maximum width).
466	#
467	if [ $width -lt $max_width ]; then
468		n=$(( ${#label2} + 7 ))
469
470		# Add 16.6% width for Xdialog(1)
471		n=$(( $n + $n / 6 ))
472
473		if [ $n -gt $width -a $n -gt $min_width ]; then
474			if [ $n -lt $max_width ]; then
475				width=$n
476			else
477				width=$max_width
478			fi
479		fi
480	fi
481
482	# Add height for a second inputbox
483	height=$(( $height + 2 ))
484	[ $height -le $max_height ] || height=$max_height
485
486	#
487	# Bump width for second initial text (if not already at maximum width).
488	# NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
489	#
490	if [ $width -lt $max_width ]; then
491		n=$(( ${#init2} + 7 ))
492
493		# Add 16.6% width for Xdialog(1)
494		n=$(( $n + $n / 6 ))
495
496		if [ $n -gt $width -a $n -gt $min_width ]; then
497			if [ $n -lt $max_width ]; then
498				width=$n
499			else
500				width=$max_width
501			fi
502		fi
503	fi
504
505	# Return both
506	echo "$height $width"
507}
508
509# f_dialog_menu_size $title $backtitle $prompt $hline \
510#                    $tag1 $item1 $tag2 $item2 ...
511#
512# Not all versions of dialog(1) perform auto-sizing of the width and height of
513# `--menu' boxes sensibly.
514#
515# This function helps solve this issue by taking as arguments (in order of
516# appearance) the title, backtitle, prompt, hline and list of tag/item pairs,
517# returning the optimal width and height for the menu (not exceeding the actual
518# terminal width or height).
519#
520# Output is in the format of "height width rows".
521#
522f_dialog_menu_size()
523{
524	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
525	local min_width min_rows max_size
526
527	if [ "$USE_XDIALOG" ]; then
528		min_width=35
529		min_rows=1
530		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
531	else
532		min_width=24
533		min_rows=0
534		max_size=$( stty size 2> /dev/null ) # usually "24 80"
535		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
536	fi
537
538	local max_width="${max_size##*[$IFS]}"
539	local max_height="${max_size%%[$IFS]*}"
540	local box_size="$( f_dialog_infobox_size \
541	                   	"$title" "$btitle" "$prompt" "$hline" )"
542	local box_height="${box_size%%[$IFS]*}"
543	local box_width="${box_size##*[$IFS]}"
544	local max_rows=$(( $max_height - 8 ))
545	local height width=$box_width rows=$min_rows
546
547	shift 4 # title/btitle/prompt/hline
548
549	# If there's no prompt, bump the max-rows by 1
550	[ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
551
552	#
553	# The sum total between the longest tag-length and longest item-length
554	# should be used for the menu width (not to exceed terminal width).
555	#
556	# Also, calculate the number of rows (not to exceed terminal height).
557	#
558	local longest_tag=0 longest_item=0
559	while [ $# -ge 2 ]; do
560		local tag="$1" item="$2"
561		shift 2 # tag/item
562
563		[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
564		[ ${#item} -gt $longest_item ] && longest_item=${#item}
565		[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
566	done
567
568	# Update width
569	n=$(( $longest_tag + $longest_item + 10 ))
570	[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
571	if [ $n -gt $width -a $n -gt $min_width ]; then
572		if [ $n -lt $max_width ]; then
573			width=$n
574		else
575			width=$max_width
576		fi
577	fi
578
579	# Fix rows and set height
580	[ $rows -gt 0 ] || rows=1
581	if [ "$USE_XDIALOG" ]; then
582		height=$(( $rows + $box_height + 7 ))
583	else
584		height=$(( $rows + $box_height + 4 ))
585	fi
586	[ $height -le $max_height ] || height=$max_height
587
588	# Return all three
589	echo "$height $width $rows"
590}
591
592# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \
593#                              $tag1 $item1 $help1 $tag2 $item2 $help2 ...
594#
595# Not all versions of dialog(1) perform auto-sizing of the width and height of
596# `--menu' boxes sensibly.
597#
598# This function helps solve this issue by taking as arguments (in order of
599# appearance) the title, backtitle, prompt, hline and list of tag/item/help
600# triplets, returning the optimal width and height for the menu (not exceeding
601# the actual terminal width or height).
602#
603# Output is in the format of "height width rows".
604#
605f_dialog_menu_with_help_size()
606{
607	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
608	local min_width min_rows max_size
609
610	if [ "$USE_XDIALOG" ]; then
611		min_width=35
612		min_rows=1
613		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
614	else
615		min_width=24
616		min_rows=0
617		max_size=$( stty size 2> /dev/null ) # usually "24 80"
618		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
619	fi
620
621	local max_width="${max_size##*[$IFS]}"
622	local max_height="${max_size%%[$IFS]*}"
623	local box_size="$( f_dialog_infobox_size \
624	                   	"$title" "$btitle" "$prompt" "$hline" )"
625	local box_height="${box_size%%[$IFS]*}"
626	local box_width="${box_size##*[$IFS]}"
627	local max_rows=$(( $max_height - 8 ))
628	local height width=$box_width rows=$min_rows
629
630	shift 4 # title/btitle/prompt/hline
631
632	# If there's no prompt, bump the max-rows by 1
633	[ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
634
635	#
636	# The sum total between the longest tag-length and longest item-length
637	# should be used for the menu width (not to exceed terminal width).
638	#
639	# Also, calculate the number of rows (not to exceed terminal height).
640	#
641	# Also, calculate the longest help while we're here. This will be used
642	# to influence the width of the menu if (and only-if) using Xdialog(1).
643	#
644	local longest_tag=0 longest_item=0 longest_help=0
645	while [ $# -ge 3 ]; do
646		local tag="$1" item="$2" help="$3"
647		shift 3 # tag/item/help
648
649		[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
650		[ ${#item} -gt $longest_item ] && longest_item=${#item}
651		[ ${#help} -gt $longest_help ] && longest_help=${#help}
652		[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
653	done
654
655	# Update width
656	n=$(( $longest_tag + $longest_item + 10 ))
657	[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
658	if [ $n -gt $width -a $n -gt $min_width ]; then
659		if [ $n -lt $max_width ]; then
660			width=$n
661		else
662			width=$max_width
663		fi
664	fi
665
666	# Update width for help text if using Xdialog(1)
667	if [ "$USE_XDIALOG" ]; then
668		n=$(( $longest_help + 10 ))
669		n=$(( $n + $n / 6 )) # +16.6%
670		if [ $n -gt $width -a $n -gt $min_width ]; then
671			if [ $n -lt $max_width ]; then
672				width=$n
673			else
674				width=$max_width
675			fi
676		fi
677	fi
678
679	# Fix rows and set height
680	[ $rows -gt 0 ] || rows=1
681	if [ "$USE_XDIALOG" ]; then
682		height=$(( $rows + $box_height + 8 ))
683	else
684		height=$(( $rows + $box_height + 4 ))
685	fi
686	[ $height -le $max_height ] || height=$max_height
687
688	# Return all three
689	echo "$height $width $rows"
690}
691
692# f_dialog_radiolist_size $title $backtitle $prompt $hline \
693#                         $tag1 $item1 $status1 $tag2 $item2 $status2 ...
694#
695# Not all versions of dialog(1) perform auto-sizing of the width and height of
696# `--radiolist' boxes sensibly.
697#
698# This function helps solve this issue by taking as arguments (in order of
699# appearance) the title, backtitle, prompt, hline and list of tag/item/status
700# triplets, returning the optimal width and height for the radiolist (not
701# exceeding the actual terminal width or height).
702#
703# Output is in the format of "height width rows".
704#
705f_dialog_radiolist_size()
706{
707	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
708	local min_width min_rows max_size
709
710	if [ "$USE_XDIALOG" ]; then
711		min_width=35
712		min_rows=1
713		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
714	else
715		min_width=24
716		min_rows=0
717		max_size=$( stty size 2> /dev/null ) # usually "24 80"
718		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
719	fi
720
721	local max_width="${max_size##*[$IFS]}"
722	local max_height="${max_size%%[$IFS]*}"
723	local box_size="$( f_dialog_infobox_size \
724	                   	"$title" "$btitle" "$prompt" "$hline" )"
725	local box_height="${box_size%%[$IFS]*}"
726	local box_width="${box_size##*[$IFS]}"
727	local max_rows=$(( $max_height - 8 ))
728	local height width=$box_width rows=$min_rows
729
730	shift 4 # title/btitle/prompt/hline
731
732	#
733	# The sum total between the longest tag-length, longest item-length,
734	# and radio-button width should be used for the menu width (not to
735	# exceed terminal width).
736	#
737	# Also, calculate the number of rows (not to exceed terminal height).
738	#
739	local longest_tag=0 longest_item=0
740	while [ $# -ge 3 ]; do
741		local tag="$1" item="$2"
742		shift 3 # tag/item/status
743
744		[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
745		[ ${#item} -gt $longest_item ] && longest_item=${#item}
746		[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
747	done
748
749	# Update width
750	n=$(( $longest_tag + $longest_item + 13 ))
751	[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
752	if [ $n -gt $width -a $n -gt $min_width ]; then
753		if [ $n -lt $max_width ]; then
754			width=$n
755		else
756			width=$max_width
757		fi
758	fi
759
760	# Fix rows and set height
761	[ $rows -gt 0 ] || rows=1
762	if [ "$USE_XDIALOG" ]; then
763		height=$(( $rows + $box_height + 7 ))
764	else
765		height=$(( $rows + $box_height + 4 ))
766	fi
767	[ $height -le $max_height ] || height=$max_height
768
769	# Return all three
770	echo "$height $width $rows"
771}
772
773# f_dialog_checklist_size $title $backtitle $prompt $hline \
774#                         $tag1 $item1 $status1 $tag2 $item2 $status2 ...
775#
776# Not all versions of dialog(1) perform auto-sizing of the width and height of
777# `--checklist' boxes sensibly.
778#
779# This function helps solve this issue by taking as arguments (in order of
780# appearance) the title, backtitle, prompt, hline and list of tag/item/status
781# triplets, returning the optimal width and height for the checklist (not
782# exceeding the actual terminal width or height).
783#
784# Output is in the format of "height width rows".
785#
786f_dialog_checklist_size()
787{
788	f_dialog_radiolist_size "$@"
789}
790
791# f_dialog_radiolist_with_help_size $title $backtitle $prompt $hline \
792#                                   $tag1 $item1 $status1 $help1 \
793#                                   $tag2 $item2 $status2 $help2 ...
794#
795# Not all versions of dialog(1) perform auto-sizing of the width and height of
796# `--radiolist' boxes sensibly.
797#
798# This function helps solve this issue by taking as arguments (in order of
799# appearance) the title, backtitle, prompt, hline and list of
800# tag/item/status/help quads, returning the optimal width and height for the
801# radiolist (not exceeding the actual terminal width or height).
802#
803# Output is in the format of "height width rows".
804#
805f_dialog_radiolist_with_help_size()
806{
807	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
808	local min_width min_rows max_size
809
810	if [ "$USE_XDIALOG" ]; then
811		min_width=35
812		min_rows=1
813		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
814	else
815		min_width=24
816		min_rows=0
817		max_size=$( stty size 2> /dev/null ) # usually "24 80"
818		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
819	fi
820
821	local max_width="${max_size##*[$IFS]}"
822	local max_height="${max_size%%[$IFS]*}"
823	local box_size="$( f_dialog_infobox_size \
824	                   	"$title" "$btitle" "$prompt" "$hline" )"
825	local box_height="${box_size%%[$IFS]*}"
826	local box_width="${box_size##*[$IFS]}"
827	local max_rows=$(( $max_height - 8 ))
828	local height width=$box_width rows=$min_rows
829
830	shift 4 # title/btitle/prompt/hline
831
832	#
833	# The sum total between the longest tag-length, longest item-length,
834	# and radio-button width should be used for the menu width (not to
835	# exceed terminal width).
836	#
837	# Also, calculate the number of rows (not to exceed terminal height).
838	#
839	# Also, calculate the longest help while we're here. This will be used
840	# to influence the width of the menu if (and only-if) using Xdialog(1).
841	#
842	local longest_tag=0 longest_item=0 longest_help=0
843	while [ $# -ge 4 ]; do
844		local tag="$1" item="$2" status="$3" help="$4"
845		shift 4 # tag/item/status/help
846
847		[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
848		[ ${#item} -gt $longest_item ] && longest_item=${#item}
849		[ ${#help} -gt $longest_help ] && longest_help=${#help}
850		[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
851	done
852
853	# Update width
854	n=$(( $longest_tag + $longest_item + 13 ))
855	[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
856	if [ $n -gt $width -a $n -gt $min_width ]; then
857		if [ $n -lt $max_width ]; then
858			width=$n
859		else
860			width=$max_width
861		fi
862	fi
863
864	# Update width for help text if using Xdialog(1)
865	if [ "$USE_XDIALOG" ]; then
866		n=$(( $longest_help + 10 ))
867		n=$(( $n + $n / 6 )) # +16.6%
868		if [ $n -gt $width -a $n -gt $min_width ]; then
869			if [ $n -lt $max_width ]; then
870				width=$n
871			else
872				width=$max_width
873			fi
874		fi
875	fi
876
877	# Fix rows and set height
878	[ $rows -gt 0 ] || rows=1
879	if [ "$USE_XDIALOG" ]; then
880		height=$(( $rows + $box_height + 7 ))
881	else
882		height=$(( $rows + $box_height + 4 ))
883	fi
884	[ $height -le $max_height ] || height=$max_height
885
886	# Return all three
887	echo "$height $width $rows"
888}
889
890# f_dialog_checklist_with_help_size $title $backtitle $prompt $hline \
891#                                   $tag1 $item1 $status1 $help1 \
892#                                   $tag2 $item2 $status2 $help2 ...
893#
894# Not all versions of dialog(1) perform auto-sizing of the width and height of
895# `--checklist' boxes sensibly.
896#
897# This function helps solve this issue by taking as arguments (in order of
898# appearance) the title, backtitle, prompt, hline and list of
899# tag/item/status/help quads, returning the optimal width and height for the
900# checklist (not exceeding the actual terminal width or height).
901#
902# Output is in the format of "height width rows".
903#
904f_dialog_checklist_with_help_size()
905{
906	f_dialog_radiolist_with_help_size "$@"
907}
908
909# f_dialog_calendar_size $title $backtitle $prompt [$hline]
910#
911# Not all versions of dialog(1) perform auto-sizing of the width and height of
912# `--calendar' boxes sensibly.
913#
914# This function helps solve this issue by taking as arguments (in order of
915# appearance) the title, backtitle, prompt, and [optionally] hline returning
916# the optimal width and height for the box (not exceeding the actual terminal
917# width and height).
918#
919# Newline character sequences (``\n'') in $prompt are expanded as-is done by
920# dialog(1).
921#
922# Output is in the format of "height width".
923#
924f_dialog_calendar_size()
925{
926	local title="$1" btitle="$2" prompt="$3" hline="$4" n
927	local size="$( f_dialog_infobox_size \
928	               		"$title" "$btitle" "$prompt" "$hline" )"
929	local height="${size%%[$IFS]*}"
930	local width="${size##*[$IFS]}"
931
932	local min_width min_height max_size
933	if [ "$USE_XDIALOG" ]; then
934		min_height=15
935		min_width=55
936		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
937	else
938		min_height=0
939		min_width=40
940		max_size=$( stty size 2> /dev/null ) # usually "24 80"
941		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
942	fi
943	local max_height="${max_size%%[$IFS]*}"
944	local max_width="${max_size##*[$IFS]}"
945
946	#
947	# Enforce the minimum width for displaying the calendar
948	#
949	[ $width -ge $min_width ] || width=$min_width
950
951	#
952	# When using dialog(1), the calendar box is unique from other dialog(1)
953	# boxes in-that the height passed should not accomodate the 15-lines
954	# required to display the calendar. This does not apply to Xdialog(1).
955	#
956	# When using Xdialog(1), the height must accomodate the 15-lines
957	# required to display the calendar.
958	#
959	# NOTE: Also under dialog(1), because we can't predict whether the user
960	# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
961	# 16 rather than 15. This does not apply to Xdialog(1).
962	#
963	max_height=$(( $max_height - 16 ))
964	height=$( echo "$prompt" | f_number_of_lines )
965	if [ "$USE_XDIALOG" ]; then
966		# Add height to accomodate for the embedded calendar widget
967		height=$(( $height + $min_height - 1 ))
968
969		# Also, bump height if backtitle is enabled
970		if [ "$btitle" ]; then
971			local n="$( echo "$btitle" | f_number_of_lines )"
972			height=$(( $height + $n + 2 ))
973		fi
974	else
975		[ "$prompt" ] && height=$(( $height + 1 ))
976	fi
977	[ $height -le $max_height ] || height=$max_height
978
979	#
980	# The calendar box refuses to display if too large.
981	#
982	max_width=$(( $max_width - 2 ))
983	[ $width -le $max_width ] || width=$max_width
984
985	# Return both
986	echo "$height $width"
987}
988
989# f_dialog_timebox_size $title $backtitle $prompt [$hline]
990#
991# Not all versions of dialog(1) perform auto-sizing of the width and height of
992# `--timebox' boxes sensibly.
993#
994# This function helps solve this issue by taking as arguments (in order of
995# appearance) the title, backtitle, prompt, and [optionally] hline returning
996# the optimal width and height for the box (not exceeding the actual terminal
997# width and height).
998#
999# Newline character sequences (``\n'') in $prompt are expanded as-is done by
1000# dialog(1).
1001#
1002# Output is in the format of "height width".
1003#
1004f_dialog_timebox_size()
1005{
1006	local title="$1" btitle="$2" prompt="$3" hline="$4" n
1007	local size="$( f_dialog_infobox_size \
1008	               		"$title" "$btitle" "$prompt" "$hline" )"
1009	local height="${size%%[$IFS]*}"
1010	local width="${size##*[$IFS]}"
1011
1012	local min_width min_height max_size
1013	if [ "$USE_XDIALOG" ]; then
1014		min_width=40
1015		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
1016	else
1017		min_height=0
1018		min_width=20
1019		max_size=$( stty size 2> /dev/null ) # usually "24 80"
1020		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
1021	fi
1022	local max_height="${max_size%%[$IFS]*}"
1023	local max_width="${max_size##*[$IFS]}"
1024
1025	#
1026	# Enforce the minimum width for displaying the timebox
1027	#
1028	[ $width -ge $min_width ] || width=$min_width
1029
1030	#
1031	# When using dialog(1), the timebox box is unique from other dialog(1)
1032	# boxes in-that the height passed should not accomodate the 6-lines
1033	# required to display the timebox. This does not apply to Xdialog(1).
1034	#
1035	# When using Xdialog(1), the height seems to have no effect. All values
1036	# provide the same results.
1037	#
1038	# NOTE: Also under dialog(1), because we can't predict whether the user
1039	# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
1040	# 7 rather than 6. This does not apply to Xdialog(1).
1041	#
1042	if [ "$USE_XDIALOG" ]; then
1043		height=0 # Autosize; all values produce same results
1044	else
1045		max_height=$(( $max_height - 7 ))
1046		height=$( echo "$prompt" | f_number_of_lines )
1047		height=$(( $height + 1 ))
1048		[ $height -le $max_height ] || height=$max_height
1049		[ "$prompt" ] && height=$(( $height + 1 ))
1050	fi
1051
1052	#
1053	# The timebox box refuses to display if too large.
1054	#
1055	max_width=$(( $max_width - 2 ))
1056	[ $width -le $max_width ] || width=$max_width
1057
1058	# Return both
1059	echo "$height $width"
1060}
1061
1062############################################################ CLEAR FUNCTIONS
1063
1064# f_dialog_clear
1065#
1066# Clears any/all previous dialog(1) displays.
1067#
1068f_dialog_clear()
1069{
1070	$DIALOG --clear
1071}
1072
1073############################################################ INFO FUNCTIONS
1074
1075# f_dialog_info $info_text ...
1076#
1077# Throw up a dialog(1) infobox. The infobox remains until another dialog is
1078# displayed or `dialog --clear' (or f_dialog_clear) is called.
1079#
1080f_dialog_info()
1081{
1082	local info_text="$*"
1083	local size="$( f_dialog_infobox_size \
1084	               		"$DIALOG_TITLE"     \
1085	               		"$DIALOG_BACKTITLE" \
1086	               		"$info_text"        )"
1087
1088	eval $DIALOG \
1089		--title \"\$DIALOG_TITLE\"         \
1090		--backtitle \"\$DIALOG_BACKTITLE\" \
1091		${USE_XDIALOG:+--ignore-eof}       \
1092		${USE_XDIALOG:+--no-buttons}       \
1093		--infobox \"\$info_text\" $size
1094}
1095
1096# f_xdialog_info $info_text ...
1097#
1098# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces
1099# EOF. This implies that you must execute this either as an rvalue to a pipe,
1100# lvalue to indirection or in a sub-shell that provides data on stdin.
1101#
1102f_xdialog_info()
1103{
1104	local info_text="$*"
1105	local size="$( f_dialog_infobox_size \
1106	               		"$DIALOG_TITLE"     \
1107	               		"$DIALOG_BACKTITLE" \
1108	               		"$info_text"        )"
1109
1110	eval $DIALOG \
1111		--title \"\$DIALOG_TITLE\"         \
1112		--backtitle \"\$DIALOG_BACKTITLE\" \
1113		--no-close --no-buttons            \
1114		--infobox \"\$info_text\" $size    \
1115		-1 # timeout of -1 means abort when EOF on stdin
1116}
1117
1118############################################################ MSGBOX FUNCTIONS
1119
1120# f_dialog_msgbox $msg_text ...
1121#
1122# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER
1123# or ESC, acknowledging the modal dialog.
1124#
1125# If the user presses ENTER, the exit status is zero (success), otherwise if
1126# the user presses ESC the exit status is 255.
1127#
1128f_dialog_msgbox()
1129{
1130	local msg_text="$*"
1131	local size="$( f_dialog_buttonbox_size \
1132	               		"$DIALOG_TITLE"     \
1133	               		"$DIALOG_BACKTITLE" \
1134	               		"$msg_text"         )"
1135
1136	eval $DIALOG \
1137		--title \"\$DIALOG_TITLE\"         \
1138		--backtitle \"\$DIALOG_BACKTITLE\" \
1139		--ok-label \"\$msg_ok\"            \
1140		--msgbox \"\$msg_text\" $size
1141}
1142
1143############################################################ TEXTBOX FUNCTIONS
1144
1145# f_dialog_textbox $file
1146#
1147# Display the contents of $file (or an error if $file does not exist, etc.) in
1148# a dialog(1) textbox (which has a scrollable region for the text). The textbox
1149# remains until the user presses ENTER or ESC, acknowledging the modal dialog.
1150#
1151# If the user presses ENTER, the exit status is zero (success), otherwise if
1152# the user presses ESC the exit status is 255.
1153#
1154f_dialog_textbox()
1155{
1156	local file="$1"
1157	local contents retval size
1158
1159	contents=$( cat "$file" 2>&1 )
1160	retval=$?
1161
1162	size=$( f_dialog_buttonbox_size \
1163	        	"$DIALOG_TITLE"     \
1164	        	"$DIALOG_BACKTITLE" \
1165	        	"$contents"         )
1166
1167	if [ $retval -eq $SUCCESS ]; then
1168		eval $DIALOG \
1169			--title \"\$DIALOG_TITLE\"         \
1170			--backtitle \"\$DIALOG_BACKTITLE\" \
1171			--exit-label \"\$msg_ok\"          \
1172			--no-cancel                        \
1173			--textbox \"\$file\" $size
1174	else
1175		eval $DIALOG \
1176			--title \"\$DIALOG_TITLE\"         \
1177			--backtitle \"\$DIALOG_BACKTITLE\" \
1178			--ok-label \"\$msg_ok\"            \
1179			--msgbox \"\$contents\" $size
1180	fi
1181}
1182
1183############################################################ YESNO FUNCTIONS
1184
1185# f_dialog_yesno $msg_text ...
1186#
1187# Display a dialog(1) Yes/No prompt to allow the user to make some decision.
1188# The yesno prompt remains until the user presses ENTER or ESC, acknowledging
1189# the modal dialog.
1190#
1191# If the user chooses YES the exit status is zero, or chooses NO the exit
1192# status is one, or presses ESC the exit status is 255.
1193#
1194f_dialog_yesno()
1195{
1196	local msg_text="$*"
1197	local hline="$hline_arrows_tab_enter"
1198
1199	f_interactive || return 0 # If non-interactive, return YES all the time
1200
1201	local size="$( f_dialog_buttonbox_size \
1202	               		"$DIALOG_TITLE"     \
1203	               		"$DIALOG_BACKTITLE" \
1204	               		"$msg_text"         \
1205	               		"$hline"            )"
1206
1207	if [ "$USE_XDIALOG" ]; then
1208		eval $DIALOG \
1209			--title \"\$DIALOG_TITLE\"         \
1210			--backtitle \"\$DIALOG_BACKTITLE\" \
1211			--hline \"\$hline\"                \
1212			--ok-label \"\$msg_yes\"           \
1213			--cancel-label \"\$msg_no\"        \
1214			--yesno \"\$msg_text\" $size
1215	else
1216		eval $DIALOG \
1217			--title \"\$DIALOG_TITLE\"         \
1218			--backtitle \"\$DIALOG_BACKTITLE\" \
1219			--hline \"\$hline\"                \
1220			--yes-label \"\$msg_yes\"          \
1221			--no-label \"\$msg_no\"            \
1222			--yesno \"\$msg_text\" $size
1223	fi
1224}
1225
1226# f_dialog_noyes $msg_text ...
1227#
1228# Display a dialog(1) No/Yes prompt to allow the user to make some decision.
1229# The noyes prompt remains until the user presses ENTER or ESC, acknowledging
1230# the modal dialog.
1231#
1232# If the user chooses YES the exit status is zero, or chooses NO the exit
1233# status is one, or presses ESC the exit status is 255.
1234#
1235# NOTE: This is just like the f_dialog_yesno function except "No" is default.
1236#
1237f_dialog_noyes()
1238{
1239	local msg_text="$*"
1240	local hline="$hline_arrows_tab_enter"
1241
1242	f_interactive || return 1 # If non-interactive, return NO all the time
1243
1244	local size="$( f_dialog_buttonbox_size \
1245	               		"$DIALOG_TITLE"     \
1246	               		"$DIALOG_BACKTITLE" \
1247	               		"$msg_text"         \
1248	               		"$hline"            )"
1249
1250	if [ "$USE_XDIALOG" ]; then
1251		eval $DIALOG \
1252			--title \"\$DIALOG_TITLE\"         \
1253			--backtitle \"\$DIALOG_BACKTITLE\" \
1254			--hline \"\$hline\"                \
1255			--default-no                       \
1256			--ok-label \"\$msg_yes\"           \
1257			--cancel-label \"\$msg_no\"        \
1258			--yesno \"\$msg_text\" $size
1259	else
1260		eval $DIALOG \
1261			--title \"\$DIALOG_TITLE\"         \
1262			--backtitle \"\$DIALOG_BACKTITLE\" \
1263			--hline \"\$hline\"                \
1264			--defaultno                        \
1265			--yes-label \"\$msg_yes\"          \
1266			--no-label \"\$msg_no\"            \
1267			--yesno \"\$msg_text\" $size
1268	fi
1269}
1270
1271############################################################ INPUT FUNCTIONS
1272
1273# f_dialog_inputstr
1274#
1275# Obtain the inputstr entered by the user from the most recently displayed
1276# dialog(1) inputbox and clean up any temporary files/variables.
1277#
1278f_dialog_inputstr()
1279{
1280	# Skip warnings and trim leading/trailing whitespace from user input
1281	eval echo \"\$DIALOG_INPUTBOX_$$\" | awk '
1282		BEGIN { found = 0 }
1283		{
1284			if ( ! found )
1285			{
1286				if ( $0 ~ /^$/ ) next
1287				if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
1288				found = 1
1289			}
1290			sub(/^[[:space:]]*/, "")
1291			sub(/[[:space:]]*$/, "")
1292			print
1293		}
1294	'
1295	setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive
1296	return $SUCCESS
1297}
1298
1299# f_dialog_input $prompt [$init [$hline]]
1300#
1301# Prompt the user with a dialog(1) inputbox to enter some value. The inputbox
1302# remains until the the user presses ENTER or ESC, or otherwise ends the
1303# editing session, by selecting `Cancel' for example.
1304#
1305# If the user presses ENTER, the exit status is zero (success), otherwise if
1306# the user presses ESC the exit status is 255, or if the user chose Cancel, the
1307# exit status is instead 1.
1308#
1309# NOTE: The hline should correspond to the type of data you want from the user.
1310# NOTE: Should not be used to edit multiline values.
1311#
1312f_dialog_input()
1313{
1314	local prompt="$1" init="$2" hline="$3"
1315	local size="$( f_dialog_inputbox_size \
1316	               		"$DIALOG_TITLE"     \
1317	                        "$DIALOG_BACKTITLE" \
1318	               		"$prompt"           \
1319	               		"$init"             \
1320	               		"$hline"            )"
1321
1322	local opterm="--"
1323	[ "$USE_XDIALOG" ] && opterm=
1324
1325	local dialog_input
1326	dialog_input=$(
1327		eval $DIALOG \
1328			--title \"\$DIALOG_TITLE\"         \
1329			--backtitle \"\$DIALOG_BACKTITLE\" \
1330			--hline \"\$hline\"                \
1331			--ok-label \"\$msg_ok\"            \
1332			--cancel-label \"\$msg_cancel\"    \
1333			--inputbox \"\$prompt\" $size      \
1334			$opterm \"\$init\"                 \
1335			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1336	)
1337	local retval=$?
1338
1339	setvar DIALOG_INPUTBOX_$$ "$dialog_input"
1340	f_dialog_inputstr
1341
1342	return $retval
1343}
1344
1345############################################################ MENU FUNCTIONS
1346
1347# f_dialog_menutag
1348#
1349# Obtain the menutag chosen by the user from the most recently displayed
1350# dialog(1) menu and clean up any temporary files/variables.
1351#
1352f_dialog_menutag()
1353{
1354	# Skip warnings
1355	eval echo \"\$DIALOG_MENU_$$\" | awk '
1356		BEGIN { found = 0 }
1357		{
1358			if ( found ) # ... just spew
1359			{
1360				print
1361				next
1362			}
1363			if ( $0 ~ /^$/ ) next
1364			if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
1365			found = 1
1366			print
1367		}
1368	'
1369	setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive
1370	return $SUCCESS
1371}
1372
1373# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ...
1374#
1375# To use the `--menu' option of dialog(1) you must pass an ordered list of
1376# tag/item pairs on the command-line. When the user selects a menu option the
1377# tag for that item is printed to stderr.
1378#
1379# This function allows you to dereference the tag chosen by the user back into
1380# the item associated with said tag.
1381#
1382# Pass the tag chosen by the user as the first argument, followed by the
1383# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1384# passed to dialog(1) for consistency).
1385#
1386# If the tag cannot be found, NULL is returned.
1387#
1388f_dialog_menutag2item()
1389{
1390	local tag="$1" tagn item
1391	shift 1 # tag
1392
1393	while [ $# -gt 0 ]; do
1394		tagn="$1"
1395		item="$2"
1396		shift 2 # tagn/item
1397
1398		if [ "$tag" = "$tagn" ]; then
1399			echo "$item"
1400			return $SUCCESS
1401		fi
1402	done
1403	return $FAILURE
1404}
1405
1406# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \
1407#                                             $tag2 $item2 $help2 ...
1408#
1409# To use the `--menu' option of dialog(1) with the `--item-help' option, you
1410# must pass an ordered list of tag/item/help triplets on the command-line. When
1411# the user selects a menu option the tag for that item is printed to stderr.
1412#
1413# This function allows you to dereference the tag chosen by the user back into
1414# the item associated with said tag (help is discarded/ignored).
1415#
1416# Pass the tag chosen by the user as the first argument, followed by the
1417# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
1418# as was passed to dialog(1) for consistency).
1419#
1420# If the tag cannot be found, NULL is returned.
1421#
1422f_dialog_menutag2item_with_help()
1423{
1424	local tag="$1" tagn item
1425	shift 1 # tag
1426
1427	while [ $# -gt 0 ]; do
1428		tagn="$1"
1429		item="$2"
1430		shift 3 # tagn/item/help
1431
1432		if [ "$tag" = "$tagn" ]; then
1433			echo "$item"
1434			return $SUCCESS
1435		fi
1436	done
1437	return $FAILURE
1438}
1439
1440# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ...
1441#
1442# To use the `--menu' option of dialog(1) you must pass an ordered list of
1443# tag/item pairs on the command-line. When the user selects a menu option the
1444# tag for that item is printed to stderr.
1445#
1446# This function allows you to dereference the tag chosen by the user back into
1447# the index associated with said tag. The index is the one-based tag/item pair
1448# array position within the ordered list of tag/item pairs passed to dialog(1).
1449#
1450# Pass the tag chosen by the user as the first argument, followed by the
1451# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1452# passed to dialog(1) for consistency).
1453#
1454# If the tag cannot be found, NULL is returned.
1455#
1456f_dialog_menutag2index()
1457{
1458	local tag="$1" tagn n=1
1459	shift 1 # tag
1460
1461	while [ $# -gt 0 ]; do
1462		tagn="$1"
1463		shift 2 # tagn/item
1464
1465		if [ "$tag" = "$tagn" ]; then
1466			echo $n
1467			return $SUCCESS
1468		fi
1469		n=$(( $n + 1 ))
1470	done
1471	return $FAILURE
1472}
1473
1474# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \
1475#                                              $tag2 $item2 $help2 ...
1476#
1477# To use the `--menu' option of dialog(1) with the `--item-help' option, you
1478# must pass an ordered list of tag/item/help triplets on the command-line. When
1479# the user selects a menu option the tag for that item is printed to stderr.
1480#
1481# This function allows you to dereference the tag chosen by the user back into
1482# the index associated with said tag. The index is the one-based tag/item/help
1483# triplet array position within the ordered list of tag/item/help triplets
1484# passed to dialog(1).
1485#
1486# Pass the tag chosen by the user as the first argument, followed by the
1487# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
1488# as was passed to dialog(1) for consistency).
1489#
1490# If the tag cannot be found, NULL is returned.
1491#
1492f_dialog_menutag2index_with_help()
1493{
1494	local tag="$1" tagn n=1
1495	shift 1 # tag
1496
1497	while [ $# -gt 0 ]; do
1498		tagn="$1"
1499		shift 3 # tagn/item/help
1500
1501		if [ "$tag" = "$tagn" ]; then
1502			echo $n
1503			return $SUCCESS
1504		fi
1505		n=$(( $n + 1 ))
1506	done
1507	return $FAILURE
1508}
1509
1510############################################################ INIT FUNCTIONS
1511
1512# f_dialog_init
1513#
1514# Initialize (or re-initialize) the dialog module after setting/changing any
1515# of the following environment variables:
1516#
1517# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
1518# 	              that Xdialog(1) should be used instead of dialog(1).
1519#
1520# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
1521# 	              that (while running as root) sudo(8) authentication is
1522# 	              required to proceed.
1523#
1524f_dialog_init()
1525{
1526	DIALOG_SELF_INITIALIZE=
1527
1528	#
1529	# Clone terminal stdout so we can redirect to it from within sub-shells
1530	#
1531	eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1
1532
1533	#
1534	# Add `-S' and `-X' to the list of standard arguments supported by all
1535	#
1536	case "$GETOPTS_STDARGS" in
1537	*SX*) : good ;; # already present
1538	   *) GETOPTS_STDARGS="${GETOPTS_STDARGS}SX"
1539	esac
1540
1541	#
1542	# Process stored command-line arguments
1543	#
1544	f_dprintf "f_dialog_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \
1545	          "$ARGV" "$GETOPTS_STDARGS"
1546	SECURE=$( set -- $ARGV
1547		while getopts "$GETOPTS_STDARGS" flag > /dev/null; do
1548			case "$flag" in
1549			S) echo 1;;
1550			\?) continue;;
1551			esac
1552		done
1553	)
1554	USE_XDIALOG=$( set -- $ARGV
1555		while getopts $GETOPTS_STDARGS flag > /dev/null; do
1556			case "$flag" in
1557			S|X) echo 1;;
1558			\?) continue;;
1559			esac
1560		done
1561	)
1562	f_dprintf "f_dialog_init: SECURE=[%s] USE_XDIALOG=[%s]" \
1563	          "$SECURE" "$USE_XDIALOG"
1564
1565	#
1566	# Process `-X' command-line option
1567	#
1568	[ "$USE_XDIALOG" ] && DIALOG=Xdialog
1569
1570	#
1571	# Sanity check, or die gracefully
1572	#
1573	if ! f_have $DIALOG; then
1574		unset USE_XDIALOG
1575		failed_dialog="$DIALOG"
1576		DIALOG=dialog
1577		f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
1578	fi
1579
1580	#
1581	# If we're already running as root but we got there by way of sudo(8)
1582	# and we have X11, we should merge the xauth(1) credentials from our
1583	# original user.
1584	#
1585	if [ "$USE_XDIALOG" ] &&
1586	   [ "$( id -u )" = "0" ] &&
1587	   [ "$SUDO_USER" -a "$DISPLAY" ]
1588	then
1589		if ! f_have xauth; then
1590			# Die gracefully, as we [likely] can't use Xdialog(1)
1591			unset USE_XDIALOG
1592			DIALOG=dialog
1593			f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth"
1594		fi
1595		HOSTNAME=$(hostname)
1596		displaynum="${DISPLAY#*:}"
1597		eval xauth -if \~$SUDO_USER/.Xauthority extract - \
1598			\"\$HOSTNAME/unix:\$displaynum\" \
1599			\"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \
1600			~root/.Xauthority merge - > /dev/null 2>&1'
1601	fi
1602
1603	#
1604	# Probe Xdialog(1) for maximum height/width constraints, or die
1605	# gracefully
1606	#
1607	if [ "$USE_XDIALOG" ]; then
1608		if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 )
1609		then
1610			# Xdialog(1) failed, fall back to dialog(1)
1611			unset USE_XDIALOG
1612			size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \
1613			                                "$DIALOG_BACKTITLE" \
1614			                                "$maxsize" "" )
1615			eval dialog \
1616				--title \"\$DIALOG_TITLE\"         \
1617				--backtitle \"\$DIALOG_BACKTITLE\" \
1618				--ok-label \"\$msg_ok\"            \
1619				--msgbox \"\$maxsize\" $size
1620			exit $FAILURE
1621		fi
1622
1623		XDIALOG_MAXSIZE=$(
1624			set -- ${maxsize##*:}
1625
1626			height=${1%,}
1627			width=$2
1628
1629			echo $height $width
1630		)
1631		unset maxsize
1632	fi
1633
1634	#
1635	# If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE.
1636	# The reason for this is because many dialog(1) applications use
1637	# --backtitle for the program name (which is better suited as
1638	# --title with Xdialog(1)).
1639	#
1640	if [ "$USE_XDIALOG" ]; then
1641		_DIALOG_TITLE="$DIALOG_TITLE"
1642		DIALOG_TITLE="$DIALOG_BACKTITLE"
1643		DIALOG_BACKTITLE="$_DIALOG_TITLE"
1644		unset _DIALOG_TITLE
1645	fi
1646
1647	f_dprintf "f_dialog_init: dialog(1) API initialized."
1648}
1649
1650############################################################ MAIN
1651
1652#
1653# Self-initialize unless requested otherwise
1654#
1655f_dprintf "%s: DIALOG_SELF_INITIALIZE=[%s]" \
1656          dialog.subr "$DIALOG_SELF_INITIALIZE"
1657case "$DIALOG_SELF_INITIALIZE" in
1658""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
1659*) f_dialog_init
1660esac
1661
1662f_dprintf "%s: Successfully loaded." dialog.subr
1663
1664fi # ! $_DIALOG_SUBR
1665