1#!/bin/sh 2#- 3# Copyright (c) 2012 Ron McDowell 4# Copyright (c) 2012-2021 Devin Teske 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28# 29############################################################ INCLUDES 30 31# When common.subr is included, it automatically scans "$@" for `-d' and/or 32# `-D file' arguments to conditionally enable debugging. Similarly, when 33# dialog.subr is included, it automatically scans "$@" for `-X' and/or `-S'. 34# To prevent this scanning from becoming confused by extra options, define 35# any/all extra arguments to use in the optstring to getopts when scanning 36# for dedicated options such as those described. 37# 38# NOTE: This needs to be declared before including `common.subr'. 39# NOTE: You really only need to list flags that require an argument as unknown 40# flags are silently accepted unless they take an argument (in which case 41# the following argument will terminate option processing unless it looks 42# like a flag). 43# 44GETOPTS_EXTRA="f:" 45 46BSDCFG_SHARE="/usr/share/bsdconfig" 47. $BSDCFG_SHARE/common.subr || exit 1 48f_dprintf "%s: loading includes..." "$0" 49f_include $BSDCFG_SHARE/dialog.subr 50f_include $BSDCFG_SHARE/mustberoot.subr 51f_include $BSDCFG_SHARE/strings.subr 52 53BSDCFG_LIBE="/usr/libexec/bsdconfig" 54f_include_lang $BSDCFG_LIBE/include/messages.subr 55 56BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp 57USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp 58 59############################################################ CONFIGURATION 60 61# 62# Alternate `local' libexec directory for add-on modules (e.g., from ports) 63# 64BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig" 65 66############################################################ FUNCTIONS 67 68# usage 69# 70# display usage and exit 71# 72usage() 73{ 74 local index="INDEX" 75 local cmd_list # Calculated below 76 77 cd $BSDCFG_LIBE 78 # No need to preserve CWD (headed toward exit) 79 80 # Test for language-specific indices 81 f_quietly ls */"$index.${LANG:-$LC_ALL}" && 82 index="$index.${LANG:-$LC_ALL}" 83 84 cmd_list=$( 85 awk '/^menu_selection="/ { 86 sub(/\|.*/, "") 87 sub(/^menu_selection="/, "") 88 print 89 }' */$index | sort 90 ) 91 92 local alt_cmd_list # Calculated below (if $BSDCFG_LOCAL_LIBE exists) 93 if f_quietly cd $BSDCFG_LOCAL_LIBE; then 94 # No need to preserve CWD (headed toward exit) 95 96 # Test for language-specific indices 97 f_quietly ls */"$index.${LANG:-$LC_ALL}" && 98 index="$index.${LANG:-$LC_ALL}" 99 100 alt_cmd_list=$( 101 awk '/^menu_selection="/ { 102 sub(/\|.*/, "") 103 sub(/^menu_selection="/, "") 104 print 105 }' */$index 2> /dev/null | sort 106 ) 107 108 # Conflate lists, removing duplicates 109 cmd_list=$( printf "%s\n%s\n" \ 110 "$cmd_list" "$alt_cmd_list" | sort -u ) 111 fi 112 113 # 114 # Determine the longest command-length (in characters) 115 # 116 local longest_cmd 117 longest_cmd=$( echo "$cmd_list" | f_longest_line_length ) 118 f_dprintf "longest_cmd=[%s]" "$longest_cmd" 119 120 # 121 # Determine the maximum width of terminal/console 122 # 123 local max_size="$( stty size 2> /dev/null )" 124 : ${max_size:="24 80"} 125 local max_width="${max_size#*[$IFS]}" 126 f_dprintf "max_width=[%s]" "$max_width" 127 128 # 129 # Using the longest command-length as the width of a single column, 130 # determine if we can use more than one column to display commands. 131 # 132 local x=$longest_cmd ncols=1 133 x=$(( $x + 8 )) # Accommodate leading tab character 134 x=$(( $x + 3 + $longest_cmd )) # Pre-load end of next column 135 while [ $x -lt $max_width ]; do 136 ncols=$(( $ncols + 1 )) 137 x=$(( $x + 3 + $longest_cmd )) 138 done 139 f_dprintf "ncols=[%u] x=[%u]" $ncols $x 140 141 # 142 # Re-format the command-list into multiple columns 143 # 144 cmd_list=$( eval "$( echo "$cmd_list" | 145 awk -v ncols=$ncols -v size=$longest_cmd ' 146 BEGIN { 147 n = 0 148 row_item[1] = "" 149 } 150 function print_row() 151 { 152 fmt = "printf \"\\t%-" size "s" 153 for (i = 1; i < cur_col; i++) 154 fmt = fmt " %-" size "s" 155 fmt = fmt "\\n\"" 156 printf "%s", fmt 157 for (i = 1; i <= cur_col; i++) 158 printf " \"%s\"", row_item[i] 159 print "" 160 } 161 { 162 n++ 163 cur_col = (( n - 1 ) % ncols ) + 1 164 printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n", 165 cur_col, $0 166 row_item[cur_col] = $0 167 if ( cur_col == ncols ) print_row() 168 } 169 END { 170 if ( cur_col < ncols ) print_row() 171 }' )" 172 ) 173 174 f_usage $BSDCFG_LIBE/USAGE \ 175 "PROGRAM_NAME" "$pgm" \ 176 "COMMAND_LIST" "$cmd_list" 177 178 # NOTREACHED 179} 180 181# dialog_menu_main 182# 183# Display the dialog(1)-based application main menu. 184# 185dialog_menu_main() 186{ 187 local title="$DIALOG_TITLE" 188 local btitle="$DIALOG_BACKTITLE" 189 local prompt="$msg_menu_text" 190 local menu_list=" 191 'X' '$msg_exit' '$msg_exit_bsdconfig' 192 '1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system' 193 " # END-QUOTE 194 local defaultitem= # Calculated below 195 local hline= 196 197 # 198 # Pick up the base modules (directories named `[0-9][0-9][0-9].*') 199 # 200 local menuitem menu_title menu_help menu_selection index=2 201 for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do 202 [ -f "$BSDCFG_LIBE/$menuitem/INDEX" ] || continue 203 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break 204 205 menu_program= menu_title= menu_help= 206 f_include_lang $BSDCFG_LIBE/$menuitem/INDEX 207 [ "$menu_program" ] || continue 208 209 case "$menu_program" in 210 /*) : already fully qualified ;; 211 *) menu_program="$menuitem/$menu_program" 212 esac 213 214 f_substr -v tag "$DIALOG_MENU_TAGS" $index 1 215 setvar "menu_program$tag" "$menu_program" 216 217 f_shell_escape "$menu_title" menu_title 218 f_shell_escape "$menu_help" menu_help 219 menu_list="$menu_list '$tag' '$menu_title' '$menu_help'" 220 221 index=$(( $index + 1 )) 222 done 223 224 # 225 # Process the `local' libexec sources. 226 # 227 # Whereas modules in $BSDCFG_LIBE must be named [0-9][0-9][0-9].* 228 # modules in $BSDCFG_LOCAL_LIBE should NOT be named this way (making it 229 # more practical for port-maintainers). 230 # 231 # This also has the fortunate side-effect of making the de-duplication 232 # effort rather simple (because so-called `base' modules must be named 233 # differently than add-on modules). 234 # 235 local separator_added= 236 for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * ) 237 do 238 # Skip the module if it looks like a `base' module 239 case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac 240 241 [ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue 242 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break 243 244 menu_program= menu_title= menu_help= 245 f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue 246 [ "$menu_program" ] || continue 247 248 if [ ! "$separator_added" ]; then 249 menu_list="$menu_list '-' '-' ''" 250 separator_added=1 251 fi 252 253 case "$menu_program" in 254 /*) : already fully qualified ;; 255 *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program" 256 esac 257 258 f_substr -v tag "$DIALOG_MENU_TAGS" $index 1 259 setvar "menu_program$tag" "$menu_program" 260 261 f_shell_escape "$menu_title" menu_title 262 f_shell_escape "$menu_help" menu_help 263 menu_list="$menu_list '$tag' '$menu_title' '$menu_help'" 264 265 index=$(( $index + 1 )) 266 done 267 268 local height width rows 269 eval f_dialog_menu_with_help_size height width rows \ 270 \"\$title\" \ 271 \"\$btitle\" \ 272 \"\$prompt\" \ 273 \"\$hline\" \ 274 $menu_list 275 276 # Obtain default-item from previously stored selection 277 f_dialog_default_fetch defaultitem 278 279 local menu_choice 280 menu_choice=$( eval $DIALOG \ 281 --clear \ 282 --title \"\$title\" \ 283 --backtitle \"\$btitle\" \ 284 --hline \"\$hline\" \ 285 --item-help \ 286 --ok-label \"\$msg_ok\" \ 287 --cancel-label \"\$msg_exit_bsdconfig\" \ 288 --help-button \ 289 --help-label \"\$msg_help\" \ 290 ${USE_XDIALOG:+--help \"\"} \ 291 --default-item \"\$defaultitem\" \ 292 --menu \"\$prompt\" \ 293 $height $width $rows \ 294 $menu_list \ 295 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 296 ) 297 local retval=$? 298 f_dialog_data_sanitize menu_choice 299 f_dialog_menutag_store "$menu_choice" 300 301 # Only update default-item on success 302 [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" 303 304 return $retval 305} 306 307############################################################ MAIN 308 309# 310# If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or 311# as a valid resword (see script.subr for additional details about reswords). 312# 313if [ "$pgm" != "bsdconfig" ]; then 314 if indexfile=$( f_index_file "$pgm" ) && 315 cmd=$( f_index_menusel_command "$indexfile" "$pgm" ) 316 then 317 f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*" 318 exec "$cmd" "$@" || exit 1 319 else 320 f_include $BSDCFG_SHARE/script.subr 321 for resword in $RESWORDS; do 322 [ "$pgm" = "$resword" ] || continue 323 # Found a match 324 f_dprintf "pgm=[%s] A valid resWord!" "$pgm" 325 f_dispatch $resword $resword "$@" 326 exit $? 327 done 328 fi 329fi 330 331# 332# Process command-line arguments 333# 334scripts_loaded=0 335while getopts f:h$GETOPTS_STDARGS flag; do 336 case "$flag" in 337 f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr 338 f_script_load "$OPTARG" 339 scripts_loaded=$(( $scripts_loaded + 1 )) ;; 340 h|\?) usage ;; 341 esac 342done 343shift $(( $OPTIND - 1 )) 344 345# If we've loaded any scripts, do not continue any further 346[ $scripts_loaded -gt 0 ] && exit 347 348# 349# Initialize 350# 351f_dialog_title "$msg_main_menu" 352 353[ "$SECURE" ] && f_mustberoot_init 354 355# Incorporate rc-file if it exists 356[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc" 357 358# 359# If a non-option argument was passed, process it as a menuitem selection... 360# 361if [ "$1" ]; then 362 # 363 # ...unless it's a long-option for usage. 364 # 365 case "$1" in -help|--help|-\?) 366 usage 367 # NOTREACHED 368 esac 369 370 # 371 # Find the INDEX (possibly i18n) claiming this keyword and get the 372 # command to execute from the menu_selection line. 373 # 374 if ! { indexfile=$( f_index_file "$1" ) && 375 cmd=$( f_index_menusel_command "$indexfile" "$1" ) 376 }; then 377 # No matches, display usage (which shows valid keywords) 378 f_err "%s: %s: $msg_not_found\n" "$pgm" "$1" 379 usage 380 # NOTREACHED 381 fi 382 383 f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*" 384 shift 385 exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1 386 # NOTREACHED 387fi 388 389# 390# Launch application main menu 391# 392while :; do 393 dialog_menu_main 394 retval=$? 395 f_dialog_menutag_fetch mtag 396 f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" 397 398 if [ $retval -eq $DIALOG_HELP ]; then 399 f_show_help "$BSDCONFIG_HELPFILE" 400 continue 401 elif [ $retval -ne $DIALOG_OK ]; then 402 f_die 403 fi 404 405 case "$mtag" in 406 X) break ;; 407 1) # Usage 408 f_show_help "$USAGE_HELPFILE" 409 continue 410 esac 411 412 # Anything else is a dynamically loaded menuitem 413 414 f_getvar menu_program$mtag menu_program 415 case "$menu_program" in 416 /*) cmd="$menu_program" ;; 417 *) cmd="$BSDCFG_LIBE/$menu_program" 418 esac 419 f_dprintf "cmd=[%s]" "$cmd" 420 $cmd ${USE_XDIALOG:+-X} 421done 422 423exit $SUCCESS 424 425################################################################################ 426# END 427################################################################################ 428