xref: /illumos-gate/usr/src/cmd/abi/spectrans/spec2trace/trace.c (revision d2a70789f056fc6c9ce3ab047b52126d80b0e3da)
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 (c) 1997-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  *
31  * trace.c -- a  simple translator from spec source to c source for
32  *	a apptrace interposer library.  This file implements the
33  *	(interface to) the front end. Other files implement the middle
34  *	and databases, and generate.c implements the back end.
35  *
36  */
37 
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <time.h>
43 #include <string.h>
44 
45 #include "parser.h"
46 #include "trace.h"
47 
48 #include "util.h"
49 #include "db.h"
50 #include "symtab.h"
51 #include "io.h"
52 #include "printfuncs.h"
53 #include "errlog.h"
54 #include "parseproto.h"
55 
56 static int  Verbose;
57 
58 /* File globals. This would be better as a class. */
59 /* The first four (commented out) of these enums are defined in parser.h */
60 enum {
61 	/* XLATOR_KW_NOTFOUND = 0, */
62 	/* XLATOR_KW_FUNC, */
63 	/* XLATOR_KW_DATA */
64 	/* XLATOR_KW_END */
65 	XLATOR_KW_EXCP = 4,
66 	XLATOR_KW_DECL,
67 	XLATOR_KW_INCL,
68 	XLATOR_KW_ERRNO,
69 	XLATOR_KW_ERRVAL,
70 	XLATOR_KW_ARCH,
71 	XLATOR_KW_WEAK
72 };
73 #define	FIRST_TOKEN 4	/* Must match the first token in the above enum */
74 
75 static xlator_keyword_t Keywords[] = {
76 	{ "exception", XLATOR_KW_EXCP },
77 	{ "declaration", XLATOR_KW_DECL },
78 	{ "include", XLATOR_KW_INCL },
79 	{ "errno", XLATOR_KW_ERRNO },
80 	{ "errval", XLATOR_KW_ERRVAL},
81 	{ "arch", XLATOR_KW_ARCH},
82 	{ "weak", XLATOR_KW_WEAK},
83 	{ "weakfor", XLATOR_KW_WEAK},
84 	{ "alias", XLATOR_KW_WEAK},
85 	{ NULL, XLATOR_KW_NOTFOUND }
86 };
87 
88 static struct stats_t {
89 	int	libraries,
90 		files,
91 		interfaces,
92 		lines;
93 	int	errors,
94 		warnings,
95 		skips;
96 	time_t	start,
97 		end;
98 } Statistics;
99 
100 #define	LINE	(m.mi_line_number-(m.mi_nlines-1))
101 
102 static void stats_init(void);
103 static void stats_report(void);
104 
105 static int collect_binding(int const, char *, int);
106 static int collect_prototype(char *, int, int);
107 static int collect_include(char *, int);
108 static int collect_errval(char *, int);
109 static int collect_arch(char *);
110 
111 static void generate_includes(void);
112 static void generate_init(void);
113 static void generate_interface(void);
114 static void generate_closedown(void);
115 static int generate_aux_file();
116 
117 /* Local (static) parsing functions. */
118 static char *to_actual();
119 static int to_basetype(char *);
120 static char *de_const(char *);
121 static char *strpqcpy(char *, char *, char *);
122 
123 /*
124  * xlator_init -- initialize translator, called at startup-time
125  *	with a struct translator_info of information the translator
126  *	might need, returning a list of ``interesting'' spec keywords
127  *	for the front end to select and pass to the back end translator.
128  *
129  */
130 xlator_keyword_t *
131 xlator_init(const Translator_info *t_info)
132 {
133 	int	i;
134 
135 	errlog(BEGIN, "xlator_init() {");
136 
137 	/* Save interesting parameters. */
138 	stats_init();
139 	db_set_source_directory(".");
140 	db_set_target_directory(".");
141 	Verbose = t_info->ti_verbosity;
142 	seterrseverity(Verbose); /* Ditto. */
143 	db_set_output_file(t_info->ti_output_file);
144 	db_set_arch(t_info->ti_arch);
145 
146 	/* Display passed argument and return value. */
147 	errlog(VERBOSE, "Keywords[] = {");
148 	for (i = 0; Keywords[i].key != NULL; i++) {
149 		errlog(VERBOSE, "    \"%s\", ", Keywords[i].key);
150 	}
151 	errlog(VERBOSE, "    (char *) NULL");
152 	errlog(VERBOSE, "};");
153 
154 	errlog(END, "}");
155 	return (Keywords);
156 }
157 
158 /*
159  * xlator_startlib -- called on starting a new library, so back end
160  *	translator can decide to change output file/directory if desired.
161  */
162 int
163 xlator_startlib(char const *libname)
164 {
165 	errlog(BEGIN, "xlator_startlib() ");
166 
167 	Statistics.libraries++;
168 	db_set_current_library(libname);
169 	errlog(VERBOSE, "now in library \"%s\"", libname);
170 	errlog(END, "}");
171 	return (SUCCESS_RC);
172 }
173 
174 /*
175  * xlator_startfile -- ditto, called on starting each new spec file in the
176  *	specified library.
177  */
178 int
179 xlator_startfile(char const *filename)
180 {
181 	int	rc = SUCCESS_RC;
182 	char	infile[MAXLINE],
183 		outfile[MAXLINE],
184 		*lib = db_get_current_library();
185 
186 	seterrline(0, filename, "", "");
187 	errlog(BEGIN, "xlator_startfile() {");
188 	Statistics.files++;
189 	db_set_current_file(filename);
190 	errlog(TRACING, "now in file \"%s\" in lib \"%s\"",
191 		filename, lib);
192 
193 	/* Generate filenames. */
194 	(void) snprintf(infile, sizeof (infile), "%s", filename);
195 	(void) snprintf(outfile, sizeof (outfile), "%s.c",
196 		db_get_output_file());
197 
198 	/* Open .c file. */
199 	if (open_code_file() == NO) {
200 		rc = ERROR_RC;
201 	}
202 
203 	generate_init(); /* Write stuff to the c file. */
204 	symtab_clear_includes(); /* Clear out the per-file data. */
205 	errlog(END, "}");
206 	return (rc);
207 }
208 
209 /*
210  * xlator_start_if -- tritto, called on starting each new
211  *	interface in the spec file.
212  */
213 int
214 xlator_start_if(const Meta_info m, int const token, char *value)
215 {
216 	char ifname[BUFSIZ];
217 	char *kw;
218 
219 	switch (token) {
220 	case XLATOR_KW_FUNC:
221 		kw = "Function";
222 		break;
223 	case XLATOR_KW_DATA:
224 		kw = "Data";
225 		break;
226 	default:
227 		/* This should never happen */
228 		errlog(ERROR,
229 		    "\"%s\", line %d: Implementation error! "
230 		    "Please file a bug\n", __FILE__, __LINE__);
231 		return (XLATOR_FATAL);
232 	}
233 
234 	seterrline(LINE, m.mi_filename, kw, value);
235 	errlog(BEGIN, "xlator_start_if() {");
236 
237 /*
238  * XXX Note whether interface is function or data in some state data item.
239  * We'll need it later when writing interceptors.
240  */
241 
242 	Statistics.interfaces++;
243 	(void) strpqcpy(ifname, value, nextsep2(value));
244 	if (*ifname == '\0') {
245 		errlog(INPUT|ERROR|FATAL,
246 		    "missing argument in \"%s\" line", kw);
247 	}
248 	db_set_current_interface(ifname);
249 	errlog(VERBOSE, "interface='%s'", value);
250 	if (token == XLATOR_KW_DATA) {
251 		Statistics.skips++;
252 		errlog(VERBOSE, "telling front end to skip '%s'", value);
253 		errlog(END, "}");
254 		return (SKIP_RC); /* Tell front end to skip it for us. */
255 	}
256 
257 	errlog(TRACING, "now in interface \"%s\"", value);
258 
259 	symtab_new_function(m.mi_line_number, m.mi_filename);
260 		/* Also cleans junk out of symbol table. */
261 	errlog(END, "}");
262 	return (SUCCESS_RC);
263 }
264 
265 /*
266  * xlator_take_kvpair -- the primary call: collect a datum provide by the
267  *	front-end wrapper.
268  */
269 int
270 xlator_take_kvpair(Meta_info m, int const token, char *value)
271 {
272 	int retval;
273 	char *key = Keywords[token-FIRST_TOKEN].key;
274 
275 	int line = LINE; /* TBD */
276 	symtab_set_filename(m.mi_filename);
277 
278 	value = strnormalize(value);
279 
280 	seterrline(line, m.mi_filename, key, value);
281 	errlog(BEGIN, "xlator_take_kvpair() {");
282 	Statistics.lines++;
283 	errlog(VERBOSE, "key='%s', value='%s'",
284 	    (key) ? key : "<nil>",
285 	    (value) ? value : "<nil>");
286 	switch (token) {
287 	case XLATOR_KW_DECL:
288 
289 	/*
290 	 * XXX Check state item to see that it is a function,
291 	 * else do not emit interceptor
292 	 */
293 		symtab_clear_function(); /* Always use last one. */
294 		errlog(END, "}");
295 		retval = collect_prototype(value, line, m.mi_ext_cnt);
296 		break;
297 
298 	case XLATOR_KW_INCL:
299 		errlog(END, "}"); /* Use union of all includes. */
300 		retval = collect_include(value, line);
301 		if (retval == ERROR_RC) {
302 			errlog(FATAL|INPUT, "Bad include line in spec file");
303 		}
304 		break;
305 
306 	case XLATOR_KW_EXCP:
307 		symtab_clear_exception(); /* Always use last. */
308 		retval = collect_binding(token, value, line);
309 		break;
310 
311 	case XLATOR_KW_ERRNO:
312 		symtab_clear_errval(); /* Always use last. */
313 		retval = collect_errval("errno", line);
314 		break;
315 
316 	case XLATOR_KW_ERRVAL:
317 		symtab_clear_errval(); /* Always use last. */
318 		retval =  collect_errval(value, line);
319 		break;
320 
321 	case XLATOR_KW_ARCH:
322 		retval = collect_arch(value);
323 		break;
324 
325 	case XLATOR_KW_WEAK:
326 		if (m.mi_extended == 1) {
327 			errlog(ERROR, "\"%s\", line %d: "
328 			    "Warning: Cannot use extends with a weak "
329 			    "interface",
330 			    m.mi_filename,
331 			    m.mi_line_number);
332 		}
333 		retval = SUCCESS_RC;
334 		break;
335 	default:
336 		retval = ERROR_RC;
337 	}
338 
339 	errlog(END, "}");
340 
341 	return (retval);
342 }
343 
344 /*
345  * xlator_end_if -- called at the end of the interface, to trigger
346  *	per-interface processing now entire thing has been seen.
347  */
348 /*ARGSUSED*/
349 int
350 xlator_end_if(const Meta_info m, char const *value)
351 {
352 	seterrline(LINE, m.mi_filename, "end", value);
353 	errlog(BEGIN, "xlator_end_if() {");
354 	if (symtab_get_skip() == YES) {
355 		symtab_set_skip(NO);
356 		Statistics.skips++;
357 	} else {
358 		generate_interface();
359 	}
360 	errlog(END, "}");
361 	return (SUCCESS_RC);
362 }
363 
364 /*
365  * xlator_endfile -- called at the end of the file, to trigger per-file
366  * processing.
367  */
368 int
369 xlator_endfile(void)
370 {
371 	errlog(BEGIN, "xlator_endfile() {");
372 
373 	generate_closedown();
374 	errlog(END, "}");
375 	return ((commit_code_file() == YES)? SUCCESS_RC: ERROR_RC);
376 }
377 
378 /*
379  * xlator_endlib -- ditto, at the end of the library.
380  */
381 int
382 xlator_endlib(void)
383 {
384 	errlog(BEGIN, "xlator_endlib() {");
385 	errlog(END, "}");
386 	return (SUCCESS_RC);
387 }
388 
389 /*
390  * xlator_end -- the end of the processing, called so translator
391  *	can do cleanup, write makefiles, etc.
392  */
393 int
394 xlator_end(void)
395 {
396 	int	rc = SUCCESS_RC;
397 
398 	errlog(BEGIN, "xlator_end() {");
399 	rc += !generate_aux_file();
400 	stats_report();
401 	errlog(END, "}");
402 	return (rc);
403 }
404 
405 
406 /*
407 ** utilities for this layer/phase only.
408 */
409 
410 /*
411  * stats_init -- note what time it is...
412  */
413 static void
414 stats_init(void)
415 {
416 	Statistics.start = time(NULL);
417 }
418 
419 /*
420  * stats_report -- say how much we just did
421  */
422 #define	max(a, b) (a > b)? a: b
423 
424 static void
425 stats_report(void)
426 {
427 	double	seconds;
428 
429 	Statistics.end = time(NULL);
430 	seconds = difftime(Statistics.end, Statistics.start);
431 
432 	switch (Verbose) {
433 	default:
434 		/*FALLTHROUGH*/
435 	case 1:
436 		(void) fprintf(stderr, "Statistics:\n"
437 		    "    %d libraries\n    %d files\n"
438 		    "    %d interfaces\n    %d lines\n"
439 		    "    %d errors\n    %d warnings\n"
440 		    "    %d skips\n"
441 		    "in %.0f seconds, at %.1f lines/minute.\n",
442 		    Statistics.libraries, Statistics.files,
443 		    Statistics.interfaces, Statistics.lines,
444 		    Statistics.errors, Statistics.warnings,
445 		    Statistics.skips,
446 		    seconds, Statistics.lines*60.0/seconds);
447 		break;
448 	case 0:
449 		if (Statistics.errors != 0 || Statistics.warnings != 0) {
450 			(void) fprintf(stderr,
451 			    "spec2trace: %d errors %d warnings.\n",
452 			    Statistics.errors, Statistics.warnings);
453 		}
454 		break;
455 	}
456 }
457 
458 
459 /*
460  * Tiny stats class...
461  */
462 void
463 stats_add_warning(void)
464 {
465 	Statistics.warnings++;
466 }
467 
468 void
469 stats_add_error(void)
470 {
471 	Statistics.errors++;
472 }
473 
474 /*
475  * collect_includes -- collect a global list of include files,
476  *	converting the comma- or space-separated input list into a
477  *	structure for the database to store.
478  *	As this can cause problems will ill-structured
479  *	files, there is a mechanism to allow exclusion of
480  *	certain files, (or certain combinations).  At
481  *	the moment, the mechanism is TBD, as is the second arg.
482  */
483 /*ARGSUSED1*/
484 int
485 collect_include(char *p, int line)
486 {
487 	char	*include;
488 	int	len;
489 
490 	errlog(BEGIN, "collect_include() {");
491 	if ((include = strtok(p, ", ")) != NULL) {
492 		for (; include != NULL; include = strtok(NULL, ", ")) {
493 			include  = skipb(include);
494 
495 			/*
496 			 * Make sure the include file's name
497 			 * has legitimate C syntax - i.e. it's in double
498 			 * quotes or angle brackets.
499 			 */
500 			if (*include != '"' && *include != '<')
501 				return (ERROR_RC);
502 
503 			len = strlen(include);
504 
505 			if (include[len-1] != '"' && include[len-1] != '>')
506 				return (ERROR_RC);
507 
508 			/*
509 			 * If include filename syntax is OK, add it to
510 			 * the list
511 			 */
512 			symtab_add_includes(include);
513 		}
514 	}
515 	errlog(END, "}");
516 	return (SUCCESS_RC);
517 }
518 
519 /*
520  * collect_binding -- take a binding and stuff it into the database
521  *	in canonical form (with the word return in it).
522  */
523 int
524 collect_binding(int const token, char *value, int line)
525 {
526 	char	*file = db_get_current_file();
527 
528 	errlog(BEGIN, "collect_binding() {");
529 	errlog(VERBOSE, "name=\"%s\", value=\"%s\", line=%d\n",
530 	    Keywords[token-FIRST_TOKEN].key, value, line);
531 
532 	if (token == XLATOR_KW_EXCP) {
533 		symtab_set_exception(value, line, file);
534 	} else {
535 		errlog(FATAL|INPUT, "programmer error: impossible binding.");
536 	}
537 	errlog(END, "}");
538 	return (SUCCESS_RC);
539 }
540 
541 /*
542  * collect_errval -- collect the error variable name (only)
543  *	from the line.  This is expected to be the first
544  *	or only thing in a space- or comma-separated list.
545  *	Collecting errno/errval possible value is left TBD.
546  */
547 int
548 collect_errval(char *p, int line)
549 {
550 	char	*name;
551 
552 	errlog(BEGIN, "collect_errval() {");
553 	name = strtok(p, " \t\n\r");
554 	symtab_set_errval(name, line, db_get_current_file(), "int", "int", 0);
555 	errlog(END, "}");
556 	return (SUCCESS_RC);
557 }
558 
559 /*
560  * collect_arch -- collect architecture.
561  */
562 int
563 collect_arch(char *value)
564 {
565 	char const	*arch = db_get_arch();
566 	char	*buf, *p;
567 	char	*t;
568 
569 	errlog(BEGIN, "collect_arch() {");
570 	if (value == 0 || *value == '\0')
571 		errlog(FATAL|INPUT, "No architectures defined in ARCH line");
572 
573 	if ((buf = strdup(value)) == NULL)
574 		errlog(FATAL, "Could not allocate memory in ARCH directive");
575 
576 	t = buf;
577 	while ((p = strtok(t, " \r\t\n")) != NULL) {
578 		if (strcmp(p, arch) == 0 || strcmp(p, "all") == 0)
579 			goto cleanup;
580 		t = NULL;
581 	}
582 	symtab_set_skip(YES);
583 
584 cleanup:
585 	free(buf);
586 	return (SUCCESS_RC);
587 }
588 
589 /*
590  * de_const -- get rid of const meta-types. This is actually a
591  *	dodge to avoid writing a base-type function early in the
592  *	process. This may turn into to_basetype() or to_primitivetype().
593  */
594 static char *
595 de_const(char *type)
596 {
597 	char *p, *q;
598 	int i;
599 
600 	p = skipb(type);
601 
602 	q = strstr(type, "const");
603 	if (q > p) {
604 		for (i = 0; i < 5; i++) {
605 			*q++ = '\0';
606 		}
607 		(void) sprintf(type, "%s%s", strnormalize(p), q);
608 		return (type);
609 	} else if (p == q) {
610 		return (skipb(nextsep(p)));
611 	} else {
612 		return (type);
613 	}
614 
615 }
616 
617 /*
618  * to_basetype -- convert a C type declaration into its base type and return
619  * 	the number of levels of indirection.
620  *	Destructive and eats ``const''.
621  */
622 static int
623 to_basetype(char *str)
624 {
625 	char	*p = str,
626 		buffer[MAXLINE+1],
627 		*q = &buffer[0];
628 	int	levels = 0;
629 
630 	assert(strlen(str) < MAXLINE, "string exceeded MAXLINE");
631 	buffer[0] = NULL;
632 	for (; *p != NULL; p++) {
633 		switch (*p) {
634 		case ' ': /* Convert spaces to single ' '. */
635 			if (*(q-1) != ' ')
636 				*q++ = ' ';
637 			break;
638 		case '*': /* Convert * to _P. */
639 			if (*(q-1) != ' ')
640 				*q++ = ' ';
641 			levels++;
642 			break;
643 		case 'c': /* This might be a const */
644 			if (strncmp(p, "const", 5) == 0) {
645 				p += 4;
646 			} else {
647 				*q++ = *p;
648 			}
649 			break;
650 		default:
651 			/* Otherwise just copy. */
652 			*q++ = *p;
653 			break;
654 		}
655 		*q = NULL;
656 	}
657 	assert(q < &buffer[MAXLINE], "q fell off end of buffer");
658 	q--;
659 	while (*q == ' ') {
660 		*q-- = NULL;
661 	}
662 	assert(strlen(buffer) < MAXLINE, "buffer length exceeded MAXLINE");
663 	(void) strcpy(str, buffer);
664 	return (levels);
665 }
666 
667 /*
668  * to_actual -- create an actual-argument list for use
669  *	when calling the function.
670  */
671 static char *
672 to_actual(void)
673 {
674 	ENTRY	*p;
675 	static char buffer[MAXLINE+1];
676 	int	n;
677 
678 	*buffer = NULL;
679 	if ((p = symtab_get_first_arg()) != NULL) {
680 		n = MAXLINE - snprintf(buffer, MAXLINE, "%s", name_of(p));
681 		for (p = symtab_get_next_arg(); p != NULL;
682 						p = symtab_get_next_arg()) {
683 			if (*name_of(p) != NULL)
684 				n -= snprintf(strend(buffer), n,
685 					", %s", name_of(p));
686 		}
687 	}
688 	return (buffer);
689 }
690 
691 /*
692  * strpqcpy -- string copy that takes whatever begins with p and ends
693  *	just before q.
694  */
695 static char *
696 strpqcpy(char *target, char *p, char *q)
697 {
698 	char	saved;
699 
700 	saved = *q;
701 	*q = NULL;
702 	(void) strcpy(target, p);
703 	*q = saved;
704 	return (target);
705 }
706 
707 #ifndef lint
708 int
709 breakpoint(void)
710 {
711 	return (0);
712 }
713 #endif
714 
715 
716 int
717 collect_prototype(char *p, int line, int extcnt)
718 {
719 	char	f_type[BUFSIZ];	/* The function. */
720 	char	f_basetype[BUFSIZ];
721 	char	f_name[BUFSIZ];
722 	char	a_name[BUFSIZ];	/* The arguments. */
723 	char	a_basetype[BUFSIZ];
724 	char	a_type[BUFSIZ];
725 	char	*file = db_get_current_file();
726 	char	*interface = db_get_current_interface();
727 	char	*q;
728 	char const *parse_err;
729 	char	tmp_proto[BUFSIZ], buf[BUFSIZ];
730 	decl_t	*pp, *funargs;
731 	type_t	*tp;
732 	int	levels, a_levels;
733 
734 	tmp_proto[BUFSIZ-1] = 0;
735 	errlog(BEGIN, "collect_prototype() {");
736 	if (p[strlen(p)-1] != ';')
737 		(void) snprintf(tmp_proto, BUFSIZ, "%s;", p);
738 	else
739 		(void) snprintf(tmp_proto, BUFSIZ, "%s", p);
740 
741 	/* save prototype in symbol table */
742 	symtab_set_prototype(p);
743 
744 	errlog(VERBOSE, "parsing prototype: %s\n", tmp_proto);
745 
746 	/* Parse Prototype */
747 	if ((parse_err = decl_Parse(tmp_proto, &pp)) != NULL) {
748 		errlog(FATAL|INPUT, "bad prototype: %s\n\t%s\n", parse_err, p);
749 	}
750 
751 	if (extcnt == 0) {
752 		char *dname = decl_GetName(pp);
753 		if (strcmp(interface, dname) != 0)
754 			errlog(FATAL|INPUT, "function and declaration"
755 			    " name mismatch\nfunction name = %s,"
756 			    " declaration name = %s\n", interface,
757 			    dname);
758 	}
759 
760 	tp = decl_GetType(pp);
761 
762 	if (type_IsPtrFun(tp)) {
763 		errlog(FATAL|INPUT, "function %s is declared as a data item"
764 		    " (pointer to function)\n", interface);
765 	} else if (!type_IsFunction(tp)) {
766 		errlog(FATAL|INPUT, "function %s is declared as a data item",
767 		    interface);
768 	}
769 
770 	if (type_IsVarargs(tp)) {
771 		symtab_set_skip(YES);
772 		decl_Destroy(pp);
773 		return (SUCCESS_RC);
774 	}
775 
776 	decl_GetTraceInfo(pp, f_type, f_basetype, &funargs);
777 	(void) sprintf(buf, "%s", strnormalize(f_type));
778 	(void) strcpy(f_type, buf);
779 	(void) sprintf(buf, "%s", strnormalize(f_basetype));
780 	(void) strcpy(f_basetype, buf);
781 	levels = to_basetype(f_basetype);
782 
783 	/* get interface name from 'Begin' line */
784 	(void) strpqcpy(f_name, interface, nextsep(interface));
785 	(void) decl_SetName(pp, f_name);
786 
787 	errlog(VERBOSE, "f_name=%s, f_basetype=%s, f_type=%s\n",
788 		f_name, f_basetype, f_type);
789 
790 	symtab_set_function(f_name, line, file, f_type, f_basetype, levels);
791 
792 	db_add_print_types(f_basetype,
793 	    (q = de_const(type_of(symtab_get_function()))));
794 
795 	symtab_add_print_types(f_basetype, q);
796 
797 	/* args list */
798 	while (funargs) {
799 		(void) snprintf(a_type, BUFSIZ, "%s ",
800 			strnormalize(declspec_ToString(buf, funargs->d_ds)));
801 		(void) snprintf(a_basetype, BUFSIZ, "%s",
802 			strnormalize(de_const(declspec_ToString(buf,
803 			funargs->d_ds))));
804 
805 		tp = funargs->d_type;
806 
807 		for (a_levels = 0; tp; ) {
808 			if (tp->t_dt == DD_PTR || tp->t_dt == DD_ARY) {
809 				(void) strcat(a_type, "*");
810 				a_levels++;
811 			}
812 			tp = tp->t_next;
813 		}
814 
815 		/*
816 		 * XXX: This is a hack to work around bug in yacc parser
817 		 *  "int foo(void)" prototypes get interpreted as having 1
818 		 *  argument with the d_name of the argument being NULL.
819 		 */
820 		if (funargs->d_name) {
821 			(void) snprintf(a_name, 20, "%s", funargs->d_name);
822 
823 			errlog(VERBOSE,
824 			    "a_name = %s, a_basetype = %s, a_type = %s\n",
825 			    a_name, a_basetype, a_type);
826 
827 			symtab_add_args(a_name, line, file,
828 			    a_type, a_basetype, a_levels);
829 			db_add_print_types(a_basetype,
830 			    q = de_const(type_of(symtab_get_last_arg())));
831 			symtab_add_print_types(a_basetype, q);
832 		}
833 
834 		funargs = funargs->d_next;
835 	}
836 	symtab_set_formals(decl_ToFormal(pp));
837 	symtab_set_actuals(to_actual());
838 
839 	symtab_set_cast(decl_ToString(buf, DTS_CAST, pp, NULL));
840 
841 	decl_Destroy(pp);
842 
843 	errlog(END, "}");
844 	return (SUCCESS_RC);
845 }
846 
847 
848 /*
849  * generators
850  */
851 
852 /*
853  * generate_init -- prime the code generator as required.
854  */
855 static void
856 generate_init(void)
857 {
858 	errlog(BEGIN, "generate_init() {");
859 
860 	(void) fprintf(Headfp,
861 	    "/*\n"
862 	    " * Generated by spec2trace %s: do not edit this file.\n */\n\n",
863 	    TRACE_VERSION);
864 
865 	(void) fprintf(Headfp,
866 	    "#ifndef true\n"
867 	    "#define\ttrue 1\n"
868 	    "#define\tfalse 0\n"
869 	    "#endif\n\n"
870 	    "static char const *oparen = \"(\";\n"
871 	    "static char const *retstr = \"  return = \";\n"
872 	    "static char const *errnostr = \" errno = \";\n"
873 	    "static char const *nilstr = \"<nil>\";\n"
874 	    "\n");
875 
876 	errlog(END, "}");
877 }
878 
879 
880 /*
881  * generate_interface -- call the two main parts of the per-interface
882  *	code generation.
883  */
884 static void
885 generate_interface(void)
886 {
887 	ENTRY	*function = symtab_get_function();
888 
889 	errlog(BEGIN, "generate_interface() {");
890 	/* Check for required information. */
891 	if (validity_of(function) == NO) {
892 		symtab_set_skip(YES);
893 		errlog(WARNING|INPUT, "no prototype for interface "
894 			"it will be skipped");
895 		errlog(END, "}");
896 		return;
897 	}
898 
899 	/* Generate the current interface 's print-functions declarations. */
900 	generate_print_declarations(Bodyfp);
901 
902 	/* Generate the linkage part (a function and a struct */
903 	generate_linkage(function);
904 
905 	/* Generate the actual interceptor. */
906 	generate_interceptor(function);
907 	errlog(END, "}");
908 }
909 
910 
911 /*
912  * generate_closedown -- produce includes.
913  */
914 static void
915 generate_closedown(void)
916 {
917 	errlog(BEGIN, "generate_closedown() {");
918 
919 	/* Print includes to primary file. */
920 	generate_includes();
921 	(void) putc('\n', Headfp);
922 	errlog(END, "}");
923 }
924 
925 /*
926  * generate_aux_file -- generate one additional .pf file with
927  *	print-function pointers.
928  */
929 static int
930 generate_aux_file(void)
931 {
932 	FILE	*fp;
933 	char	pathname[MAXLINE];
934 
935 	errlog(BEGIN, "generate_aux_file() {");
936 	/* Open file */
937 	(void) snprintf(pathname, sizeof (pathname), "%s.pf",
938 		db_get_output_file());
939 	errlog(TRACING,  "output file = '%s'", pathname);
940 	if ((fp = fopen(pathname, "w")) == NULL) {
941 		errlog(FATAL, "%s: %s", pathname, strerror(errno));
942 	}
943 
944 	/*
945 	 * Declare and initialize all print function pointers to null.
946 	 * Some spec files result in nothing being put into the .pf
947 	 * file.  We must create the file since make(1) does not cope
948 	 * well with absent files that it expects to have built.  So
949 	 * now the build gets empty compilation unit warnings...  So
950 	 * we unconditionally create a static pointer.
951 	 */
952 	(void) fprintf(fp,
953 	    "/* Do not edit this file: it is a generated one. */\n\n"
954 	    "static char const *__abi_place_holder;\n\n");
955 
956 	generate_print_definitions(fp);
957 
958 	/* Close file */
959 	if (fclose(fp) != 0) {
960 		errlog(FATAL, "fclose %s: %s", pathname, strerror(errno));
961 	}
962 	errlog(END, "}");
963 	return (YES);
964 }
965 
966 
967 
968 /*
969  * generate_includes -- generate #includes to Headfp
970  */
971 static void
972 generate_includes(void)
973 {
974 	char	*include;
975 
976 	errlog(BEGIN, "generate_includes() {");
977 	errlog(TRACING,  "includes=");
978 	for (include = symtab_get_first_include(); include != NULL;
979 	    include = symtab_get_next_include())
980 		(void) fprintf(Headfp, "#include %s\n", include);
981 
982 	(void) fprintf(Headfp, "\n#include <stdio.h>\n"
983 	    "#include <dlfcn.h>\n"
984 	    "#include <apptrace.h>\n\n");
985 
986 	errlog(TRACING,  "\n");
987 	errlog(END, "}");
988 }
989