]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bsdconfig/bsdconfig
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / bsdconfig / bsdconfig
1 #!/bin/sh
2 #-
3 # Copyright (c) 2012 Ron McDowell
4 # Copyright (c) 2012-2013 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 #
45 GETOPTS_EXTRA="f:"
46
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
53
54 BSDCFG_LIBE="/usr/libexec/bsdconfig"
55 f_include_lang $BSDCFG_LIBE/include/messages.subr
56
57 BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp
58 USAGE_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 #
65 BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig"
66
67 ############################################################ FUNCTIONS
68
69 # usage
70 #
71 # display usage and exit
72 #
73 usage()
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 )) # 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 ))
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 #
186 dialog_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                 tag=$( f_substr "$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                 tag=$( f_substr "$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 #
314 if [ "$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] indexfile=[%s] cmd=[%s]" \
319                           "$pgm" "$indexfile" "$cmd"
320                 exec "$cmd" "$@" || exit 1
321         else
322                 f_include $BSDCFG_SHARE/script.subr
323                 for resword in $RESWORDS; do
324                         [ "$pgm" = "$resword" ] || continue
325                         # Found a match
326                         f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
327                         f_dispatch $resword
328                         exit $?
329                 done
330         fi
331 fi
332
333 #
334 # Process command-line arguments
335 #
336 scripts_loaded=0
337 while getopts f:h$GETOPTS_STDARGS flag; do
338         case "$flag" in
339         f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
340            f_script_load "$OPTARG"
341            scripts_loaded=$(( $scripts_loaded + 1 )) ;;
342         h|\?) usage ;;
343         esac
344 done
345 shift $(( $OPTIND - 1 ))
346
347 # If we've loaded any scripts, do not continue any further
348 [ $scripts_loaded -gt 0 ] && exit
349
350 #
351 # Initialize
352 #
353 f_dialog_title "$msg_main_menu"
354
355 [ "$SECURE" ] && f_mustberoot_init
356
357 # Incorporate rc-file if it exists
358 [ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
359
360 #
361 # If a non-option argument was passed, process it as a menuitem selection...
362 #
363 if [ "$1" ]; then
364         #
365         # ...unless it's a long-option for usage.
366         #
367         case "$1" in -help|--help|-\?)
368                 usage
369                 # Not reached
370         esac
371
372         #
373         # Find the INDEX (possibly i18n) claiming this keyword and get the
374         # command to execute from the menu_selection line.
375         #
376         if ! { indexfile=$( f_index_file "$1" ) &&
377                cmd=$( f_index_menusel_command "$indexfile" "$1" )
378         }; then
379                 # no matches, display usage (which shows valid keywords)
380                 f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
381                 usage
382                 # Not reached
383         fi
384
385         shift
386         exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
387         # Not reached
388 fi
389
390 #
391 # Launch application main menu
392 #
393 while :; 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}
422 done
423
424 exit $SUCCESS
425
426 ################################################################################
427 # END
428 ################################################################################