#include "c.h"


static char *fmt, *fp, *fmtend;	/* format string, current & limit pointer */
static Tree args;		/* printf arguments */
static Symbol frameno;		/* local holding frame number */

/* appendstr - append str to the evolving format string, expanding it if necessary */
static void appendstr(char *str) {
	do
		if (fp == fmtend) {
			if (fp) {
				char *s = allocate(2*(fmtend - fmt), FUNC);
				strncpy(s, fmt, fmtend - fmt);
				fp = s + (fmtend - fmt);
				fmtend = s + 2*(fmtend - fmt);
				fmt = s;
			} else {
				fp = fmt = allocate(80, FUNC);
				fmtend = fmt + 80;
			}
		}
	while ((*fp++ = *str++) != 0);
	fp--;
}

/* tracevalue - append format and argument to print the value of e */
static void tracevalue(Tree e, int lev) {
	Type ty = unqual(e->type);

	switch (ty->op) {
	case INT:
		if (ty == chartype || ty == signedchar)
			appendstr("'\\x%02x'");
		else if (ty == longtype)
			appendstr("0x%ld");
		else
			appendstr("0x%d");
		break;
	case UNSIGNED:
		if (ty == chartype || ty == unsignedchar)
			appendstr("'\\x%02x'");
		else if (ty == unsignedlong)
			appendstr("0x%lx");
		else
			appendstr("0x%x");
		break;
	case FLOAT:
		if (ty == longdouble)
			appendstr("%Lg");
		else
			appendstr("%g");
		break;
	case POINTER:
		if (unqual(ty->type) == chartype
		||  unqual(ty->type) == signedchar
		||  unqual(ty->type) == unsignedchar) {
			static Symbol null;
			if (null == NULL)
				null = mkstr("(null)");
			tracevalue(cast(e, unsignedtype), lev + 1);
			appendstr(" \"%.30s\"");
			e = condtree(e, e, pointer(idtree(null->u.c.loc)));
		} else {
			appendstr("("); appendstr(typestring(ty, "")); appendstr(")0x%x");
		}
		break;
	case STRUCT: {
		Field q;
		appendstr("("); appendstr(typestring(ty, "")); appendstr("){");
		for (q = ty->u.sym->u.s.flist; q; q = q->link) {
			appendstr(q->name); appendstr("=");
			tracevalue(field(addrof(e), q->name), lev + 1);
			if (q->link)
				appendstr(",");
		}
		appendstr("}");
		return;
		}
	case UNION:
		appendstr("("); appendstr(typestring(ty, "")); appendstr("){...}");
		return;
	case ARRAY:
		if (lev && ty->type->size > 0) {
			int i;
			e = pointer(e);
			appendstr("{");
			for (i = 0; i < ty->size/ty->type->size; i++) {
				Tree p = (*optree['+'])(ADD, e, consttree(i, inttype));
				if (isptr(p->type) && isarray(p->type->type))
					p = retype(p, p->type->type);
				else
					p = rvalue(p);
				if (i)
					appendstr(",");
				tracevalue(p, lev + 1);
			}
			appendstr("}");
		} else
			appendstr(typestring(ty, ""));
		return;
	default:
		assert(0);
	}
	e = cast(e, promote(ty));
	args = tree(mkop(ARG,e->type), e->type, e, args);
}

/* tracefinis - complete & generate the trace call to print */
static void tracefinis(Symbol printer) {
	Tree *ap;
	Symbol p;

	*fp = 0;
	p = mkstr(string(fmt));
	for (ap = &args; *ap; ap = &(*ap)->kids[1])
		;
	*ap = tree(ARG+P, charptype, pointer(idtree(p->u.c.loc)), 0);
	walk(calltree(pointer(idtree(printer)), freturn(printer->type), args, NULL), 0, 0);
	args = 0;
	fp = fmtend = 0;
}

/* tracecall - generate code to trace entry to f */
static void tracecall(Symbol printer, Symbol f) {
	int i;
	Symbol counter = genident(STATIC, inttype, GLOBAL);

	defglobal(counter, BSS);
	(*IR->space)(counter->type->size);
	frameno = genident(AUTO, inttype, level);
	addlocal(frameno);
	appendstr(f->name); appendstr("#");
	tracevalue(asgn(frameno, incr(INCR, idtree(counter), consttree(1, inttype))), 0);
	appendstr("(");
	for (i = 0; f->u.f.callee[i]; i++) {
		if (i)
			appendstr(",");
		appendstr(f->u.f.callee[i]->name); appendstr("=");
		tracevalue(idtree(f->u.f.callee[i]), 0);
	}
	if (variadic(f->type))
		appendstr(",...");
	appendstr(") called\n");
	tracefinis(printer);
}

/* tracereturn - generate code to trace return e */
static void tracereturn(Symbol printer, Symbol f, Tree e) {
	appendstr(f->name); appendstr("#");
	tracevalue(idtree(frameno), 0);
	appendstr(" returned");
	if (freturn(f->type) != voidtype && e) {
		appendstr(" ");
		tracevalue(e, 0);
	}
	appendstr("\n");
	tracefinis(printer);
}

/* trace_init - initialize for tracing */
void trace_init(int argc, char *argv[]) {
	int i;
	static int inited;

	if (inited)
		return;
	inited = 1;
	type_init(argc, argv);
	if (IR)
		for (i = 1; i < argc; i++)
			if (strncmp(argv[i], "-t", 2) == 0 &&  strchr(argv[i], '=') == NULL) {
				Symbol printer = mksymbol(EXTERN,
					argv[i][2] ? &argv[i][2] : "printf",
				ftype(inttype, ptr(qual(CONST, chartype))));
				printer->defined = 0;
				attach((Apply)tracecall,   printer, &events.entry);
				attach((Apply)tracereturn, printer, &events.returns);
				break;
			}
}