summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bsdconfig/bsdconfig
blob: b23e083bcf6881eb9675007b16674714c8d73b61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
#!/bin/sh
#-
# Copyright (c) 2012 Ron McDowell
# Copyright (c) 2012-2013 Devin Teske
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
############################################################ INCLUDES

# When common.subr is included, it automatically scans "$@" for `-d' and/or
# `-D file' arguments to conditionally enable debugging. Similarly, when
# dialog.subr is included, it automatically scans "$@" for `-X' and/or `-S'.
# To prevent this scanning from becoming confused by extra options, define
# any/all extra arguments to use in the optstring to getopts when scanning
# for dedicated options such as those described.
#
# NOTE: This needs to be declared before including `common.subr'.
# NOTE: You really only need to list flags that require an argument as unknown
#       flags are silently accepted unless they take an argument (in which case
#       the following argument will terminate option processing unless it looks
#       like a flag).
#
GETOPTS_EXTRA="f:"

BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." "$0"
f_include $BSDCFG_SHARE/dialog.subr
f_include $BSDCFG_SHARE/mustberoot.subr
f_include $BSDCFG_SHARE/strings.subr

BSDCFG_LIBE="/usr/libexec/bsdconfig"
f_include_lang $BSDCFG_LIBE/include/messages.subr

BSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp
USAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp

############################################################ CONFIGURATION

#
# Alternate `local' libexec directory for add-on modules (e.g., from ports)
#
BSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig"

############################################################ FUNCTIONS

# usage
#
# display usage and exit
#
usage()
{
	local index="INDEX"
	local cmd_list # Calculated below

	cd $BSDCFG_LIBE
		# No need to preserve CWD (headed toward exit)

	# Test for language-specific indices
	f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
		index="$index.${LANG:-$LC_ALL}"

	cmd_list=$(
		awk '/^menu_selection="/ {
			sub(/\|.*/, "")
			sub(/^menu_selection="/, "")
			print
		}' */$index | sort
	)

	local alt_cmd_list # Calculated below (if $BSDCFG_LOCAL_LIBE exists)
	if f_quietly cd $BSDCFG_LOCAL_LIBE; then
		# No need to preserve CWD (headed toward exit)

		# Test for language-specific indices
		f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
			index="$index.${LANG:-$LC_ALL}"

		alt_cmd_list=$(
			awk '/^menu_selection="/ {
				sub(/\|.*/, "")
				sub(/^menu_selection="/, "")
				print
			}' */$index 2> /dev/null | sort
		)

		# Conflate lists, removing duplicates
		cmd_list=$( printf "%s\n%s\n" \
		                   "$cmd_list" "$alt_cmd_list" | sort -u )
	fi

	#
	# Determine the longest command-length (in characters)
	#
	local longest_cmd
	longest_cmd=$( echo "$cmd_list" | f_longest_line_length )
	f_dprintf "longest_cmd=[%s]" "$longest_cmd"

	#
	# Determine the maximum width of terminal/console
	#
	local max_size="$( stty size 2> /dev/null )"
	: ${max_size:="24 80"}
	local max_width="${max_size#*[$IFS]}"
	f_dprintf "max_width=[%s]" "$max_width"

	#
	# Using the longest command-length as the width of a single column,
	# determine if we can use more than one column to display commands.
	#
	local x=$longest_cmd ncols=1
	x=$(( $x + 8 )) # Accomodate leading tab character
	x=$(( $x + 3 + $longest_cmd )) # Preload end of next column
	while [ $x -lt $max_width ]; do
		ncols=$(( $ncols + 1 ))
		x=$(( $x + 3 + $longest_cmd ))
	done
	f_dprintf "ncols=[%u] x=[%u]" $ncols $x

	#
	# Re-format the command-list into multiple columns
	#
	cmd_list=$( eval "$( echo "$cmd_list" |
		awk -v ncols=$ncols -v size=$longest_cmd '
		BEGIN {
			n = 0
			row_item[1] = ""
		}
		function print_row()
		{
			fmt = "printf \"\\t%-" size "s"
			for (i = 1; i < cur_col; i++)
				fmt = fmt "   %-" size "s"
			fmt = fmt "\\n\""
			printf "%s", fmt
			for (i = 1; i <= cur_col; i++)
				printf " \"%s\"", row_item[i]
			print ""
		}
		{
			n++
			cur_col = (( n - 1 ) % ncols ) + 1
			printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n",
			       cur_col, $0
			row_item[cur_col] = $0
			if ( cur_col == ncols ) print_row()
		}
		END {
			if ( cur_col < ncols ) print_row()
		}' )"
	)

	f_usage $BSDCFG_LIBE/USAGE \
	        "PROGRAM_NAME" "$pgm" \
	        "COMMAND_LIST" "$cmd_list"

	# Never reached
}

