1252995Sdteske#!/bin/sh
2252995Sdteske#-
3252995Sdteske# Copyright (c) 2012 Ron McDowell
4263791Sdteske# Copyright (c) 2012-2014 Devin Teske
5252995Sdteske# All rights reserved.
6252995Sdteske#
7252995Sdteske# Redistribution and use in source and binary forms, with or without
8252995Sdteske# modification, are permitted provided that the following conditions
9252995Sdteske# are met:
10252995Sdteske# 1. Redistributions of source code must retain the above copyright
11252995Sdteske#    notice, this list of conditions and the following disclaimer.
12252995Sdteske# 2. Redistributions in binary form must reproduce the above copyright
13252995Sdteske#    notice, this list of conditions and the following disclaimer in the
14252995Sdteske#    documentation and/or other materials provided with the distribution.
15252995Sdteske#
16252995Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17252995Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18252995Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19252995Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20252995Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21252995Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22252995Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23252995Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24252995Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25252995Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26252995Sdteske# SUCH DAMAGE.
27252995Sdteske#
28252995Sdteske# $FreeBSD$
29252995Sdteske#
30252995Sdteske############################################################ INCLUDES
31252995Sdteske
32252995Sdteske# When common.subr is included, it automatically scans "$@" for `-d' and/or
33252995Sdteske# `-D file' arguments to conditionally enable debugging. Similarly, when
34252995Sdteske# dialog.subr is included, it automatically scans "$@" for `-X' and/or `-S'.
35252995Sdteske# To prevent this scanning from becoming confused by extra options, define
36252995Sdteske# any/all extra arguments to use in the optstring to getopts when scanning
37252995Sdteske# for dedicated options such as those described.
38252995Sdteske#
39252995Sdteske# NOTE: This needs to be declared before including `common.subr'.
40252995Sdteske# NOTE: You really only need to list flags that require an argument as unknown
41252995Sdteske#       flags are silently accepted unless they take an argument (in which case
42252995Sdteske#       the following argument will terminate option processing unless it looks
43252995Sdteske#       like a flag).
44252995Sdteske#
45252995SdteskeGETOPTS_EXTRA="f:"
46252995Sdteske
47252995SdteskeBSDCFG_SHARE="/usr/share/bsdconfig"
48252995Sdteske. $BSDCFG_SHARE/common.subr || exit 1
49252995Sdteskef_dprintf "%s: loading includes..." "$0"
50252995Sdteskef_include $BSDCFG_SHARE/dialog.subr
51252995Sdteskef_include $BSDCFG_SHARE/mustberoot.subr
52252995Sdteskef_include $BSDCFG_SHARE/strings.subr
53252995Sdteske
54252995SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig"
55252995Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr
56252995Sdteske
57252995SdteskeBSDCONFIG_HELPFILE=$BSDCFG_LIBE/include/bsdconfig.hlp
58252995SdteskeUSAGE_HELPFILE=$BSDCFG_LIBE/include/usage.hlp
59252995Sdteske
60252995Sdteske############################################################ CONFIGURATION
61252995Sdteske
62252995Sdteske#
63252995Sdteske# Alternate `local' libexec directory for add-on modules (e.g., from ports)
64252995Sdteske#
65252995SdteskeBSDCFG_LOCAL_LIBE="/usr/local/libexec/bsdconfig"
66252995Sdteske
67252995Sdteske############################################################ FUNCTIONS
68252995Sdteske
69252995Sdteske# usage
70252995Sdteske#
71252995Sdteske# display usage and exit
72252995Sdteske#
73252995Sdteskeusage()
74252995Sdteske{
75252995Sdteske	local index="INDEX"
76252995Sdteske	local cmd_list # Calculated below
77252995Sdteske
78252995Sdteske	cd $BSDCFG_LIBE
79252995Sdteske		# No need to preserve CWD (headed toward exit)
80252995Sdteske
81252995Sdteske	# Test for language-specific indices
82252995Sdteske	f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
83252995Sdteske		index="$index.${LANG:-$LC_ALL}"
84252995Sdteske
85252995Sdteske	cmd_list=$(
86252995Sdteske		awk '/^menu_selection="/ {
87252995Sdteske			sub(/\|.*/, "")
88252995Sdteske			sub(/^menu_selection="/, "")
89252995Sdteske			print
90252995Sdteske		}' */$index | sort
91252995Sdteske	)
92252995Sdteske
93252995Sdteske	local alt_cmd_list # Calculated below (if $BSDCFG_LOCAL_LIBE exists)
94252995Sdteske	if f_quietly cd $BSDCFG_LOCAL_LIBE; then
95252995Sdteske		# No need to preserve CWD (headed toward exit)
96252995Sdteske
97252995Sdteske		# Test for language-specific indices
98252995Sdteske		f_quietly ls */"$index.${LANG:-$LC_ALL}" &&
99252995Sdteske			index="$index.${LANG:-$LC_ALL}"
100252995Sdteske
101252995Sdteske		alt_cmd_list=$(
102252995Sdteske			awk '/^menu_selection="/ {
103252995Sdteske				sub(/\|.*/, "")
104252995Sdteske				sub(/^menu_selection="/, "")
105252995Sdteske				print
106252995Sdteske			}' */$index 2> /dev/null | sort
107252995Sdteske		)
108252995Sdteske
109252995Sdteske		# Conflate lists, removing duplicates
110252995Sdteske		cmd_list=$( printf "%s\n%s\n" \
111252995Sdteske		                   "$cmd_list" "$alt_cmd_list" | sort -u )
112252995Sdteske	fi
113252995Sdteske
114252995Sdteske	#
115252995Sdteske	# Determine the longest command-length (in characters)
116252995Sdteske	#
117252995Sdteske	local longest_cmd
118252995Sdteske	longest_cmd=$( echo "$cmd_list" | f_longest_line_length )
119252995Sdteske	f_dprintf "longest_cmd=[%s]" "$longest_cmd"
120252995Sdteske
121252995Sdteske	#
122252995Sdteske	# Determine the maximum width of terminal/console
123252995Sdteske	#
124252995Sdteske	local max_size="$( stty size 2> /dev/null )"
125252995Sdteske	: ${max_size:="24 80"}
126252995Sdteske	local max_width="${max_size#*[$IFS]}"
127252995Sdteske	f_dprintf "max_width=[%s]" "$max_width"
128252995Sdteske
129252995Sdteske	#
130252995Sdteske	# Using the longest command-length as the width of a single column,
131252995Sdteske	# determine if we can use more than one column to display commands.
132252995Sdteske	#
133252995Sdteske	local x=$longest_cmd ncols=1
134252995Sdteske	x=$(( $x + 8 )) # Accomodate leading tab character
135252995Sdteske	x=$(( $x + 3 + $longest_cmd )) # Preload end of next column
136252995Sdteske	while [ $x -lt $max_width ]; do
137252995Sdteske		ncols=$(( $ncols + 1 ))
138252995Sdteske		x=$(( $x + 3 + $longest_cmd ))
139252995Sdteske	done
140252995Sdteske	f_dprintf "ncols=[%u] x=[%u]" $ncols $x
141252995Sdteske
142252995Sdteske	#
143252995Sdteske	# Re-format the command-list into multiple columns
144252995Sdteske	#
145252995Sdteske	cmd_list=$( eval "$( echo "$cmd_list" |
146252995Sdteske		awk -v ncols=$ncols -v size=$longest_cmd '
147252995Sdteske		BEGIN {
148252995Sdteske			n = 0
149252995Sdteske			row_item[1] = ""
150252995Sdteske		}
151252995Sdteske		function print_row()
152252995Sdteske		{
153252995Sdteske			fmt = "printf \"\\t%-" size "s"
154252995Sdteske			for (i = 1; i < cur_col; i++)
155252995Sdteske				fmt = fmt "   %-" size "s"
156252995Sdteske			fmt = fmt "\\n\""
157252995Sdteske			printf "%s", fmt
158252995Sdteske			for (i = 1; i <= cur_col; i++)
159252995Sdteske				printf " \"%s\"", row_item[i]
160252995Sdteske			print ""
161252995Sdteske		}
162252995Sdteske		{
163252995Sdteske			n++
164252995Sdteske			cur_col = (( n - 1 ) % ncols ) + 1
165252995Sdteske			printf "f_dprintf \"row_item[%u]=[%%s]\" \"%s\"\n",
166252995Sdteske			       cur_col, $0
167252995Sdteske			row_item[cur_col] = $0
168252995Sdteske			if ( cur_col == ncols ) print_row()
169252995Sdteske		}
170252995Sdteske		END {
171252995Sdteske			if ( cur_col < ncols ) print_row()
172252995Sdteske		}' )"
173252995Sdteske	)
174252995Sdteske
175252995Sdteske	f_usage $BSDCFG_LIBE/USAGE \
176252995Sdteske	        "PROGRAM_NAME" "$pgm" \
177252995Sdteske	        "COMMAND_LIST" "$cmd_list"
178252995Sdteske
179252995Sdteske	# Never reached
180252995Sdteske}
181252995Sdteske
182252995Sdteske# dialog_menu_main
183252995Sdteske#
184252995Sdteske# Display the dialog(1)-based application main menu.
185252995Sdteske#
186252995Sdteskedialog_menu_main()
187252995Sdteske{
188252995Sdteske	local title="$DIALOG_TITLE"
189252995Sdteske	local btitle="$DIALOG_BACKTITLE"
190252995Sdteske	local prompt="$msg_menu_text"
191252995Sdteske	local menu_list="
192252995Sdteske		'X' '$msg_exit'  '$msg_exit_bsdconfig'
193252995Sdteske		'1' '$msg_usage' '$msg_quick_start_how_to_use_this_menu_system'
194252995Sdteske	" # END-QUOTE
195252995Sdteske	local defaultitem= # Calculated below
196252995Sdteske	local hline=
197252995Sdteske
198252995Sdteske	#
199252995Sdteske	# Pick up the base modules (directories named `[0-9][0-9][0-9].*')
200252995Sdteske	#
201252995Sdteske	local menuitem menu_title menu_help menu_selection index=2
202252995Sdteske	for menuitem in $( cd $BSDCFG_LIBE && ls -d [0-9][0-9][0-9].* ); do
203252995Sdteske		[ -f "$BSDCFG_LIBE/$menuitem/INDEX" ] || continue
204252995Sdteske		[ $index -lt ${#DIALOG_MENU_TAGS} ] || break
205252995Sdteske
206252995Sdteske		menu_program= menu_title= menu_help=
207252995Sdteske		f_include_lang $BSDCFG_LIBE/$menuitem/INDEX
208252995Sdteske		[ "$menu_program" ] || continue
209252995Sdteske
210252995Sdteske		case "$menu_program" in
211252995Sdteske		/*) : already fully qualified ;;
212252995Sdteske		 *) menu_program="$menuitem/$menu_program"
213252995Sdteske		esac
214252995Sdteske
215252995Sdteske		tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
216252995Sdteske		setvar "menu_program$tag" "$menu_program"
217252995Sdteske
218252995Sdteske		f_shell_escape "$menu_title" menu_title
219252995Sdteske		f_shell_escape "$menu_help" menu_help
220252995Sdteske		menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"
221252995Sdteske
222252995Sdteske		index=$(( $index + 1 ))
223252995Sdteske	done
224252995Sdteske
225252995Sdteske	#
226252995Sdteske	# Process the `local' libexec sources.
227252995Sdteske	#
228252995Sdteske	# Whereas modules in $BSDCFG_LIBE must be named [0-9][0-9][0-9].*
229252995Sdteske	# modules in $BSDCFG_LOCAL_LIBE should NOT be named this way (making it
230252995Sdteske	# more practical for port-maintainers).
231252995Sdteske	#
232252995Sdteske	# This also has the fortunate side-effect of making the de-duplication
233252995Sdteske	# effort rather simple (because so-called `base' modules must be named
234252995Sdteske	# differently than add-on modules).
235252995Sdteske	#
236252995Sdteske	local separator_added=
237252995Sdteske	for menuitem in $( cd "$BSDCFG_LOCAL_LIBE" 2> /dev/null && ls -d * )
238252995Sdteske	do
239252995Sdteske		# Skip the module if it looks like a `base' module
240252995Sdteske		case "$menuitem" in [0-9][0-9][0-9].*) continue;; esac
241252995Sdteske
242252995Sdteske		[ -f "$BSDCFG_LOCAL_LIBE/$menuitem/INDEX" ] || continue
243252995Sdteske		[ $index -lt ${#DIALOG_MENU_TAGS} ] || break
244252995Sdteske
245252995Sdteske		menu_program= menu_title= menu_help=
246252995Sdteske		f_include_lang $BSDCFG_LOCAL_LIBE/$menuitem/INDEX || continue
247252995Sdteske		[ "$menu_program" ] || continue
248252995Sdteske
249252995Sdteske		if [ ! "$separator_added" ]; then
250252995Sdteske			menu_list="$menu_list '-' '-' ''"
251252995Sdteske			separator_added=1
252252995Sdteske		fi
253252995Sdteske
254252995Sdteske		case "$menu_program" in
255252995Sdteske		/*) : already fully qualified ;;
256252995Sdteske		 *) menu_program="$BSDCFG_LOCAL_LIBE/$menuitem/$menu_program"
257252995Sdteske		esac
258252995Sdteske
259252995Sdteske		tag=$( f_substr "$DIALOG_MENU_TAGS" $index 1 )
260252995Sdteske		setvar "menu_program$tag" "$menu_program"
261252995Sdteske
262252995Sdteske		f_shell_escape "$menu_title" menu_title
263252995Sdteske		f_shell_escape "$menu_help" menu_help
264252995Sdteske		menu_list="$menu_list '$tag' '$menu_title' '$menu_help'"
265252995Sdteske
266252995Sdteske		index=$(( $index + 1 ))
267252995Sdteske	done
268252995Sdteske
269252995Sdteske	local height width rows
270252995Sdteske	eval f_dialog_menu_with_help_size height width rows \
271252995Sdteske	                                  \"\$title\"  \
272252995Sdteske	                                  \"\$btitle\" \
273252995Sdteske	                                  \"\$prompt\" \
274252995Sdteske	                                  \"\$hline\"  \
275252995Sdteske	                                  $menu_list
276252995Sdteske
277252995Sdteske	# Obtain default-item from previously stored selection
278252995Sdteske	f_dialog_default_fetch defaultitem
279252995Sdteske
280252995Sdteske	local menu_choice
281252995Sdteske	menu_choice=$( eval $DIALOG \
282252995Sdteske		--clear                                 \
283252995Sdteske		--title \"\$title\"                     \
284252995Sdteske		--backtitle \"\$btitle\"                \
285252995Sdteske		--hline \"\$hline\"                     \
286252995Sdteske		--item-help                             \
287252995Sdteske		--ok-label \"\$msg_ok\"                 \
288252995Sdteske		--cancel-label \"\$msg_exit_bsdconfig\" \
289252995Sdteske		--help-button                           \
290252995Sdteske		--help-label \"\$msg_help\"             \
291252995Sdteske		${USE_XDIALOG:+--help \"\"}             \
292252995Sdteske		--default-item \"\$defaultitem\"        \
293252995Sdteske		--menu \"\$prompt\"                     \
294252995Sdteske		$height $width $rows                    \
295252995Sdteske		$menu_list                              \
296252995Sdteske		2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
297252995Sdteske	)
298252995Sdteske	local retval=$?
299252995Sdteske	f_dialog_data_sanitize menu_choice
300252995Sdteske	f_dialog_menutag_store "$menu_choice"
301252995Sdteske
302252995Sdteske	# Only update default-item on success
303263791Sdteske	[ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice"
304252995Sdteske
305252995Sdteske	return $retval
306252995Sdteske}
307252995Sdteske
308252995Sdteske############################################################ MAIN
309252995Sdteske
310252995Sdteske#
311252995Sdteske# If $0 is not "bsdconfig", interpret it either as a keyword to a menuitem or
312252995Sdteske# as a valid resword (see script.subr for additional details about reswords).
313252995Sdteske#
314252995Sdteskeif [ "$pgm" != "bsdconfig" ]; then
315252995Sdteske	if indexfile=$( f_index_file "$pgm" ) &&
316252995Sdteske	   cmd=$( f_index_menusel_command "$indexfile" "$pgm" )
317252995Sdteske	then
318263791Sdteske		f_dprintf "pgm=[%s] cmd=[%s] *=[%s]" "$pgm" "$cmd" "$*"
319252995Sdteske		exec "$cmd" "$@" || exit 1
320252995Sdteske	else
321252995Sdteske		f_include $BSDCFG_SHARE/script.subr
322252995Sdteske		for resword in $RESWORDS; do
323252995Sdteske			[ "$pgm" = "$resword" ] || continue
324252995Sdteske			# Found a match
325252995Sdteske			f_dprintf "pgm=[%s] A valid resWord!" "$pgm"
326263791Sdteske			f_dispatch $resword $resword "$@"
327252995Sdteske			exit $?
328252995Sdteske		done
329252995Sdteske	fi
330252995Sdteskefi
331252995Sdteske
332252995Sdteske#
333252995Sdteske# Process command-line arguments
334252995Sdteske#
335252995Sdteskescripts_loaded=0
336252995Sdteskewhile getopts f:h$GETOPTS_STDARGS flag; do
337252995Sdteske	case "$flag" in
338252995Sdteske	f) [ $scripts_loaded -eq 0 ] && f_include $BSDCFG_SHARE/script.subr
339252995Sdteske	   f_script_load "$OPTARG"
340252995Sdteske	   scripts_loaded=$(( $scripts_loaded + 1 )) ;;
341252995Sdteske	h|\?) usage ;;
342252995Sdteske	esac
343252995Sdteskedone
344263791Sdteskeshift $(( $OPTIND - 1 ))
345252995Sdteske
346252995Sdteske# If we've loaded any scripts, do not continue any further
347252995Sdteske[ $scripts_loaded -gt 0 ] && exit
348252995Sdteske
349252995Sdteske#
350252995Sdteske# Initialize
351252995Sdteske#
352252995Sdteskef_dialog_title "$msg_main_menu"
353252995Sdteske
354252995Sdteske[ "$SECURE" ] && f_mustberoot_init
355252995Sdteske
356252995Sdteske# Incorporate rc-file if it exists
357252995Sdteske[ -f "$HOME/.bsdconfigrc" ] && f_include "$HOME/.bsdconfigrc"
358252995Sdteske
359252995Sdteske#
360252995Sdteske# If a non-option argument was passed, process it as a menuitem selection...
361252995Sdteske#
362252995Sdteskeif [ "$1" ]; then
363252995Sdteske	#
364252995Sdteske	# ...unless it's a long-option for usage.
365252995Sdteske	#
366252995Sdteske	case "$1" in -help|--help|-\?)
367252995Sdteske		usage
368252995Sdteske		# Not reached
369252995Sdteske	esac
370252995Sdteske
371252995Sdteske	#
372252995Sdteske	# Find the INDEX (possibly i18n) claiming this keyword and get the
373252995Sdteske	# command to execute from the menu_selection line.
374252995Sdteske	#
375252995Sdteske	if ! { indexfile=$( f_index_file "$1" ) &&
376252995Sdteske	       cmd=$( f_index_menusel_command "$indexfile" "$1" )
377252995Sdteske	}; then
378252995Sdteske		# no matches, display usage (which shows valid keywords)
379252995Sdteske		f_err "%s: %s: $msg_not_found\n" "$pgm" "$1"
380252995Sdteske		usage
381252995Sdteske		# Not reached
382252995Sdteske	fi
383252995Sdteske
384263791Sdteske	f_dprintf "cmd=[%s] *=[%s]" "$cmd" "$*"
385252995Sdteske	shift
386252995Sdteske	exec $cmd ${USE_XDIALOG:+-X} "$@" || exit 1
387252995Sdteske	# Not reached
388252995Sdteskefi
389252995Sdteske
390252995Sdteske#
391252995Sdteske# Launch application main menu
392252995Sdteske#
393252995Sdteskewhile :; do
394252995Sdteske	dialog_menu_main
395252995Sdteske	retval=$?
396252995Sdteske	f_dialog_menutag_fetch mtag
397252995Sdteske	f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
398252995Sdteske
399263791Sdteske	if [ $retval -eq $DIALOG_HELP ]; then
400252995Sdteske		f_show_help "$BSDCONFIG_HELPFILE"
401252995Sdteske		continue
402263791Sdteske	elif [ $retval -ne $DIALOG_OK ]; then
403252995Sdteske		f_die
404252995Sdteske	fi
405252995Sdteske
406252995Sdteske	case "$mtag" in
407252995Sdteske	X) break ;;
408252995Sdteske	1) # Usage
409252995Sdteske	   f_show_help "$USAGE_HELPFILE"
410252995Sdteske	   continue
411252995Sdteske	esac
412252995Sdteske
413252995Sdteske	# Anything else is a dynamically loaded menuitem
414252995Sdteske
415252995Sdteske	f_getvar menu_program$mtag menu_program
416252995Sdteske	case "$menu_program" in
417252995Sdteske	/*) cmd="$menu_program" ;;
418252995Sdteske	 *) cmd="$BSDCFG_LIBE/$menu_program"
419252995Sdteske	esac
420252995Sdteske	f_dprintf "cmd=[%s]" "$cmd"
421252995Sdteske	$cmd ${USE_XDIALOG:+-X}
422252995Sdteskedone
423252995Sdteske
424252995Sdteskeexit $SUCCESS
425252995Sdteske
426252995Sdteske################################################################################
427252995Sdteske# END
428252995Sdteske################################################################################
429