/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* $Id$ */
/*	Lint outdef construction	*/

#include	"parameters.h"

#ifdef	LINT

#include	<assert.h>
#include	<ack_string.h>
#include	<alloc.h>
#include	"interface.h"
#ifdef ANSI
#include	<flt_arith.h>
#endif /* ANSI */
#include	"arith.h"
#include	"type.h"
#include	"proto.h"
#include	"declar.h"
#include	"decspecs.h"
#include	"LLlex.h"
#include	"Lpars.h"
#include	"stack.h"
#include	"def.h"
#include	"struct.h"
#include	"field.h"
#include	"level.h"
#include	"label.h"
#include	"code.h"
#include	"expr.h"
#include	"l_lint.h"
#include	"l_comment.h"
#include	"l_outdef.h"
#include	"l_class.h"

extern char *symbol2str();

int stat_number = 9999;			/* static scope number */
struct outdef OutDef;

PRIVATE struct outdef OutCall;

PRIVATE local_EFDC();
PRIVATE output_def();
PRIVATE outargs();
PRIVATE outarg();
PRIVATE outargstring();
PRIVATE outargtype();
PRIVATE add_expr_arg();
PRIVATE def2decl();

lint_declare_idf(idf, sc)
	struct idf *idf;
	int sc;
{
	register struct def *def = idf->id_def;
	register int is_function = def->df_type->tp_fund == FUNCTION;

	if (level == L_GLOBAL) {
		lint_ext_def(idf, sc);
		if (is_function)
			def2decl(sc);
		if (sc != TYPEDEF)
			outdef();
	}
	else
	if (level >= L_LOCAL && sc != STATIC && is_function) {
		local_EFDC(idf);
	}
}

lint_non_function_decl(ds, dc)
	struct decspecs *ds;
	struct declarator *dc;
{
	register struct def *def = dc->dc_idf->id_def;
	register int is_function = def->df_type->tp_fund == FUNCTION;

	if (is_function)
		def2decl(ds->ds_sc);
	if (def->df_sc != TYPEDEF)
		outdef();
}

lint_ext_def(idf, sc)
	struct idf *idf;
{
/* At this place the following fields of the output definition can be
 * filled:
 *		od_name, od_statnr, od_class, od_file, od_line, od_type.
 * For variable definitions and declarations this will be all.
 * For functions the fields od_nrargs and od_arg are filled after parsing
 * the arguments.
 * The od_valreturned field is known at the end of the function definition.
 * sc indicates the storage class defined by the declaration specifier.
 */
	register struct def *def = idf->id_def;
	register struct type *type = def->df_type;

	OutDef.od_name = idf->id_text;
	OutDef.od_statnr = (sc == STATIC ? stat_number : 0);

	switch (type->tp_fund) {
	case ERRONEOUS:
		OutDef.od_class = XXDF;
		break;
	case FUNCTION:
		/* For the moment assume it will be a definition.
		 * If no compound_statement follows, it is a declaration,
		 * in which case the class will be adjusted by def2decl().
		 */
		OutDef.od_class = (sc == STATIC ? SFDF : EFDF);
		break;
	default:	/* a variable */
		OutDef.od_class =
			sc == EXTERN ? EVDC :
			sc == STATIC ? SVDF : EVDF;
		break;
	}
	OutDef.od_file = def->df_file;
	OutDef.od_line = def->df_line;
	OutDef.od_type = (type->tp_fund == FUNCTION ? type->tp_up : type);
	OutDef.od_valreturned = NORETURN;
}

PRIVATE
def2decl(sc)
	int sc;
{
/* It was assumed we were parsing a function definition.
 * There was no compound statement following, so actually it was a
 * declaration. This function updates the class.
 */
	OutDef.od_class = (sc == STATIC ? XXDF : EFDC);
}

set_od_valreturned(n)
{
	OutDef.od_valreturned = n;
}

PRIVATE
local_EFDC(idf)
	struct idf *idf;
{
	struct outdef od;

