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
54static 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 */
58enum {
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
73static 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
86static 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
100static void stats_init(void);
101static void stats_report(void);
102
103static int collect_binding(int const, char *, int);
104static int collect_prototype(char *, int, int);
105static int collect_include(char *, int);
106static int collect_errval(char *, int);
107static int collect_arch(char *);
108
109static void generate_includes(void);
110static void generate_init(void);
111static void generate_interface(void);
112static void generate_closedown(void);
113static int generate_aux_file();
114
115/* Local (static) parsing functions. */
116static char *to_actual();
117static int to_basetype(char *);
118static char *de_const(char *);
119static 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 */
128xlator_keyword_t *
129xlator_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 */
160int
161xlator_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 */
176int
177xlator_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 */
211int
212xlator_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 */
267int
268xlator_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*/
347int
348xlator_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 */
366int
367xlator_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 */
379int
380xlator_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 */
391int
392xlator_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 */
411static void
412stats_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
422static void
423stats_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 */
460void
461stats_add_warning(void)
462{
463	Statistics.warnings++;
464}
465
466void
467stats_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*/
482int
483collect_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 */
521int
522collect_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 */
545int
546collect_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 */
560int
561collect_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
582cleanup:
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 */
592static char *
593de_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 */
620static int
621to_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 */
669static char *
670to_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 */
693static char *
694strpqcpy(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
706int
707breakpoint(void)
708{
709	return (0);
710}
711#endif
712
713
714int
715collect_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 */
853static void
854generate_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 */
882static void
883generate_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 */
912static void
913generate_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 */
927static int
928generate_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 */
969static void
970generate_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