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