#!/bin/sh
#
# NetHack configuration utility. Version 1.1.0
#
# Author: J. Ali Harlow, ali@avrc.city.ac.uk
#
# Copyright (c) 1999-2000, J. Ali Harlow.
#	Free for use with NetHack or any derivatives covered by its license.
#
# Usage: config <template> <toplevel>
#		[-D|-U]<SWITCH> ... <toplevel>
#

WS="[ 	]"
SC="\/\*"
EC="\*\/"
dollar='$'
dflag=""
# find_line		Find the line to act on
# Usage:	find_line <context> <regexp> <filename>
find_line()
{
    if [ -z "$1" ]; then
	egrep -n "$2" "$3" | awk -F: 'NR==1 { print $1; }'
    else
	awk "\
	  BEGIN {found=best=dist=0} \
	  /$1/ {found=NR;} \
	  /$2/ {if (found){if(!best||dist>(NR-found)){best=NR;dist=NR-found}}} \
	  END {if (best)print best}" "$3"
    fi
}
initialise()
{
    echo "" > /tmp/$1.$$
}
# Return an extended regular expression to find an enabled definition for $1
# If a second argument is given, then $1 must be defined as this value.
regex_enabled()
{
    case $label in
    *_c|*_h)
	# #define $1 <repl> [/* <com> */]
	if [ $# -eq 2 ]; then
	    REPL="${WS}+$2${WS}*"
	else
	    REPL="(${WS}+([^\*]|\*[^/])*${WS}*|)"
	fi
	COM="(${SC}([^\*]|\*[^/])*${EC})"
	echo "^${WS}*#${WS}*define${WS}+$1${REPL}${COM}?${WS}*${dollar}"
	;;
    *akefile|*akefile.*)
	# $1=<repl> [# <com>]
	if [ $# -eq 2 ]; then
	    REPL="${WS}*$2${WS}*"
	else
	    REPL="${WS}*[^#]*${WS}*"
	fi
	echo "^${WS}*$1${WS}*=${REPL}(#.*)?${dollar}"
	;;
    esac
}
# Return a list of sed commands to disable an enabled definition for $1,
# addressed by $2.
subst_disable()
{
    case $label in
    *_c|*_h)
	DEFN="#${WS}*define${WS}${WS}*$1"
	REPL=".*[^ 	]"
	# s:#define $1 <repl> /* <com> */:/* #define $1 <repl> */ /* <com> */:
	echo "$2 s:^\(${WS}*\)\(${DEFN}${WS}${REPL}\)\(${WS}*${SC}.*${EC}\):\1${SC} \2 ${EC}\3:"
	# s:#define $1 /* <com> */:/* #define $1 */ /* <com> */:
	echo "$2 s:^\(${WS}*\)\(${DEFN}\)\(${WS}*${SC}.*${EC}\):\1${SC} \2 ${EC}\3:"
	# s:#define $1 <repl>:/* #define $1 <repl> */:
	echo "$2 s:^\(${WS}*\)\(${DEFN}${WS}${REPL}\):\1${SC} \2 ${EC}:"
	# s:#define $1 :/* #define $1 */:
	echo "$2 s:^\(${WS}*\)\(${DEFN}${WS}*\)${dollar}:\1${SC} \2 ${EC}:"
	;;
    *akefile|*akefile.*)
	# s:$1=:# $1=:
	echo "$2 s:^${WS}*$1${WS}*=:# &:"
	;;
    esac
}
# Return an extended regular expression to find a disabled definition for $1
# If a second argument is given, then $1 must be defined as this value.
regex_disabled()
{
    case $label in
    *_c|*_h)
	# /* #define $1 <repl> */
	if [ $# -eq 2 ]; then
	    REPL="${WS}+$2${WS}*"
	else
	    REPL="(${WS}+([^\*]|\*[^/])*${WS}*|)"
	fi
	echo "^${WS}*${SC}${WS}*#${WS}*define${WS}+$1${REPL}${EC}"
	;;
    *akefile|*akefile.*)
	# # $1=<repl>
	echo "^${WS}*#${WS}*$1${WS}*=${WS}*$2"
	;;
    esac
}
# Return a list of sed commands to enable a disabled definition for $1,
# addressed by $2.
subst_enable()
{
    case $label in
    *_c|*_h)
	# s:/* #define $1 <repl> */ /* <com> */:#define $1 <repl> /* <com> */:
	echo "$2 s:^\(${WS}*\)${SC}${WS}*\(.*\) ${EC}\(${WS}*${SC}.*${EC}\):\1\2\3:"
	echo "$2 s:^\(${WS}*\)${SC}${WS}*\(.*\)${EC}\(${WS}*${SC}.*${EC}\):\1\2\3:"
	# s:/* #define $1 <repl> /* <com> */:#define $1 <repl> /* <com> */:
	echo "$2 s:^\(${WS}*\)${SC}${WS}*\(.*\)\(${WS}*${SC}.*${EC}\):\1\2\3:"
	# s:/* #define $1 <repl> */:#define $1 <repl>:
	echo "$2 s:^\(${WS}*\)${SC}${WS}*\(.*\) ${EC}:\1\2:"
	echo "$2 s:^\(${WS}*\)${SC}${WS}*\(.*\)${EC}:\1\2:"
	;;
    *akefile|*akefile.*)
	# s:# $1=:$1=:
	echo "$2 s:^\(${WS}*\)# \(${WS}*$1${WS}*=\):\1\2:"
	echo "$2 s:^\(${WS}*\)#\(${WS}*$1${WS}*=\):\1\2:"
	;;
    esac
}
# Return a list of sed commands to change the value of the existing enabled
# definition for $1 on line $3 to $2.
subst_change()
{
    case $label in
    *_c|*_h)
	DEFN="#${WS}*define${WS}${WS}*$1"
	REPL=".*[^ 	]"
	# s:#define $1 <repl> /* <com> */:#define $1 $2 /* <com> */:
	echo "$3 s:^\(${WS}*\)\(${DEFN}${WS}\)${REPL}\(${WS}*${SC}.*${EC}\):\1\2$2\3:"
	# s:#define $1 /* <com> */:#define $1 $2 /* <com> */:
	echo "$3 s:^\(${WS}*\)\(${DEFN}\)\(${WS}*${SC}.*${EC}\):\1\2$2\3:"
	echo "$3 t"
	# s:#define $1 <repl>:#define $1 $2:
	echo "$3 s:^\(${WS}*\)\(${DEFN}${WS}\)${REPL}:\1\2$2:"
	# s:#define $1:#define $1 $2:
	echo "$3 s:^\(${WS}*\)\(${DEFN}\)${WS}*${dollar}:\1\2	$2:"
	;;
    *akefile|*akefile.*)
	# s:$1=<repl>:$1=$2:
	echo "$3 s:^\(${WS}*$1${WS}*=${WS}*\)[^#]*:\1$2:"
	;;
    esac
}
# Disable any existing defns.
# If context supplied (with undefine -C <context> <symbol>) then only
# the first defn. after the first occurance of <context> in the file will
# be commented out. Note: The line which contains <context> is the first
# line searched.
undefine()
{
    context=""
    if [ "$1" = "-C" ]; then
    	context="$2"
	shift;shift
    fi
    if [ -z "$context" ]; then
	echo config: Disabling all definitions for $1
	subst_disable $1 >> /tmp/$label.$$
    else
	regex=`regex_enabled $1`
	line=`find_line "$context" "$regex" $filename`
	if [ -n "$line" ]; then
	    echo config: Disabling definition for $1 on line $line
	    subst_disable $1 $line >> /tmp/$label.$$
	else
	    regex=`regex_disabled $1`
	    line=`find_line "$context" "$regex" $filename`
	    if [ -z "$line" ]; then
		echo Warning: no existing definition of $1 to disable in $label
		if [ -n "$dflag" ]; then
		    regex=`regex_enabled $1`
		    echo + egrep -n "$regex" $filename
		    egrep -n "$regex" $filename
		    if [ -n "$context" ]; then
			echo + awk "/$context/ {print NR \":\" \$0}" $filename
			awk "/$context/ {print NR \":\" \$0}" $filename
		    fi
		fi
	    fi
	fi
    fi
}
# Enable an existing defn. of $1 for $2 (otherwise no action)
# If no second argument:
#    If context supplied (with define -C <context> <symbol>) then
#    the closest defn. after an occurance of <context> in the file will
#    be enabled (first defn. in a tie-break situation). Note: The line
#    which contains <context> is the first line searched.
#    If no context supplied then the first defn. in the file will be enabled.
define()
{
    context=""
    if [ "$1" = "-C" ]; then
    	context="$2"
	shift;shift
    fi
    regex="`regex_disabled $1 $2`|`regex_enabled $1 $2`"
    line=`find_line "$context" "$regex" $filename`
    if [ -n "$line" ]; then
	echo config: Enabling definition for $1 on line $line
	subst_enable $1 $line >> /tmp/$label.$$
    else
	if [ -n "$2" ]; then
	    echo Warning: no existing definition of $1 as $2 to enable in $label
	else
	    echo Warning: no existing definition of $1 to enable in $label
	fi
	if [ -n "$dflag" ]; then
	    echo + egrep -n "$regex" $filename
	    egrep -n "$regex" $filename
	    if [ -n "$context" ]; then
		echo + awk "/$context/ {print NR \":\" \$0}" $filename
		awk "/$context/ {print NR \":\" \$0}" $filename
	    fi
	fi
    fi
}
# Change an _enabled_ existing defn. for $1 to $2
redefine()
{
    context=""
    if [ "$1" = "-C" ]; then
    	context="$2"
	shift;shift
    fi
    regex=`regex_enabled $1`
    line=`find_line "$context" "$regex" $filename`
    if [ -n "$line" ]; then
	echo config: Changing definition for $1 on line $line to $2
	subst_change "$1" "$2" "$line" >> /tmp/$label.$$
    else
	echo Warning: no existing definition of $1 to redefine in $label
	if [ -n "$dflag" ]; then
	    echo + egrep -n "$regex" $filename
	    egrep -n "$regex" $filename
	    if [ -n "$context" ]; then
		echo + awk "/$context/ {print NR \":\" \$0}" $filename
		awk "/$context/ {print NR \":\" \$0}" $filename
	    fi
	fi
    fi
}
# Remove any disabled defns. for $1
sanitize()
{
    regex=`regex_disabled $1`
    lines=`egrep -n "$regex" $filename | awk -F: '{ print $1; }'`
    for line in lines; do
	echo config: Removing disabled definition for $1 on line $line
	echo "$line d" >> /tmp/$label.$$
    done
}
BEGIN()
{
    :
}
END()
{
    sed -f /tmp/$label.$$ < $filename > /tmp/$label.new.$$
    cmp -s /tmp/$label.new.$$ $filename || cat /tmp/$label.new.$$ > $filename
    rm -f /tmp/$label.$$ /tmp/$label.new.$$
}
configure()
{
    label=$1
    filename=$2
    initialise $label
}
usage()
{
    echo "Usage: config [-d] <template> <toplevel>" >&2
    echo "              [-d] [-D|-U]<SWITCH> ... <toplevel>" >&2
    exit 1
}
if [ $# -lt 2 ]; then usage; fi
toplevel=`echo "$*" | sed 's/.* //'`
switch_mode=0
finished=0
while [ $# -gt 1 -a $finished -eq 0 ]; do
    case $1 in
    -d)
	dflag="-d"
	shift
	;;
    -D*)
	if [ $switch_mode -eq 0 ]; then
	    switch_mode=1
	    configure config_h $toplevel/include/config.h
	    BEGIN
	fi
	switch=`echo $1 | sed 's/-D//'`
	define $switch
	shift
	;;
    -U*)
	if [ $switch_mode -eq 0 ]; then
	    switch_mode=1
	    configure config_h $toplevel/include/config.h
	    BEGIN
	fi
	switch=`echo $1 | sed 's/-U//'`
	undefine $switch
	shift
	;;
    *)	finished=1
	    ;;
    esac
done
if [ $switch_mode -eq 0 ]; then
    if [ $# -ne 2 ]; then usage; fi
    template=$1
    if [ -r ${template}.configure ]; then
	. ${template}.configure
    else
	echo "Unknown template $template" >&2
	exit 1
    fi
else
    if [ $# -ne 1 ]; then usage; fi
    END
fi
exit 0
