#!/bin/sh
#
#  shell script to generate a sendmail chn-tbl.m4 file containing all
#  the routing information for this host from a set of channel tables.
#
if [ $# -le 1 ]
then	echo	"usage: $0 [-o outfile] -chn file..."
	exit
fi

#
#  first, create the awk programs that compile the channel tables
#

rm -f $$.*
outf=chn-tbl.m4

#
#  Standard format channel compiler
#
cat > $$.std.awk <<'EOF'
NR==1	{
	MINCLSIZE = 3
	c = 1
	mailer = substr(mailer, 2)
	cl_outl = prefix "classl"
	cl_outf = prefix "classf"
	printf "\n# %s (%s channel)\n", file, mailer
	}

NF==1	{ $2 = substr($1, 1, index($1,".")-1) }		# shorthand

/%s/	{
	# process wildcards
	lhs = sprintf($1,"$+","$+","$+")
	rhs = sprintf($1,"$2","$3","$4")
	dest = sprintf($2,"$2","$3","$4")
	printf "R$+@%s\t\t$@<$1@%s>%s.%s\n", lhs, rhs, dest, mailer >> S16
	break
	}

	{
	i = index($1, ".")
	subdom = substr($1,1,i-1)
	restdom = substr($1,i+1)

	if (subdom == $2 && restdom != "")
		link["$2/" restdom] = link["$2/" restdom] " " subdom
	else
		link[ $2 "/" restdom ] = link[ $2 "/" restdom ] " " subdom
	}

END	{
	for (i in link)
	{	j = index(i,"/")
		dest = substr(i, 1, j-1)
		rdom = substr(i, j+1)
		n = split(link[i], sub, " ")
		if (n >= MINCLSIZE && c <= length(classes))
		{	C = substr(classes, c, 1)
			c++
			printf "C%s%s\n", C , link[i]	>> cl_outf
			printf "R$+@$=%s.%s\t\t$@<$1@$2.%s>%s.%s\n", \
				C, rdom, rdom, dest, mailer
		}
		else
		{	host = dest
			for (j=1; j<=n; j++)
			{	if (dest == "$2") host = sub[j]
				printf "R$+@%s.%s\t\t$@<$1@%s.%s>%s.%s\n", \
					sub[j], rdom, sub[j], rdom, host, mailer
			}
		}
	}

	print "" >>cl_outf
	print substr(classes, c) > cl_outl
	}
EOF

#
#   Janet channel compiler
#
cat > $$.janet.awk <<'EOF'
NR==1	{
	printf "\n# %s (Janet channel)\n", file
	swap1 = sprintf("R$+@$+\t\t$:$>9$1@$2\t\tconvert to NRS style")
	swap2 = sprintf("R$+@$+\t\t$:$>9$1@$2\t\tconvert back to 822 style")
	}

NF==1	{ $2 = $1 }		# shorthand

/%s/	{
	if (wcnt++ == 0)  print swap1 >> S16

	# process wildcards
	lhs = sprintf($1,"$+","$+","$+")
	rhs = sprintf($1,"$2","$3","$4")
	dest = sprintf($2,"$2","$3","$4")
	if (rhs == dest)
		jntaddr = "$1@" dest
	else
		jntaddr = "$1%" rhs "@" dest
	printf "R$+@%s\t\t$@<%s>%s.janet\n", lhs, jntaddr, dest >> S16
	break
	}

	{
	if (cnt++ == 0)  print swap1

	if ($1 == $2)
		jntaddr = "$1@" $2
	else
		jntaddr = "$1%" $1 "@" $2
	printf "R$+@%s\t\t$@<%s>%s.janet\n", $1, jntaddr, $2
	}

END	{
	if (cnt > 0) print swap2
	if (wcnt > 0) print swap2 >> S16
	}
EOF

#
#  UUCP channel compiler
#
cat > $$.uucp.awk <<'EOF'
NR==1	{
	MINCLSIZE = 3
	c = 1
	link[""]=""
	cl_outl = prefix "classl"
	cl_outf = prefix "classf"
	printf "\n# %s (UUCP channel)\n", file
	printf "R$+@$+\t\t\t$:$>8$1@$2\t\tconvert to uucp addresses\n"
	}

	{
	i = index($1, ".")
	subdom = substr($1,1,i-1)
	restdom = substr($1,i+1)
	udest = subdom "!%s"

	if (NF == 1) $2 = udest		# shorthand

	if (udest == $2)
		link[ "/" restdom ] = link[ "/" restdom ] " " subdom
	else
	{	pathl = length($2) - length(udest)
		if ( substr($2, pathl) == ("!" udest) )
		{	path = substr($2, 1, pathl-1)
			link[ path "/" restdom ] = link[ path "/" restdom ] " " subdom
		}
		else
		{	i = index($2, "!")
			host = substr($2,1,i-1)
			addr = sprintf( substr($2,i+1), "$1")
			printf "R%s!$+\t\t$@<%s>%s.uucp\n", $1, addr, host
		}
	}
	}

END	{
	for (i in link)
	{	j = index(i, "/")
		path = substr(i, 1, j-1)
		rdom = substr(i, j+1)
		p = index(path, "!")
		n = split(link[i], sub, " ")
		if (n >= MINCLSIZE && c <= length(classes))
		{	C = substr(classes, c, 1)
			c++
			printf "C%s%s\n", C, link[i]	>>cl_outf
			if (path == "")
			{	host = "$1"
				addr = ""
			}
			else if (p == 0)
			{	host = path
				addr = "$1!"
			}
			else
			{	host = substr(path,1,p-1)
				addr = substr(path,p+1) "!$1!"
			}
			printf "R$=%s.%s!$+\t\t$@<%s$2>%s.uucp\n", C, rdom, addr, host
		}
		else
		{	for (j=1; j<=n; j++)
			{	if (path == "")
				{	host = sub[j]
					addr = ""
				}
				else if (p == 0)
				{	host = path
					addr = sub[j] "!"
				}
				else
				{	host = substr(path,1,p-1)
					addr = substr(path,p+1) "!" sub[j] "!"
				}
				printf "R%s.%s!$+\t\t$@<%s$1>%s.uucp\n", \
						sub[j], rdom, addr, host
			}
		}
	}
	print "" >>cl_outf
	print substr(classes, c) > cl_outl
	print "\nR$+!$+\t\t\t$:$>7$1!$2\t\tconvert back to 822 style\n"
	}
EOF

#
#  News channel compiler
#
cat > $$.news.awk << 'EOF'
NR==1	{ printf "\n# %s (news channel)\n", file }
	{
	lhs = sprintf($1,"$+","$+","$+")
	rhs = sprintf($1,"$1","$2","$3")
	dest = sprintf($2,"$1","$2","$3")

	if (NF == 1)
		printf "R%s\t\t$@<%s>news\n", lhs, rhs
	else
		printf "R%s\t\t$@$>3 %s@%s\n", lhs, rhs, dest
	}
EOF


#
#  Top level domain relay compiler
#
cat > $$.top.awk <<'EOF'
NR==1	{
	MINCLSIZE = 3
	c = 1
	cl_outl = prefix "classl"
	cl_outf = prefix "classf"
	relay[""] = ""
	}
$1=="DEFAULT" || $1=="$=T"	{ default = $2 ; break }
$1=="ALL" || $1=="$+"		{ all = $2 ; break }

	{ relay[$2] = relay[$2] " " $1 }

END	{
	for (i in relay)
	{	n = split(relay[i], doms, " ")
		if (n >= MINCLSIZE && c <= length(classes))
		{	C = substr(classes, c, 1)
			c++
			printf "C%s%s\n", C, relay[i]	>> cl_outf
			printf "R$+@$+.$=%s\t\t$@$>16$1%%$2.$3@%s\n", C, i
		}
		else
		{	for (j=1; j<=n; j++)
				printf "R$+@$+.%s\t\t$@$>16$1%%$2.%s@%s\n", \
					doms[j], doms[j], i
		}
	}
	if (default)	printf "R$+@$+.$=T\t\t$@$>16$1%%$2.$3@%s\n", default
	if (all)	printf "R$+@$+\t\t$@$>16$1%%$2@%s\n", all

	print "" >>cl_outf
	print substr(classes, c) > cl_outl
	}
EOF

#---------------------------------------

#
#  awk scripts for compiling to ida databases
#
cat > $$.std.ida.awk <<'EOF'
NR==1	{
	mailer = substr(mailer, 2)
	printf "\n# %s (%s channel)\n", file, mailer
	printf "R$+@$+\t\t$:$(%s $2 $@ $1 $: $1@$2 $)\t\tlookup database\n",key
	printf "R<$+>$+.%s\t\t$@<$1>$2.%s\t\tfound a match\n", mailer,mailer
	}
NF==1	{ $2 = substr($1, 1, index($1,".")-1) }		# shorthand
	{ printf "%s\t<%%s@%s>%s.%s\n", $1, $1, $2, mailer > dbmf }
EOF

cat > $$.janet.ida.awk <<'EOF'
NR==1   {
	printf "\n# %s (Janet channel)\n", file
	printf "R$+@$+\t\t$:$>9$1@$2\t\tconvert to NRS style\n"
	printf "R$+@$+\t\t$:$(%s $2 $@ $1 $: $1@$2 $)\t\tlookup database\n",key
	printf "R<$+>$+.janet\t\t$@<$1>$2.janet\t\tfound a match\n"
	}
NF==1	{ $2 = $1 }		# shorthand
	{
	if ($1 == $2)
		jntaddr = "%s@" $2
	else
		jntaddr = "%s%%" $1 "@" $2

	printf "%s\t<%s>%s.janet\n", $1, jntaddr, $2 > dbmf
	}
END     { printf "\nR$+@$+\t\t$:$>9$1@$2\t\tconvert back to 822 style\n" }
EOF


#---------------------------------------

#
#  compile the tables; output goes into various files
#

strip="sed -e /#/s/#.*// -e /^\$/d"
classes="NOPQRSUVWXYZ"
dbkeys="BCDEFHIJ"
cp /dev/null $$.classf

while [ $# -gt 0 ]
do
	case $1 in

	-o)	outf=$2
		;;

	-local)	$strip -e 's/^LHOST\./$=w./' $2 | awk '
		{ printf "R$+@%s\t\t$@$1@$J\n", $1	   >> S15
		  printf "R$+@%s\t\t$@<$1@$J>error\n", $1  >> S17
		}'  S15=$$.S15  S17=$$.S17 -
		;;

	-ether|-tcp|-csnet|-decnet|-xerox)
		$strip $2 | awk -f $$.std.awk mailer=$1 file=$2 prefix=$$. \
			    classes=$classes S16=$$.S16 - >> $$.S17
		classes=`cat $$.classl`
		;;

	-janet)	$strip $2 | awk -f $$.janet.awk file=$2 S16=$$.S16 - >> $$.S17
		;;

	-uucp)	$strip $2 | awk -f $$.uucp.awk file=$2 prefix=$$. \
			    classes=$classes - >> $$.S17
		classes=`cat $$.classl`
		;;

	-news)	$strip $2 | awk -f $$.news.awk file=$2 - >> $$.S18
		;;

	-top)	$strip $2 | awk -f $$.top.awk prefix=$$. \
			    classes=$classes - >> $$.S16
		classes=`cat $$.classl`
		;;

	-ida)	shift
		key=`expr substr $dbkeys 1 1`
		dbkeys=`expr substr $dbkeys 2 26`
		dbmfile=`basename $2 .chn`

		case $1 in
		-janet)	$strip $2 | awk -f $$.janet.ida.awk file=$2 \
				key=$key dbmf=$dbmfile.ida - >> $$.S17
			;;

		*)	$strip $2 | awk -f $$.std.ida.awk mailer=$1 file=$2 \
				key=$key dbmf=$dbmfile.ida - >> $$.S17
			;;
		esac

		dbm load $dbmfile.ida $dbmfile
		echo "OK$key`pwd`/$dbmfile" >> $$.S17
		rm $dbmfile.ida
		;;

	*)	echo "usage: $0 [-o file] -chn file..."
		rm -f $$.*
		exit 1
		;;
	esac
	shift
	shift