# dialog_menu_main
#
# Display the dialog(1)-based application main menu.
#
dialog_menu_main()
{
	local title="$DIALOG_TITLE"
	local btitle="$DIALOG_BACKTITLE"
	local prompt="$msg_menu_text"
	local menu_list="
		'X' '$msg_exit'  '$msg_exit_bsdconfig'
		'1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system'
	" # END-QUOTE
	local defaultitem= # Calculated below
	local hline=

	#
	# Pick up the base modules (directories named `[0-9][0-9][0-9].*')
	#
	local menuitem menu_title menu_help menu_selection index=2
	for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do
		[ -f "$BSDCFG_LIBE/$menuitem/INDEX" ] || continue
		[ $index -lt ${#DIALOG_MENU_TAGS} ] || break

		menu_program= menu_title= menu_help=
		f_include_lang $BSDCFG_LIBE/$menuitem/INDEX
		[ "$menu_program" ] || continue

		case "$menu_program" in
		/*) : already fully qualified ;;
		 *) menu_program="$menuitem/$menu_program"
		esac

		tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
		setvar "menu_program$tag" "$menu_program"

		f_shell_escape "$menu_title" menu_title
		f_shell_escape "$menu_help" menu_help
		menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"

		index=$(( $index + 1 ))
	done

	#
	# Process the `local' libexec sources.
	#
	# Whereas modules in $BSDCFG_LIBE must be named [0-9][0-9][0-9].*
	# modules in $BSDCFG_LOCAL_LIBE should NOT be named this way (making it
	# more practical for port-maintainers).
	#
	# This also has the fortunate side-effect of making the de-duplication
	# effort rather simple (because so-called `base' modules must be named
	# differently than add-on modules).
	#
	local separator_added=
	for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * )
	do
		# Skip the module if it looks like a `base' module
		case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac

		[ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue
		[ $index -lt ${#DIALOG_MENU_TAGS} ] || break

		menu_program= menu_title= menu_help=
		f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue
		[ "$menu_program" ] || continue

		if [ ! "$separator_added" ]; then
			menu_list="$menu_list '-' '-' ''"
			separator_added=1
		fi

		case "$menu_program" in
		/*) : already fully qualified ;;
		 *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program"
		esac

		tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
		setvar "menu_program$tag" "$menu_program"

		f_shell_escape "$menu_title" menu_title
		f_shell_escape "$menu_help" menu_help
		menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"

		index=$(( $index + 1 ))
	done

	local height width rows
	eval f_dialog_menu_with_help_size height width rows \
	                                  \"\$title\"  \
	                                  \"\$btitle\" \
	                                  \"\$prompt\" \
	                                  \"\$hline\"  \
	                                  $menu_list

	# Obtain default-item from previously stored selection
	f_dialog_default_fetch defaultitem

	local menu_choice
	menu_choice=$( eval $DIALOG \
		--clear                                 \
		--title \"\$title\"                     \
		--backtitle \"\$btitle\"                \
		--hline \"\$hline\"                     \
		--item-help                             \
		--ok-label \"\$msg_ok\"                 \
		--cancel-label \"\$msg_exit_bsdconfig\" \
		--help-button                           \
		--help-label \"\$msg_help\"             \
		${USE_XDIALOG:+--help \"\"}             \
		--default-item \"\$defaultitem\"        \
		--menu \"\$prompt\"                     \
		$height $width $rows                    \
		$menu_list                              \
		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
	)
	local retval=$?
	f_dialog_data_sanitize menu_choice
	f_dialog_menutag_store "$menu_choice"

	# Only update default-item on success
	[ $retval -eq 0 ] && f_dialog_default_store "$menu_choice"

	return $retval
}

############################################################ MAIN

#
# If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or
# as a valid resword (see script.subr for additional details about reswords).
#
if [ "$pgm" != "bsdconfig" ]; then
	if indexfile=$( f_index_file "$pgm" ) &&
	   cmd=$( f_index_menusel_command "$indexfile" "$pgm" )
	then
		f_dprintf "pgm=[%s] indexfile=[%s] cmd=[%s]" \
		          "$pgm" "$indexfile" "$cmd"
		exec "$cmd" "$@" || exit 1
	else
		f_include $BSDCFG_SHARE/script.subr
		for resword in $RESWORDS; do
			[ "$pgm" = "$resword" ] || continue
			# Found a match
			f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
			f_dispatch $resword
			exit $?
		done
	fi
fi

#
# Process command-line arguments
#
scripts_loaded=0
while getopts f:h$GETOPTS_STDARGS flag; do
	case "$flag" in
	f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
	   f_script_load "$OPTARG"
	   scripts_loaded=$(( $scripts_loaded + 1 )) ;;
	h|\?) usage ;;
	esac
done
shift $(( $OPTIND -1 ))

# If we've loaded any scripts, do not continue any further
[ $scripts_loaded -gt 0 ] && exit

#
# Initialize
#
f_dialog_title "$msg_main_menu"

[ "$SECURE" ] && f_mustberoot_init

# Incorporate rc-file if it exists
[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"

#
# If a non-option argument was passed, process it as a menuitem selection...
#
if [ "$1" ]; then
	#
	# ...unless it's a long-option for usage.
	#
	case "$1" in -help|--help|-\?)
		usage
		# Not reached
	esac

	#
	# Find the INDEX (possibly i18n) claiming this keyword and get the
	# command to execute from the menu_selection line.
	#
	if ! { indexfile=$( f_index_file "$1" ) &&
	       cmd=$( f_index_menusel_command "$indexfile" "$1" )
	}; then
		# no matches, display usage (which shows valid keywords)
		f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
		usage
		# Not reached
	fi

	shift
	exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
	# Not reached
fi

#
# Launch application main menu
#
while :; do
	dialog_menu_main
	retval=$?
	f_dialog_menutag_fetch mtag
	f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"

	if [ $retval -eq 2 ]; then
		# The Help button was pressed
		f_show_help "$BSDCONFIG_HELPFILE"
		continue
	elif [ $retval -ne 0 ]; then
		f_die
	fi

	case "$mtag" in
	X) break ;;
	1) # Usage
	   f_show_help "$USAGE_HELPFILE"
	   continue
	esac

	# Anything else is a dynamically loaded menuitem

	f_getvar menu_program$mtag menu_program
	case "$menu_program" in
	/*) cmd="$menu_program" ;;
	 *) cmd="$BSDCFG_LIBE/$menu_program"
	esac
	f_dprintf "cmd=[%s]" "$cmd"
	$cmd ${USE_XDIALOG:+-X}
done

exit $SUCCESS

################################################################################
# END
################################################################################
OpenPOWER on IntegriCloud