freebsd-skq/usr.sbin/bsdconfig/share/dialog.subr

1517 lines
42 KiB
Plaintext

if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1
#
# Copyright (c) 2006-2013 Devin Teske
# All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
############################################################ INCLUDES
BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." dialog.subr
f_include $BSDCFG_SHARE/strings.subr
f_include $BSDCFG_SHARE/variable.subr
BSDCFG_LIBE="/usr/libexec/bsdconfig"
f_include_lang $BSDCFG_LIBE/include/messages.subr
############################################################ CONFIGURATION
#
# Default file descriptor to link to stdout for dialog(1) passthru allowing
# execution of dialog from within a sub-shell (so-long as its standard output
# is explicitly redirected to this file descriptor).
#
: ${DIALOG_TERMINAL_PASSTHRU_FD:=${TERMINAL_STDOUT_PASSTHRU:-3}}
############################################################ GLOBALS
#
# Default name of dialog(1) utility
# NOTE: This is changed to "Xdialog" by the optional `-X' argument
#
DIALOG="dialog"
#
# Default dialog(1) title and backtitle text
#
DIALOG_TITLE="$pgm"
DIALOG_BACKTITLE="bsdconfig"
#
# Settings used while interacting with dialog(1)
#
DIALOG_MENU_TAGS="123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyz"
#
# Declare that we are fully-compliant with Xdialog(1) by unset'ing all
# compatibility settings.
#
unset XDIALOG_HIGH_DIALOG_COMPAT
unset XDIALOG_FORCE_AUTOSIZE
unset XDIALOG_INFOBOX_TIMEOUT
#
# Default behavior is to call f_dialog_init() automatically when loaded.
#
: ${DIALOG_SELF_INITIALIZE=1}
#
# Default terminal size (used if/when running without a controlling terminal)
#
: ${DEFAULT_TERMINAL_SIZE:=24 80}
############################################################ GENERIC FUNCTIONS
# f_dialog_title [$new_title]
#
# Set the title of future dialog(1) ($DIALOG_TITLE) or backtitle of Xdialog(1)
# ($DIALOG_BACKTITLE) invocations. If no arguments are given or the first
# argument is NULL, the current title is returned.
#
# Each time this function is called, a backup of the current values is made
# allowing a one-time (single-level) restoration of the previous title using the
# f_dialog_title_restore() function (below).
#
f_dialog_title()
{
local new_title="$1"
if [ "${1+set}" ]; then
if [ "$USE_XDIALOG" ]; then
_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
DIALOG_BACKTITLE="$new_title"
else
_DIALOG_TITLE="$DIALOG_TITLE"
DIALOG_TITLE="$new_title"
fi
else
if [ "$USE_XDIALOG" ]; then
echo "$DIALOG_BACKTITLE"
else
echo "$DIALOG_TITLE"
fi
fi
}
# f_dialog_title_restore
#
# Restore the previous title set by the last call to f_dialog_title().
# Restoration is non-recursive and only works to restore the most-recent title.
#
f_dialog_title_restore()
{
if [ "$USE_XDIALOG" ]; then
DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
else
DIALOG_TITLE="$_DIALOG_TITLE"
fi
}
# f_dialog_backtitle [$new_backtitle]
#
# Set the backtitle of future dialog(1) ($DIALOG_BACKTITLE) or title of
# Xdialog(1) ($DIALOG_TITLE) invocations. If no arguments are given or the
# first argument is NULL, the current backtitle is returned.
#
f_dialog_backtitle()
{
local new_backtitle="$1"
if [ "${1+set}" ]; then
if [ "$USE_XDIALOG" ]; then
_DIALOG_TITLE="$DIALOG_TITLE"
DIALOG_TITLE="$new_backtitle"
else
_DIALOG_BACKTITLE="$DIALOG_BACKTITLE"
DIALOG_BACKTITLE="$new_backtitle"
fi
else
if [ "$USE_XDIALOG" ]; then
echo "$DIALOG_TITLE"
else
echo "$DIALOG_BACKTITLE"
fi
fi
}
# f_dialog_backtitle_restore
#
# Restore the previous backtitle set by the last call to f_dialog_backtitle().
# Restoration is non-recursive and only works to restore the most-recent
# backtitle.
#
f_dialog_backtitle_restore()
{
if [ "$USE_XDIALOG" ]; then
DIALOG_TITLE="$_DIALOG_TITLE"
else
DIALOG_BACKTITLE="$_DIALOG_BACKTITLE"
fi
}
############################################################ SIZE FUNCTIONS
# f_dialog_infobox_size $title $backtitle $prompt [$hline]
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--infobox' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, and [optionally] hline returning
# the optimal width and height for the box (not exceeding the actual terminal
# width or height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# dialog(1).
#
# Output is in the format of "height width".
#
f_dialog_infobox_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
local min_width max_size
if [ "$USE_XDIALOG" ]; then
min_width=35
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_width=24
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_height="${max_size%%[$IFS]*}"
local max_width="${max_size##*[$IFS]}"
local height width=$min_width
#
# Bump width for long titles (but don't exceed terminal width).
#
n=$(( ${#title} + 4 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
# Add 16.6% width for Xdialog(1)
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
#
# For Xdialog(1), bump width for long backtitles (which appear within
# the window; don't exceed maximum width).
#
if [ "$USE_XDIALOG" ]; then
n=$(( ${#btitle} + 4 ))
n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
#
# Bump width for long prompts (if not already at maximum width).
#
if [ $width -lt $max_width ]; then
n=$( echo "$prompt" | f_longest_line_length )
n=$(( $n + 4 ))
# Add 16.6% width for Xdialog(1)
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
#
# Bump width for long hlines (if not already at maximum width).
# NOTE: Though Xdialog(1) supports `--hline', it's not currently used.
#
if [ ! "$USE_XDIALOG" ]; then
if [ $width -lt $max_width ]; then
n=$(( ${#hline} + 10 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
fi
#
# Set height based on number of rows in prompt
#
height=$( echo -n "$prompt" | f_number_of_lines )
height=$(( $height + 2 ))
#
# For Xdialog(1) bump height if backtitle is enabled (displayed in the
# X11 window with a separator line between the backtitle and msg text)
#
if [ "$USE_XDIALOG" -a "$btitle" ]; then
n=$( echo "$btitle" | f_number_of_lines )
height=$(( $height + $n + 2 ))
fi
# Make sure height is less than maximum screen size
[ $height -le $max_height ] || height=$max_height
# Return both
echo "$height $width"
}
# f_dialog_buttonbox_size $title $backtitle $prompt [$hline]
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--msgbox' and `--yesno' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, and [optionally] hline returning
# the optimal width and height for the box (not exceeding the actual terminal
# width or height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# dialog(1).
#
# Output is in the format of "height width".
#
f_dialog_buttonbox_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4"
local size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local height="${size%%[$IFS]*}"
local width="${size##*[$IFS]}"
# Add height to accomodate the buttons
height=$(( $height + 2 ))
# Adjust for clipping with Xdialog(1) on Linux/GTK2
[ "$USE_XDIALOG" ] && height=$(( $height + 3 ))
#
# Enforce maximum height regardless
#
local max_size
if [ "$USE_XDIALOG" ]; then
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_height="${max_size%%[$IFS]*}"
[ $height -le $max_height ] || height=$max_height
# Return both
echo "$height $width"
}
# f_dialog_inputbox_size $title $backtitle $prompt $init [$hline]
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--inputbox' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, initial text, and [optionally]
# hline returning the optimal width and height for the box (not exceeding the
# actual terminal width and height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# dialog(1).
#
# Output is in the format of "height width".
#
f_dialog_inputbox_size()
{
local title="$1" btitle="$2" prompt="$3" init="$4" hline="$5" n
local size="$( f_dialog_buttonbox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local height="${size%%[$IFS]*}"
local width="${size##*[$IFS]}"
local min_width max_size
if [ "$USE_XDIALOG" ]; then
min_width=35
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_width=24
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_height="${max_size%%[$IFS]*}"
local max_width="${max_size##*[$IFS]}"
#
# Add height to accomodate the input box
#
[ ! "$USE_XDIALOG" ] && height=$(( $height + 3 ))
[ $height -le $max_height ] || height=$max_height
#
# Bump width for initial text (if not already at maximum width).
# NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
#
if [ $width -lt $max_width ]; then
n=$(( ${#init} + 7 ))
# Add 16.6% width for Xdialog(1)
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
# Return both
echo "$height $width"
}
# f_xdialog_2inputsbox_size $title $backtitle $prompt \
# $label1 $init1 $label2 $init2
#
# Xdialog(1) does not perform auto-sizing of the width and height of
# `--2inputsbox' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, label for the first field, initial
# text for said field, label for the second field, and initial text for said
# field returning the optimal width and height for the box (not exceeding the
# actual terminal width and height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# Xdialog(1).
#
# Output is in the format of "height width".
#
f_xdialog_2inputsbox_size()
{
local title="$1" btitle="$2" prompt="$3"
local label1="$4" init1="$5" label2="$6" init2="$7" n
local size="$( f_dialog_inputbox_size \
"$title" "$btitle" "$prompt" "$init1" )"
local height="${size%%[$IFS]*}"
local width="${size##*[$IFS]}"
local min_width=35
local max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
local max_height="${max_size%%[$IFS]*}"
local max_width="${max_size##*[$IFS]}"
# Add height for first label
height=$(( $height + 2 ))
#
# Bump width for first label text (if not already at maximum width).
#
if [ $width -lt $max_width ]; then
n=$(( ${#label1} + 7 ))
# Add 16.6% width for Xdialog(1)
n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
# Add height for second label
height=$(( $height + 2 ))
#
# Bump width for second label text (if not already at maximum width).
#
if [ $width -lt $max_width ]; then
n=$(( ${#label2} + 7 ))
# Add 16.6% width for Xdialog(1)
n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
# Add height for a second inputbox
height=$(( $height + 2 ))
[ $height -le $max_height ] || height=$max_height
#
# Bump width for second initial text (if not already at maximum width).
# NOTE: Something neither dialog(1)/Xdialog(1) do, but worth it!
#
if [ $width -lt $max_width ]; then
n=$(( ${#init2} + 7 ))
# Add 16.6% width for Xdialog(1)
n=$(( $n + $n / 6 ))
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
# Return both
echo "$height $width"
}
# f_dialog_menu_size $title $backtitle $prompt $hline \
# $tag1 $item1 $tag2 $item2 ...
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--menu' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, hline and list of tag/item pairs,
# returning the optimal width and height for the menu (not exceeding the actual
# terminal width or height).
#
# Output is in the format of "height width rows".
#
f_dialog_menu_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
local min_width min_rows max_size
if [ "$USE_XDIALOG" ]; then
min_width=35
min_rows=1
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_width=24
min_rows=0
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_width="${max_size##*[$IFS]}"
local max_height="${max_size%%[$IFS]*}"
local box_size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local box_height="${box_size%%[$IFS]*}"
local box_width="${box_size##*[$IFS]}"
local max_rows=$(( $max_height - 8 ))
local height width=$box_width rows=$min_rows
shift 4 # title/btitle/prompt/hline
# If there's no prompt, bump the max-rows by 1
[ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
#
# The sum total between the longest tag-length and longest item-length
# should be used for the menu width (not to exceed terminal width).
#
# Also, calculate the number of rows (not to exceed terminal height).
#
local longest_tag=0 longest_item=0
while [ $# -ge 2 ]; do
local tag="$1" item="$2"
shift 2 # tag/item
[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
[ ${#item} -gt $longest_item ] && longest_item=${#item}
[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
done
# Update width
n=$(( $longest_tag + $longest_item + 10 ))
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
# Fix rows and set height
[ $rows -gt 0 ] || rows=1
if [ "$USE_XDIALOG" ]; then
height=$(( $rows + $box_height + 7 ))
else
height=$(( $rows + $box_height + 4 ))
fi
[ $height -le $max_height ] || height=$max_height
# Return all three
echo "$height $width $rows"
}
# f_dialog_menu_with_help_size $title $backtitle $prompt $hline \
# $tag1 $item1 $help1 $tag2 $item2 $help2 ...
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--menu' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, hline and list of tag/item/help
# triplets, returning the optimal width and height for the menu (not exceeding
# the actual terminal width or height).
#
# Output is in the format of "height width rows".
#
f_dialog_menu_with_help_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
local min_width min_rows max_size
if [ "$USE_XDIALOG" ]; then
min_width=35
min_rows=1
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_width=24
min_rows=0
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_width="${max_size##*[$IFS]}"
local max_height="${max_size%%[$IFS]*}"
local box_size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local box_height="${box_size%%[$IFS]*}"
local box_width="${box_size##*[$IFS]}"
local max_rows=$(( $max_height - 8 ))
local height width=$box_width rows=$min_rows
shift 4 # title/btitle/prompt/hline
# If there's no prompt, bump the max-rows by 1
[ "$prompt" ] || max_rows=$(( $max_rows + 1 ))
#
# The sum total between the longest tag-length and longest item-length
# should be used for the menu width (not to exceed terminal width).
#
# Also, calculate the number of rows (not to exceed terminal height).
#
# Also, calculate the longest help while we're here. This will be used
# to influence the width of the menu if (and only-if) using Xdialog(1).
#
local longest_tag=0 longest_item=0 longest_help=0
while [ $# -ge 3 ]; do
local tag="$1" item="$2" help="$3"
shift 3 # tag/item/help
[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
[ ${#item} -gt $longest_item ] && longest_item=${#item}
[ ${#help} -gt $longest_help ] && longest_help=${#help}
[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
done
# Update width
n=$(( $longest_tag + $longest_item + 10 ))
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
# Update width for help text if using Xdialog(1)
if [ "$USE_XDIALOG" ]; then
n=$(( $longest_help + 10 ))
n=$(( $n + $n / 6 )) # +16.6%
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
fi
# Fix rows and set height
[ $rows -gt 0 ] || rows=1
if [ "$USE_XDIALOG" ]; then
height=$(( $rows + $box_height + 8 ))
else
height=$(( $rows + $box_height + 4 ))
fi
[ $height -le $max_height ] || height=$max_height
# Return all three
echo "$height $width $rows"
}
# f_dialog_radiolist_size $title $backtitle $prompt $hline \
# $tag1 $item1 $status1 $tag2 $item2 $status2 ...
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--radiolist' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, hline and list of tag/item/status
# triplets, returning the optimal width and height for the radiolist (not
# exceeding the actual terminal width or height).
#
# Output is in the format of "height width rows".
#
f_dialog_radiolist_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n=0
local min_width min_rows max_size
if [ "$USE_XDIALOG" ]; then
min_width=35
min_rows=1
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_width=24
min_rows=0
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_width="${max_size##*[$IFS]}"
local max_height="${max_size%%[$IFS]*}"
local box_size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local box_height="${box_size%%[$IFS]*}"
local box_width="${box_size##*[$IFS]}"
local max_rows=$(( $max_height - 8 ))
local height width=$box_width rows=$min_rows
shift 4 # title/btitle/prompt/hline
#
# The sum total between the longest tag-length, longest item-length,
# and radio-button width should be used for the menu width (not to
# exceed terminal width).
#
# Also, calculate the number of rows (not to exceed terminal height).
#
local longest_tag=0 longest_item=0
while [ $# -ge 3 ]; do
local tag="$1" item="$2"
shift 3 # tag/item/status
[ ${#tag} -gt $longest_tag ] && longest_tag=${#tag}
[ ${#item} -gt $longest_item ] && longest_item=${#item}
[ $rows -lt $max_rows ] && rows=$(( $rows + 1 ))
done
# Update width
n=$(( $longest_tag + $longest_item + 13 ))
[ "$USE_XDIALOG" ] && n=$(( $n + $n / 6 )) # Add 16.6% for Xdialog(1)
if [ $n -gt $width -a $n -gt $min_width ]; then
if [ $n -lt $max_width ]; then
width=$n
else
width=$max_width
fi
fi
# Fix rows and set height
[ $rows -gt 0 ] || rows=1
if [ "$USE_XDIALOG" ]; then
height=$(( $rows + $box_height + 7 ))
else
height=$(( $rows + $box_height + 4 ))
fi
[ $height -le $max_height ] || height=$max_height
# Return all three
echo "$height $width $rows"
}
# f_dialog_calendar_size $title $backtitle $prompt [$hline]
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--calendar' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, and [optionally] hline returning
# the optimal width and height for the box (not exceeding the actual terminal
# width and height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# dialog(1).
#
# Output is in the format of "height width".
#
f_dialog_calendar_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n
local size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local height="${size%%[$IFS]*}"
local width="${size##*[$IFS]}"
local min_width min_height max_size
if [ "$USE_XDIALOG" ]; then
min_height=15
min_width=55
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_height=0
min_width=40
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_height="${max_size%%[$IFS]*}"
local max_width="${max_size##*[$IFS]}"
#
# Enforce the minimum width for displaying the calendar
#
[ $width -ge $min_width ] || width=$min_width
#
# When using dialog(1), the calendar box is unique from other dialog(1)
# boxes in-that the height passed should not accomodate the 15-lines
# required to display the calendar. This does not apply to Xdialog(1).
#
# When using Xdialog(1), the height must accomodate the 15-lines
# required to display the calendar.
#
# NOTE: Also under dialog(1), because we can't predict whether the user
# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
# 16 rather than 15. This does not apply to Xdialog(1).
#
max_height=$(( $max_height - 16 ))
height=$( echo "$prompt" | f_number_of_lines )
if [ "$USE_XDIALOG" ]; then
# Add height to accomodate for the embedded calendar widget
height=$(( $height + $min_height - 1 ))
# Also, bump height if backtitle is enabled
if [ "$btitle" ]; then
local n="$( echo "$btitle" | f_number_of_lines )"
height=$(( $height + $n + 2 ))
fi
else
[ "$prompt" ] && height=$(( $height + 1 ))
fi
[ $height -le $max_height ] || height=$max_height
#
# The calendar box refuses to display if too large.
#
max_width=$(( $max_width - 2 ))
[ $width -le $max_width ] || width=$max_width
# Return both
echo "$height $width"
}
# f_dialog_timebox_size $title $backtitle $prompt [$hline]
#
# Not all versions of dialog(1) perform auto-sizing of the width and height of
# `--timebox' boxes sensibly.
#
# This function helps solve this issue by taking as arguments (in order of
# appearance) the title, backtitle, prompt, and [optionally] hline returning
# the optimal width and height for the box (not exceeding the actual terminal
# width and height).
#
# Newline character sequences (``\n'') in $prompt are expanded as-is done by
# dialog(1).
#
# Output is in the format of "height width".
#
f_dialog_timebox_size()
{
local title="$1" btitle="$2" prompt="$3" hline="$4" n
local size="$( f_dialog_infobox_size \
"$title" "$btitle" "$prompt" "$hline" )"
local height="${size%%[$IFS]*}"
local width="${size##*[$IFS]}"
local min_width min_height max_size
if [ "$USE_XDIALOG" ]; then
min_width=40
max_size="$XDIALOG_MAXSIZE" # see CONFIGURATION
else
min_height=0
min_width=20
max_size=$( stty size 2> /dev/null ) # usually "24 80"
: ${max_size:=$DEFAULT_TERMINAL_SIZE}
fi
local max_height="${max_size%%[$IFS]*}"
local max_width="${max_size##*[$IFS]}"
#
# Enforce the minimum width for displaying the timebox
#
[ $width -ge $min_width ] || width=$min_width
#
# When using dialog(1), the timebox box is unique from other dialog(1)
# boxes in-that the height passed should not accomodate the 6-lines
# required to display the timebox. This does not apply to Xdialog(1).
#
# When using Xdialog(1), the height seems to have no effect. All values
# provide the same results.
#
# NOTE: Also under dialog(1), because we can't predict whether the user
# has disabled shadow's in their `$HOME/.dialogrc' file, we'll subtract
# 7 rather than 6. This does not apply to Xdialog(1).
#
if [ "$USE_XDIALOG" ]; then
height=0 # Autosize; all values produce same results
else
max_height=$(( $max_height - 7 ))
height=$( echo "$prompt" | f_number_of_lines )
height=$(( $height + 1 ))
[ $height -le $max_height ] || height=$max_height
[ "$prompt" ] && height=$(( $height + 1 ))
fi
#
# The timebox box refuses to display if too large.
#
max_width=$(( $max_width - 2 ))
[ $width -le $max_width ] || width=$max_width
# Return both
echo "$height $width"
}
############################################################ CLEAR FUNCTIONS
# f_dialog_clear
#
# Clears any/all previous dialog(1) displays.
#
f_dialog_clear()
{
$DIALOG --clear
}
############################################################ INFO FUNCTIONS
# f_dialog_info $info_text ...
#
# Throw up a dialog(1) infobox. The infobox remains until another dialog is
# displayed or `dialog --clear' (or f_dialog_clear) is called.
#
f_dialog_info()
{
local info_text="$*"
local size="$( f_dialog_infobox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$info_text" )"
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
${USE_XDIALOG:+--ignore-eof} \
${USE_XDIALOG:+--no-buttons} \
--infobox \"\$info_text\" $size
}
# f_xdialog_info $info_text ...
#
# Throw up an Xdialog(1) infobox and do not dismiss it until stdin produces
# EOF. This implies that you must execute this either as an rvalue to a pipe,
# lvalue to indirection or in a sub-shell that provides data on stdin.
#
f_xdialog_info()
{
local info_text="$*"
local size="$( f_dialog_infobox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$info_text" )"
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--no-close --no-buttons \
--infobox \"\$info_text\" $size \
-1 # timeout of -1 means abort when EOF on stdin
}
############################################################ MSGBOX FUNCTIONS
# f_dialog_msgbox $msg_text ...
#
# Throw up a dialog(1) msgbox. The msgbox remains until the user presses ENTER
# or ESC, acknowledging the modal dialog.
#
# If the user presses ENTER, the exit status is zero (success), otherwise if
# the user presses ESC the exit status is 255.
#
f_dialog_msgbox()
{
local msg_text="$*"
local size="$( f_dialog_buttonbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg_text" )"
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--ok-label \"\$msg_ok\" \
--msgbox \"\$msg_text\" $size
}
############################################################ TEXTBOX FUNCTIONS
# f_dialog_textbox $file
#
# Display the contents of $file (or an error if $file does not exist, etc.) in
# a dialog(1) textbox (which has a scrollable region for the text). The textbox
# remains until the user presses ENTER or ESC, acknowledging the modal dialog.
#
# If the user presses ENTER, the exit status is zero (success), otherwise if
# the user presses ESC the exit status is 255.
#
f_dialog_textbox()
{
local file="$1"
local contents retval size
contents=$( cat "$file" 2>&1 )
retval=$?
size=$( f_dialog_buttonbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$contents" )
if [ $retval -eq $SUCCESS ]; then
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--exit-label \"\$msg_ok\" \
--no-cancel \
--textbox \"\$file\" $size
else
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--ok-label \"\$msg_ok\" \
--msgbox \"\$contents\" $size
fi
}
############################################################ YESNO FUNCTIONS
# f_dialog_yesno $msg_text ...
#
# Display a dialog(1) Yes/No prompt to allow the user to make some decision.
# The yesno prompt remains until the user presses ENTER or ESC, acknowledging
# the modal dialog.
#
# If the user chooses YES the exit status is zero, or chooses NO the exit
# status is one, or presses ESC the exit status is 255.
#
f_dialog_yesno()
{
local msg_text="$*"
local hline="$hline_arrows_tab_enter"
f_interactive || return 0 # If non-interactive, return YES all the time
local size="$( f_dialog_buttonbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg_text" \
"$hline" )"
if [ "$USE_XDIALOG" ]; then
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--ok-label \"\$msg_yes\" \
--cancel-label \"\$msg_no\" \
--yesno \"\$msg_text\" $size
else
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--yes-label \"\$msg_yes\" \
--no-label \"\$msg_no\" \
--yesno \"\$msg_text\" $size
fi
}
# f_dialog_noyes $msg_text ...
#
# Display a dialog(1) No/Yes prompt to allow the user to make some decision.
# The noyes prompt remains until the user presses ENTER or ESC, acknowledging
# the modal dialog.
#
# If the user chooses YES the exit status is zero, or chooses NO the exit
# status is one, or presses ESC the exit status is 255.
#
# NOTE: This is just like the f_dialog_yesno function except "No" is default.
#
f_dialog_noyes()
{
local msg_text="$*"
local hline="$hline_arrows_tab_enter"
f_interactive || return 1 # If non-interactive, return NO all the time
local size="$( f_dialog_buttonbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg_text" \
"$hline" )"
if [ "$USE_XDIALOG" ]; then
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--default-no \
--ok-label \"\$msg_yes\" \
--cancel-label \"\$msg_no\" \
--yesno \"\$msg_text\" $size
else
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--defaultno \
--yes-label \"\$msg_yes\" \
--no-label \"\$msg_no\" \
--yesno \"\$msg_text\" $size
fi
}
############################################################ INPUT FUNCTIONS
# f_dialog_inputstr
#
# Obtain the inputstr entered by the user from the most recently displayed
# dialog(1) inputbox and clean up any temporary files/variables.
#
f_dialog_inputstr()
{
# Skip warnings and trim leading/trailing whitespace from user input
eval echo \"\$DIALOG_INPUTBOX_$$\" | awk '
BEGIN { found = 0 }
{
if ( ! found )
{
if ( $0 ~ /^$/ ) next
if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
found = 1
}
sub(/^[[:space:]]*/, "")
sub(/[[:space:]]*$/, "")
print
}
'
setvar DIALOG_INPUTBOX_$$ "" # scrub memory in case data was sensitive
return $SUCCESS
}
# f_dialog_input $prompt [$init [$hline]]
#
# Prompt the user with a dialog(1) inputbox to enter some value. The inputbox
# remains until the the user presses ENTER or ESC, or otherwise ends the
# editing session, by selecting `Cancel' for example.
#
# If the user presses ENTER, the exit status is zero (success), otherwise if
# the user presses ESC the exit status is 255, or if the user chose Cancel, the
# exit status is instead 1.
#
# NOTE: The hline should correspond to the type of data you want from the user.
# NOTE: Should not be used to edit multiline values.
#
f_dialog_input()
{
local prompt="$1" init="$2" hline="$3"
local size="$( f_dialog_inputbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$prompt" \
"$init" \
"$hline" )"
local opterm="--"
[ "$USE_XDIALOG" ] && opterm=
local dialog_input
dialog_input=$(
eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--ok-label \"\$msg_ok\" \
--cancel-label \"\$msg_cancel\" \
--inputbox \"\$prompt\" $size \
$opterm \"\$init\" \
2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
)
local retval=$?
setvar DIALOG_INPUTBOX_$$ "$dialog_input"
f_dialog_inputstr
return $retval
}
############################################################ MENU FUNCTIONS
# f_dialog_menutag
#
# Obtain the menutag chosen by the user from the most recently displayed
# dialog(1) menu and clean up any temporary files/variables.
#
f_dialog_menutag()
{
# Skip warnings
eval echo \"\$DIALOG_MENU_$$\" | awk '
BEGIN { found = 0 }
{
if ( found ) # ... just spew
{
print
next
}
if ( $0 ~ /^$/ ) next
if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next
found = 1
print
}
'
setvar DIALOG_MENU_$$ "" # scrub memory in case data was sensitive
return $SUCCESS
}
# f_dialog_menutag2item $tag_chosen $tag1 $item1 $tag2 $item2 ...
#
# To use the `--menu' option of dialog(1) you must pass an ordered list of
# tag/item pairs on the command-line. When the user selects a menu option the
# tag for that item is printed to stderr.
#
# This function allows you to dereference the tag chosen by the user back into
# the item associated with said tag.
#
# Pass the tag chosen by the user as the first argument, followed by the
# ordered list of tag/item pairs (HINT: use the same tag/item list as was
# passed to dialog(1) for consistency).
#
# If the tag cannot be found, NULL is returned.
#
f_dialog_menutag2item()
{
local tag="$1" tagn item
shift 1 # tag
while [ $# -gt 0 ]; do
tagn="$1"
item="$2"
shift 2 # tagn/item
if [ "$tag" = "$tagn" ]; then
echo "$item"
return $SUCCESS
fi
done
return $FAILURE
}
# f_dialog_menutag2item_with_help $tag_chosen $tag1 $item1 $help1 \
# $tag2 $item2 $help2 ...
#
# To use the `--menu' option of dialog(1) with the `--item-help' option, you
# must pass an ordered list of tag/item/help triplets on the command-line. When
# the user selects a menu option the tag for that item is printed to stderr.
#
# This function allows you to dereference the tag chosen by the user back into
# the item associated with said tag (help is discarded/ignored).
#
# Pass the tag chosen by the user as the first argument, followed by the
# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
# as was passed to dialog(1) for consistency).
#
# If the tag cannot be found, NULL is returned.
#
f_dialog_menutag2item_with_help()
{
local tag="$1" tagn item
shift 1 # tag
while [ $# -gt 0 ]; do
tagn="$1"
item="$2"
shift 3 # tagn/item/help
if [ "$tag" = "$tagn" ]; then
echo "$item"
return $SUCCESS
fi
done
return $FAILURE
}
# f_dialog_menutag2index $tag_chosen $tag1 $item1 $tag2 $item2 ...
#
# To use the `--menu' option of dialog(1) you must pass an ordered list of
# tag/item pairs on the command-line. When the user selects a menu option the
# tag for that item is printed to stderr.
#
# This function allows you to dereference the tag chosen by the user back into
# the index associated with said tag. The index is the one-based tag/item pair
# array position within the ordered list of tag/item pairs passed to dialog(1).
#
# Pass the tag chosen by the user as the first argument, followed by the
# ordered list of tag/item pairs (HINT: use the same tag/item list as was
# passed to dialog(1) for consistency).
#
# If the tag cannot be found, NULL is returned.
#
f_dialog_menutag2index()
{
local tag="$1" tagn n=1
shift 1 # tag
while [ $# -gt 0 ]; do
tagn="$1"
shift 2 # tagn/item
if [ "$tag" = "$tagn" ]; then
echo $n
return $SUCCESS
fi
n=$(( $n + 1 ))
done
return $FAILURE
}
# f_dialog_menutag2index_with_help $tag_chosen $tag1 $item1 $help1 \
# $tag2 $item2 $help2 ...
#
# To use the `--menu' option of dialog(1) with the `--item-help' option, you
# must pass an ordered list of tag/item/help triplets on the command-line. When
# the user selects a menu option the tag for that item is printed to stderr.
#
# This function allows you to dereference the tag chosen by the user back into
# the index associated with said tag. The index is the one-based tag/item/help
# triplet array position within the ordered list of tag/item/help triplets
# passed to dialog(1).
#
# Pass the tag chosen by the user as the first argument, followed by the
# ordered list of tag/item/help triplets (HINT: use the same tag/item/help list
# as was passed to dialog(1) for consistency).
#
# If the tag cannot be found, NULL is returned.
#
f_dialog_menutag2index_with_help()
{
local tag="$1" tagn n=1
shift 1 # tag
while [ $# -gt 0 ]; do
tagn="$1"
shift 3 # tagn/item/help
if [ "$tag" = "$tagn" ]; then
echo $n
return $SUCCESS
fi
n=$(( $n + 1 ))
done
return $FAILURE
}
############################################################ INIT FUNCTIONS
# f_dialog_init
#
# Initialize (or re-initialize) the dialog module after setting/changing any
# of the following environment variables:
#
# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
# that Xdialog(1) should be used instead of dialog(1).
#
# SECURE Either NULL or Non-NULL. If given a value will indicate
# that (while running as root) sudo(8) authentication is
# required to proceed.
#
f_dialog_init()
{
DIALOG_SELF_INITIALIZE=
#
# Clone terminal stdout so we can redirect to it from within sub-shells
#
eval exec $DIALOG_TERMINAL_PASSTHRU_FD\>\&1
#
# Process stored command-line arguments
#
SECURE=$( set -- "$ARGV"
while getopts S flag > /dev/null; do
case "$flag" in
S) echo 1;;
\?) continue;;
esac
done
)
USE_XDIALOG=$( set -- "$ARGV"
while getopts SX flag > /dev/null; do
case "$flag" in
S|X) echo 1;;
\?) continue;;
esac
done
)
#
# Process `-X' command-line option
#
[ "$USE_XDIALOG" ] && DIALOG=Xdialog
#
# Sanity check, or die gracefully
#
if ! f_have $DIALOG; then
unset USE_XDIALOG
failed_dialog="$DIALOG"
DIALOG=dialog
f_die 1 "$msg_no_such_file_or_directory" "$pgm" "$failed_dialog"
fi
#
# If we're already running as root but we got there by way of sudo(8)
# and we have X11, we should merge the xauth(1) credentials from our
# original user.
#
if [ "$USE_XDIALOG" ] &&
[ "$( id -u )" = "0" ] &&
[ "$SUDO_USER" -a "$DISPLAY" ]
then
if ! f_have xauth; then
# Die gracefully, as we [likely] can't use Xdialog(1)
unset USE_XDIALOG
DIALOG=dialog
f_die 1 "$msg_no_such_file_or_directory" "$pgm" "xauth"
fi
HOSTNAME=$(hostname)
displaynum="${DISPLAY#*:}"
eval xauth -if \~$SUDO_USER/.Xauthority extract - \
\"\$HOSTNAME/unix:\$displaynum\" \
\"\$HOSTNAME:\$displaynum\" | sudo sh -c 'xauth -ivf \
~root/.Xauthority merge - > /dev/null 2>&1'
fi
#
# Probe Xdialog(1) for maximum height/width constraints, or die
# gracefully
#
if [ "$USE_XDIALOG" ]; then
if ! maxsize=$( LANG= LC_ALL= $DIALOG --print-maxsize 2>&1 )
then
# Xdialog(1) failed, fall back to dialog(1)
unset USE_XDIALOG
size=$( f_dialog_buttonbox_size "$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$maxsize" "" )
eval dialog \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--ok-label \"\$msg_ok\" \
--msgbox \"\$maxsize\" $size
exit $FAILURE
fi
XDIALOG_MAXSIZE=$(
set -- ${maxsize##*:}
height=${1%,}
width=$2
echo $height $width
)
unset maxsize
fi
#
# If using Xdialog(1), swap DIALOG_TITLE with DIALOG_BACKTITLE.
# The reason for this is because many dialog(1) applications use
# --backtitle for the program name (which is better suited as
# --title with Xdialog(1)).
#
if [ "$USE_XDIALOG" ]; then
_DIALOG_TITLE="$DIALOG_TITLE"
DIALOG_TITLE="$DIALOG_BACKTITLE"
DIALOG_BACKTITLE="$_DIALOG_TITLE"
unset _DIALOG_TITLE
fi
f_dprintf "f_dialog_init: dialog(1) API initialized."
}
############################################################ MAIN
#
# Self-initialize unless requested otherwise
#
f_dprintf "%s: DIALOG_SELF_INITIALIZE=[%s]" \
dialog.subr "$DIALOG_SELF_INITIALIZE"
case "$DIALOG_SELF_INITIALIZE" in
""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
*) f_dialog_init
esac
f_dprintf "%s: Successfully loaded." dialog.subr
fi # ! $_DIALOG_SUBR