dialog.subr revision 245117
1193323Sedif [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1
2193323Sed#
3193323Sed# Copyright (c) 2006-2013 Devin Teske
4193323Sed# All Rights Reserved.
5193323Sed#
6193323Sed# Redistribution and use in source and binary forms, with or without
7193323Sed# modification, are permitted provided that the following conditions
8193323Sed# are met:
9193323Sed# 1. Redistributions of source code must retain the above copyright
10193323Sed#    notice, this list of conditions and the following disclaimer.
11193323Sed# 2. Redistributions in binary form must reproduce the above copyright
12193323Sed#    notice, this list of conditions and the following disclaimer in the
13193323Sed#    documentation and/or other materials provided with the distribution.
14193323Sed#
15193323Sed# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16193323Sed# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17193323Sed# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18193323Sed# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19193323Sed# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20193323Sed# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21193323Sed# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22193323Sed# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23193323Sed# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24193323Sed# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25193323Sed# SUCH DAMAGE.
26193323Sed#
27193323Sed# $FreeBSD: head/usr.sbin/bsdconfig/share/dialog.subr 245117 2013-01-07 00:18:03Z dteske $
28193323Sed#
29193323Sed############################################################ INCLUDES
30193323Sed
31193323SedBSDCFG_SHARE="/usr/share/bsdconfig"
32193323Sed. $BSDCFG_SHARE/common.subr || exit 1
33193323Sedf_dprintf "%s: loading includes..." dialog.subr
34193323Sedf_include $BSDCFG_SHARE/strings.subr
35193323Sedf_include $BSDCFG_SHARE/variable.subr
36193323Sed
37193323SedBSDCFG_LIBE="/usr/libexec/bsdconfig"
38193323Sedf_include_lang $BSDCFG_LIBE/include/messages.subr
39193323Sed
40193323Sed############################################################ CONFIGURATION
41193323Sed
42193323Sed#
43193323Sed# Default file descriptor to link to stdout for dialog(1) passthru allowing
44193323Sed# execution of dialog from within a sub-shell (so-long as its standard output
45193323Sed# is explicitly redirected to this file descriptor).
46193323Sed#
47193323Sed: ${DIALOG_TERMINAL_PASSTHRU_FD:=${TERMINAL_STDOUT_PASSTHRU:-3}}
48193323Sed
49193323Sed############################################################ GLOBALS
50193323Sed
51199481Srdivacky#
52193323Sed# Default name of dialog(1) utility
53193323Sed# NOTE: This is changed to "Xdialog" by the optional `-X' argument
54193323Sed#
55193323SedDIALOG="dialog"
56193323Sed
57193323Sed#
58193323Sed# Default dialog(1) title and backtitle text
59193323Sed#
60193323SedDIALOG_TITLE="$pgm"
61193323SedDIALOG_BACKTITLE="bsdconfig"
62193323Sed
63193323Sed#
64193323Sed# Settings used while interacting with dialog(1)
65193323Sed#
66193323SedDIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz"
67193323Sed
68193323Sed#
69193323Sed# Declare that we are fully-compliant with Xdialog(1) by unset'ing all
70193323Sed# compatibility settings.
71193323Sed#
72193323Sedunset XDIALOG_HIGH_DIALOG_COMPAT
73193323Sedunset XDIALOG_FORCE_AUTOSIZE
74193323Sedunset XDIALOG_INFOBOX_TIMEOUT
75193323Sed
76193323Sed#
77193323Sed# Default behavior is to call f_dialog_init() automatically when loaded.
78193323Sed#
79193323Sed: ${DIALOG_SELF_INITIALIZE=1}
80193323Sed
81193323Sed#
82193323Sed# Default terminal size (used if/when running without a controlling terminal)
83193323Sed#
84193323Sed: ${DEFAULT_TERMINAL_SIZE:=24 80}
85193323Sed
86193323Sed############################################################ GENERIC FUNCTIONS
87193323Sed
88193323Sed# f_dialog_title [$new_title]
89193323Sed#
90193323Sed# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1)
91193323Sed# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first
92193323Sed# argument is NULL, the current title is returned.
93193323Sed#
94193323Sed# Each time this function is called, a backup of the current values is made
95193323Sed# allowing a one-time (single-level) restoration of the previous title using the
96193323Sed# f_dialog_title_restore() function (below).
97193323Sed#
98193323Sedf_dialog_title()
99193323Sed{
100193323Sed	local new_title="$1"
101193323Sed
102193323Sed	if [ "${1+set}" ]; then
103193323Sed		if [ "$USE_XDIALOG" ]; then
104193323Sed			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
105193323Sed			DIALOG_BACKTITLE="$new_title"
106193323Sed		else
107193323Sed			_DIALOG_TITLE="$DIALOG_TITLE"
108193323Sed			DIALOG_TITLE="$new_title"
109193323Sed		fi
110193323Sed	else
111193323Sed		if [ "$USE_XDIALOG" ]; then
112193323Sed			echo "$DIALOG_BACKTITLE"
113193323Sed		else
114193323Sed			echo "$DIALOG_TITLE"
115193323Sed		fi
116193323Sed	fi
117193323Sed}
118193323Sed
119193323Sed# f_dialog_title_restore
120193323Sed#
121193323Sed# Restore the previous title set by the last call to f_dialog_title().
122193323Sed# Restoration is non-recursive and only works to restore the most-recent title.
123193323Sed#
124193323Sedf_dialog_title_restore()
125193323Sed{
126193323Sed	if [ "$USE_XDIALOG" ]; then
127193323Sed		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
128193323Sed	else
129193323Sed		DIALOG_TITLE="$_DIALOG_TITLE"
130193323Sed	fi
131193323Sed}
132193323Sed
133193323Sed# f_dialog_backtitle [$new_backtitle]
134193323Sed#
135193323Sed# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of
136193323Sed# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the
137193323Sed# first argument is NULL, the current backtitle is returned.
138193323Sed#
139193323Sedf_dialog_backtitle()
140193323Sed{
141193323Sed	local new_backtitle="$1"
142193323Sed
143193323Sed	if [ "${1+set}" ]; then
144193323Sed		if [ "$USE_XDIALOG" ]; then
145193323Sed			_DIALOG_TITLE="$DIALOG_TITLE"
146193323Sed			DIALOG_TITLE="$new_backtitle"
147193323Sed		else
148193323Sed			_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
149193323Sed			DIALOG_BACKTITLE="$new_backtitle"
150193323Sed		fi
151193323Sed	else
152193323Sed		if [ "$USE_XDIALOG" ]; then
153193323Sed			echo "$DIALOG_TITLE"
154193323Sed		else
155193323Sed			echo "$DIALOG_BACKTITLE"
156193323Sed		fi
157193323Sed	fi
158193323Sed}
159193323Sed
160193323Sed# f_dialog_backtitle_restore
161193323Sed#
162193323Sed# Restore the previous backtitle set by the last call to f_dialog_backtitle().
163193323Sed# Restoration is non-recursive and only works to restore the most-recent
164193323Sed# backtitle.
165193323Sed#
166193323Sedf_dialog_backtitle_restore()
167193323Sed{
168193323Sed	if [ "$USE_XDIALOG" ]; then
169193323Sed		DIALOG_TITLE="$_DIALOG_TITLE"
170193323Sed	else
171193323Sed		DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
172193323Sed	fi
173193323Sed}
174193323Sed
175193323Sed############################################################ SIZE FUNCTIONS
176193323Sed
177199481Srdivacky# f_dialog_infobox_size $title $backtitle $prompt [$hline]
178193323Sed#
179193323Sed# Not all versions of dialog(1) perform auto-sizing of the width and height of
180193323Sed# `--infobox' boxes sensibly.
181193323Sed#
182193323Sed# This function helps solve this issue by taking as arguments (in order of
183193323Sed# appearance) the title, backtitle, prompt, and [optionally] hline returning
184193323Sed# the optimal width and height for the box (not exceeding the actual terminal
185193323Sed# width or height).
186193323Sed#
187193323Sed# Newline character sequences (``\n'') in $prompt are expanded as-is done by
188193323Sed# dialog(1).
189193323Sed#
190193323Sed# Output is in the format of "height width".
191193323Sed#
192193323Sedf_dialog_infobox_size()
193193323Sed{
194193323Sed	local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
195193323Sed	local min_width max_size
196193323Sed
197193323Sed	if [ "$USE_XDIALOG" ]; then
198193323Sed		min_width=35
199193323Sed		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
200193323Sed	else
201193323Sed		min_width=24
202193323Sed		max_size=$( stty size 2> /dev/null ) # usually "24 80"
203193323Sed		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
204193323Sed	fi
205193323Sed
206193323Sed	local max_height="${max_size%%[$IFS]*}"
207193323Sed	local max_width="${max_size##*[$IFS]}"
208193323Sed	local height width=$min_width
209193323Sed
210193323Sed	#
211193323Sed	# Bump width for long titles (but don't exceed terminal width).
212193323Sed	#
213193323Sed	n=$(( ${#title} + 4 ))
214193323Sed	if [ $n -gt $width -a $n -gt $min_width ]; then
215193323Sed		# Add 16.6% width for Xdialog(1)
216193323Sed		[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
217193323Sed
218193323Sed		if [ $n -lt $max_width ]; then
219193323Sed			width=$n
220193323Sed		else
221193323Sed			width=$max_width
222193323Sed		fi
223193323Sed	fi
224193323Sed
225193323Sed	#
226193323Sed	# For Xdialog(1), bump width for long backtitles (which appear within
227193323Sed	# the window; don't exceed maximum width).
228193323Sed	#
229193323Sed	if [ "$USE_XDIALOG" ]; then
230193323Sed		n=$(( ${#btitle} + 4 ))
231193323Sed		n=$(( $n + $n / 6 ))
232193323Sed		if [ $n -gt $width -a $n -gt $min_width ]; then
233193323Sed			if [ $n -lt $max_width ]; then
234193323Sed				width=$n
235193323Sed			else
236193323Sed				width=$max_width
237193323Sed			fi
238193323Sed		fi
239193323Sed	fi
240193323Sed
241193323Sed	#
242193323Sed	# Bump width for long prompts (if not already at maximum width).
243193323Sed	#
244193323Sed	if [ $width -lt $max_width ]; then
245193323Sed		n=$( echo "$prompt" | f_longest_line_length )
246193323Sed		n=$(( $n + 4 ))
247193323Sed
248193323Sed		# Add 16.6% width for Xdialog(1)
249193323Sed		[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
250193323Sed
251193323Sed		if [ $n -gt $width -a $n -gt $min_width ]; then
252193323Sed			if [ $n -lt $max_width ]; then
253193323Sed				width=$n
254193323Sed			else
255193323Sed				width=$max_width
256193323Sed			fi
257198892Srdivacky		fi
258193323Sed	fi
259193323Sed
260193323Sed	#
261193323Sed	# Bump width for long hlines (if not already at maximum width).
262193323Sed	# NOTE: Though Xdialog(1) supports `--hline', it's not currently used.
263193323Sed	#
264193323Sed	if [ ! "$USE_XDIALOG" ]; then
265193323Sed		if [ $width -lt $max_width ]; then
266193323Sed			n=$(( ${#hline} + 10 ))
267193323Sed			if [ $n -gt $width -a $n -gt $min_width ]; then
268193323Sed				if [ $n -lt $max_width ]; then
269193323Sed					width=$n
270193323Sed				else
271193323Sed					width=$max_width
272193323Sed				fi
273193323Sed			fi
274193323Sed		fi
275193323Sed	fi
276193323Sed
277193323Sed	#
278193323Sed	# Set height based on number of rows in prompt
279193323Sed	#
280193323Sed	height=$( echo -n "$prompt" | f_number_of_lines )
281193323Sed	height=$(( $height + 2 ))
282193323Sed
283193323Sed	#
284193323Sed	# For Xdialog(1) bump height if backtitle is enabled (displayed in the
285193323Sed	# X11 window with a separator line between the backtitle and msg text)
286193323Sed	#
287193323Sed	if [ "$USE_XDIALOG" -a "$btitle" ]; then
288199481Srdivacky		n=$( echo "$btitle" | f_number_of_lines )
289199481Srdivacky		height=$(( $height + $n + 2 ))
290199481Srdivacky	fi
291199481Srdivacky
292193323Sed	# Make sure height is less than maximum screen size
293199481Srdivacky	[ $height -le $max_height ] || height=$max_height
294193323Sed
295193323Sed	# Return both
296193323Sed	echo "$height $width"
297193323Sed}
298193323Sed
299193323Sed# f_dialog_buttonbox_size $title $backtitle $prompt [$hline]
300193323Sed#
301193323Sed# Not all versions of dialog(1) perform auto-sizing of the width and height of
302193323Sed# `--msgbox' and `--yesno' boxes sensibly.
303193323Sed#
304193323Sed# This function helps solve this issue by taking as arguments (in order of
305193323Sed# appearance) the title, backtitle, prompt, and [optionally] hline returning
306193323Sed# the optimal width and height for the box (not exceeding the actual terminal
307193323Sed# width or height).
308193323Sed#
309193323Sed# Newline character sequences (``\n'') in $prompt are expanded as-is done by
310193323Sed# 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 2 ]; do
741		local tag="$1" item="$2" help="$3"
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_calendar_size $title $backtitle $prompt [$hline]
774#
775# Not all versions of dialog(1) perform auto-sizing of the width and height of
776# `--calendar' boxes sensibly.
777#
778# This function helps solve this issue by taking as arguments (in order of
779# appearance) the title, backtitle, prompt, and [optionally] hline returning
780# the optimal width and height for the box (not exceeding the actual terminal
781# width and height).
782#
783# Newline character sequences (``\n'') in $prompt are expanded as-is done by
784# dialog(1).
785#
786# Output is in the format of "height width".
787#
788f_dialog_calendar_size()
789{
790	local title="$1" btitle="$2" prompt="$3" hline="$4" n
791	local size="$( f_dialog_infobox_size \
792	               		"$title" "$btitle" "$prompt" "$hline" )"
793	local height="${size%%[$IFS]*}"
794	local width="${size##*[$IFS]}"
795
796	local min_width min_height max_size
797	if [ "$USE_XDIALOG" ]; then
798		min_height=15
799		min_width=55
800		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
801	else
802		min_height=0
803		min_width=40
804		max_size=$( stty size 2> /dev/null ) # usually "24 80"
805		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
806	fi
807	local max_height="${max_size%%[$IFS]*}"
808	local max_width="${max_size##*[$IFS]}"
809
810	#
811	# Enforce the minimum width for displaying the calendar
812	#
813	[ $width -ge $min_width ] || width=$min_width
814
815	#
816	# When using dialog(1), the calendar box is unique from other dialog(1)
817	# boxes in-that the height passed should not accomodate the 15-lines
818	# required to display the calendar. This does not apply to Xdialog(1).
819	#
820	# When using Xdialog(1), the height must accomodate the 15-lines
821	# required to display the calendar.
822	#
823	# NOTE: Also under dialog(1), because we can't predict whether the user
824	# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
825	# 16 rather than 15. This does not apply to Xdialog(1).
826	#
827	max_height=$(( $max_height - 16 ))
828	height=$( echo "$prompt" | f_number_of_lines )
829	if [ "$USE_XDIALOG" ]; then
830		# Add height to accomodate for the embedded calendar widget
831		height=$(( $height + $min_height - 1 ))
832
833		# Also, bump height if backtitle is enabled
834		if [ "$btitle" ]; then
835			local n="$( echo "$btitle" | f_number_of_lines )"
836			height=$(( $height + $n + 2 ))
837		fi
838	else
839		[ "$prompt" ] && height=$(( $height + 1 ))
840	fi
841	[ $height -le $max_height ] || height=$max_height
842
843	#
844	# The calendar box refuses to display if too large.
845	#
846	max_width=$(( $max_width - 2 ))
847	[ $width -le $max_width ] || width=$max_width
848
849	# Return both
850	echo "$height $width"
851}
852
853# f_dialog_timebox_size $title $backtitle $prompt [$hline]
854#
855# Not all versions of dialog(1) perform auto-sizing of the width and height of
856# `--timebox' boxes sensibly.
857#
858# This function helps solve this issue by taking as arguments (in order of
859# appearance) the title, backtitle, prompt, and [optionally] hline returning
860# the optimal width and height for the box (not exceeding the actual terminal
861# width and height).
862#
863# Newline character sequences (``\n'') in $prompt are expanded as-is done by
864# dialog(1).
865#
866# Output is in the format of "height width".
867#
868f_dialog_timebox_size()
869{
870	local title="$1" btitle="$2" prompt="$3" hline="$4" n
871	local size="$( f_dialog_infobox_size \
872	               		"$title" "$btitle" "$prompt" "$hline" )"
873	local height="${size%%[$IFS]*}"
874	local width="${size##*[$IFS]}"
875
876	local min_width min_height max_size
877	if [ "$USE_XDIALOG" ]; then
878		min_width=40
879		max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
880	else
881		min_height=0
882		min_width=20
883		max_size=$( stty size 2> /dev/null ) # usually "24 80"
884		: ${max_size:=$DEFAULT_TERMINAL_SIZE}
885	fi
886	local max_height="${max_size%%[$IFS]*}"
887	local max_width="${max_size##*[$IFS]}"
888
889	#
890	# Enforce the minimum width for displaying the timebox
891	#
892	[ $width -ge $min_width ] || width=$min_width
893
894	#
895	# When using dialog(1), the timebox box is unique from other dialog(1)
896	# boxes in-that the height passed should not accomodate the 6-lines
897	# required to display the timebox. This does not apply to Xdialog(1).
898	#
899	# When using Xdialog(1), the height seems to have no effect. All values
900	# provide the same results.
901	#
902	# NOTE: Also under dialog(1), because we can't predict whether the user
903	# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
904	# 7 rather than 6. This does not apply to Xdialog(1).
905	#
906	if [ "$USE_XDIALOG" ]; then
907		height=0 # Autosize; all values produce same results
908	else
909		max_height=$(( $max_height - 7 ))
910		height=$( echo "$prompt" | f_number_of_lines )
911		height=$(( $height + 1 ))
912		[ $height -le $max_height ] || height=$max_height
913		[ "$prompt" ] && height=$(( $height + 1 ))
914	fi
915
916	#
917	# The timebox box refuses to display if too large.
918	#
919	max_width=$(( $max_width - 2 ))
920	[ $width -le $max_width ] || width=$max_width
921
922	# Return both
923	echo "$height $width"
924}
925
926############################################################ CLEAR FUNCTIONS
927
928# f_dialog_clear
929#
930# Clears any/all previous dialog(1) displays.
931#
932f_dialog_clear()
933{
934	$DIALOG --clear
935}
936
937############################################################ INFO FUNCTIONS
938
939# f_dialog_info $info_text ...
940#
941# Throw up a dialog(1) infobox. The infobox remains until another dialog is
942# displayed or `dialog --clear' (or f_dialog_clear) is called.
943#
944f_dialog_info()
945{
946	local info_text="$*"
947	local size="$( f_dialog_infobox_size \
948	               		"$DIALOG_TITLE"     \
949	               		"$DIALOG_BACKTITLE" \
950	               		"$info_text"        )"
951
952	eval $DIALOG \
953		--title \"\$DIALOG_TITLE\"         \
954		--backtitle \"\$DIALOG_BACKTITLE\" \
955		${USE_XDIALOG:+--ignore-eof}       \
956		${USE_XDIALOG:+--no-buttons}       \
957		--infobox \"\$info_text\" $size
958}
959
960# f_xdialog_info $info_text ...
961#
962# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces
963# EOF. This implies that you must execute this either as an rvalue to a pipe,
964# lvalue to indirection or in a sub-shell that provides data on stdin.
965#
966f_xdialog_info()
967{
968	local info_text="$*"
969	local size="$( f_dialog_infobox_size \
970	               		"$DIALOG_TITLE"     \
971	               		"$DIALOG_BACKTITLE" \
972	               		"$info_text"        )"
973
974	eval $DIALOG \
975		--title \"\$DIALOG_TITLE\"         \
976		--backtitle \"\$DIALOG_BACKTITLE\" \
977		--no-close --no-buttons            \
978		--infobox \"\$info_text\" $size    \
979		-1 # timeout of -1 means abort when EOF on stdin
980}
981
982############################################################ MSGBOX FUNCTIONS
983
984# f_dialog_msgbox $msg_text ...
985#
986# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER
987# or ESC, acknowledging the modal dialog.
988#
989# If the user presses ENTER, the exit status is zero (success), otherwise if
990# the user presses ESC the exit status is 255.
991#
992f_dialog_msgbox()
993{
994	local msg_text="$*"
995	local size="$( f_dialog_buttonbox_size \
996	               		"$DIALOG_TITLE"     \
997	               		"$DIALOG_BACKTITLE" \
998	               		"$msg_text"         )"
999
1000	eval $DIALOG \
1001		--title \"\$DIALOG_TITLE\"         \
1002		--backtitle \"\$DIALOG_BACKTITLE\" \
1003		--ok-label \"\$msg_ok\"            \
1004		--msgbox \"\$msg_text\" $size
1005}
1006
1007############################################################ TEXTBOX FUNCTIONS
1008
1009# f_dialog_textbox $file
1010#
1011# Display the contents of $file (or an error if $file does not exist, etc.) in
1012# a dialog(1) textbox (which has a scrollable region for the text). The textbox
1013# remains until the user presses ENTER or ESC, acknowledging the modal dialog.
1014#
1015# If the user presses ENTER, the exit status is zero (success), otherwise if
1016# the user presses ESC the exit status is 255.
1017#
1018f_dialog_textbox()
1019{
1020	local file="$1"
1021	local contents retval size
1022
1023	contents=$( cat "$file" 2>&1 )
1024	retval=$?
1025
1026	size=$( f_dialog_buttonbox_size \
1027	        	"$DIALOG_TITLE"     \
1028	        	"$DIALOG_BACKTITLE" \
1029	        	"$contents"         )
1030
1031	if [ $retval -eq $SUCCESS ]; then
1032		eval $DIALOG \
1033			--title \"\$DIALOG_TITLE\"         \
1034			--backtitle \"\$DIALOG_BACKTITLE\" \
1035			--exit-label \"\$msg_ok\"          \
1036			--no-cancel                        \
1037			--textbox \"\$file\" $size
1038	else
1039		eval $DIALOG \
1040			--title \"\$DIALOG_TITLE\"         \
1041			--backtitle \"\$DIALOG_BACKTITLE\" \
1042			--ok-label \"\$msg_ok\"            \
1043			--msgbox \"\$contents\" $size
1044	fi
1045}
1046
1047############################################################ YESNO FUNCTIONS
1048
1049# f_dialog_yesno $msg_text ...
1050#
1051# Display a dialog(1) Yes/No prompt to allow the user to make some decision.
1052# The yesno prompt remains until the user presses ENTER or ESC, acknowledging
1053# the modal dialog.
1054#
1055# If the user chooses YES the exit status is zero, or chooses NO the exit
1056# status is one, or presses ESC the exit status is 255.
1057#
1058f_dialog_yesno()
1059{
1060	local msg_text="$*"
1061	local hline="$hline_arrows_tab_enter"
1062
1063	f_interactive || return 0 # If non-interactive, return YES all the time
1064
1065	local size="$( f_dialog_buttonbox_size \
1066	               		"$DIALOG_TITLE"     \
1067	               		"$DIALOG_BACKTITLE" \
1068	               		"$msg_text"         \
1069	               		"$hline"            )"
1070
1071	if [ "$USE_XDIALOG" ]; then
1072		eval $DIALOG \
1073			--title \"\$DIALOG_TITLE\"         \
1074			--backtitle \"\$DIALOG_BACKTITLE\" \
1075			--hline \"\$hline\"                \
1076			--ok-label \"\$msg_yes\"           \
1077			--cancel-label \"\$msg_no\"        \
1078			--yesno \"\$msg_text\" $size
1079	else
1080		eval $DIALOG \
1081			--title \"\$DIALOG_TITLE\"         \
1082			--backtitle \"\$DIALOG_BACKTITLE\" \
1083			--hline \"\$hline\"                \
1084			--yes-label \"\$msg_yes\"          \
1085			--no-label \"\$msg_no\"            \
1086			--yesno \"\$msg_text\" $size
1087	fi
1088}
1089
1090# f_dialog_noyes $msg_text ...
1091#
1092# Display a dialog(1) No/Yes prompt to allow the user to make some decision.
1093# The noyes prompt remains until the user presses ENTER or ESC, acknowledging
1094# the modal dialog.
1095#
1096# If the user chooses YES the exit status is zero, or chooses NO the exit
1097# status is one, or presses ESC the exit status is 255.
1098#
1099# NOTE: This is just like the f_dialog_yesno function except "No" is default.
1100#
1101f_dialog_noyes()
1102{
1103	local msg_text="$*"
1104	local hline="$hline_arrows_tab_enter"
1105
1106	f_interactive || return 1 # If non-interactive, return NO all the time
1107
1108	local size="$( f_dialog_buttonbox_size \
1109	               		"$DIALOG_TITLE"     \
1110	               		"$DIALOG_BACKTITLE" \
1111	               		"$msg_text"         \
1112	               		"$hline"            )"
1113
1114	if [ "$USE_XDIALOG" ]; then
1115		eval $DIALOG \
1116			--title \"\$DIALOG_TITLE\"         \
1117			--backtitle \"\$DIALOG_BACKTITLE\" \
1118			--hline \"\$hline\"                \
1119			--default-no                       \
1120			--ok-label \"\$msg_yes\"           \
1121			--cancel-label \"\$msg_no\"        \
1122			--yesno \"\$msg_text\" $size
1123	else
1124		eval $DIALOG \
1125			--title \"\$DIALOG_TITLE\"         \
1126			--backtitle \"\$DIALOG_BACKTITLE\" \
1127			--hline \"\$hline\"                \
1128			--defaultno                        \
1129			--yes-label \"\$msg_yes\"          \
1130			--no-label \"\$msg_no\"            \
1131			--yesno \"\$msg_text\" $size
1132	fi
1133}
1134
1135############################################################ INPUT FUNCTIONS
1136
1137# f_dialog_inputstr
1138#
1139# Obtain the inputstr entered by the user from the most recently displayed
1140# dialog(1) inputbox and clean up any temporary files/variables.
1141#
1142f_dialog_inputstr()
1143{
1144	# Skip warnings and trim leading/trailing whitespace from user input
1145	eval echo \"\$DIALOG_INPUTBOX_$$\" | awk '
1146		BEGIN { found = 0 }
1147		{
1148			if ( ! found )
1149			{
1150				if ( $0 ~ /^$/ ) next
1151				if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
1152				found = 1
1153			}
1154			sub(/^[[:space:]]*/, "")
1155			sub(/[[:space:]]*$/, "")
1156			print
1157		}
1158	'
1159	setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive
1160	return $SUCCESS
1161}
1162
1163# f_dialog_input $prompt [$init [$hline]]
1164#
1165# Prompt the user with a dialog(1) inputbox to enter some value. The inputbox
1166# remains until the the user presses ENTER or ESC, or otherwise ends the
1167# editing session, by selecting `Cancel' for example.
1168#
1169# If the user presses ENTER, the exit status is zero (success), otherwise if
1170# the user presses ESC the exit status is 255, or if the user chose Cancel, the
1171# exit status is instead 1.
1172#
1173# NOTE: The hline should correspond to the type of data you want from the user.
1174# NOTE: Should not be used to edit multiline values.
1175#
1176f_dialog_input()
1177{
1178	local prompt="$1" init="$2" hline="$3"
1179	local size="$( f_dialog_inputbox_size \
1180	               		"$DIALOG_TITLE"     \
1181	                        "$DIALOG_BACKTITLE" \
1182	               		"$prompt"           \
1183	               		"$init"             \
1184	               		"$hline"            )"
1185
1186	local opterm="--"
1187	[ "$USE_XDIALOG" ] && opterm=
1188
1189	local dialog_input
1190	dialog_input=$(
1191		eval $DIALOG \
1192			--title \"\$DIALOG_TITLE\"         \
1193			--backtitle \"\$DIALOG_BACKTITLE\" \
1194			--hline \"\$hline\"                \
1195			--ok-label \"\$msg_ok\"            \
1196			--cancel-label \"\$msg_cancel\"    \
1197			--inputbox \"\$prompt\" $size      \
1198			$opterm \"\$init\"                 \
1199			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
1200	)
1201	local retval=$?
1202
1203	setvar DIALOG_INPUTBOX_$$ "$dialog_input"
1204	f_dialog_inputstr
1205
1206	return $retval
1207}
1208
1209############################################################ MENU FUNCTIONS
1210
1211# f_dialog_menutag
1212#
1213# Obtain the menutag chosen by the user from the most recently displayed
1214# dialog(1) menu and clean up any temporary files/variables.
1215#
1216f_dialog_menutag()
1217{
1218	# Skip warnings
1219	eval echo \"\$DIALOG_MENU_$$\" | awk '
1220		BEGIN { found = 0 }
1221		{
1222			if ( found ) # ... just spew
1223			{
1224				print
1225				next
1226			}
1227			if ( $0 ~ /^$/ ) next
1228			if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
1229			found = 1
1230			print
1231		}
1232	'
1233	setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive
1234	return $SUCCESS
1235}
1236
1237# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ...
1238#
1239# To use the `--menu' option of dialog(1) you must pass an ordered list of
1240# tag/item pairs on the command-line. When the user selects a menu option the
1241# tag for that item is printed to stderr.
1242#
1243# This function allows you to dereference the tag chosen by the user back into
1244# the item associated with said tag.
1245#
1246# Pass the tag chosen by the user as the first argument, followed by the
1247# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1248# passed to dialog(1) for consistency).
1249#
1250# If the tag cannot be found, NULL is returned.
1251#
1252f_dialog_menutag2item()
1253{
1254	local tag="$1" tagn item
1255	shift 1 # tag
1256
1257	while [ $# -gt 0 ]; do
1258		tagn="$1"
1259		item="$2"
1260		shift 2 # tagn/item
1261
1262		if [ "$tag" = "$tagn" ]; then
1263			echo "$item"
1264			return $SUCCESS
1265		fi
1266	done
1267	return $FAILURE
1268}
1269
1270# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \
1271#                                             $tag2 $item2 $help2 ...
1272#
1273# To use the `--menu' option of dialog(1) with the `--item-help' option, you
1274# must pass an ordered list of tag/item/help triplets on the command-line. When
1275# the user selects a menu option the tag for that item is printed to stderr.
1276#
1277# This function allows you to dereference the tag chosen by the user back into
1278# the item associated with said tag (help is discarded/ignored).
1279#
1280# Pass the tag chosen by the user as the first argument, followed by the
1281# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
1282# as was passed to dialog(1) for consistency).
1283#
1284# If the tag cannot be found, NULL is returned.
1285#
1286f_dialog_menutag2item_with_help()
1287{
1288	local tag="$1" tagn item
1289	shift 1 # tag
1290
1291	while [ $# -gt 0 ]; do
1292		tagn="$1"
1293		item="$2"
1294		shift 3 # tagn/item/help
1295
1296		if [ "$tag" = "$tagn" ]; then
1297			echo "$item"
1298			return $SUCCESS
1299		fi
1300	done
1301	return $FAILURE
1302}
1303
1304# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ...
1305#
1306# To use the `--menu' option of dialog(1) you must pass an ordered list of
1307# tag/item pairs on the command-line. When the user selects a menu option the
1308# tag for that item is printed to stderr.
1309#
1310# This function allows you to dereference the tag chosen by the user back into
1311# the index associated with said tag. The index is the one-based tag/item pair
1312# array position within the ordered list of tag/item pairs passed to dialog(1).
1313#
1314# Pass the tag chosen by the user as the first argument, followed by the
1315# ordered list of tag/item pairs (HINT: use the same tag/item list as was
1316# passed to dialog(1) for consistency).
1317#
1318# If the tag cannot be found, NULL is returned.
1319#
1320f_dialog_menutag2index()
1321{
1322	local tag="$1" tagn n=1
1323	shift 1 # tag
1324
1325	while [ $# -gt 0 ]; do
1326		tagn="$1"
1327		shift 2 # tagn/item
1328
1329		if [ "$tag" = "$tagn" ]; then
1330			echo $n
1331			return $SUCCESS
1332		fi
1333		n=$(( $n + 1 ))
1334	done
1335	return $FAILURE
1336}
1337
1338# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \
1339#                                              $tag2 $item2 $help2 ...
1340#
1341# To use the `--menu' option of dialog(1) with the `--item-help' option, you
1342# must pass an ordered list of tag/item/help triplets on the command-line. When
1343# the user selects a menu option the tag for that item is printed to stderr.
1344#
1345# This function allows you to dereference the tag chosen by the user back into
1346# the index associated with said tag. The index is the one-based tag/item/help
1347# triplet array position within the ordered list of tag/item/help triplets
1348# passed to dialog(1).
1349#
1350# Pass the tag chosen by the user as the first argument, followed by the
1351# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
1352# as was passed to dialog(1) for consistency).
1353#
1354# If the tag cannot be found, NULL is returned.
1355#
1356f_dialog_menutag2index_with_help()
1357{
1358	local tag="$1" tagn n=1
1359	shift 1 # tag
1360
1361	while [ $# -gt 0 ]; do
1362		tagn="$1"
1363		shift 3 # tagn/item/help
1364
1365		if [ "$tag" = "$tagn" ]; then
1366			echo $n
1367			return $SUCCESS
1368		fi
1369		n=$(( $n + 1 ))
1370	done
1371	return $FAILURE
1372}
1373
1374############################################################ INIT FUNCTIONS
1375
1376# f_dialog_init
1377#
1378# Initialize (or re-initialize) the dialog module after setting/changing any
1379# of the following environment variables:
1380#
1381# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
1382# 	              that Xdialog(1) should be used instead of dialog(1).
1383#
1384# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
1385# 	              that (while running as root) sudo(8) authentication is
1386# 	              required to proceed.
1387#
1388f_dialog_init()
1389{
1390	DIALOG_SELF_INITIALIZE=
1391
1392	#
1393	# Clone terminal stdout so we can redirect to it from within sub-shells
1394	#
1395	eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1
1396
1397	#
1398	# Process stored command-line arguments
1399	#
1400	SECURE=$( set -- "$ARGV"
1401		while getopts S flag > /dev/null; do
1402			case "$flag" in
1403			S) echo 1;;
1404			\?) continue;;
1405			esac
1406		done
1407	)
1408	USE_XDIALOG=$( set -- "$ARGV"
1409		while getopts SX flag > /dev/null; do
1410			case "$flag" in
1411			S|X) echo 1;;
1412			\?) continue;;
1413			esac
1414		done
1415	)
1416
1417	#
1418	# Process `-X' command-line option
1419	#
1420	[ "$USE_XDIALOG" ] && DIALOG=Xdialog
1421
1422	#
1423	# Sanity check, or die gracefully
1424	#
1425	if ! f_have $DIALOG; then
1426		unset USE_XDIALOG
1427		failed_dialog="$DIALOG"
1428		DIALOG=dialog
1429		f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
1430	fi
1431
1432	#
1433	# If we're already running as root but we got there by way of sudo(8)
1434	# and we have X11, we should merge the xauth(1) credentials from our
1435	# original user.
1436	#
1437	if [ "$USE_XDIALOG" ] &&
1438	   [ "$( id -u )" = "0" ] &&
1439	   [ "$SUDO_USER" -a "$DISPLAY" ]
1440	then
1441		if ! f_have xauth; then
1442			# Die gracefully, as we [likely] can't use Xdialog(1)
1443			unset USE_XDIALOG
1444			DIALOG=dialog
1445			f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth"
1446		fi
1447		HOSTNAME=$(hostname)
1448		displaynum="${DISPLAY#*:}"
1449		eval xauth -if \~$SUDO_USER/.Xauthority extract - \
1450			\"\$HOSTNAME/unix:\$displaynum\" \
1451			\"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \
1452			~root/.Xauthority merge - > /dev/null 2>&1'
1453	fi
1454
1455	#
1456	# Probe Xdialog(1) for maximum height/width constraints, or die
1457	# gracefully
1458	#
1459	if [ "$USE_XDIALOG" ]; then
1460		if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 )
1461		then
1462			# Xdialog(1) failed, fall back to dialog(1)
1463			unset USE_XDIALOG
1464			size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \
1465			                                "$DIALOG_BACKTITLE" \
1466			                                "$maxsize" "" )
1467			eval dialog \
1468				--title \"\$DIALOG_TITLE\"         \
1469				--backtitle \"\$DIALOG_BACKTITLE\" \
1470				--ok-label \"\$msg_ok\"            \
1471				--msgbox \"\$maxsize\" $size
1472			exit $FAILURE
1473		fi
1474
1475		XDIALOG_MAXSIZE=$(
1476			set -- ${maxsize##*:}
1477
1478			height=${1%,}
1479			width=$2
1480
1481			echo $height $width
1482		)
1483		unset maxsize
1484	fi
1485
1486	#
1487	# If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE.
1488	# The reason for this is because many dialog(1) applications use
1489	# --backtitle for the program name (which is better suited as
1490	# --title with Xdialog(1)).
1491	#
1492	if [ "$USE_XDIALOG" ]; then
1493		_DIALOG_TITLE="$DIALOG_TITLE"
1494		DIALOG_TITLE="$DIALOG_BACKTITLE"
1495		DIALOG_BACKTITLE="$_DIALOG_TITLE"
1496		unset _DIALOG_TITLE
1497	fi
1498
1499	f_dprintf "f_dialog_init: dialog(1) API initialized."
1500}
1501
1502############################################################ MAIN
1503
1504#
1505# Self-initialize unless requested otherwise
1506#
1507f_dprintf "%s: DIALOG_SELF_INITIALIZE=[%s]" \
1508          dialog.subr "$DIALOG_SELF_INITIALIZE"
1509case "$DIALOG_SELF_INITIALIZE" in
1510""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
1511*) f_dialog_init
1512esac
1513
1514f_dprintf "%s: Successfully loaded." dialog.subr
1515
1516fi # ! $_DIALOG_SUBR
1517