#! /bin/sh

# $NetBSD: mkoptions.sh,v 1.5 2017/11/15 09:21:19 kre Exp $

#
# It would be more sensible to generate 2 .h files, one which
# is for everyone to use, defines the "variables" and (perhaps) generates
# the externs (though they could just be explicit in options.h)
# and one just for options.c which generates the initialisation.
#
# But then I'd have to deal with making the Makefile handle that properly...
# (this is simpler there, and it just means a bit more sh compile time.)

set -f
IFS=' 	'	# blank, tab (no newline)

IF="$1"
OF="${3+$3/}$2"

E_FILE=$(${MKTEMP:-mktemp} -t MKO.E.$$)
O_FILE=$(${MKTEMP:-mktemp} -t MKO.O.$$)
trap 'rm -f "${E_FILE}" "${O_FILE}"' EXIT

exec 5> "${E_FILE}"
exec 6> "${O_FILE}"

{
	printf '/*\n * File automatically generated by %s.\n' "$0"
	printf ' * Do not edit, do not add to cvs.\n'
	printf ' */\n\n'

	printf '#ifdef DEFINE_OPTIONS\n'
	printf 'struct optent optlist[] = {\n'
} >"${OF}"

FIRST=true

${SED:-sed} <"${IF}"			\
	-e '/^$/d'			\
	-e '/^#/d'			\
	-e '/^[ 	]*\//d'		\
	-e '/^[ 	]*\*/d'		\
	-e '/^[ 	]*;/d'			|
sort -b -k2,2f -k2,2 < "${IF}"			|
while read line
do
	# Look for comments in various styles, and ignore them
	# (these should generally be already removed by sed above)

	case "${line}" in
	'')	continue;;
	/*)	continue;;
	\**)	continue;;
	\;*)	continue;;
	\#*)	continue;;
	esac

	case "${line}" in
	*'#if'*)
		COND="${line#*#}"
		COND="#${COND%%#*}"
		;;
	*)
		COND=
		;;
	esac
	set -- ${line%%[ 	]#*}

	var="$1" name="$2"

	case "${var}" in
	('' | [!A-Za-z_]* | *[!A-Za-z0-9_]*)
		printf >&2 "Bad var name: '%s'\\n" "${var}"
		# exit 1
		continue	# just ignore it for now
	esac

	case "${name}" in
	?) 	set -- ${var} '' $name $3 $4; name= ;;
	esac

	chr="$3" set="$4" dflt="$5"

	case "${chr}" in
	-)	chr= set= dflt="$4";;
	''|?)	;;
	*)	printf >&2 'flag "%s": Not a character\n' "${chr}"; continue;;
	esac

	# options must have some kind of name, or they are useless...
	test -z "${name}${chr}" && continue

	case "${set}" in
	-)	set= ;;
	[01] | [Oo][Nn] | [Oo][Ff][Ff])	dflt="${set}"; set= ;;
	''|?)	;;
	*)	printf >&2 'set "%s": Not a character\n' "${set}"; continue;;
	esac

	case "${dflt}" in
	'')		;;
	[Oo][Nn])	dflt=1;;
	[Oo][Ff][Ff])	dflt=0;;
	[01])		;;
	*)	printf >&2 'default "%s" invalid, use 0 off 1 on\n'; continue;;
	esac

	# validation complete, now to generate output

	if [ -n "${COND}" ]
	then
		printf '%s\n' "${COND}" >&4
		printf '%s\n' "${COND}" >&5
		printf '%s\n' "${COND}" >&6
	fi

	printf '\t_SH_OPT_%s,\n' "${var}" >&5

	if [ -n "${name}" ]
	then
		printf '    { "%s", ' "${name}"	>&4
	else
		printf '    { 0, '		>&4
	fi

	if [ -n "${chr}" ]
	then
		printf "'%s', " "${chr}"	>&4
	else
		chr=
		printf '0, '			>&4
	fi

	if [ -n "${set}" ]
	then
		printf "'%s', 0, " "${set}"	>&4
	else
		printf '0, 0, '			>&4
	fi

	if [ -n "${dflt}" ]
	then
		printf '%s },\n' "${dflt}"	>&4
	else
		printf '0 },\n'			>&4
	fi

	printf '#define %s\toptlist[_SH_OPT_%s].val\n' "${var}" "${var}" >&6

	if [ -n "${COND}" ]
	then
		printf '#endif\n' >&4
		printf '#endif\n' >&5
		printf '#endif\n' >&6
	fi

	test -z "${chr}" && continue

	printf '%s _SH_OPT_%s %s\n' "${chr}" "${var}" "${COND}"

done 4>>"${OF}" | sort -t' ' -k1,1f -k1,1 | while read chr index COND
do
	if $FIRST
	then
		printf '    { 0, 0, 0, 0, 0 }\n};\n'
		printf '#endif\n\n'

		printf 'enum shell_opt_names {\n'
		cat "${E_FILE}"
		printf '};\n\n'

		printf '#ifdef DEFINE_OPTIONS\n'
		printf 'const unsigned char optorder[] = {\n'
		FIRST=false
	fi
	[ -n "${COND}" ] && printf '%s\n' "${COND}"
	printf '\t%s,\n' "${index}"
	[ -n "${COND}" ] && printf '#endif\n'

done >>"${OF}"

{
	printf '};\n\n'
	printf '#define NOPTS (sizeof optlist / sizeof optlist[0] - 1)\n'
	printf 'int sizeof_optlist = sizeof optlist;\n\n'
	printf	\
	   'const int option_flags = (sizeof optorder / sizeof optorder[0]);\n'
	printf '\n#else\n\n'
	printf 'extern struct optent optlist[];\n'
	printf 'extern int sizeof_optlist;\n'
	printf 'extern const unsigned char optorder[];\n'
	printf 'extern const int option_flags;\n'
	printf '\n#endif\n\n'

	cat "${O_FILE}"
} >> "${OF}"

exit 0
