/*
 * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"@(#)interceptor.c	1.2	99/05/14 SMI"

/*
 * interceptor.c -- a functional decomposition of generate.c,
 *	the code generator for apptrace
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "parser.h"
#include "trace.h"
#include "util.h"
#include "db.h"
#include "symtab.h"
#include "io.h"
#include "bindings.h"
#include "printfuncs.h"
#include "errlog.h"
#include "parseproto.h"

static void generate_i_declarations(char *, int, char *);
static void generate_i_preamble(ENTRY *);
static void generate_i_call();
static int  generate_i_bindings(int);
static void generate_i_postamble(ENTRY *, int, char *, char *);
static void generate_i_evaluations(ENTRY *);
static void generate_i_prints(ENTRY *, char *, char *);
static void generate_i_closedown(char *, int);
static void generate_i_live_vars(ENTRY *);
static void generate_return_printf(int);
static char *variables_get_errorname(void);

/*
 * generate_interceptor -- make code for an individual interceptor, written
 *	as an output grammar
 */
void
generate_interceptor(ENTRY *function)
{
	char	*prototype = symtab_get_prototype(),
		*library_name = db_get_current_library(),
		*function_name,
		*error_name;
	int	void_func;

	errlog(BEGIN, "generate_interceptor() {");

	/* Check for required information. */
	if (validity_of(function) == NO) {
		symtab_set_skip(YES);
		errlog(WARNING|INPUT, "No prototype for interface, "
			"it will be skipped");
		errlog(END, "}");
		return;
	}

	/* Collect things we'll use more than once. */
	function_name = name_of(function);

	error_name = variables_get_errorname();

	void_func = is_void(function);

	/* Generate the comment for the interceptor function */
	(void) fprintf(Bodyfp,
	    "/*\n"
	    " * %s \n"
	    " *\tfrom \"%s\", line %d\n"
	    " */\n",
	    prototype, symtab_get_filename(), line_of(function));

	/*
	 * Emit "artificial" prototype here so that if there's a
	 * disagreement between it and the prototype contained in the
	 * declaring header, the compiler will flag it.
	 */
	{
		decl_t *dp;
		char *buf;
		char const *err;
		size_t s;

		s = strlen(prototype) + 2;
		buf = malloc(s);
		if (buf == NULL)
			abort();
		(void) strcpy(buf, prototype);
		buf[s - 2] = ';';
		buf[s - 1] = '\0';

		err = decl_Parse(buf, &dp);
		if (err != NULL)
			errlog(FATAL, "\"%s\", line %d: %s: %s",
			    symtab_get_filename(), line_of(function),
			    err, prototype);

		/* generate the mapfile entry */
		(void) fprintf(Mapfp, "\t__abi_%s;\n", decl_GetName(dp));

		(void) decl_ToString(buf, DTS_DECL, dp, function_name);
		(void) fprintf(Bodyfp, "#line %d \"%s\"\nextern %s;\n",
		    line_of(function), symtab_get_filename(), buf);

		(void) fprintf(Bodyfp, "static %s\n{\n", prototype);

		(void) decl_ToString(buf, DTS_RET, dp, "_return");
		generate_i_declarations(error_name, void_func, buf);
		decl_Destroy(dp);
		free(buf);
	}

	generate_i_preamble(function);
	generate_i_call(function, void_func, library_name, error_name);
	generate_i_postamble(function, void_func, error_name, library_name);

	errlog(END, "}");
}

/*
 * print_function_signature -- print the line defining the function, without
 *      an ``extern'' prefix or either a ``;'' or ''{'' suffix.
 */
void
print_function_signature(char *xtype, char *name, char *formals)
{
	char	buffer[MAXLINE];

	(void) snprintf(buffer, sizeof (buffer), "%s", name);
	(void) fprintf(Bodyfp,  xtype, buffer);
	if (strstr(xtype, "(*") == NULL) {
		(void) fprintf(Bodyfp,  "(%s)", formals);
	}
}


/*
 * generate_i_declarations -- generate the declarations which
 *      are local to the interceptor function itself.
 */
