1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Read in "high-level" adb script and emit C program.
31 * The input may have specifications within {} which
32 * we analyze and then emit C code to generate the
33 * ultimate adb acript.
34 * We are just a filter; no arguments are accepted.
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#define	streq(s1, s2)	(strcmp(s1, s2) == 0)
42
43#define	LINELEN  	1024	/* max line length expected in input */
44#define	STRLEN		128	/* for shorter strings */
45#define	NARGS		5	/* number of emitted subroutine arguments */
46
47/*
48 * Format specifier strings
49 * which are recognized by adbgen when surrounded by {}
50 */
51#define	FSTR_PTR	"POINTER"
52#define	FSTR_LONG_DEC	"LONGDEC"
53#define	FSTR_LONG_OCT	"LONGOCT"
54#define	FSTR_ULONG_DEC	"ULONGDEC"
55#define	FSTR_ULONG_HEX	"ULONGHEX"
56#define	FSTR_ULONG_OCT	"ULONGOCT"
57
58/*
59 * Types of specifications in {}.
60 */
61#define	PTR_HEX		0	/* emit hex pointer format char */
62#define	LONG_DEC	1	/* emit decimal long format char */
63#define	LONG_OCT	2	/* emit octal unsigned long format char */
64#define	ULONG_DEC	3	/* emit decimal unsigned long format char */
65#define	ULONG_HEX	4	/* emit hexadecimal long format char */
66#define	ULONG_OCT	5	/* emit octal unsigned long format char */
67
68#define	FMT_ENTRIES	6	/* number of adbgen format specifier strings */
69
70#define	PRINT   	6	/* print member name with format */
71#define	INDIRECT	7	/* fetch member value */
72#define	OFFSETOK	8	/* insist that the offset is ok */
73#define	SIZEOF		9	/* print sizeof struct */
74#define	END		10	/* get offset to end of struct */
75#define	OFFSET		11	/* just emit offset */
76#define	EXPR		12	/* arbitrary C expression */
77
78/*
79 * Special return code from nextchar.
80 */
81#define	CPP		-2	/* cpp line, restart parsing */
82
83typedef struct adbgen_fmt {
84	char *f_str;
85	char f_char;
86} adbgen_fmt_t;
87
88char struct_name[STRLEN];	/* struct name */
89char member[STRLEN];		/* member name */
90char format[STRLEN];		/* adb format spec */
91char arg[NARGS][STRLEN];	/* arg list for called subroutine */
92char *ptr_hex_fmt;		/* adb format character for pointer in hex */
93char *long_dec_fmt;		/* adb format character for long in decimal */
94char *ulong_dec_fmt;		/* adb format character for ulong in decimal */
95char *ulong_hex_fmt;		/* adb format character for ulong in hex */
96char *long_oct_fmt;		/* adb format character for long in octal */
97char *ulong_oct_fmt;		/* adb format character for ulong in octal */
98
99int line_no = 1;		/* input line number - for error messages */
100int specsize;			/* size of {} specification - 1 or 2 parts */
101int state;			/* XXX 1 = gathering a printf */
102				/* This is a kludge so we emit pending */
103				/* printf's when we see a CPP line */
104
105adbgen_fmt_t adbgen_fmt_tbl [FMT_ENTRIES] = {
106	{FSTR_PTR},
107	{FSTR_LONG_DEC},
108	{FSTR_LONG_OCT},
109	{FSTR_ULONG_DEC},
110	{FSTR_ULONG_HEX},
111	{FSTR_ULONG_OCT}
112};
113
114void emit_call(char *name, int nargs);
115void emit_end(void);
116void emit_expr(void);
117void emit_indirect(void);
118void emit_offset(void);
119void emit_offsetok(void);
120void emit_print(void);
121void emit_printf(char *cp);
122void emit_sizeof(void);
123void generate(void);
124int get_type(void);
125int nextchar(char *cp);
126void read_spec(void);
127char *start_printf(void);
128
129int
130main(int argc, char **argv)
131{
132	char *cp;
133	int c;
134	int warn_flag = 0;
135	int is_lp64 = 0;
136	char *usage = "adbgen1 [-w] [-m ilp32|lp64] < <macro file>\n";
137
138	while ((c = getopt(argc, argv, "m:w")) != EOF) {
139		switch (c) {
140		case 'm':
141			if (streq(optarg, "ilp32"))
142				is_lp64 = 0;
143			else if (streq(optarg, "lp64"))
144				is_lp64 = 1;
145			else
146				fprintf(stderr, usage);
147			break;
148		case 'w':
149			warn_flag++;
150			break;
151		case '?':
152			fprintf(stderr, usage);
153			break;
154		}
155	}
156	if (is_lp64) {
157		adbgen_fmt_tbl[PTR_HEX].f_char = 'J';
158		adbgen_fmt_tbl[LONG_DEC].f_char = 'e';
159		adbgen_fmt_tbl[LONG_OCT].f_char = 'g';
160		adbgen_fmt_tbl[ULONG_DEC].f_char = 'E';
161		adbgen_fmt_tbl[ULONG_HEX].f_char = 'J';
162		adbgen_fmt_tbl[ULONG_OCT].f_char = 'G';
163	} else {
164		adbgen_fmt_tbl[PTR_HEX].f_char = 'X';
165		adbgen_fmt_tbl[LONG_DEC].f_char = 'D';
166		adbgen_fmt_tbl[LONG_OCT].f_char = 'Q';
167		adbgen_fmt_tbl[ULONG_DEC].f_char = 'U';
168		adbgen_fmt_tbl[ULONG_HEX].f_char = 'X';
169		adbgen_fmt_tbl[ULONG_OCT].f_char = 'O';
170	}
171
172	/*
173	 * Get structure name.
174	 */
175	cp = struct_name;
176	while ((c = nextchar(NULL)) != '\n') {
177		if (c == EOF) {
178			fprintf(stderr, "Premature EOF\n");
179			exit(1);
180		}
181		if (c == CPP)
182			continue;
183		*cp++ = (char)c;
184	}
185	*cp = '\0';
186	/*
187	 * Basically, the generated program is just an ongoing printf
188	 * with breaks for {} format specifications.
189	 */
190	printf("\n");
191	printf("#include <sys/types.h>\n");
192	printf("#include <sys/inttypes.h>\n");
193	printf("\n\n");
194	printf("int do_fmt(char *acp);\n");
195	printf("void format(char *name, size_t size, char *fmt);\n");
196	printf("void indirect(off_t offset, size_t size, "
197	    "char *base, char *member);\n");
198	printf("void offset(off_t off);\n");
199	printf("void offsetok(void);\n");
200	printf("\n\n");
201	printf("main(int argc, char *argv[])\n");
202	printf("{\n");
203	if (warn_flag) {
204		printf("\textern int warnings;\n\n\twarnings = 0;\n");
205	}
206	cp = start_printf();
207	while ((c = nextchar(cp)) != EOF) {
208		switch (c) {
209		case '"':
210			*cp++ = '\\';	/* escape ' in string */
211			*cp++ = '"';
212			break;
213		case '\n':
214			*cp++ = '\\';	/* escape newline in string */
215			*cp++ = 'n';
216			break;
217		case '{':
218			emit_printf(cp);
219			read_spec();
220			generate();
221			cp = start_printf();
222			break;
223		case CPP:
224			/*
225			 * Restart printf after cpp line.
226			 */
227			cp = start_printf();
228			break;
229		default:
230			*cp++ = c;
231			break;
232		}
233		if (cp - arg[1] >= STRLEN - 10) {
234			emit_printf(cp);
235			cp = start_printf();
236		}
237	}
238	emit_printf(cp);
239
240	/* terminate program, checking for "error" mode */
241	printf("\n\tif (argc > 1 && strcmp(argv[1], \"-e\") == 0) {\n");
242	printf("\t\textern int warns;\n\n");
243	printf("\t\tif (warns)\n");
244	printf("\t\t\treturn (1);\n");
245	printf("\t}\n");
246	printf("\treturn (0);\n");
247	printf("}\n");
248
249	return (0);
250}
251
252int
253nextchar(char *cp)
254{
255	int c;
256	static int newline = 1;
257
258	c = getchar();
259	/*
260	 * Lines beginning with '#' and blank lines are passed right through.
261	 */
262	while (newline) {
263		switch (c) {
264		case '#':
265			if (state)
266				emit_printf(cp);
267			do {
268				putchar(c);
269				c = getchar();
270				if (c == EOF)
271					return (c);
272			} while (c != '\n');
273			putchar(c);
274			line_no++;
275			return (CPP);
276		case '\n':
277			if (state)
278				emit_printf(cp);
279			putchar(c);
280			c = getchar();
281			line_no++;
282			break;
283		default:
284			newline = 0;
285			break;
286		}
287	}
288	if (c == '\n') {
289		newline++;
290		line_no++;
291	}
292	return (c);
293}
294
295/*
296 * Get started on printf of ongoing adb script.
297 */
298char *
299start_printf(void)
300{
301	char *cp;
302
303	strcpy(arg[0], "\"%s\"");
304	cp = arg[1];
305	*cp++ = '"';
306	state = 1;			/* XXX */
307	return (cp);
308}
309
310/*
311 * Emit call to printf to print part of ongoing adb script.
312 */
313void
314emit_printf(cp)
315	char *cp;
316{
317	*cp++ = '"';
318	*cp = '\0';
319	emit_call("printf", 2);
320	state = 0;			/* XXX */
321}
322
323/*
324 * Read {} specification.
325 * The first part (up to a comma) is put into "member".
326 * The second part, if present, is put into "format".
327 */
328void
329read_spec(void)
330{
331	char *cp;
332	int c;
333	int nesting;
334
335	cp = member;
336	specsize = 1;
337	nesting = 0;
338	while ((c = nextchar(NULL)) != '}' || (c == '}' && nesting)) {
339		switch (c) {
340		case EOF:
341			fprintf(stderr, "Unexpected EOF inside {}\n");
342			exit(1);
343		case '\n':
344			fprintf(stderr, "Newline not allowed in {}, line %d\n",
345				line_no);
346			exit(1);
347		case '#':
348			fprintf(stderr, "# not allowed in {}, line %d\n",
349				line_no);
350			exit(1);
351		case ',':
352			if (specsize == 2) {
353				fprintf(stderr, "Excessive commas in {}, ");
354				fprintf(stderr, "line %d\n", line_no);
355				exit(1);
356			}
357			specsize = 2;
358			*cp = '\0';
359			cp = format;
360			break;
361		case '{':
362			/*
363			 * Allow up to one set of nested {}'s for adbgen
364			 * requests of the form {member, {format string}}
365			 */
366			if (!nesting) {
367				nesting = 1;
368				*cp++ = c;
369			} else {
370				fprintf(stderr, "Too many {'s, line %d\n",
371					line_no);
372				exit(1);
373			}
374			break;
375		case '}':
376			*cp++ = c;
377			nesting = 0;
378			break;
379		default:
380			*cp++ = c;
381			break;
382		}
383	}
384	*cp = '\0';
385	if (cp == member) {
386		specsize = 0;
387	}
388}
389
390/*
391 * Decide what type of input specification we have.
392 */
393int
394get_type(void)
395{
396	int i;
397
398	if (specsize == 1) {
399		if (streq(member, "SIZEOF")) {
400			return (SIZEOF);
401		}
402		if (streq(member, "OFFSETOK")) {
403			return (OFFSETOK);
404		}
405		if (streq(member, "END")) {
406			return (END);
407		}
408		for (i = 0; i < FMT_ENTRIES; i++)
409			if (streq(member, adbgen_fmt_tbl[i].f_str))
410				return (i);
411		return (OFFSET);
412	}
413	if (specsize == 2) {
414		if (member[0] == '*') {
415			return (INDIRECT);
416		}
417		if (streq(member, "EXPR")) {
418			return (EXPR);
419		}
420		return (PRINT);
421	}
422	fprintf(stderr, "Invalid specification, line %d\n", line_no);
423	exit(1);
424}
425
426/*
427 * Generate the appropriate output for an input specification.
428 */
429void
430generate(void)
431{
432	char *cp;
433	int type;
434
435	type = get_type();
436
437	switch (type) {
438	case PTR_HEX:
439	case LONG_DEC:
440	case LONG_OCT:
441	case ULONG_DEC:
442	case ULONG_HEX:
443	case ULONG_OCT:
444		cp = start_printf();
445		*cp++ = adbgen_fmt_tbl[type].f_char;
446		emit_printf(cp);
447		break;
448	case PRINT:
449		emit_print();
450		break;
451	case OFFSET:
452		emit_offset();
453		break;
454	case INDIRECT:
455		emit_indirect();
456		break;
457	case OFFSETOK:
458		emit_offsetok();
459		break;
460	case SIZEOF:
461		emit_sizeof();
462		break;
463	case EXPR:
464		emit_expr();
465		break;
466	case END:
467		emit_end();
468		break;
469	default:
470		fprintf(stderr, "Internal error in generate\n");
471		exit(1);
472	}
473}
474
475/*
476 * Emit calls to set the offset and print a member.
477 */
478void
479emit_print(void)
480{
481	char *cp;
482	char fmt_request[STRLEN];
483	int i;
484	char number[STRLEN];
485
486	emit_offset();
487	/*
488	 * Emit call to "format" subroutine
489	 */
490	sprintf(arg[0], "\"%s\"", member);
491	sprintf(arg[1], "sizeof ((struct %s *)0)->%s",
492		struct_name, member);
493
494	/*
495	 * Split the format string into <number><format character string>
496	 * This is for format strings that contain format specifier requests
497	 * like {POINTER_HEX}, {LONG_DEC}, etc. which need to be substituted
498	 * with a format character instead.
499	 */
500	for (cp = format, i = 0; *cp >= '0' && *cp <= '9' && *cp != '\0';
501	    cp++, i++)
502		number[i] = *cp;
503	number[i] = '\0';
504
505	for (i = 0; i < FMT_ENTRIES; i++) {
506		(void) sprintf(fmt_request, "{%s}", adbgen_fmt_tbl[i].f_str);
507		if (streq(cp, fmt_request)) {
508			sprintf(arg[2], "\"%s%c\"",
509				number, adbgen_fmt_tbl[i].f_char);
510			break;
511		}
512	}
513	if (i == FMT_ENTRIES)
514		sprintf(arg[2], "\"%s\"", format);
515
516	emit_call("format", 3);
517}
518
519/*
520 * Emit calls to set the offset and print a member.
521 */
522void
523emit_offset(void)
524{
525	/*
526	 * Emit call to "offset" subroutine
527	 */
528	sprintf(arg[0], "(off_t) &(((struct %s *)0)->%s)",
529		struct_name, member);
530	emit_call("offset", 1);
531}
532
533/*
534 * Emit call to indirect routine.
535 */
536void
537emit_indirect(void)
538{
539	sprintf(arg[0], "(off_t) &(((struct %s *)0)->%s)",
540		struct_name, member+1);
541	sprintf(arg[1], "sizeof ((struct %s *)0)->%s", struct_name, member+1);
542	sprintf(arg[2], "\"%s\"", format);	/* adb register name */
543	sprintf(arg[3], "\"%s\"", member);
544	emit_call("indirect", 4);
545}
546
547/*
548 * Emit call to "offsetok" routine.
549 */
550void
551emit_offsetok(void)
552{
553	emit_call("offsetok", 0);
554}
555
556/*
557 * Emit call to printf the sizeof the structure.
558 */
559void
560emit_sizeof(void)
561{
562	sprintf(arg[0], "\"0t%%d\"");
563	sprintf(arg[1], "sizeof (struct %s)", struct_name);
564	emit_call("printf", 2);
565}
566
567/*
568 * Emit call to printf an arbitrary C expression.
569 */
570void
571emit_expr(void)
572{
573	sprintf(arg[0], "\"0t%%d\"");
574	sprintf(arg[1], "(%s)", format);
575	emit_call("printf", 2);
576}
577
578/*
579 * Emit call to set offset to end of struct.
580 */
581void
582emit_end(void)
583{
584	sprintf(arg[0], "sizeof (struct %s)", struct_name);
585	emit_call("offset", 1);
586}
587
588/*
589 * Emit call to subroutine name with nargs arguments from arg array.
590 */
591void
592emit_call(char *name, int nargs)
593{
594	int i;
595
596	printf("\t%s(", name);		/* name of subroutine */
597	for (i = 0; i < nargs; i++) {
598		if (i > 0) {
599			printf(", ");	/* argument separator */
600		}
601		printf("%s", arg[i]);	/* argument */
602	}
603	printf(");\n");			/* end of call */
604}
605