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