	od.od_class = EFDC;
	od.od_statnr = 0;
	od.od_name = idf->id_text;
	od.od_file = idf->id_def->df_file;
	od.od_line = idf->id_def->df_line;
	od.od_type = idf->id_def->df_type->tp_up;
	output_def(&od);
	/* The other fields are not used for this class. */
}

lint_formals()
{
/* Make a list of 'struct argument's containing the types of the formal
 * parameters of the function definition just parsed.
 */
	register struct stack_entry *se = stack_level_of(L_FORMAL1)->sl_entry;
	register struct argument **hook = &OutDef.od_arg;
	register int nrargs = 0;

	while (se) {
		register struct type *type = se->se_idf->id_def->df_type;
		register struct argument *arg = new_argument();

		if (f_FORMAT && nrargs == f_FORMATn) {
			if (	!f_FORMATvar
			&&	(	type->tp_fund != POINTER
				||	type->tp_up->tp_fund != CHAR
				)
			) {
				warning("format parameter %d is not pointer to char",
					nrargs);
			}
			arg->ar_type = string_type;
			arg->ar_class = ArgString;
			arg->CAS_VALUE = f_FORMAT;
			arg->CAS_LEN = strlen(f_FORMAT);
			f_FORMAT = 0;
		}
		else {
			arg->ar_type = type;
			arg->ar_class = ArgFormal;
		}
		*hook = arg;
		hook = &arg->next;

		nrargs++;
		se = se->next;
	}

	if (f_FORMAT) {
		/*	f_FORMAT has not been consumed, perhaps due to
			a varargs-like construction; add erroneous ArgFormals
			until f_FORMATn, then an ArgString, if necessary.
		*/
		if (!f_FORMATvar) {
			warning("FORMAT%d function has only %d argument%s",
				f_FORMATn, nrargs, nrargs == 1 ? "" : "s"
			);
		}

		while (nrargs < f_FORMATn) {
			register struct argument *arg = new_argument();
			
			arg->ar_type = error_type;
			arg->ar_class = ArgFormal;
			*hook = arg;
			hook = &arg->next;
			nrargs++;
		}
		if (nrargs == f_FORMATn) {
			register struct argument *arg = new_argument();
			
			arg->ar_type = string_type;
			arg->ar_class = ArgString;
			arg->CAS_VALUE = f_FORMAT;
			arg->CAS_LEN = strlen(f_FORMAT);
			f_FORMAT = 0;
			*hook = arg;
			hook = &arg->next;
			nrargs++;
		}
		/* life is full of duplicated code; this is no good */
	}

	if (f_VARARGSn > nrargs) {
		warning("VARARGS%d function has only %d argument%s",
			f_VARARGSn, nrargs, nrargs == 1 ? "" : "s"
		);
		f_VARARGSn = nrargs;
	}
	OutDef.od_nrargs = nrargs;
}

output_proto(idf, def)
	struct idf *idf;
	struct def *def;
{
	/* fund == FUNCTION && sc != STATIC */
	register struct proto *pl = def->df_type->tp_proto;
	register int nrargs = 0;

	if (!pl) return;

	OutDef.od_name = idf->id_text;
	OutDef.od_statnr = 0;
	OutDef.od_class = PFDF;
	OutDef.od_file = def->df_file;
	OutDef.od_line = def->df_line;
	OutDef.od_type = def->df_type->tp_up;
	OutDef.od_valreturned = NORETURN;/*???*/

	while (pl) {
		register struct type *type = pl->pl_type;
		register struct argument *arg = new_argument();

		if (type) {
			arg->ar_type = type;
			arg->ar_class = ArgFormal;
		}
		else {
			arg->ar_class = ArgEllipsis;
		}
		arg->next = OutDef.od_arg;
		OutDef.od_arg = arg;

		nrargs++;
		pl = pl->next;
	}

	OutDef.od_nrargs = nrargs;
	outdef();
}

output_use(idf)
	struct idf *idf;
{
/* Output the usage-definition of the variable described by idf.
 */
	OutDef.od_name = idf->id_text;
	OutDef.od_statnr = (idf->id_def->df_sc == STATIC ? stat_number : 0);
	OutDef.od_class = VU;
	OutDef.od_file = FileName;
	OutDef.od_line = LineNumber;
	OutDef.od_type = idf->id_def->df_type;
	outdef();
}