static void
generate_i_declarations(char *errname, int voidfunc, char *ret_str)
{

	errlog(BEGIN, "generate_i_declarations() {");
	if (*errname != NULL) {
		/* Create locals for errno-type variable, */
		(void) fprintf(Bodyfp,
		    "    int saved_errvar = %s;\n", errname);
		(void) fprintf(Bodyfp,  "    int functions_errvar;\n");
	}

	if (need_exception_binding()) {
		/* Create a local for that. */
		(void) fprintf(Bodyfp,  "    int exception = 0;\n");
	}
	if (! voidfunc) {
		/* Create a return value. */
		(void) fprintf(Bodyfp,  "    %s;\n", ret_str);
	}
	(void) fprintf(Bodyfp, "    sigset_t omask;\n");
	(void) putc('\n', Bodyfp);
	errlog(END, "}");
}


/*
 * generate_i_preamble -- do the actions which must occur
 *      before the call.
 */
static void
generate_i_preamble(ENTRY *function)
{
	errlog(BEGIN, "generate_i_preamble() {");
	generate_i_live_vars(function); /* Deferred. */

	if (symtab_get_nonreturn() == YES) {
		/* Make things safe for printing */
		(void) fprintf(Bodyfp,
		    "    abilock(&omask);\n");
		/* Print all the args in terse format. */
		generate_printf(function);
		(void) fputs("    putc('\\n', ABISTREAM);\n\n", Bodyfp);
		/* unlock stdio */
		(void) fprintf(Bodyfp,
		    "    abiunlock(&omask);\n");
	}

	errlog(END, "}");
}

/*
 * generate_i_call -- implement the save/call/restore cycle
 */
static void
generate_i_call(
	ENTRY	*function,
	int	void_func,
	char	*library_name,
	char	*error_name)
{
	char	*function_name = name_of(function),
		*function_cast = symtab_get_cast(),
		*actual_args = symtab_get_actuals();

	errlog(BEGIN, "generate_i_call() {");
	/* Zero the error variable. */
	if (*error_name != NULL) {
		(void) fprintf(Bodyfp,  "    %s = 0;\n", error_name);
	}

	/* Then print the call itself. */
	if (void_func) {
		(void) fprintf(Bodyfp,
		    "    (void) ABI_CALL_REAL(%s, %s, %s)(%s);\n",
		    library_name, function_name, function_cast, actual_args);
	} else {
		(void) fprintf(Bodyfp,
		    "    _return = ABI_CALL_REAL(%s, %s, %s)(%s);\n",
		    library_name, function_name, function_cast, actual_args);
	}

	/* Then set the local copy of the error variable. */
	if (*error_name != NULL) {
		(void) fprintf(Bodyfp,
		    "    functions_errvar = %s;\n", error_name);
	}
	(void) putc('\n', Bodyfp);

	/* Make things safe for printing */
	(void) fprintf(Bodyfp,
	    "    abilock(&omask);\n");

	errlog(END, "}");
}

/*
 * generate_i_postamble -- do all the things which come
 *      after the call.  In the case of apptrace, this is most of the work.
 */
static void
generate_i_postamble(ENTRY *function, int void_func,
    char *error_name, char *library_name)
{
	errlog(BEGIN, "generate_i_postamble() {");
	if (symtab_get_nonreturn() == NO) {
		/* Print all the args in terse format. */
		generate_printf(function);
	}

	/* If it isn't supposed to return, and actually ends up here, */
	/* we'd better be prepared to print all sorts of diagnostic stuff */
	(void) putc('\n', Bodyfp);
	if (generate_i_bindings(void_func) == YES) {
		generate_return_printf(void_func);
	}

	generate_i_prints(function, library_name, name_of(function));
	generate_i_evaluations(function); /* Deferred */
	generate_i_closedown(error_name, void_func);
	errlog(END, "}");
}

/*
 * generate_i_bindings -- see about success and failure, so we can decide
 *      what to do next.
 */
