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