#!/bin/sh
#-
# Copyright (c) 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

# Prevent common.subr from auto initializing debugging (this is not an inter-
# active utility that requires debugging; also `-d' has been repurposed).
#
DEBUG_SELF_INITIALIZE=NO

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

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

f_index_menusel_keyword $BSDCFG_LIBE/$APP_DIR/INDEX "$pgm" ipgm &&
	pgm="${ipgm:-$pgm}"

############################################################ GLOBALS

#
# Options
#
USE_COLOR=1
SHOW_DESC=
SHOW_FUNCS=
FUNC_PATTERN=

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

# show_functions $file
#
# Show the functions in the given include file.
#
show_include()
{
	local file="${1#./}"

	local pattern="${FUNC_PATTERN:-.*}"
	output=$( awk \
		-v use_color=${USE_COLOR:-0} \
		-v re="$pattern" \
		-v show_desc=${SHOW_DESC:-0} '
        function _asorti(src, dest)
        {
		k = nitems = 0;

		# Copy src indices to dest and calculate array length
		for (i in src) dest[++nitems] = i

		# Sort the array of indices (dest) using insertion sort method
		for (i = 1; i <= nitems; k = i++)
		{
			idx = dest[i]
			while ((k > 0) && (dest[k] > idx))
			{
				dest[k+1] = dest[k]
				k--
			}
			dest[k+1] = idx
		}

		return nitems
        }
	/^$/,/^#/ {
		if ($0 ~ /^# f_/) {
			if (!match($2, re)) next
			fn = $2
			if (use_color)
				syntax[fn] = sprintf("+%s%s%s\n",
				       substr($0, 2, RSTART),
				       substr($0, 2 + RSTART, RLENGTH),
				       substr($0, 2 + RSTART + RLENGTH))
			else
				syntax[fn] = "+" substr($0, 2) "\n"
			if (show_desc)
				print_more = 1
			else
				print_more = substr($0, length($0)) == "\\"
		}
		if (show_desc && print_more) {
			getline
			while ($0 ~ /^#/) {
				syntax[fn] = syntax[fn] " " substr($0, 2) "\n"
				getline
			}
			print_more = 0
		} else while (print_more) {
			getline
			syntax[fn] = syntax[fn] " " substr($0, 2) "\n"
			print_more = substr($0, length($0)) == "\\"
		}
	}
	END {
		n = _asorti(syntax, sorted_indices)
		for (i = 1; i <= n; i++)
			printf "%s", syntax[sorted_indices[i]]
	}' "$file" )
	if [ "$output" ]; then
		if [ ! "$SHOW_FUNCS" ]; then
			echo "$file"
			return $SUCCESS
		fi
		if [ "$FUNC_PATTERN" ]; then
			printf ">>> $msg_functions_in_matching\n" \
			       "$file" "$FUNC_PATTERN"
		else
			printf ">>> $msg_functions_in\n" "$file"
		fi
		echo "$output"
		echo # blank line to simplify awk(1)-based reparse
	fi
}

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

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

# Are we in a terminal?
[ -t 1 ] || USE_COLOR=

#
# Process command-line arguments
#
while getopts adfF:hn flag; do
	case "$flag" in
	a) USE_COLOR=1 ;;
	d) SHOW_DESC=1 SHOW_FUNCS=1 ;;
	f) SHOW_FUNCS=1 ;;
	F) FUNC_PATTERN="$OPTARG" ;;
	n) USE_COLOR= ;;
	h|\?) f_usage $BSDCFG_LIBE/$APP_DIR/USAGE "PROGRAM_NAME" "$pgm" ;;
	esac
done
shift $(( $OPTIND - 1 ))

# cd(1) to `share' dir so relative paths work for find and positional args
cd $BSDCFG_SHARE || f_die # Pedantic

#
# If given an argument, operate on it specifically (implied `-f') and exit
#
[ $# -gt 0 ] && SHOW_FUNCS=1
for include in "$@"; do
	# See if they've just omitted the `*.subr' suffix
	[ -f "$include.subr" -a ! -f "$include" ] && include="$include.subr"
	if [ ! -f "$include" ]; then
		printf "$msg_no_such_file_or_directory\n" "$0" "$include"
		exit $FAILURE
	elif [ ! -r "$include" ]; then
		printf "$msg_permission_denied\n" "$0" "$include"
		exit $FAILURE
	fi
	show_include "$include" || f_die
done

# Exit if we processed some include arguments
[ $# -gt 0 ] && exit $SUCCESS

#
# Operate an all known include files
# NB: If we get this far, we had no include arguments
#
find -s . -type f -and -iname '*.subr' | while read file; do
	if [ "$SHOW_FUNCS" -o "$FUNC_PATTERN" ]; then
		show_include "$file"
	else
		echo "${file#./}"
	fi
done

exit $SUCCESS

################################################################################
# END
################################################################################