1#
2# Copyright (c) 1996, 2020 Oracle and/or its affiliates.  All rights reserved.
3#
4# See the file LICENSE for license information.
5#
6# $Id$
7#
8
9BEGIN {
10	if (source_file == "" || header_file == "") {
11	    print "Usage: gen_msg.awk requires these variables to be set:";
12	    print "\theader_file\t-- the message #include file being created";
13	    print "\tsource_file\t-- the message source file being created";
14	    exit;
15	}
16	CFILE=source_file;
17	HFILE=header_file;
18	maxmsg = 0;
19}
20/^[	]*PREFIX/ {
21	prefix = $2;
22
23	# Start .c files.
24	printf("/* Do not edit: automatically built by gen_msg.awk. */\n\n") \
25	    > CFILE
26	printf("#include \"db_config.h\"\n\n") >> CFILE
27
28	# Start .h file, make the entire file conditional.
29	printf("/* Do not edit: automatically built by gen_msg.awk. */\n\n") \
30	    > HFILE
31	printf("#ifndef\t%s_AUTOMSG_H\n#define\t%s_AUTOMSG_H\n\n", prefix, prefix) \
32	    >> HFILE;
33	printf("/*\n") >> HFILE;
34	printf(" * Message sizes are simply the sum of field sizes (not\n") \
35	    >> HFILE;
36	printf(" * counting variable size parts, when DBTs are present),\n") \
37	    >> HFILE;
38	printf(" * and may be different from struct sizes due to padding.\n") \
39	    >> HFILE;
40	printf(" */\n") >> HFILE;
41}
42/^[	]*INCLUDE/ {
43	for (i = 2; i < NF; i++)
44		printf("%s ", $i) >> CFILE;
45	printf("%s\n", $i) >> CFILE;
46}
47/^[	]*BEGIN_MSG/ {
48	if (in_begin) {
49		print "Invalid format: missing END statement";
50		exit;
51	}
52	in_begin = 1;
53	nvars = 0;
54	thismsg = $2;
55	for (i = 2; i<= NF; i++) {
56		if ($i == "alloc")
57			alloc = 1;
58		else if ($i == "check_length")
59			check_length = 1;
60	}
61
62	base_name = sprintf("%s_%s", prefix, thismsg);
63	typedef_name = sprintf("%s_args", base_name);
64	msg_size_name = toupper(sprintf("%s_SIZE", base_name));
65	max_name = toupper(sprintf("%s_MAXMSG_SIZE", prefix));
66}
67/^[	]*ARG/ {
68	vars[nvars] = $2;
69	types[nvars] = $3;
70	if (types[nvars] == "DBT")
71		has_dbt = 1;
72	nvars++;
73}
74/^[	]*END/ {
75	if (!in_begin) {
76		print "Invalid format: missing BEGIN statement";
77		exit;
78	}
79	if (nvars == 0) {
80		printf("%s needs at least one field\n", thismsg);
81		exit;
82	}
83
84	sum = 0;
85	for (i = 0; i < nvars; i++)
86		sum += type_length(types[i]);
87	printf("#define\t%s\t%d\n", msg_size_name, sum) >> HFILE;
88	if (sum > maxmsg)
89		maxmsg = sum;
90
91	printf("typedef struct _%s {\n", typedef_name) >> HFILE;
92	for (i = 0; i < nvars; i++) {
93		if (types[i] == "DB_LSN" || types[i] == "DBT")
94			printf("\t%s\t\t%s;\n", types[i], vars[i]) >> HFILE;
95		else
96			printf("\t%s\t%s;\n", types[i], vars[i]) >> HFILE;
97	}
98	printf("} %s;\n\n", typedef_name) >> HFILE;
99
100	emit_marshal();
101	emit_unmarshal();
102
103	# Reinitialize variables for next time.
104	in_begin = 0;
105	alloc = 0;
106	check_length = 0;
107	has_dbt = 0;
108}
109END {
110	# End the conditional for the HFILE
111	printf("#define\t%s\t%d\n", max_name, maxmsg) >> HFILE;
112	printf("#endif\n") >> HFILE;
113}
114
115# Length of fixed part of message.  Does not count variable-length data portion
116# of DBT.
117#
118function type_length(type)
119{
120	if (type == "DB_LSN" || type == "u_int64_t")
121		return (8);
122	if (type == "DBT" || type == "u_int32_t" || type == "db_pgno_t")
123		return (4);
124	if (type == "u_int16_t")
125		return (2);
126	if (type == "u_int8_t")
127		return (1);
128	printf("unknown field type: %s", type);
129	exit(1);
130}
131
132function emit_marshal()
133{
134	pi = 1;
135	if (check_length)
136		p[pi++] = "int ";
137	else
138		p[pi++] = "void ";
139	function_name = sprintf("%s_marshal", base_name);
140	p[pi++] = function_name;
141	p[pi++] = " __P((ENV *, ";
142	p[pi++] = sprintf("%s *, u_int8_t *", typedef_name);
143	if (check_length)
144		p[pi++] = ", size_t, size_t *";
145	p[pi++] = "));";
146	proto_format(p, CFILE);
147
148	if (check_length)
149		printf("int\n") >> CFILE;
150	else
151		printf("void\n") >> CFILE;
152	printf("%s(env", function_name) >> CFILE;
153	printf(", argp, bp") >> CFILE;
154	if (check_length)
155		printf(", max, lenp") >> CFILE;
156	printf(")\n") >> CFILE;
157
158	printf("\tENV *env;\n") >> CFILE;
159	printf("\t%s *argp;\n", typedef_name) >> CFILE;
160	printf("\tu_int8_t *bp;\n") >> CFILE;
161	if (check_length)
162		printf("\tsize_t *lenp, max;\n") >> CFILE;
163	printf("{\n") >> CFILE;
164
165	if (check_length) {
166		printf("\tu_int8_t *start;\n\n") >> CFILE;
167		printf("\tif (max < %s", msg_size_name) >> CFILE;
168		for (i = 0; i < nvars; i++)
169			if (types[i] == "DBT")
170				printf("\n\t    + (size_t)argp->%s.size", \
171                                    vars[i]) >> CFILE;
172		# add in dbt sizes
173		printf(")\n") >> CFILE;
174		printf("\t\treturn (ENOMEM);\n") >> CFILE;
175		printf("\tstart = bp;\n\n") >> CFILE;
176	}
177
178	for (i = 0; i < nvars; i++) {
179		if (types[i] == "u_int32_t" || types[i] == "db_pgno_t") {
180			printf("\tDB_HTONL_COPYOUT(env, bp, argp->%s);\n", \
181                            vars[i]) >> CFILE;
182		} else if (types[i] == "u_int16_t") {
183			printf("\tDB_HTONS_COPYOUT(env, bp, argp->%s);\n", \
184                            vars[i]) >> CFILE;
185		} else if (types[i] == "u_int8_t") {
186				printf(\
187    "\t*bp++ = argp->%s;\n", vars[i]) >> CFILE;
188		} else if (types[i] == "DB_LSN") {
189			printf("\tDB_HTONL_COPYOUT(env, bp, argp->%s.file);\n",\
190                            vars[i]) >> CFILE;
191			printf( \
192                            "\tDB_HTONL_COPYOUT(env, bp, argp->%s.offset);\n", \
193                            vars[i]) >> CFILE;
194		} else if (types[i] == "DBT") {
195			printf("\tDB_HTONL_COPYOUT(env, bp, argp->%s.size);\n",\
196                            vars[i]) >> CFILE;
197			printf("\tif (argp->%s.size > 0) {\n", vars[i]) \
198                            >> CFILE;
199			printf( \
200                            "\t\tmemcpy(bp, argp->%s.data, argp->%s.size);\n", \
201                            vars[i], vars[i]) >> CFILE;
202			printf("\t\tbp += argp->%s.size;\n", vars[i]) >> CFILE;
203			printf("\t}\n") >> CFILE;
204		} else if (types[i] == "u_int64_t") {
205			printf("\tDB_HTONLL_COPYOUT(env, bp, argp->%s);\n", \
206                            vars[i]) >> CFILE;
207		} else {
208			printf("unknown field type: %s", types[i]);
209			exit(1);
210		}
211	}
212
213	if (check_length) {
214		printf("\n\t*lenp = (size_t)(bp - start);\n") >> CFILE;
215		printf("\treturn (0);\n") >> CFILE;
216	}
217	printf("}\n\n") >> CFILE;
218}
219
220function emit_unmarshal()
221{
222	pi = 1;
223	p[pi++] = "int ";
224	function_name = sprintf("%s_unmarshal", base_name);
225	p[pi++] = function_name;
226	p[pi++] = " __P((ENV *, ";
227	if (alloc)
228		p[pi++] = sprintf("%s **, u_int8_t *, ", typedef_name);
229	else
230		p[pi++] = sprintf("%s *, u_int8_t *, ", typedef_name);
231	p[pi++] = sprintf("size_t, u_int8_t **));");
232	proto_format(p, CFILE);
233
234	printf("int\n") >> CFILE;
235	if (alloc)
236		arg_name = "argpp";
237	else
238		arg_name = "argp";
239	printf("%s(env, ", function_name) >> CFILE;
240	printf("%s, bp, ", arg_name) >> CFILE;
241	printf("max, nextp)\n") >> CFILE;
242	printf("\tENV *env;\n") >> CFILE;
243	if (alloc)
244		printf("\t%s **argpp;\n", typedef_name) >> CFILE;
245	else
246		printf("\t%s *argp;\n", typedef_name) >> CFILE;
247	printf("\tu_int8_t *bp;\n") >> CFILE;
248	printf("\tsize_t max;\n") >> CFILE;
249	printf("\tu_int8_t **nextp;\n") >> CFILE;
250	printf("{\n") >> CFILE;
251	has_locals = 0;
252	if (has_dbt) {
253		printf("\tsize_t needed;\n") >> CFILE;
254		has_locals = 1;
255	}
256	if (alloc) {
257		printf("\t%s *argp;\n", typedef_name) >> CFILE;
258		printf("\tint ret;\n") >> CFILE;
259		has_locals = 1;
260	}
261	if (has_locals)
262		printf("\n") >> CFILE;
263
264	# Check that input byte buffer is long enough.
265	#
266	if (has_dbt) {
267		printf("\tneeded = %s;\n", msg_size_name) >> CFILE;
268		printf("\tif (max < needed)\n") >> CFILE;
269	} else
270		printf("\tif (max < %s)\n", msg_size_name) >> CFILE;
271	printf("\t\tgoto too_few;\n") >> CFILE;
272
273	if (alloc) {
274		printf( \
275              "\tif ((ret = __os_malloc(env, sizeof(*argp), &argp)) != 0)\n") \
276		    >> CFILE;
277		printf("\t\treturn (ret);\n\n") >> CFILE;
278	}
279
280	for (i = 0; i < nvars; i++) {
281		if (types[i] == "u_int32_t" || types[i] == "db_pgno_t") {
282			printf("\tDB_NTOHL_COPYIN(env, argp->%s, bp);\n", \
283                            vars[i]) >> CFILE;
284		} else if (types[i] == "u_int16_t") {
285			printf("\tDB_NTOHS_COPYIN(env, argp->%s, bp);\n", \
286                            vars[i]) >> CFILE;
287		} else if (types[i] == "u_int8_t") {
288				printf(\
289    "\targp->%s = *bp++;\n", vars[i]) >> CFILE;
290		} else if (types[i] == "DB_LSN") {
291			printf("\tDB_NTOHL_COPYIN(env, argp->%s.file, bp);\n", \
292                            vars[i]) >> CFILE;
293			printf( \
294                            "\tDB_NTOHL_COPYIN(env, argp->%s.offset, bp);\n", \
295                            vars[i]) >> CFILE;
296		} else if (types[i] == "DBT") {
297			printf("\tDB_NTOHL_COPYIN(env, argp->%s.size, bp);\n", \
298                            vars[i]) >> CFILE;
299			printf("\tif (argp->%s.size == 0)\n", vars[i]) >> CFILE;
300			printf("\t\targp->%s.data = NULL;\n", vars[i]) >> CFILE;
301			printf("\telse\n") >> CFILE;
302			printf("\t\targp->%s.data = bp;\n", vars[i]) >> CFILE;
303			printf("\tneeded += (size_t)argp->%s.size;\n", \
304                            vars[i]) >> CFILE;
305			printf("\tif (max < needed)\n") >> CFILE;
306			printf("\t\tgoto too_few;\n") >> CFILE;
307			printf("\tbp += argp->%s.size;\n", vars[i]) >> CFILE;
308		} else if (types[i] == "u_int64_t") {
309			printf("\tDB_NTOHLL_COPYIN(env, argp->%s, bp);\n", \
310                            vars[i]) >> CFILE;
311		} else {
312			printf("unknown field type: %s", types[i]);
313			exit(1);
314		}
315	}
316
317	printf("\n\tif (nextp != NULL)\n") >> CFILE;
318	printf("\t\t*nextp = bp;\n") >> CFILE;
319	if (alloc) {
320		printf("\t*argpp = argp;\n") >> CFILE;
321	}
322	printf("\treturn (0);\n\n") >> CFILE;
323
324	printf("too_few:\n") >> CFILE;
325	printf("\t__db_errx(env, DB_STR_A(\"3675\",\n") >> CFILE;
326	printf( \
327	 "\t    \"Not enough input bytes to fill a %%s message\",\n") >> CFILE;
328	printf("\t    \"%%s\"), \"%s\");\n", base_name) >> CFILE;
329	printf("\treturn (EINVAL);\n") >> CFILE;
330	printf("}\n\n") >> CFILE;
331}
332
333# proto_format --
334#	Pretty-print a function prototype.
335function proto_format(p, fp)
336{
337	printf("/*\n") >> fp;
338
339	s = "";
340	for (i = 1; i in p; ++i)
341		s = s p[i];
342
343	t = " * PUBLIC: "
344	if (length(s) + length(t) < 80)
345		printf("%s%s", t, s) >> fp;
346	else {
347		split(s, p, "__P");
348		len = length(t) + length(p[1]);
349		printf("%s%s", t, p[1]) >> fp
350
351		n = split(p[2], comma, ",");
352		comma[1] = "__P" comma[1];
353		for (i = 1; i <= n; i++) {
354			if (len + length(comma[i]) > 70) {
355				printf("\n * PUBLIC:	") >> fp;
356				len = 0;
357			}
358			printf("%s%s", comma[i], i == n ? "" : ",") >> fp;
359			len += length(comma[i]) + 2;
360		}
361	}
362	printf("\n */\n") >> fp;
363	delete p;
364}
365