outdef()
{
	output_def(&OutDef);
}

outcall()
{
	output_def(&OutCall);
}

PRIVATE
output_def(od)
	struct outdef *od;
{
/* As the types are output the 'struct argument's are freed, because they
 * are then not needed anymore.
 */
	if (od->od_class == XXDF || !od->od_name || od->od_name[0] == '#')
		return;

	if (LINTLIB) {
		switch (od->od_class) {
		case EFDF:
			od->od_class = LFDF;
			break;
		case EVDF:
			od->od_class = LVDF;
			break;
		case SFDF:
			/* free the 'struct argument's */
			while (od->od_arg) {
				register struct argument *tmp = od->od_arg;
				od->od_arg = od->od_arg->next;
				free_argument(tmp);
			}
			return;
		default:
			return;
		}
	}
	printf("%s:%d:%c", od->od_name, od->od_statnr, od->od_class);
	switch (od->od_class) {
	case LFDF:
	case PFDF:
	case EFDF:
	case SFDF:
		if (f_VARARGSn != -1) {
			printf(":%d", -1 - f_VARARGSn);
			outargs(od->od_arg, f_VARARGSn);
		}
		else {
			printf(":%d", od->od_nrargs);
			outargs(od->od_arg, od->od_nrargs);
		}
		od->od_arg = 0;
		printf(":%d", od->od_valreturned);
		break;
	case FC:
		printf(":%d", od->od_nrargs);
		outargs(od->od_arg, od->od_nrargs);
		od->od_arg = 0;
		printf(":%d", od->od_valused);
		break;
	case EVDF:
	case SVDF:
	case LVDF:
	case EFDC:
	case EVDC:
	case IFDC:
	case VU:
		break;
	default:
		NOTREACHED();
		/*NOTREACHED*/
	}
	printf(":");
	outargtype(od->od_type);
	printf(":%u:%s\n", od->od_line, od->od_file);
}

PRIVATE
outargs(arg, n)
	struct argument *arg;
{
/* Output the n arguments in the argument list and remove them */

	register struct argument *tmp;

	while (n--) {
		assert(arg);
		outarg(arg);
		tmp = arg;
		arg = arg->next;
		free_argument(tmp);
	}
	/* remove the remaining entries */
	while (arg) {
		tmp = arg;
		arg = arg->next;
		free_argument(tmp);
	}
}

PRIVATE
outarg(arg)
	struct argument *arg;
{
	printf(":");
	switch (arg->ar_class) {
	case ArgConst:
		if (arg->CAA_VALUE >= 0) {
			/* constant non-negative actual parameter */
			printf("+");
		}
		outargtype(arg->ar_type);
		break;

	case ArgString:
		outargstring(arg);
		break;

	case ArgFormal:
	case ArgExpr:
		outargtype(arg->ar_type);
		if (arg->ar_type->tp_fund == FUNCTION) {
			/* UGLY PATCH !!! ??? */
			/*	function names as operands are sometimes
				FUNCTION and sometimes POINTER to FUNCTION,
				depending on opaque circumstances.  E.g., in
					f(main, main);
				the first main is PtF and the second is F.
			*/
			printf("*");
		}
		break;

	case ArgEllipsis:
		printf(".");	/* one is enough for computers */
		break;

	default:
		NOTREACHED();
		/*NOTREACHED*/
	}
}

PRIVATE
outargstring(arg)
	struct argument *arg;
{
	char buff[1000];
	register char *p;

	bts2str(arg->CAS_VALUE, arg->CAS_LEN, buff);
	for (p = &buff[0]; *p; p++) {
		if (*p == '"' || *p == ':')
			*p = ' ';
	}
	printf("\"%s\"", buff);
}