static int
generate_i_bindings(int void_func)
{
	ENTRY   *e;
	char *exception;

	exception  = ((e = symtab_get_exception()) != NULL)?
	    (name_of(e)? name_of(e): ""): "";

	errlog(BEGIN, "generate_i_bindings() {");
	if (void_func && bindings_exist()) {
		/* To become a warning, as there are spec errors! TBD */
		errlog(FATAL, "exception bindings found in a "
			"void function");
	} else if (void_func || need_bindings(exception) == NO) {
		(void) fprintf(Bodyfp,
		    "    (void) putc('\\n', ABISTREAM);\n");
		(void) putc('\n', Bodyfp);
		errlog(END, "}");
		return (NO);
	} else {
		/*
		 * Then there is a return value, so we try to
		 * generate exception bindings
		 * and code to print errno on exception.
		 */
		if ((generate_bindings(exception)) != ANTONYMS) {
			/* Generate code to cross-evaluate them. */
			(void) fprintf(Bodyfp,
			    "    if (!exception) {\n");
			errlog(END, "}");
			return (YES);
		}
	}

	/* should not get here */
	errlog(END, "}");
	return (NO);
}

/*
 * generate_return_printf -- print the return value and end the line
 */
static void
generate_return_printf(int void_func)
{
	errlog(BEGIN, "generate_return_printf() {");
	if (void_func) {
		(void) fprintf(Bodyfp,  "    putc('\\n', ABISTREAM);\n");
		errlog(END, "}");
		return;
	}
	/* If its a non-void function there are bindings. */
	(void) fprintf(Bodyfp,
	    "\t/* Just end the line */\n"
	    "\tputc('\\n', ABISTREAM);\n"
	    "    }\n"
	    "    else {\n"
	    "        fprintf(ABISTREAM, \"%%s%%d (%%s)\\n\", errnostr, "
	    "functions_errvar, strerror((int)functions_errvar));\n"
	    "    }\n\n");
	errlog(END, "}");
}

/*
 * generate_i_prints -- if we're doing the verbose stuff,
 *      generate verbose printouts of the variables.
 */
static void
generate_i_prints(ENTRY *function, char *lib, char *func)
{
	ENTRY   *e;

	errlog(BEGIN, "generate_i_prints() {");
	if ((e = symtab_get_first_arg()) != NULL || !is_void(e)) {
		/* Then we have to generate code for verbose reports. */
		(void) fprintf(Bodyfp,  "    if (ABI_VFLAG(%s, %s) != 0) {\n",
			lib, func);
		generate_printfunc_calls(function);
		(void) fprintf(Bodyfp,  "    }\n");
	}
	(void) putc('\n', Bodyfp);
	errlog(END, "}");
}

/*
 * generate_i_closedown -- restore error variables and return.
 */
static void
generate_i_closedown(char *error_name, int void_func)
{
	errlog(BEGIN, "generate_i_closedown() {");
	if (*error_name != NULL) {
		/* Restore error variables. */
		(void) fprintf(Bodyfp,
		    "    %s = (functions_errvar == 0)? "
		    "            saved_errvar: functions_errvar;\n",
		    error_name);
	}

	/* unlock stdio */
	(void) fprintf(Bodyfp,
	    "    abiunlock(&omask);\n");

	/* And return. */
	(void) fprintf(Bodyfp,
	    "    return%s;\n",
	    (void_func)? "": " _return");
	(void) fprintf(Bodyfp,  "}\n");
	(void) putc('\n', Bodyfp);
	errlog(END, "}");
}


/*
 * generate_i_live_vars -- generate temps for any ``out''
 *	or ``inout'' variables in the function.  Deferred.
 */
/*ARGSUSED*/
static void
generate_i_live_vars(ENTRY *function)
{
	errlog(BEGIN, "generate_i_live_vars() {");
	errlog(END, "}");
}

/*
 * generate_i_evaluations -- generate evaluations for
 *	all the expressions. Deferred.
 */
/*ARGSUSED*/
static void
generate_i_evaluations(ENTRY *function)
{
	errlog(BEGIN, "generate_i_evaluations() {");
	errlog(END, "}");
}


static char *
variables_get_errorname(void)
{
	return ("ABI_ERRNO");
}
