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