xref: /freebsd/usr.sbin/bsdconfig/share/common.subr (revision a98ff317388a00b992f1bf8404dee596f9383f5e)
1if [ ! "$_COMMON_SUBR" ]; then _COMMON_SUBR=1
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############################################################ CONFIGURATION
31
32#
33# Default file descriptors to link to stdout/stderr for passthru allowing
34# redirection within a sub-shell to bypass directly to the terminal.
35#
36: ${TERMINAL_STDOUT_PASSTHRU:=3}}
37: ${TERMINAL_STDERR_PASSTHRU:=4}}
38
39############################################################ GLOBALS
40
41#
42# Program name
43#
44pgm="${0##*/}"
45
46#
47# Program arguments
48#
49ARGC="$#"
50ARGV="$@"
51
52#
53# Global exit status variables
54#
55SUCCESS=0
56FAILURE=1
57
58#
59# Operating environment details
60#
61export UNAME_S="$(uname -s)" # Operating System (i.e. FreeBSD)
62export UNAME_P="$(uname -p)" # Processor Architecture (i.e. i386)
63export UNAME_R="$(uname -r)" # Release Level (i.e. X.Y-RELEASE)
64
65#
66# Default behavior is to call f_debug_init() automatically when loaded.
67#
68: ${DEBUG_SELF_INITIALIZE=1}
69
70#
71# Define standard optstring arguments that should be supported by all programs
72# using this include (unless DEBUG_SELF_INITIALIZE is set to NULL to prevent
73# f_debug_init() from autamatically processing "$@" for the below arguments):
74#
75# 	d	Sets $debug to 1
76# 	D:	Sets $debugFile to $OPTARG
77#
78GETOPTS_STDARGS="dD:"
79
80#
81# The getopts builtin will return 1 either when the end of "$@" or the first
82# invalid flag is reached. This makes it impossible to determine if you've
83# processed all the arguments or simply have hit an invalid flag. In the cases
84# where we want to tolerate invalid flags (f_debug_init() for example), the
85# following variable can be appended to your optstring argument to getopts,
86# preventing it from prematurely returning 1 before the end of the arguments.
87#
88# NOTE: This assumes that all unknown flags are argument-less.
89#
90GETOPTS_ALLFLAGS="abcdefghijklmnopqrstuvwxyz"
91GETOPTS_ALLFLAGS="${GETOPTS_ALLFLAGS}ABCDEFGHIJKLMNOPQRSTUVWXYZ"
92GETOPTS_ALLFLAGS="${GETOPTS_ALLFLAGS}0123456789"
93
94#
95# When we get included, f_debug_init() will fire (unless $DEBUG_SELF_INITIALIZE
96# is set to disable automatic initialization) and process "$@" for a few global
97# options such as `-d' and/or `-D file'. However, if your program takes custom
98# flags that take arguments, this automatic processing may fail unexpectedly.
99#
100# The solution to this problem is to pre-define (before including this file)
101# the following variable (which defaults to NULL) to indicate that there are
102# extra flags that should be considered when performing automatic processing of
103# globally persistent flags.
104#
105: ${GETOPTS_EXTRA:=}
106
107############################################################ FUNCTIONS
108
109# f_dprintf $fmt [ $opts ... ]
110#
111# Sensible debug function. Override in ~/.bsdconfigrc if desired.
112# See /usr/share/examples/bsdconfig/bsdconfigrc for example.
113#
114# If $debug is set and non-NULL, prints DEBUG info using printf(1) syntax:
115# 	+ To $debugFile, if set and non-NULL
116# 	+ To standard output if $debugFile is either NULL or unset
117# 	+ To both if $debugFile begins with a single plus-sign (`+')
118#
119f_dprintf()
120{
121	[ "$debug" ] || return $SUCCESS
122	local fmt="$1"; shift
123	case "$debugFile" in ""|+*)
124	printf "DEBUG: $fmt${fmt:+\n}" "$@" >&${TERMINAL_STDOUT_PASSTHRU:-1}
125	esac
126	[ "${debugFile#+}" ] &&
127		printf "DEBUG: $fmt${fmt:+\n}" "$@" >> "${debugFile#+}"
128	return $SUCCESS
129}
130
131# f_debug_init
132#
133# Initialize debugging. Truncates $debugFile to zero bytes if set.
134#
135f_debug_init()
136{
137	#
138	# Process stored command-line arguments
139	#
140	set -- $ARGV
141	local OPTIND
142	f_dprintf "f_debug_init: ARGV=[%s] GETOPTS_STDARGS=[%s]" \
143	          "$ARGV" "$GETOPTS_STDARGS"
144	while getopts "$GETOPTS_STDARGS$GETOPTS_EXTRA$GETOPTS_ALLFLAGS" flag \
145	> /dev/null; do
146		case "$flag" in
147		d) debug=1 ;;
148		D) debugFile="$OPTARG" ;;
149		esac
150	done
151	shift $(( $OPTIND - 1 ))
152	f_dprintf "f_debug_init: debug=[%s] debugFile=[%s]" \
153	          "$debug" "$debugFile"
154
155	#
156	# Automagically enable debugging if debugFile is set (and non-NULL)
157	#
158	[ "$debugFile" ] && { [ "${debug+set}" ] || debug=1; }
159
160	#
161	# Make debugging persistant if set
162	#
163	[ "$debug" ] && export debug
164	[ "$debugFile" ] && export debugFile
165
166	#
167	# Truncate the debug file upon. Note that we will trim a leading plus
168	# (`+') from the value of debugFile to support persistant meaning that
169	# f_dprintf() should print both to standard output and $debugFile
170	# (minus the leading plus, of course).
171	#
172	local _debug_file="${debugFile#+}"
173	if [ "$_debug_file" ]; then
174		if ( umask 022 && :> "$_debug_file" ); then
175			f_dprintf "Successfully initialized debugFile \`%s'" \
176			          "$_debug_file"
177			[ "${debug+set}" ] ||
178				debug=1 # turn debugging on if not set
179		else
180			unset debugFile
181			f_dprintf "Unable to initialize debugFile \`%s'" \
182			          "$_debug_file"
183		fi
184	fi
185}
186
187# f_err $fmt [ $opts ... ]
188#
189# Print a message to stderr (fd=2).
190#
191f_err()
192{
193	printf "$@" >&${TERMINAL_STDERR_PASSTHRU:-2}
194}
195
196# f_quietly $command [ $arguments ... ]
197#
198# Run a command quietly (quell any output to stdout or stderr)
199#
200f_quietly()
201{
202	"$@" > /dev/null 2>&1
203}
204
205# f_have $anything ...
206#
207# A wrapper to the `type' built-in. Returns true if argument is a valid shell
208# built-in, keyword, or externally-tracked binary, otherwise false.
209#
210f_have()
211{
212	f_quietly type "$@"
213}
214
215# f_which $anything [$var_to_set]
216#
217# A fast built-in replacement for syntaxes such as foo=$( which bar ). In a
218# comparison of 10,000 runs of this function versus which, this function
219# completed in under 3 seconds, while `which' took almost a full minute.
220#
221# If $var_to_set is missing or NULL, output is (like which) to standard out.
222# Returns success if a match was found, failure otherwise.
223#
224f_which()
225{
226	local __name="$1" __var_to_set="$2"
227	case "$__name" in */*|'') return $FAILURE; esac
228	local __p IFS=":" __found=
229	for __p in $PATH; do
230		local __exec="$__p/$__name"
231		[ -f "$__exec" -a -x "$__exec" ] && __found=1 && break
232	done
233	if [ "$__found" ]; then
234		if [ "$__var_to_set" ]; then
235			setvar "$__var_to_set" "$__exec"
236		else
237			echo "$__exec"
238		fi
239		return $SUCCESS
240	fi
241	return $FAILURE
242}
243
244# f_getvar $var_to_get [$var_to_set]
245#
246# Utility function designed to go along with the already-builtin setvar.
247# Allows clean variable name indirection without forking or sub-shells.
248#
249# Returns error status if the requested variable ($var_to_get) is not set.
250#
251# If $var_to_set is missing or NULL, the value of $var_to_get is printed to
252# standard output for capturing in a sub-shell (which is less-recommended
253# because of performance degredation; for example, when called in a loop).
254#
255f_getvar()
256{
257	local __var_to_get="$1" __var_to_set="$2"
258	[ "$__var_to_set" ] || local value
259	eval ${__var_to_set:-value}=\"\${$__var_to_get}\"
260	eval [ \"\${$__var_to_get+set}\" ]
261	local __retval=$?
262	eval f_dprintf '"f_getvar: var=[%s] value=[%s] r=%u"' \
263		\"\$__var_to_get\" \"\$${__var_to_set:-value}\" \$__retval
264	[ "$__var_to_set" ] || { [ "$value" ] && echo "$value"; }
265	return $__retval
266}
267
268# f_isset $var
269#
270# Check if variable $var is set. Returns success if variable is set, otherwise
271# returns failure.
272#
273f_isset()
274{
275	eval [ \"\${${1%%[$IFS]*}+set}\" ]
276}
277
278# f_die [ $status [ $fmt [ $opts ... ]]]
279#
280# Abruptly terminate due to an error optionally displaying a message in a
281# dialog box using printf(1) syntax.
282#
283f_die()
284{
285	local status=$FAILURE
286
287	# If there is at least one argument, take it as the status
288	if [ $# -gt 0 ]; then
289		status=$1
290		shift 1 # status
291	fi
292
293	# If there are still arguments left, pass them to f_show_msg
294	[ $# -gt 0 ] && f_show_msg "$@"
295
296	# Optionally call f_clean_up() function if it exists
297	f_have f_clean_up && f_clean_up
298
299	exit $status
300}
301
302# f_interrupt
303#
304# Interrupt handler.
305#
306f_interrupt()
307{
308	exec 2>&1 # fix sh(1) bug where stderr gets lost within async-trap
309	f_die
310}
311
312# f_show_info $fmt [ $opts ... ]
313#
314# Display a message in a dialog infobox using printf(1) syntax.
315#
316f_show_info()
317{
318	local msg
319	msg=$( printf "$@" )
320
321	#
322	# Use f_dialog_infobox from dialog.subr if possible, otherwise fall
323	# back to dialog(1) (without options, making it obvious when using
324	# un-aided system dialog).
325	#
326	if f_have f_dialog_info; then
327		f_dialog_info "$msg"
328	else
329		dialog --infobox "$msg" 0 0
330	fi
331}
332
333# f_show_msg $fmt [ $opts ... ]
334#
335# Display a message in a dialog box using printf(1) syntax.
336#
337f_show_msg()
338{
339	local msg
340	msg=$( printf "$@" )
341
342	#
343	# Use f_dialog_msgbox from dialog.subr if possible, otherwise fall
344	# back to dialog(1) (without options, making it obvious when using
345	# un-aided system dialog).
346	#
347	if f_have f_dialog_msgbox; then
348		f_dialog_msgbox "$msg"
349	else
350		dialog --msgbox "$msg" 0 0
351	fi
352}
353
354
355# f_yesno $fmt [ $opts ... ]
356#
357# Display a message in a dialog yes/no box using printf(1) syntax.
358#
359f_yesno()
360{
361	local msg
362	msg=$( printf "$@" )
363
364	#
365	# Use f_dialog_yesno from dialog.subr if possible, otherwise fall
366	# back to dialog(1) (without options, making it obvious when using
367	# un-aided system dialog).
368	#
369	if f_have f_dialog_yesno; then
370		f_dialog_yesno "$msg"
371	else
372		dialog --yesno "$msg" 0 0
373	fi
374}
375
376# f_noyes $fmt [ $opts ... ]
377#
378# Display a message in a dialog yes/no box using printf(1) syntax.
379# NOTE: THis is just like the f_yesno function except "No" is default.
380#
381f_noyes()
382{
383	local msg
384	msg=$( printf "$@" )
385
386	#
387	# Use f_dialog_noyes from dialog.subr if possible, otherwise fall
388	# back to dialog(1) (without options, making it obvious when using
389	# un-aided system dialog).
390	#
391	if f_have f_dialog_noyes; then
392		f_dialog_noyes "$msg"
393	else
394		dialog --defaultno --yesno "$msg" 0 0
395	fi
396}
397
398# f_show_help $file
399#
400# Display a language help-file. Automatically takes $LANG and $LC_ALL into
401# consideration when displaying $file (suffix ".$LC_ALL" or ".$LANG" will
402# automatically be added prior to loading the language help-file).
403#
404# If a language has been requested by setting either $LANG or $LC_ALL in the
405# environment and the language-specific help-file does not exist we will fall
406# back to $file without-suffix.
407#
408# If the language help-file does not exist, an error is displayed instead.
409#
410f_show_help()
411{
412	local file="$1"
413	local lang="${LANG:-$LC_ALL}"
414
415	[ -f "$file.$lang" ] && file="$file.$lang"
416
417	#
418	# Use f_dialog_textbox from dialog.subr if possible, otherwise fall
419	# back to dialog(1) (without options, making it obvious when using
420	# un-aided system dialog).
421	#
422	if f_have f_dialog_textbox; then
423		f_dialog_textbox "$file"
424	else
425		dialog --msgbox "$( cat "$file" 2>&1 )" 0 0
426	fi
427}
428
429# f_include $file
430#
431# Include a shell subroutine file.
432#
433# If the subroutine file exists but returns error status during loading, exit
434# is called and execution is prematurely terminated with the same error status.
435#
436f_include()
437{
438	local file="$1"
439	f_dprintf "f_include: file=[%s]" "$file"
440	. "$file" || exit $?
441}
442
443# f_include_lang $file
444#
445# Include a language file. Automatically takes $LANG and $LC_ALL into
446# consideration when including $file (suffix ".$LC_ALL" or ".$LANG" will
447# automatically by added prior to loading the language file).
448#
449# No error is produced if (a) a language has been requested (by setting either
450# $LANG or $LC_ALL in the environment) and (b) the language file does not
451# exist -- in which case we will fall back to loading $file without-suffix.
452#
453# If the language file exists but returns error status during loading, exit
454# is called and execution is prematurely terminated with the same error status.
455#
456f_include_lang()
457{
458	local file="$1"
459	local lang="${LANG:-$LC_ALL}"
460
461	f_dprintf "f_include_lang: file=[%s] lang=[%s]" "$file" "$lang"
462	if [ -f "$file.$lang" ]; then
463		. "$file.$lang" || exit $?
464	else
465		. "$file" || exit $?
466	fi
467}
468
469# f_usage $file [ $key1 $value1 ... ]
470#
471# Display USAGE file with optional pre-processor macro definitions. The first
472# argument is the template file containing the usage text to be displayed. If
473# $LANG or $LC_ALL (in order of preference, respectively) is set, ".encoding"
474# will automatically be appended as a suffix to the provided $file pathname.
475#
476# When processing $file, output begins at the first line containing that is
477# (a) not a comment, (b) not empty, and (c) is not pure-whitespace. All lines
478# appearing after this first-line are output, including (a) comments (b) empty
479# lines, and (c) lines that are purely whitespace-only.
480#
481# If additional arguments appear after $file, substitutions are made while
482# printing the contents of the USAGE file. The pre-processor macro syntax is in
483# the style of autoconf(1), for example:
484#
485# 	f_usage $file "FOO" "BAR"
486#
487# Will cause instances of "@FOO@" appearing in $file to be replaced with the
488# text "BAR" before bering printed to the screen.
489#
490# This function is a two-parter. Below is the awk(1) portion of the function,
491# afterward is the sh(1) function which utilizes the below awk script.
492#
493f_usage_awk='
494BEGIN { found = 0 }
495{
496	if ( !found && $0 ~ /^[[:space:]]*($|#)/ ) next
497	found = 1
498	print
499}
500'
501f_usage()
502{
503	local file="$1"
504	local lang="${LANG:-$LC_ALL}"
505
506	f_dprintf "f_usage: file=[%s] lang=[%s]" "$file" "$lang"
507
508	shift 1 # file
509
510	local usage
511	if [ -f "$file.$lang" ]; then
512		usage=$( awk "$f_usage_awk" "$file.$lang" ) || exit $FAILURE
513	else
514		usage=$( awk "$f_usage_awk" "$file" ) || exit $FAILURE
515	fi
516
517	while [ $# -gt 0 ]; do
518		local key="$1"
519		export value="$2"
520		usage=$( echo "$usage" | awk \
521			"{ gsub(/@$key@/, ENVIRON[\"value\"]); print }" )
522		shift 2
523	done
524
525	f_err "%s\n" "$usage"
526
527	exit $FAILURE
528}
529
530# f_index_file $keyword
531#
532# Process all INDEX files known to bsdconfig and return the path to first file
533# containing a menu_selection line with a keyword portion matching $keyword.
534#
535# If $LANG or $LC_ALL (in order of preference, respectively) is set,
536# "INDEX.encoding" files will be searched first.
537#
538# If no file is found, error status is returned along with the NULL string.
539#
540# This function is a two-parter. Below is the awk(1) portion of the function,
541# afterward is the sh(1) function which utilizes the below awk script.
542#
543f_index_file_awk='
544# Variables that should be defined on the invocation line:
545# 	-v keyword="keyword"
546BEGIN { found = 0 }
547( $0 ~ "^menu_selection=\"" keyword "\\|" ) {
548	print FILENAME
549	found++
550	exit
551}
552END { exit ! found }
553'
554f_index_file()
555{
556	local keyword="$1"
557	local lang="${LANG:-$LC_ALL}"
558
559	f_dprintf "f_index_file: keyword=[%s] lang=[%s]" "$keyword" "$lang"
560
561	if [ "$lang" ]; then
562		awk -v keyword="$keyword" "$f_index_file_awk" \
563			$BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && return
564		# No match, fall-thru to non-i18n sources
565	fi
566	awk -v keyword="$keyword" "$f_index_file_awk" \
567		$BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX && return
568
569	# No match? Fall-thru to `local' libexec sources (add-on modules)
570
571	[ "$BSDCFG_LOCAL_LIBE" ] || return $FAILURE
572	if [ "$lang" ]; then
573		awk -v keyword="$keyword" "$f_index_file_awk" \
574			$BSDCFG_LOCAL_LIBE/*/INDEX.$lang && return
575		# No match, fall-thru to non-i18n sources
576	fi
577	awk -v keyword="$keyword" "$f_index_file_awk" \
578		$BSDCFG_LOCAL_LIBE/*/INDEX
579}
580
581# f_index_menusel_keyword $indexfile $pgm
582#
583# Process $indexfile and return only the keyword portion of the menu_selection
584# line with a command portion matching $pgm.
585#
586# This function is for internationalization (i18n) mapping of the on-disk
587# scriptname ($pgm) into the localized language (given language-specific
588# $indexfile). If $LANG or $LC_ALL (in orderder of preference, respectively) is
589# set, ".encoding" will automatically be appended as a suffix to the provided
590# $indexfile pathname.
591#
592# If, within $indexfile, multiple $menu_selection values map to $pgm, only the
593# first one will be returned. If no mapping can be made, the NULL string is
594# returned.
595#
596# If $indexfile does not exist, error status is returned with NULL.
597#
598# This function is a two-parter. Below is the awk(1) portion of the function,
599# afterward is the sh(1) function which utilizes the below awk script.
600#
601f_index_menusel_keyword_awk='
602# Variables that should be defined on the invocation line:
603# 	-v pgm="program_name"
604#
605BEGIN {
606	prefix = "menu_selection=\""
607	plen = length(prefix)
608	found = 0
609}
610{
611	if (!match($0, "^" prefix ".*\\|.*\"")) next
612
613	keyword = command = substr($0, plen + 1, RLENGTH - plen - 1)
614	sub(/^.*\|/, "", command)
615	sub(/\|.*$/, "", keyword)
616
617	if ( command == pgm )
618	{
619		print keyword
620		found++
621		exit
622	}
623}
624END { exit ! found }
625'
626f_index_menusel_keyword()
627{
628	local indexfile="$1" pgm="$2"
629	local lang="${LANG:-$LC_ALL}"
630
631	f_dprintf "f_index_menusel_keyword: index=[%s] pgm=[%s] lang=[%s]" \
632	          "$indexfile" "$pgm" "$lang"
633
634	if [ -f "$indexfile.$lang" ]; then
635		awk -v pgm="$pgm" \
636			"$f_index_menusel_keyword_awk" \
637			"$indexfile.$lang"
638	elif [ -f "$indexfile" ]; then
639		awk -v pgm="$pgm" \
640			"$f_index_menusel_keyword_awk" \
641			"$indexfile"
642	fi
643}
644
645# f_index_menusel_command $indexfile $keyword
646#
647# Process $indexfile and return only the command portion of the menu_selection
648# line with a keyword portion matching $keyword.
649#
650# This function is for mapping [possibly international] keywords into the
651# command to be executed. If $LANG or $LC_ALL (order of preference) is set,
652# ".encoding" will automatically be appended as a suffix to the provided
653# $indexfile pathname.
654#
655# If, within $indexfile, multiple $menu_selection values map to $keyword, only
656# the first one will be returned. If no mapping can be made, the NULL string is
657# returned.
658#
659# If $indexfile doesn't exist, error status is returned with NULL.
660#
661# This function is a two-parter. Below is the awk(1) portion of the function,
662# afterward is the sh(1) function which utilizes the below awk script.
663#
664f_index_menusel_command_awk='
665# Variables that should be defined on the invocation line:
666# 	-v key="keyword"
667#
668BEGIN {
669	prefix = "menu_selection=\""
670	plen = length(prefix)
671	found = 0
672}
673{
674	if (!match($0, "^" prefix ".*\\|.*\"")) next
675
676	keyword = command = substr($0, plen + 1, RLENGTH - plen - 1)
677	sub(/^.*\|/, "", command)
678	sub(/\|.*$/, "", keyword)
679
680	if ( keyword == key )
681	{
682		print command
683		found++
684		exit
685	}
686}
687END { exit ! found }
688'
689f_index_menusel_command()
690{
691	local indexfile="$1" keyword="$2" command
692	local lang="${LANG:-$LC_ALL}"
693
694	f_dprintf "f_index_menusel_command: index=[%s] key=[%s] lang=[%s]" \
695	          "$indexfile" "$keyword" "$lang"
696
697	if [ -f "$indexfile.$lang" ]; then
698		command=$( awk -v key="$keyword" \
699				"$f_index_menusel_command_awk" \
700				"$indexfile.$lang" ) || return $FAILURE
701	elif [ -f "$indexfile" ]; then
702		command=$( awk -v key="$keyword" \
703				"$f_index_menusel_command_awk" \
704				"$indexfile" ) || return $FAILURE
705	else
706		return $FAILURE
707	fi
708
709	#
710	# If the command pathname is not fully qualified fix-up/force to be
711	# relative to the $indexfile directory.
712	#
713	case "$command" in
714	/*) : already fully qualified ;;
715	*)
716		local indexdir="${indexfile%/*}"
717		[ "$indexdir" != "$indexfile" ] || indexdir="."
718		command="$indexdir/$command"
719	esac
720
721	echo "$command"
722}
723
724# f_running_as_init
725#
726# Returns true if running as init(1).
727#
728f_running_as_init()
729{
730	#
731	# When a custom init(8) performs an exec(3) to invoke a shell script,
732	# PID 1 becomes sh(1) and $PPID is set to 1 in the executed script.
733	#
734	[ ${PPID:-0} -eq 1 ] # Return status
735}
736
737# f_mounted $local_directory
738#
739# Return success if a filesystem is mounted on a particular directory.
740#
741f_mounted()
742{
743	local dir="$1"
744	[ -d "$dir" ] || return $FAILURE
745	mount | grep -Eq " on $dir \([^)]+\)$"
746}
747
748############################################################ MAIN
749
750#
751# Trap signals so we can recover gracefully
752#
753trap 'f_interrupt' SIGINT
754trap 'f_die' SIGTERM SIGPIPE SIGXCPU SIGXFSZ \
755             SIGFPE SIGTRAP SIGABRT SIGSEGV
756trap '' SIGALRM SIGPROF SIGUSR1 SIGUSR2 SIGHUP SIGVTALRM
757
758#
759# Clone terminal stdout/stderr so we can redirect to it from within sub-shells
760#
761eval exec $TERMINAL_STDOUT_PASSTHRU\>\&1
762eval exec $TERMINAL_STDERR_PASSTHRU\>\&2
763
764#
765# Self-initialize unless requested otherwise
766#
767f_dprintf "%s: DEBUG_SELF_INITIALIZE=[%s]" \
768          dialog.subr "$DEBUG_SELF_INITIALIZE"
769case "$DEBUG_SELF_INITIALIZE" in
770""|0|[Nn][Oo]|[Oo][Ff][Ff]|[Ff][Aa][Ll][Ss][Ee]) : do nothing ;;
771*) f_debug_init
772esac
773
774#
775# Log our operating environment for debugging purposes
776#
777f_dprintf "UNAME_S=[%s] UNAME_P=[%s] UNAME_R=[%s]" \
778          "$UNAME_S" "$UNAME_P" "$UNAME_R"
779
780f_dprintf "%s: Successfully loaded." common.subr
781
782fi # ! $_COMMON_SUBR
783