1#!/bin/sh
2#
3# Generate sudo_defs_table and associated defines
4#
5# Input should be formatted thusly:
6#
7# var_name
8#	TYPE
9#	description (or NULL)
10#	array of struct def_values if TYPE == T_TUPLE [optional]
11#	*callback [optional]
12
13# Deal with optional -o (output) argument
14if [ "$1" = "-o" ]; then
15    OUTFILE="$2"
16    shift 2
17else
18    OUTFILE=def_data
19fi
20if [ $# -gt 0 ]; then
21    INFILE="$1"
22    shift
23else
24    INFILE=def_data.in
25fi
26if [ $# -gt 0 ]; then
27    echo "usage: $0 [-o output] [input_file]" 1>&2
28fi
29
30${AWK-awk} -f - -v outfile=$OUTFILE $INFILE <<'EOF'
31BEGIN {
32    tuple_values[0] = "never"
33    tuple_keys["never"] = 0
34    ntuples = 1
35    header = outfile ".h"
36    cfile = outfile ".c"
37
38    type_map["T_INT"] = "ival"
39    type_map["T_UINT"] = "uival"
40    type_map["T_STR"] = "str"
41    type_map["T_FLAG"] = "flag"
42    type_map["T_MODE"] = "mode"
43    type_map["T_LIST"] = "list"
44    type_map["T_LOGFAC"] = "ival"
45    type_map["T_LOGPRI"] = "ival"
46    type_map["T_TUPLE"] = "tuple"
47    type_map["T_TIMESPEC"] = "tspec"
48    type_map["T_TIMEOUT"] = "ival"
49}
50{
51    sub(/#.*/, "", $0)
52    if (/^[[:blank:]]*$/) next
53    if (/^[[:alpha:]]/) {
54	# Store previous record and begin new one
55	if (var)
56	    records[count++] = sprintf("%s\n%s\n%s\n%s\n%s\n", var, type, desc, values, callback)
57	var = $1
58	type = ""
59	desc = ""
60	values = ""
61	callback = ""
62	state = 0
63    } else {
64	state++
65	# Strip leading/trailing whitespace
66	gsub(/^[ \t]+|[ \t]+$/, "")
67	if (state == 1) {
68	    # type
69	    type = $1
70	} else if (state == 2) {
71	    # description
72	    if ($0 == "NULL") {
73		desc = "NULL"
74	    } else {
75		# Strip leading and trailing double quote and escape the rest
76		gsub(/^"|"$/, "")
77		gsub(/"/, "\\\"")
78		desc = sprintf("N_(\"%s\")", $0)
79	    }
80	} else if (state == 3 || state == 4) {
81	    if (/^\*/) {
82		callback = substr($0, 2)
83	    } else {
84		if (type ~ /^T_TUPLE/) {
85		    values = $0
86		    # Add to tuple_values as necessary
87		    n = split(values, vals)
88		    for (i = 1; i <= n; i++) {
89			if (!(vals[i] in tuple_keys)) {
90			    tuple_keys[vals[i]] = ntuples
91			    tuple_values[ntuples++] = vals[i]
92			}
93		    }
94		}
95	    }
96	} else {
97	    die("syntax error in input near line " NR)
98	}
99    }
100}
101END {
102    if (var)
103	records[count++] = sprintf("%s\n%s\n%s\n%s\n%s\n", var, type, desc, values, callback)
104
105    print "/* generated file, do not edit */\n" > header
106    print "/* generated file, do not edit */\n" > cfile
107
108    # Print out value arrays
109    for (i = 0; i < count; i++) {
110	split(records[i], fields, "\n")
111	if (fields[4]) {
112	    if (fields[2] !~ /^T_TUPLE/)
113		die("Values list specified for non-tuple " records[1])
114	    printf "static struct def_values def_data_%s[] = {\n", fields[1] > cfile
115	    n = split(fields[4], t)
116	    for (j = 1; j <= n; j++) {
117		printf "    { \"%s\", %s },\n", t[j], t[j] > cfile
118	    }
119	    print "    { NULL, 0 }," > cfile
120	    print "};\n" > cfile
121	}
122    }
123
124    # Print each record
125    print "struct sudo_defs_types sudo_defs_table[] = {\n    {" > cfile
126    for (i = 0; i < count; i++) {
127	print_record(records[i], i)
128    }
129    print "\tNULL, 0, NULL\n    }\n};" > cfile
130
131    # Print out def_tuple
132    print "\nenum def_tuple {" > header
133    for (i = 0; i < ntuples; i++)
134	printf "%s    %s", i ? ",\n" : "", tuple_values[i] > header
135    print "\n};" > header
136}
137
138function die(msg) {
139    print msg > "/dev/stderr"
140    exit 1
141}
142
143function print_record(rec, recnum) {
144    split(rec, fields, "\n")
145    type = fields[2]
146    sub(/\|.*/, "", type)
147    if (!(type in type_map))
148    	die("unknown defaults type " fields[2])
149
150    # each variable gets a macro to access its value
151    defname = "I_" toupper(fields[1])
152    printf "#define %-23s %d\n", defname, recnum > header
153    printf "#define def_%-19s (sudo_defs_table[%s].sd_un.%s)\n",
154	fields[1], defname, type_map[type] > header
155
156    printf "\t\"%s\", %s,\n\t%s,\n", fields[1], fields[2], fields[3] > cfile
157    printf "\t%s,\n", fields[4] ? "def_data_" fields[1] : "NULL" > cfile
158    if (fields[5]) {
159	printf "\t%s,\n", fields[5] > cfile
160    }
161    print "    }, {" > cfile
162}
163EOF
164