xref: /freebsd/usr.sbin/bsdconfig/bsdconfig (revision f05cddf9)
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#
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############################################################ FUNCTIONS
61
62# usage
63#
64# display usage and exit
65#
66usage()
67{
68	local index="INDEX"
69	local cmd_list # Calculated below
70
71	cd $BSDCFG_LIBE
72		# No need to preserve CWD (headed toward exit)
73
74	# Test for language-specific indices
75	f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
76		index="$index.${LANG:-$LC_ALL}"
77
78	cmd_list=$(
79		awk '/^menu_selection="/ {
80			sub(/\|.*/, "")
81			sub(/^menu_selection="/, "")
82			print
83		}' */$index | sort
84	)
85
86	#
87	# Determine the longest command-length (in characters)
88	#
89	local longest_cmd
90	longest_cmd=$( echo "$cmd_list" | f_longest_line_length )
91	f_dprintf "longest_cmd=[%s]" "$longest_cmd"
92
93	#
94	# Determine the maximum width of terminal/console
95	#
96	local max_size="$( stty size 2> /dev/null )"
97	: ${max_size:="24 80"}
98	local max_width="${max_size#*[$IFS]}"
99	f_dprintf "max_width=[%s]" "$max_width"
100
101	#
102	# Using the longest command-length as the width of a single column,
103	# determine if we can use more than one column to display commands.
104	#
105	local x=$longest_cmd ncols=1
106	x=$(( $x + 8 )) # Accomodate leading tab character
107	x=$(( $x + 3 + $longest_cmd )) # Preload end of next column
108	while [ $x -lt $max_width ]; do
109		ncols=$(( $ncols + 1 ))
110		x=$(( $x + 3 + $longest_cmd ))
111	done
112	f_dprintf "ncols=[%u] x=[%u]" $ncols $x
113
114	#
115	# Re-format the command-list into multiple columns
116	#
117	cmd_list=$( eval "$( echo "$cmd_list" |
118		awk -v ncols=$ncols -v size=$longest_cmd '
119		BEGIN {
120			n = 0
121			row_item[1] = ""
122		}
123		function print_row()
124		{
125			fmt = "printf \"\\t%-" size "s"
126			for (i = 1; i < cur_col; i++)
127				fmt = fmt "   %-" size "s"
128			fmt = fmt "\\n\""
129			printf "%s", fmt
130			for (i = 1; i <= cur_col; i++)
131				printf " \"%s\"", row_item[i]
132			print ""
133		}
134		{
135			n++
136			cur_col = (( n - 1 ) % ncols ) + 1
137			printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n",
138			       cur_col, $0
139			row_item[cur_col] = $0
140			if ( cur_col == ncols ) print_row()
141		}
142		END {
143			if ( cur_col < ncols ) print_row()
144		}' )"
145	)
146
147	f_usage $BSDCFG_LIBE/USAGE \
148	        "PROGRAM_NAME" "$pgm" \
149	        "COMMAND_LIST" "$cmd_list"
150
151	# Never reached
152}
153
154# dialog_menu_main
155#
156# Display the dialog(1)-based application main menu.
157#
158dialog_menu_main()
159{
160	local title="$DIALOG_TITLE"
161	local btitle="$DIALOG_BACKTITLE"
162	local prompt="$msg_menu_text"
163	local menu_list="
164		'X' '$msg_exit'  '$msg_exit_bsdconfig'
165		'1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system'
166	" # END-QUOTE
167	local defaultitem= # Calculated below
168	local hline=
169
170	local menuitem menu_title menu_help menu_selection index=2
171	for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do
172		[ $index -lt ${#DIALOG_MENU_TAGS} ] || break
173		tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
174
175		menu_program=
176		menu_title=
177		menu_help=
178		f_include_lang $BSDCFG_LIBE/$menuitem/INDEX
179		[ "$menu_program" ] || continue
180
181		case "$menu_program" in
182		/*) : already fully qualified ;;
183		 *) menu_program="$menuitem/$menu_program"
184		esac
185
186		f_shell_escape "$menu_title" menu_title
187		f_shell_escape "$menu_help" menu_help
188		setvar "menu_program$tag" "$menu_program"
189		menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"
190
191		index=$(( $index + 1 ))
192	done
193
194	local height width rows
195	eval f_dialog_menu_with_help_size height width rows \
196	                                  \"\$title\"  \
197	                                  \"\$btitle\" \
198	                                  \"\$prompt\" \
199	                                  \"\$hline\"  \
200	                                  $menu_list
201
202	# Obtain default-item from previously stored selection
203	f_dialog_default_fetch defaultitem
204
205	local menu_choice
206	menu_choice=$( eval $DIALOG \
207		--clear                                 \
208		--title \"\$title\"                     \
209		--backtitle \"\$btitle\"                \
210		--hline \"\$hline\"                     \
211		--item-help                             \
212		--ok-label \"\$msg_ok\"                 \
213		--cancel-label \"\$msg_exit_bsdconfig\" \
214		--help-button                           \
215		--help-label \"\$msg_help\"             \
216		${USE_XDIALOG:+--help \"\"}             \
217		--default-item \"\$defaultitem\"        \
218		--menu \"\$prompt\"                     \
219		$height $width $rows                    \
220		$menu_list                              \
221		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
222	)
223	local retval=$?
224	f_dialog_data_sanitize menu_choice
225	f_dialog_menutag_store "$menu_choice"
226
227	# Only update default-item on success
228	[ $retval -eq 0 ] && f_dialog_default_store "$menu_choice"
229
230	return $retval
231}
232
233############################################################ MAIN
234
235#
236# If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or
237# as a valid resword (see script.subr for additional details about reswords).
238#
239if [ "$pgm" != "bsdconfig" ]; then
240	if indexfile=$( f_index_file "$pgm" ) &&
241	   cmd=$( f_index_menusel_command "$indexfile" "$pgm" )
242	then
243		f_dprintf "pgm=[%s] indexfile=[%s] cmd=[%s]" \
244		          "$pgm" "$indexfile" "$cmd"
245		exec "$cmd" "$@" || exit 1
246	else
247		f_include $BSDCFG_SHARE/script.subr
248		for resword in $RESWORDS; do
249			[ "$pgm" = "$resword" ] || continue
250			# Found a match
251			f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
252			f_dispatch $resword
253			exit $?
254		done
255	fi
256fi
257
258#
259# Process command-line arguments
260#
261scripts_loaded=0
262while getopts f:h$GETOPTS_STDARGS flag; do
263	case "$flag" in
264	f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
265	   f_script_load "$OPTARG"
266	   scripts_loaded=$(( $scripts_loaded + 1 )) ;;
267	h|\?) usage ;;
268	esac
269done
270shift $(( $OPTIND -1 ))
271
272# If we've loaded any scripts, do not continue any further
273[ $scripts_loaded -gt 0 ] && exit
274
275#
276# Initialize
277#
278f_dialog_title "$msg_main_menu"
279
280[ "$SECURE" ] && f_mustberoot_init
281
282# Incorporate rc-file if it exists
283[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
284
285#
286# If a non-option argument was passed, process it as a menuitem selection...
287#
288if [ "$1" ]; then
289	#
290	# ...unless it's a long-option for usage.
291	#
292	case "$1" in -help|--help|-\?)
293		usage
294		# Not reached
295	esac
296
297	#
298	# Find the INDEX (possibly i18n) claiming this keyword and get the
299	# command to execute from the menu_selection line.
300	#
301	if ! { indexfile=$( f_index_file "$1" ) &&
302	       cmd=$( f_index_menusel_command "$indexfile" "$1" )
303	}; then
304		# no matches, display usage (which shows valid keywords)
305		f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
306		usage
307		# Not reached
308	fi
309
310	shift
311	exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
312	# Not reached
313fi
314
315#
316# Launch application main menu
317#
318while :; do
319	dialog_menu_main
320	retval=$?
321	f_dialog_menutag_fetch mtag
322	f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
323
324	if [ $retval -eq 2 ]; then
325		# The Help button was pressed
326		f_show_help "$BSDCONFIG_HELPFILE"
327		continue
328	elif [ $retval -ne 0 ]; then
329		f_die
330	fi
331
332	case "$mtag" in
333	X) break ;;
334	1) # Usage
335	   f_show_help "$USAGE_HELPFILE"
336	   continue
337	esac
338
339	# Anything else is a dynamically loaded menuitem
340
341	f_getvar menu_program$mtag menu_program
342	case "$menu_program" in
343	/*) cmd="$menu_program" ;;
344	 *) cmd="$BSDCFG_LIBE/$menu_program"
345	esac
346	f_dprintf "cmd=[%s]" "$cmd"
347	$cmd ${USE_XDIALOG:+-X}
348done
349
350exit $SUCCESS
351
352################################################################################
353# END
354################################################################################
355