#!/bin/sh
# configuration-file reader utility
# Copyright (C) 1999  Henry Spencer.
# 
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# RCSID $Id: _confread,v 1.7 1999/04/10 22:51:24 henry Exp $
#
# Extract configuration info from /etc/ipsec.conf, repackage as assignments
# to shell variables or tab-delimited fields.  Success or failure is reported
# inline, as extra data, due to the vagaries of shell backquote handling.
# In the absence of --varprefix, output is tab-separated fields, like:
#	=	sectionname
#	:	parameter	value
#	!	status (empty for success, else complaint)
# In the presence of (say) "--varprefix IPSEC", output is like:
#	IPSEC_confreadsection="sectionname"
#	IPSECparameter="value"
#	IPSEC_confreadstatus="status" (same empty/complaint convention)
#
# The "--search parametername" option inverts the search:  instead of
# yielding the parameters of the specified name(s), it yields the names
# of sections with parameter <parametername> having (one of) the
# specified value(s).  In this case, --varprefix output is a list of
# names in the <prefix>_confreadnames variable.  Search values with
# white space in them are currently not handled properly.
#
# Typical usage:
# eval `ipsec _confread --varprefix IPSEC --type config setup`
# if test " $IPSEC_confreadstatus" != " "
# then
#	echo "$0: $IPSEC_confreadstatus -- aborting" 2>&1
#	exit 1
# fi

PATH=/usr/local/sbin:/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin
export PATH

config=/etc/ipsec.conf
include=yes
type=conn
fieldfmt=yes
prefix=
search=

for dummy
do
	case "$1" in
	--config)	config="$2" ; shift	;;
	--noinclude)	include=		;;
	--type)		type="$2" ; shift	;;
	--varprefix)	fieldfmt=
			prefix="$2"
			shift			;;
	--search)	search="$2" ; shift	;;
	--)		shift ; break		;;
	-*)		echo "$0: unknown option \`$1'" >&2 ; exit 2	;;
	*)		break			;;
	esac
	shift
done

if test "$include"
then
	ipsec _include --inband $config
else
	cat $config
fi |
awk 'BEGIN {
	type = "'"$type"'"
	names = "'"$*"'"
	prefix = "'"$prefix"'"
	search = "'"$search"'"
	searching = 0
	if (search != "") {
		searching = 1
		searchpat = "^[ \t]+" search "[ \t]*=[ \t]*"
	}
	fieldfmt = 0
	if ("'"$fieldfmt"'" == "yes")
		fieldfmt = 1
	including = 0
	if ("'"$include"'" == "yes")
		including = 1
	filename = "'"$config"'"
	lineno = 0
	originalfilename = filename
	if (fieldfmt)
		bq = eq = "\""
	else
		bq = eq = "\\\""
	failed = 0
	insection = 0
	outputting = 0
	OFS = "\t"
	o_status = "!"
	o_parm = ":"
	o_section = "="
	n = split(names, na, " ")
	for (i = 1; i <= n; i++) {
		if (na[i] in wanted)
			fail("section " bq na[i] eq " requested more than once")
		wanted[na[i]] = 1
		pending[na[i]] = 1
		if (!searching && na[i] !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/)
			fail("invalid section name " bq na[i] eq)
	}
	seen[""] = ""
	outlist = ""
}



function output(code, v1, v2) {
	if (searching && !fieldfmt && code == o_section) {
		if (outlist == "")
			outlist = v1
		else
			outlist = outlist " " v1
		next
	}
	if (fieldfmt) {
		print code, v1, v2
		return
	}
	if (code == o_status)
		print prefix "_confreadstatus=\"" v1 "\""
	else if (code == o_section)
		print prefix "_confreadsection=\"" v1 "\""
	else
		print prefix v1 "=\"" v2 "\""
}
function fail(msg) {
	output(o_status, ("(" filename ", line " lineno ") " msg))
	failed = 1
	exit
}


{
	lineno++
	# lineno is now the number of this line
}
including && $0 ~ /^#[<>:]/ {
	# _include control line
	if ($1 ~ /^#[<>]$/) {
		filename = $2
		lineno = $3 - 1
	} else if ($0 ~ /^#:/) {
		msg = substr($0, 3)
		gsub(/"/, "\\\"", msg)
		fail(msg)
	}
	next
}
$0 !~ /^[ \t]/ {
	# any non-leading-white-space line is a section end
	insection = 0
	outputting = 0
}
{
	# strip trailing comments and space
	sub(/[ \t]+(#.*)?$/, "")
}
$0 == "" || $0 ~ /^#/ {
	# empty lines and comments are ignored
	next
}
$0 !~ /^[ \t]/ && $1 == type && searching {
	# section header, during search
	insection = 1
	sectionname = $2
	next
}
$0 !~ /^[ \t]/ && ($1 != type || !($2 in wanted)) {
	# section header, but not one we want
	insection = 1
	next
}
$0 !~ /^[ \t]/ && $1 == type && ($2 in wanted) {
	# one of our wanted section headers
	if (!($2 in pending))
		fail("duplicate " type " section " bq $2 eq)
	delete pending[$2]
	tag = bq type " " $2 eq
	outputting = 1
	insection = 1
	output(o_section, $2)
	next
}
$0 !~ /^[ \t]/ {
	fail("internal error, supposedly cannot happen")
}
!insection {
	# starts with white space but not in a section... oops
	fail("parameter is not within a section")
}
searching && $0 ~ searchpat {
	match($0, searchpat)
	rest = substr($0, RLENGTH+1)
	if (rest ~ /^"/)
		rest = substr(rest, 2, length(rest)-2)
	if (rest in wanted)
		output(o_section, sectionname)
	next
}
!outputting {
	# uninteresting line
	next
}
$0 ~ /"/ && $0 !~ /^[^=]+=[ \t]*"[^"]*"$/ {
	fail("mismatched quotes in parameter value")
}
$0 !~ /^[ \t]+[a-zA-Z][a-zA-Z0-9_-]*[ \t]*=/ {
	fail("syntax error or illegal parameter name")
}
{
	sub(/^[ \t]+/, "")		# get rid of leading white space
	sub(/[ \t]*=[ \t]*/, "=")	# and embedded white space
}
$0 ~ /^also=/ {
	sub(/^also=/, "")
	if ($0 in wanted)
		fail("section " bq $0 eq " requested more than once")
	wanted[$0] = 1
	pending[$0] = 1
	if ($0 !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/)
		fail("invalid section name " bq $0 eq)
	next
}
{
	equal = match($0, /[=]/)
	name = substr($0, 1, equal-1)
	value = substr($0, equal+1)
	if (value ~ /^"/)
		value = substr(value, 2, length(value)-2)
	if (name in seen)
		fail("duplicated parameter " bq name eq)
	seen[name] = 1
	output(o_parm, name, value)
}
END {
	if (failed)
		exit 1
	filename = originalfilename
	unseen = ""
	for (i in pending)
		unseen = unseen " " i
	if (!searching && unseen != "")
		fail("did not find " type " section(s) " bq substr(unseen, 2) eq)
	if (searching && !fieldfmt)
		output(o_parm, "_confreadnames", outlist)
	output(o_status, "")
}'
