diff options
author | dteske <dteske@FreeBSD.org> | 2012-09-18 22:28:42 +0000 |
---|---|---|
committer | dteske <dteske@FreeBSD.org> | 2012-09-18 22:28:42 +0000 |
commit | 282d6b7f2c0f1fb51d911f75ef9989f62e389985 (patch) | |
tree | 3ef909c692976c6a6b0854f8d722182e78ff8915 /usr.sbin/bsdconfig/share/dialog.subr | |
parent | 969b25f00f504248a5c234274661472d753475ad (diff) | |
download | FreeBSD-src-282d6b7f2c0f1fb51d911f75ef9989f62e389985.zip FreeBSD-src-282d6b7f2c0f1fb51d911f75ef9989f62e389985.tar.gz |
Move major includes into /usr/share/bsdconfig for easy external access.
Reviewed by: adrian (co-mentor)
Approved by: adrian (co-mentor)
Diffstat (limited to 'usr.sbin/bsdconfig/share/dialog.subr')
-rw-r--r-- | usr.sbin/bsdconfig/share/dialog.subr | 1443 |
1 files changed, 1443 insertions, 0 deletions
diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr new file mode 100644 index 0000000..cced388 --- /dev/null +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -0,0 +1,1443 @@ +if [ ! "$_DIALOG_SUBR" ]; then _DIALOG_SUBR=1 +# +# Copyright (c) 2006-2012 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_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" +f_include_lang $BSDCFG_LIBE/include/messages.subr + +############################################################ CONFIGURATION + +# +# Default directory to store dialog(1) temporary files +# +: ${DIALOG_TMPDIR:="/tmp"} + +############################################################ 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 if not already +# called manually by the time the first f_dialog_*() function is used. +# +: ${DIALOG_SELF_INITIALIZE=1} + +############################################################ 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 [ "$new_title" ]; 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 [ "$new_backtitle" ]; 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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 "$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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 )) + + # + # 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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 2 ]; do + local tag="$1" item="$2" help="$3" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 ) # usually "24 80" + 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_SELF_INITIALIZE" ] && f_dialog_init + + $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 dialog_clear) is called. +# +f_dialog_info() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + 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 +} + +############################################################ 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + 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() +{ + [ "$DIALOG_SELF_INITIALIZE" ] && f_dialog_init + + local msg_text="$*" + local hline="$hline_arrows_tab_enter" + 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. +# +f_dialog_inputstr() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.inputbox.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + # Skip warnings and trim leading/trailing whitespace from user input + awk ' + BEGIN { found = 0 } + { + if ( ! found ) + { + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + } + sub(/^[[:space:]]*/, "") + sub(/[[:space:]]*$/, "") + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + return $SUCCESS +} + +############################################################ 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. +# +f_dialog_menutag() +{ + local tmpfile="$DIALOG_TMPDIR/dialog.menu.$$" + + [ -f "$tmpfile" ] || return $FAILURE + + awk ' + BEGIN { found = 0 } + { + if ( found ) # ... just spew + { + print + next + } + if ( $0 ~ /^$/ ) next + if ( $0 ~ /^Gdk-WARNING \*\*:/ ) next + found = 1 + print + } + ' "$tmpfile" 2> /dev/null + f_quietly rm -f "$tmpfile" + + 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= + + # + # 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 +} + +############################################################ CLEAN-UP FUNCTIONS + +# f_clean_up +# +# Clean-up routines (run when script exits or is killed). +# +f_clean_up() +{ + f_quietly rm -f "$DIALOG_TMPDIR"/dialog.*.$$ +} + +############################################################ MAIN + +# +# Trap signals so we can recover gracefully +# +trap 'f_clean_up' EXIT + +fi # ! $_DIALOG_SUBR |