#!/bin/sh
#
# Generate sudo_defs_table and associated defines
#
# Input should be formatted thusly:
#
# var_name
#	TYPE
#	description (or NULL)
#	array of struct def_values if TYPE == T_TUPLE [optional]
#	*callback [optional]

# Deal with optional -o (output) argument
if [ "$1" = "-o" ]; then
    OUTFILE="$2"
    shift 2
else
    OUTFILE=def_data
fi
if [ $# -gt 0 ]; then
    INFILE="$1"
    shift
else
    INFILE=def_data.in
fi
if [ $# -gt 0 ]; then
    echo "usage: $0 [-o output] [input_file]" 1>&2
fi

${AWK-awk} -f - -v outfile=$OUTFILE $INFILE <<'EOF'
BEGIN {
    tuple_values[0] = "never"
    tuple_keys["never"] = 0
    ntuples = 1
    header = outfile ".h"
    cfile = outfile ".c"

    type_map["T_INT"] = "ival"
    type_map["T_UINT"] = "uival"
    type_map["T_STR"] = "str"
    type_map["T_FLAG"] = "flag"
    type_map["T_MODE"] = "mode"
    type_map["T_LIST"] = "list"
    type_map["T_LOGFAC"] = "ival"
    type_map["T_LOGPRI"] = "ival"
    type_map["T_TUPLE"] = "tuple"
    type_map["T_TIMESPEC"] = "tspec"
    type_map["T_TIMEOUT"] = "ival"
}
{
    sub(/#.*/, "", $0)
    if (/^[[:blank:]]*$/) next
    if (/^[[:alpha:]]/) {
	# Store previous record and begin new one
	if (var)
	    records[count++] = sprintf("%s\n%s\n%s\n%s\n%s\n", var, type, desc, values, callback)
	var = $1
	type = ""
	desc = ""
	values = ""
	callback = ""
	state = 0
    } else {
	state++
	# Strip leading/trailing whitespace
	gsub(/^[ \t]+|[ \t]+$/, "")
	if (state == 1) {
	    # type
	    type = $1
	} else if (state == 2) {
	    # description
	    if ($0 == "NULL") {
		desc = "NULL"
	    } else {
		# Strip leading and trailing double quote and escape the rest
		gsub(/^"|"$/, "")
		gsub(/"/, "\\\"")
		desc = sprintf("N_(\"%s\")", $0)
	    }
	} else if (state == 3 || state == 4) {
	    if (/^\*/) {
		callback = substr($0, 2)
	    } else {
		if (type ~ /^T_TUPLE/) {
		    values = $0
		    # Add to tuple_values as necessary
		    n = split(values, vals)
		    for (i = 1; i <= n; i++) {
			if (!(vals[i] in tuple_keys)) {
			    tuple_keys[vals[i]] = ntuples
			    tuple_values[ntuples++] = vals[i]
			}
		    }
		}
	    }
	} else {
	    die("syntax error in input near line " NR)
	}
    }
}
END {
    if (var)
	records[count++] = sprintf("%s\n%s\n%s\n%s\n%s\n", var, type, desc, values, callback)

    print "/* generated file, do not edit */\n" > header
    print "/* generated file, do not edit */\n" > cfile

    # Print out value arrays
    for (i = 0; i < count; i++) {
	split(records[i], fields, "\n")
	if (fields[4]) {
	    if (fields[2] !~ /^T_TUPLE/)
		die("Values list specified for non-tuple " records[1])
	    printf "static struct def_values def_data_%s[] = {\n", fields[1] > cfile
	    n = split(fields[4], t)
	    for (j = 1; j <= n; j++) {
		printf "    { \"%s\", %s },\n", t[j], t[j] > cfile
	    }
	    print "    { NULL, 0 }," > cfile
	    print "};\n" > cfile
	}
    }

    # Print each record
    print "struct sudo_defs_types sudo_defs_table[] = {\n    {" > cfile
    for (i = 0; i < count; i++) {
	print_record(records[i], i)
    }
    print "\tNULL, 0, NULL\n    }\n};" > cfile

    # Print out def_tuple
    print "\nenum def_tuple {" > header
    for (i = 0; i < ntuples; i++)
	printf "%s    %s", i ? ",\n" : "", tuple_values[i] > header
    print "\n};" > header
}

function die(msg) {
    print msg > "/dev/stderr"
    exit 1
}

function print_record(rec, recnum) {
    split(rec, fields, "\n")
    type = fields[2]
    sub(/\|.*/, "", type)
    if (!(type in type_map))
    	die("unknown defaults type: " fields[2])

    # each variable gets a macro to access its value
    defname = "I_" toupper(fields[1])
    printf "#define %-23s %d\n", defname, recnum > header
    printf "#define def_%-19s (sudo_defs_table[%s].sd_un.%s)\n",
	fields[1], defname, type_map[type] > header

    printf "\t\"%s\", %s,\n\t%s,\n", fields[1], fields[2], fields[3] > cfile
    printf "\t%s,\n", fields[4] ? "def_data_" fields[1] : "NULL" > cfile
    if (fields[5]) {
	printf "\t%s,\n", fields[5] > cfile
    }
    print "    }, {" > cfile
}
EOF
