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