PRIVATE
outargtype(tp)
	struct type *tp;
{
	switch (tp->tp_fund) {
	case POINTER:
		outargtype(tp->tp_up);
		printf("*");
		break;

	case ARRAY:
		outargtype(tp->tp_up);
		printf("*");	/* compatible with [] */
		break;

	case FUNCTION:
		outargtype(tp->tp_up);
		printf("()");
		break;

	case STRUCT:
	case UNION:
	case ENUM:
		/* watch out for anonymous identifiers; the count field does
		   not have to be the same for all compilation units.
		   Remove it, so that pass 2 does not see it. The only
		   problem with this is that pass2 will not see a difference
		   between two non-tagged types declared on the same line.
		*/
		printf("%s ", symbol2str(tp->tp_fund));
		if (is_anon_idf(tp->tp_idf)) {
			/* skip the #<num>, replace it by '#anonymous id' */
			printf("#anonymous id%s",
				strchr(tp->tp_idf->id_text, ' ')
			);
		}
		else {
			printf(tp->tp_idf->id_text);
		}
		break;

	case CHAR:
	case INT:
	case SHORT:
	case LONG:
	case ULONG:
	case FLOAT:
	case DOUBLE:
	case LNGDBL:
	case VOID:
	case ERRONEOUS:
		if (tp->tp_unsigned) {
			printf("unsigned ");
		}
		printf("%s", symbol2str(tp->tp_fund));
		break;
	default:
		NOTREACHED();
		/*NOTREACHED*/
	}
}

#ifdef	IMPLICIT
PRIVATE
implicit_func_decl(idf, file, line)
	struct idf *idf;
	char *file;
	unsigned int line;
{
	struct outdef od;

	od.od_class = IFDC;
	od.od_statnr = 0;
	od.od_name = idf->id_text;
	od.od_file = file;
	od.od_line = line;
	od.od_type = idf->id_def->df_type->tp_up;
	output_def(&od);
	/* The other fields are not used for this class. */
}
#endif	/* IMPLICIT */

fill_outcall(ex, used)
	struct expr *ex;
	int used;
{
	register struct idf *idf = ex->OP_LEFT->VL_IDF;
	register struct def *def = idf->id_def;

#ifdef	IMPLICIT
	if (def->df_sc == IMPLICIT && !idf->id_def->df_used) {
		/* IFDC, first time */
		implicit_func_decl(idf, ex->ex_file, ex->ex_line);
	}
#endif	/* IMPLICIT */

	OutCall.od_type = def->df_type->tp_up;
	OutCall.od_statnr = (def->df_sc == STATIC ? stat_number : 0);
	OutCall.od_class = FC;
	OutCall.od_name = idf->id_text;
	OutCall.od_file = ex->ex_file;
	OutCall.od_line = ex->ex_line;
	OutCall.od_arg = (struct argument *)0;
	OutCall.od_nrargs = 0;

	if ((ex = ex->OP_RIGHT) != 0) {
		/* function call with arguments: */
		/* store types of argument expressions in 'struct argument's */
		while (ex->ex_class == Oper && ex->OP_OPER == PARCOMMA) {
			add_expr_arg(ex->OP_RIGHT);
			ex = ex->OP_LEFT;
		}
		add_expr_arg(ex);
	}
	OutCall.od_valused = used;	/* USED, IGNORED or VOIDED */
}

PRIVATE
add_expr_arg(e)
	struct expr *e;
{
	register struct argument *arg;

	arg = new_argument();
	arg->ar_type = e->ex_type;
	if (is_cp_cst(e)) {
		arg->ar_class = ArgConst;
		arg->CAA_VALUE = e->VL_VALUE;
	}
	else if (	e->ex_type == string_type
		&&	e->ex_class == Value
		&&	e->VL_CLASS == Label
		) {
		/* it may be a string; let's look it up */
		register struct string_cst *sc = str_list;

		while (sc) {
			if (sc->sc_dlb == e->VL_LBL)
				break;
			sc = sc->next;
		}
		if (sc) {
			/* it was a string */
			arg->ar_class = ArgString;
			arg->CAS_VALUE = sc->sc_value;
			arg->CAS_LEN = sc->sc_len - 1;	/* included the \0 */
		}
		else {
			arg->ar_class = ArgExpr;
		}
	}
	else {
		arg->ar_class = ArgExpr;
	}
	arg->next = OutCall.od_arg;
	OutCall.od_arg = arg;
	OutCall.od_nrargs++;
}

#endif	/* LINT */
