3 # Copyright (c) 2012 Ron McDowell
4 # Copyright (c) 2012-2014 Devin Teske
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
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.
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
30 ############################################################ INCLUDES
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.
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
47 BSDCFG_SHARE="/usr/share/bsdconfig"
48 . $BSDCFG_SHARE/common.subr || exit 1
49 f_dprintf "%s: loading includes..." "$0"
50 f_include $BSDCFG_SHARE/dialog.subr
51 f_include $BSDCFG_SHARE/mustberoot.subr
52 f_include $BSDCFG_SHARE/strings.subr
54 BSDCFG_LIBE="/usr/libexec/bsdconfig"
55 f_include_lang $BSDCFG_LIBE/include/messages.subr
57 BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp
58 USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp
60 ############################################################ CONFIGURATION
63 # Alternate `local' libexec directory for add-on modules (e.g., from ports)
65 BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig"
67 ############################################################ FUNCTIONS
71 # display usage and exit
76 local cmd_list # Calculated below
79 # No need to preserve CWD (headed toward exit)
81 # Test for language-specific indices
82 f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
83 index="$index.${LANG:-$LC_ALL}"
86 awk '/^menu_selection="/ {
88 sub(/^menu_selection="/, "")
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)
97 # Test for language-specific indices
98 f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
99 index="$index.${LANG:-$LC_ALL}"
102 awk '/^menu_selection="/ {
104 sub(/^menu_selection="/, "")
106 }' */$index 2> /dev/null | sort
109 # Conflate lists, removing duplicates
110 cmd_list=$( printf "%s\n%s\n" \
111 "$cmd_list" "$alt_cmd_list" | sort -u )
115 # Determine the longest command-length (in characters)
118 longest_cmd=$( echo "$cmd_list" | f_longest_line_length )
119 f_dprintf "longest_cmd=[%s]" "$longest_cmd"
122 # Determine the maximum width of terminal/console
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"
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.
133 local x=$longest_cmd ncols=1
134 x=$(( $x + 8 )) # Accomodate 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 ))
140 f_dprintf "ncols=[%u] x=[%u]" $ncols $x
143 # Re-format the command-list into multiple columns
145 cmd_list=$( eval "$( echo "$cmd_list" |
146 awk -v ncols=$ncols -v size=$longest_cmd '
153 fmt = "printf \"\\t%-" size "s"
154 for (i = 1; i < cur_col; i++)
155 fmt = fmt " %-" size "s"
158 for (i = 1; i <= cur_col; i++)
159 printf " \"%s\"", row_item[i]
164 cur_col = (( n - 1 ) % ncols ) + 1
165 printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n",
167 row_item[cur_col] = $0
168 if ( cur_col == ncols ) print_row()
171 if ( cur_col < ncols ) print_row()
175 f_usage $BSDCFG_LIBE/USAGE \
176 "PROGRAM_NAME" "$pgm" \
177 "COMMAND_LIST" "$cmd_list"
184 # Display the dialog(1)-based application main menu.
188 local title="$DIALOG_TITLE"
189 local btitle="$DIALOG_BACKTITLE"
190 local prompt="$msg_menu_text"
192 'X' '$msg_exit' '$msg_exit_bsdconfig'
193 '1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system'
195 local defaultitem= # Calculated below
199 # Pick up the base modules (directories named `[0-9][0-9][0-9].*')
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
206 menu_program= menu_title= menu_help=
207 f_include_lang $BSDCFG_LIBE/$menuitem/INDEX
208 [ "$menu_program" ] || continue
210 case "$menu_program" in
211 /*) : already fully qualified ;;
212 *) menu_program="$menuitem/$menu_program"
215 tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
216 setvar "menu_program$tag" "$menu_program"
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'"
222 index=$(( $index + 1 ))
226 # Process the `local' libexec sources.
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).
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).
236 local separator_added=
237 for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * )
239 # Skip the module if it looks like a `base' module
240 case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac
242 [ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue
243 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break
245 menu_program= menu_title= menu_help=
246 f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue
247 [ "$menu_program" ] || continue
249 if [ ! "$separator_added" ]; then
250 menu_list="$menu_list '-' '-' ''"
254 case "$menu_program" in
255 /*) : already fully qualified ;;
256 *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program"
259 tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
260 setvar "menu_program$tag" "$menu_program"
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'"
266 index=$(( $index + 1 ))
269 local height width rows
270 eval f_dialog_menu_with_help_size height width rows \
277 # Obtain default-item from previously stored selection
278 f_dialog_default_fetch defaultitem
281 menu_choice=$( eval $DIALOG \
283 --title \"\$title\" \
284 --backtitle \"\$btitle\" \
285 --hline \"\$hline\" \
287 --ok-label \"\$msg_ok\" \
288 --cancel-label \"\$msg_exit_bsdconfig\" \
290 --help-label \"\$msg_help\" \
291 ${USE_XDIALOG:+--help \"\"} \
292 --default-item \"\$defaultitem\" \
293 --menu \"\$prompt\" \
294 $height $width $rows \
296 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
299 f_dialog_data_sanitize menu_choice
300 f_dialog_menutag_store "$menu_choice"
302 # Only update default-item on success
303 [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice"
308 ############################################################ MAIN
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).
314 if [ "$pgm" != "bsdconfig" ]; then
315 if indexfile=$( f_index_file "$pgm" ) &&
316 cmd=$( f_index_menusel_command "$indexfile" "$pgm" )
318 f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*"
319 exec "$cmd" "$@" || exit 1
321 f_include $BSDCFG_SHARE/script.subr
322 for resword in $RESWORDS; do
323 [ "$pgm" = "$resword" ] || continue
325 f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
326 f_dispatch $resword $resword "$@"
333 # Process command-line arguments
336 while getopts f:h$GETOPTS_STDARGS flag; do
338 f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
339 f_script_load "$OPTARG"
340 scripts_loaded=$(( $scripts_loaded + 1 )) ;;
344 shift $(( $OPTIND - 1 ))
346 # If we've loaded any scripts, do not continue any further
347 [ $scripts_loaded -gt 0 ] && exit
352 f_dialog_title "$msg_main_menu"
354 [ "$SECURE" ] && f_mustberoot_init
356 # Incorporate rc-file if it exists
357 [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
360 # If a non-option argument was passed, process it as a menuitem selection...
364 # ...unless it's a long-option for usage.
366 case "$1" in -help|--help|-\?)
372 # Find the INDEX (possibly i18n) claiming this keyword and get the
373 # command to execute from the menu_selection line.
375 if ! { indexfile=$( f_index_file "$1" ) &&
376 cmd=$( f_index_menusel_command "$indexfile" "$1" )
378 # no matches, display usage (which shows valid keywords)
379 f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
384 f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*"
386 exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
391 # Launch application main menu
396 f_dialog_menutag_fetch mtag
397 f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
399 if [ $retval -eq $DIALOG_HELP ]; then
400 f_show_help "$BSDCONFIG_HELPFILE"
402 elif [ $retval -ne $DIALOG_OK ]; then
409 f_show_help "$USAGE_HELPFILE"
413 # Anything else is a dynamically loaded menuitem
415 f_getvar menu_program$mtag menu_program
416 case "$menu_program" in
417 /*) cmd="$menu_program" ;;
418 *) cmd="$BSDCFG_LIBE/$menu_program"
420 f_dprintf "cmd=[%s]" "$cmd"
421 $cmd ${USE_XDIALOG:+-X}
426 ################################################################################
428 ################################################################################