1if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1 2# 3# Copyright (c) 2012 Ron McDowell 4# Copyright (c) 2012 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############################################################ GLOBALS 31 32# 33# Program name 34# 35pgm="${0##*/}" 36 37# 38# Program arguments 39# 40ARGC="$#" 41ARGV="$@" 42 43# 44# Global exit status variables 45# 46SUCCESS=0 47FAILURE=1 48 49############################################################ FUNCTIONS 50 51# 52# This is an empty function by default, to use it, copy 53# /usr/share/examples/bsdconfig/bsdconfigrc to $HOME/.bsdconfigrc 54# 55f_dprintf() 56{ 57 : this page intentionally left blank 58} 59 60# f_err $fmt [ $opts ... ] 61# 62# Print a message to stderr (fd=2). 63# 64f_err() 65{ 66 printf "$@" >&2 67} 68 69# f_quietly $command [ $arguments ... ] 70# 71# Run a command quietly (quell any output to stdout or stderr) 72# 73f_quietly() 74{ 75 "$@" > /dev/null 2>&1 76} 77 78# f_have $anything ... 79# 80# A wrapper to the `type' built-in. Returns true if argument is a valid shell 81# built-in, keyword, or externally-tracked binary, otherwise false. 82# 83f_have() 84{ 85 f_quietly type "$@" 86} 87 88# f_die [ $status [ $fmt [ $opts ... ]]] 89# 90# Abruptly terminate due to an error optionally displaying a message in a 91# dialog box using printf(1) syntax. 92# 93f_die() 94{ 95 local status=$FAILURE 96 97 # If there is at least one argument, take it as the status 98 if [ $# -gt 0 ]; then 99 status=$1 100 shift 1 # status 101 fi 102 103 # If there are still arguments left, pass them to f_show_msg 104 [ $# -gt 0 ] && f_show_msg "$@" 105 106 # Optionally call f_clean_up() function if it exists 107 f_have f_clean_up && f_clean_up 108 109 exit $status 110} 111 112# f_interrupt 113# 114# Interrupt handler. 115# 116f_interrupt() 117{ 118 exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap 119 f_die 120} 121 122# f_show_msg $fmt [ $opts ... ] 123# 124# Display a message in a dialog box using printf(1) syntax. 125# 126f_show_msg() 127{ 128 local msg 129 msg=$( printf "$@" ) 130 131 # 132 # Use f_dialog_msgbox from dialog.subr if possible, otherwise fall 133 # back to dialog(1) (without options, making it obvious when using 134 # un-aided system dialog). 135 # 136 if f_have f_dialog_msgbox; then 137 f_dialog_msgbox "$msg" 138 else 139 dialog --msgbox "$msg" 0 0 140 fi 141} 142 143# f_show_help $file 144# 145# Display a language help-file. Automatically takes $LANG and $LC_ALL into 146# consideration when displaying $file (suffix ".$LC_ALL" or ".$LANG" will 147# automatically be added prior to loading the language help-file). 148# 149# If a language has been requested by setting either $LANG or $LC_ALL in the 150# environment and the language-specific help-file does not exist we will fall 151# back to $file without-suffix. 152# 153# If the language help-file does not exist, an error is displayed instead. 154# 155f_show_help() 156{ 157 local file="$1" 158 local lang="${LANG:-$LC_ALL}" 159 160 [ -f "$file.$lang" ] && file="$file.$lang" 161 162 # 163 # Use f_dialog_textbox from dialog.subr if possible, otherwise fall 164 # back to dialog(1) (without options, making it obvious when using 165 # un-aided system dialog). 166 # 167 if f_have f_dialog_textbox; then 168 f_dialog_textbox "$file" 169 else 170 dialog --msgbox "$( cat "$file" 2>&1 )" 0 0 171 fi 172} 173 174# f_include $file 175# 176# Include a shell subroutine file. 177# 178# If the subroutine file exists but returns error status during loading, exit 179# is called and execution is prematurely terminated with the same error status. 180# 181f_include() 182{ 183 local file="$1" 184 . "$file" || exit $? 185} 186 187# f_include_lang $file 188# 189# Include a language file. Automatically takes $LANG and $LC_ALL into 190# consideration when including $file (suffix ".$LC_ALL" or ".$LANG" will 191# automatically by added prior to loading the language file). 192# 193# No error is produced if (a) a language has been requested (by setting either 194# $LANG or $LC_ALL in the environment) and (b) the language file does not 195# exist -- in which case we will fall back to loading $file without-suffix. 196# 197# If the language file exists but returns error status during loading, exit 198# is called and execution is prematurely terminated with the same error status. 199# 200f_include_lang() 201{ 202 local file="$1" 203 local lang="${LANG:-$LC_ALL}" 204 205 f_dprintf "lang=[$lang]" 206 if [ -f "$file.$lang" ]; then 207 . "$file.$lang" || exit $? 208 else 209 . "$file" || exit $? 210 fi 211} 212 213# f_usage $file [ $key1 $value1 ... ] 214# 215# Display USAGE file with optional pre-processor macro definitions. The first 216# argument is the template file containing the usage text to be displayed. If 217# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding" 218# will automatically be appended as a suffix to the provided $file pathname. 219# 220# When processing $file, output begins at the first line containing that is 221# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines 222# appearing after this first-line are output, including (a) comments (b) empty 223# lines, and (c) lines that are purely whitespace-only. 224# 225# If additional arguments appear after $file, substitutions are made while 226# printing the contents of the USAGE file. The pre-processor macro syntax is in 227# the style of autoconf(1), for example: 228# 229# f_usage $file "FOO" "BAR" 230# 231# Will cause instances of "@FOO@" appearing in $file to be replaced with the 232# text "BAR" before bering printed to the screen. 233# 234# This function is a two-parter. Below is the awk(1) portion of the function, 235# afterward is the sh(1) function which utilizes the below awk script. 236# 237f_usage_awk=' 238BEGIN { found = 0 } 239{ 240 if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next 241 found = 1 242 print 243} 244' 245f_usage() 246{ 247 local file="$1" 248 local lang="${LANG:-$LC_ALL}" 249 250 f_dprintf "lang=[$lang]" 251 252 shift 1 # file 253 254 local usage 255 if [ -f "$file.$lang" ]; then 256 usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE 257 else 258 usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE 259 fi 260 261 while [ $# -gt 0 ]; do 262 local key="$1" 263 export value="$2" 264 usage=$( echo "$usage" | awk \ 265 "{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" ) 266 shift 2 267 done 268 269 f_err "%s\n" "$usage" 270 271 exit $FAILURE 272} 273 274# f_index_file $keyword 275# 276# Process all INDEX files known to bsdconfig and return the path to first file 277# containing a menu_selection line with a keyword portion matching $keyword. 278# 279# If $LANG or $LC_ALL (in order of preference, respectively) is set, 280# "INDEX.encoding" files will be searched first. 281# 282# If no file is found, error status is returned along with the NULL string. 283# 284# This function is a two-parter. Below is the awk(1) portion of the function, 285# afterward is the sh(1) function which utilizes the below awk script. 286# 287f_index_file_awk=' 288# Variables that should be defined on the invocation line: 289# -v keyword="keyword" 290BEGIN { found = 0 } 291( $0 ~ "^menu_selection=\"" keyword "\\|" ) { 292 print FILENAME 293 found++ 294 exit 295} 296END { exit ! found } 297' 298f_index_file() 299{ 300 local keyword="$1" 301 local lang="${LANG:-$LC_ALL}" 302 303 f_dprintf "lang=[$lang]" 304 305 if [ "$lang" ]; then 306 awk -v keyword="$keyword" "$f_index_file_awk" \ 307 $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && 308 return 309 # No match, fall-thru to non-i18n sources 310 fi 311 awk -v keyword="$keyword" "$f_index_file_awk" \ 312 $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX 313} 314 315# f_index_menusel_keyword $indexfile $pgm 316# 317# Process $indexfile and return only the keyword portion of the menu_selection 318# line with a command portion matching $pgm. 319# 320# This function is for internationalization (i18n) mapping of the on-disk 321# scriptname ($pgm) into the localized language (given language-specific 322# $indexfile). If $LANG or $LC_ALL (in orderder of preference, respectively) is 323# set, ".encoding" will automatically be appended as a suffix to the provided 324# $indexfile pathname. 325# 326# If, within $indexfile, multiple $menu_selection values map to $pgm, only the 327# first one will be returned. If no mapping can be made, the NULL string is 328# returned. 329# 330# If $indexfile does not exist, error status is returned with NULL. 331# 332# This function is a two-parter. Below is the awk(1) portion of the function, 333# afterward is the sh(1) function which utilizes the below awk script. 334# 335f_index_menusel_keyword_awk=' 336# Variables that should be defined on the invocation line: 337# -v pgm="program_name" 338# 339BEGIN { 340 prefix = "menu_selection=\"" 341 plen = length(prefix) 342 found = 0 343} 344{ 345 if (!match($0, "^" prefix ".*\\|.*\"")) next 346 347 keyword = command = substr($0, plen + 1, RLENGTH - plen - 1) 348 sub(/^.*\|/, "", command) 349 sub(/\|.*$/, "", keyword) 350 351 if ( command == pgm ) 352 { 353 print keyword 354 found++ 355 exit 356 } 357} 358END { exit ! found } 359' 360f_index_menusel_keyword() 361{ 362 local indexfile="$1" pgm="$2" 363 local lang="${LANG:-$LC_ALL}" 364 365 f_dprintf "lang=[$lang]" 366 367 if [ -f "$indexfile.$lang" ]; then 368 awk -v pgm="$pgm" \ 369 "$f_index_menusel_keyword_awk" \ 370 "$indexfile.$lang" 371 elif [ -f "$indexfile" ]; then 372 awk -v pgm="$pgm" \ 373 "$f_index_menusel_keyword_awk" \ 374 "$indexfile" 375 fi 376} 377 378# f_index_menusel_command $indexfile $keyword 379# 380# Process $indexfile and return only the command portion of the menu_selection 381# line with a keyword portion matching $keyword. 382# 383# This function is for mapping [possibly international] keywords into the 384# command to be executed. If $LANG or $LC_ALL (order of preference) is set, 385# ".encoding" will automatically be appended as a suffix to the provided 386# $indexfile pathname. 387# 388# If, within $indexfile, multiple $menu_selection values map to $keyword, only 389# the first one will be returned. If no mapping can be made, the NULL string is 390# returned. 391# 392# If $indexfile doesn't exist, error status is returned with NULL. 393# 394# This function is a two-parter. Below is the awk(1) portion of the function, 395# afterward is the sh(1) function which utilizes the below awk script. 396# 397f_index_menusel_command_awk=' 398# Variables that should be defined on the invocation line: 399# -v key="keyword" 400# 401BEGIN { 402 prefix = "menu_selection=\"" 403 plen = length(prefix) 404 found = 0 405} 406{ 407 if (!match($0, "^" prefix ".*\\|.*\"")) next 408 409 keyword = command = substr($0, plen + 1, RLENGTH - plen - 1) 410 sub(/^.*\|/, "", command) 411 sub(/\|.*$/, "", keyword) 412 413 if ( keyword == key ) 414 { 415 print command 416 found++ 417 exit 418 } 419} 420END { exit ! found } 421' 422f_index_menusel_command() 423{ 424 local indexfile="$1" keyword="$2" command 425 local lang="${LANG:-$LC_ALL}" 426 427 f_dprintf "lang=[$lang]" 428 429 if [ -f "$indexfile.$lang" ]; then 430 command=$( awk -v key="$keyword" \ 431 "$f_index_menusel_command_awk" \ 432 "$indexfile.$lang" ) || return $FAILURE 433 elif [ -f "$indexfile" ]; then 434 command=$( awk -v key="$keyword" \ 435 "$f_index_menusel_command_awk" \ 436 "$indexfile" ) || return $FAILURE 437 else 438 return $FAILURE 439 fi 440 441 # 442 # If the command pathname is not fully qualified fix-up/force to be 443 # relative to the $indexfile directory. 444 # 445 case "$command" in 446 /*) : already fully qualified ;; 447 *) 448 local indexdir="${indexfile%/*}" 449 [ "$indexdir" != "$indexfile" ] || indexdir="." 450 command="$indexdir/$command" 451 esac 452 453 echo "$command" 454} 455 456############################################################ MAIN 457 458# 459# Trap signals so we can recover gracefully 460# 461trap 'f_interrupt' SIGINT 462trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \ 463 SIGFPE SIGTRAP SIGABRT SIGSEGV 464trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM 465 466fi # ! $_COMMON_SUBR 467