done

#---------------------------------------

#
#  now glue all the bits together
#

#
#  The header
#
cat > $outf <<EOF
###############################################################################
#####
#####	@(#)$outf	UK-2.1 sendmail configuration		18/11/88
#####
#####	The mailer selection and routing
#####
###############################################################################

# classes
EOF

#
#  the classes
#
while read class group
do
	echo "$group" | fmt | sed "s/^/$class/"
done < $$.classf >> $outf

#
#  the rules
#
cat - $$.S15 >> $outf <<'EOF'

###########################################
#  Ruleset 15  --  local domain handling  #
###########################################

#
#  This rule converts various forms of local domain names
#  into the standard ($J) name
#

S15
ifdef(`MULTIHOST',
`R$+@$=w.$J		$@$1@$J			host specific name')
EOF

cat - $$.S16 >> $outf <<'EOF'

#############################
#  Ruleset 16  --  routing  #
#############################

#
#  This rule trys to find an "<address>host.network" triple for a given
#  domain address.  Given a domain address "u@a.b.c.d", it calls the
#  channel matcher with u@a.b.c.d, and if nothing matched, it calls
#  the channel matcher again, with "u%a.b.c.d@b.c.d" and  "u%a.b.c.d@c.d".
#  If there's still no match, then the top level domain relaying is done
#  e.g. "u%a.b.c.d@d" -> "u%a.b.c.d@x.y.z" and the rule is retried.
#

S16
R$+@$+			$:$>17$1@$2		initial routing
R<$+>$+			$@<$1>$2		success, return triple

#  initial match failed, retry with successively higher level domains
R<$+@$+>		$:<$1%$2@$2>		u@a.b.c -> u%a.b.c@a.b.c
R<$+@$-.$+>		$>17$1@$3		retry routing
R<$+>$+			$@<$1>$2		success, return triple

#  match failed, attempt to match with "general" rules (wildcards);
#  if this fails then try matching top level domain to find relay domain
R<$+@$->		$:$>4$1			restore original address

EOF

cat - $$.S17 >> $outf <<'EOF'

######################################
#  Ruleset 17  --  channel matching  #
######################################

#
#  This rule takes a full domain address and trys to match it with a
#  domain given on the LHS of a rule.
#  If one matches, the "<address>host.network" triple given on the RHS
#  is returned.  Else  "<address>" is returned.
#

S17
R$+@$J			$@<$1@$J>error		unknown local subdomain error
EOF

#
#  complete ruleset 17
#
cat >> $outf <<'EOF'

R$+			$@<$1>			no match, return "<address>"
EOF

if [ -f $$.S18 ]
then	cat - $$.S18 >> $outf <<'EOF'

#############################################
#  Ruleset 18  --  newsgroup name matching  #
#############################################

#
#  This rule takes a local address and trys to match it with a
#  newsgroup name given on the LHS of a rule.
#  If one matches, the "<name>news" double given on the RHS
#  is returned. If a relayed newsgroup is found, ruleset 3 is
#  re-entered with name@relay. Else  "address" is returned.
#

S18
# send non-local addresses straight back
R$+@$+          	$@$1@$2         	not a local address

EOF
fi

rm -f $$.*
exit
