/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*           Copyright (c) 1984-2006 AT&T Knowledge Ventures            *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                      by AT&T Knowledge Ventures                      *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * make dump and trace routines
 */

#include "make.h"

static int		dumpall;	/* don't be selective		*/

/*
 * list a rule name in re-readable form
 */

static void
dumpname(register Sfio_t* sp, register Rule_t* r, char* sep)
{
	register char*	s;
	register char*	t;
	register int	paren;
	int		quote;

	if (!dumpall && state.list)
	{
		s = (r->property & P_state) || (r->dynamic & D_alias) ? r->name : unbound(r);
		for (t = s, paren = 0; *t && (paren || !istype(*t, C_TERMINAL)); t++)
			if (*t == '(')
				paren++;
			else if (*t == ')')
				paren--;
		if (quote = (*t || t == s))
			sfputc(sp, '"');
		for (; *s; s++)
		{
			switch (*s)
			{
			case '"':
				if (quote)
					sfputc(sp, '\\');
				break;
			case '$':
				if (*(s + 1) == '(')
					sfputc(sp, '$');
				break;
			}
			sfputc(sp, *s);
		}
		if (quote)
			sfputc(sp, '"');
	}
	else
	{
		if (*r->name)
			sfputr(sp, r->name, -1);
		else
		{
			sfputc(sp, '"');
			sfputc(sp, '"');
		}
		if (!(r->property & P_state) && r->uname)
			sfprintf(sp, "==%s", r->uname);
	}
#if DEBUG
	if (state.test & 0x00004000)
		sfprintf(sp, "@%p", r);
#endif
	sfputr(sp, sep, -1);
}

/*
 * list a single rule and its attributes
 */

static int
listrule(const char* s, char* v, void* h)
{
	register Rule_t*	r = (Rule_t*)v;
	register List_t*	p;
	register int		n;
	register Sfio_t*	sp = (Sfio_t*)h;

	if (!dumpall && state.list && (r->name != s ||
		!(r->property & (P_accept|P_after|P_always|P_archive|P_before|P_command|P_force|P_foreground|P_functional|P_ignore|P_immediate|P_implicit|P_joint|P_local|P_make|P_multiple|P_operator|P_parameter|P_read|P_repeat|P_target|P_terminal|P_use|P_virtual)) && !((r->property & P_attribute) && r->attribute) && !r->semaphore))
			return 0;
	sfputc(sp, '\n');
	dumpname(sp, r, " : ");
	if (dumpall || !state.list)
	{
		if ((r->property & P_staterule) && isaltstate(r->name))
			sfputr(sp, "cancel=", -1);
		sfprintf(sp, "[%s] ", timestr(r->time));
	}
	if (!(r->property & P_attribute))
	{
		if (r->attribute)
			for (p = internal.attribute->prereqs; p; p = p->next)
				if (r->attribute & p->rule->attribute)
					sfputr(sp, p->rule->name, ' ');
		if (r->scan)
			for (p = internal.scan->prereqs; p; p = p->next)
				if (p->rule->scan == r->scan)
				{
					sfputr(sp, p->rule->name, ' ');
					break;
				}
	}
	if (dumpall || !state.list)
	{
		if (r->property & P_state)
		{
			if (r->property & P_staterule)
			{
				if (!isaltstate(r->name))
					sfprintf(sp, "event=[%s] ", timestr(r->event));
			}
			else if (r->property & P_statevar)
				sfputr(sp, "statevar", ' ');
			else
				sfputr(sp, "state", ' ');
		}
		if (r->view > r->preview)
			sfprintf(sp, "view=%c/%c ", VIEWOFFSET + r->view, VIEWOFFSET + r->preview);
		else if (r->view)
			sfprintf(sp, "view=%c ", VIEWOFFSET + r->view);
		else if (r->preview && r->preview <= state.maxview)
			sfprintf(sp, "preview=%c ", VIEWOFFSET + r->preview);
		if (r->action && !*r->action)
			sfputr(sp, "null", ' ');
		if (r->must)
			sfprintf(sp, "must=%d ", r->must);
		if (r->semaphore)
			sfprintf(sp, "semaphore=%d ", r->semaphore - 1);

		if (r->property & P_archive)
			sfputr(sp, "archive", ' ');
		if (r->property & P_accept)
			sfputr(sp, "accept", ' ');
		if (r->active)
			sfputr(sp, "active", ' ');
		if (r->property & P_after)
			sfputr(sp, "after", ' ');
		if (r->property & P_always)
			sfputr(sp, "always", ' ');
		if (r->property & P_attribute)
		{
			if (r->attribute)
				sfprintf(sp, "attribute=0x%08x ", r->attribute);
			else if (r->scan)
				sfprintf(sp, "scan=%d ", r->scan);
			else
				sfputr(sp, "attribute", ' ');
		}
		if (r->property & P_before)
			sfputr(sp, "before", ' ');
		if (r->property & P_command)
			sfputr(sp, "command", ' ');
		if (r->property & P_dontcare)
			sfputr(sp, "dontcare", ' ');
		if (r->property & P_force)
			sfputr(sp, "force", ' ');
		if (r->property & P_foreground)
			sfputr(sp, "foreground", ' ');
		if (r->property & P_functional)
			sfputr(sp, "functional", ' ');
		if (r->property & P_ignore)
			sfputr(sp, "ignore", ' ');
		if (r->property & P_immediate)
			sfputr(sp, "immediate", ' ');
		if (r->property & P_implicit)
			sfputr(sp, "implicit", ' ');
		if (r->property & P_internal)
			sfputr(sp, "internal", ' ');
		if (r->property & P_joint)
			sfputr(sp, "joint", ' ');
		if (r->property & P_local)
			sfputr(sp, "local", ' ');
		if (r->property & P_make)
			sfputr(sp, "make", ' ');
		if (r->property & P_metarule)
			sfputr(sp, "metarule", ' ');
		if (r->property & P_multiple)
			sfputr(sp, "multiple", ' ');
		if (r->property & P_operator)
			sfputr(sp, "operator", ' ');
		if (r->property & P_parameter)
			sfputr(sp, "parameter", ' ');
		if (r->property & P_read)
			sfputr(sp, "read", ' ');
		if (r->property & P_readonly)
			sfputr(sp, "readonly", ' ');
		if (r->property & P_repeat)
			sfputr(sp, "repeat", ' ');
		if (r->property & P_target)
			sfputr(sp, "target", ' ');
		if (r->property & P_terminal)
			sfputr(sp, "terminal", ' ');
		if (r->property & P_use)
			sfputr(sp, "use", ' ');
		if (r->property & P_virtual)
			sfputr(sp, "virtual", ' ');

		if (r->dynamic & D_alias)
			sfputr(sp, "alias", ' ');
		if (r->dynamic & D_aliaschanged)
			sfputr(sp, "aliaschanged", ' ');
#if BINDINDEX
		if (r->dynamic & D_bindindex)
			sfputr(sp, "bindindex", ' ');
#endif
		if (r->dynamic & D_built)
			sfputr(sp, "built", ' ');
		if (r->dynamic & D_cached)
			sfputr(sp, "cached", ' ');
		if (r->dynamic & D_compiled)
			sfputr(sp, "compiled", ' ');
		if (r->dynamic & D_context)
			sfputr(sp, "context", ' ');
		if (r->dynamic & D_dynamic)
			sfputr(sp, "dynamic", ' ');
		if (r->dynamic & D_entries)
			sfputr(sp, "entries", ' ');
		if (r->dynamic & D_garbage)
			sfputr(sp, "garbage", ' ');
		if (r->dynamic & D_global)
			sfputr(sp, "global", ' ');
		if (r->dynamic & D_hasafter)
			sfputr(sp, "hasafter", ' ');
		if (r->dynamic & D_hasbefore)
			sfputr(sp, "hasbefore", ' ');
		if (r->dynamic & D_hasmake)
			sfputr(sp, "hasmake", ' ');
		if (r->dynamic & D_hasscope)
			sfputr(sp, "hasscope", ' ');
		if (r->dynamic & D_hassemaphore)
			sfputr(sp, "hassemaphore", ' ');
		if (r->dynamic & D_index)
			sfputr(sp, "index", ' ');
		if (r->dynamic & D_intermediate)
			sfputr(sp, "intermediate", ' ');
		if (r->dynamic & D_lower)
			sfputr(sp, "lower", ' ');
		if (r->dynamic & D_lowres)
			sfputr(sp, "lowres", ' ');
		if (r->dynamic & D_member)
			sfputr(sp, "member", ' ');
		if (r->dynamic & D_membertoo)
			sfputr(sp, "membertoo", ' ');
		if (r->dynamic & D_regular)
			sfputr(sp, "regular", ' ');
		if (r->dynamic & D_same)
			sfputr(sp, "same", ' ');
		if (r->dynamic & D_scanned)
			sfputr(sp, "scanned", ' ');
		if (r->dynamic & D_scope)
			sfputr(sp, "scope", ' ');
		if (r->dynamic & D_select0)
			sfputr(sp, "select0", ' ');
		if (r->dynamic & D_select1)
			sfputr(sp, "select1", ' ');
		if (r->dynamic & D_source)
			sfputr(sp, "source", ' ');
		if (r->dynamic & D_triggered)
			sfputr(sp, "triggered", ' ');
		if ((r->property & (P_state|P_statevar)) == P_state)
			sfputr(sp, "state", -1);
		else if (!(r->dynamic & D_bound))
			sfputr(sp, "unbound", -1);
		else
			switch (r->status)
			{
			case NOTYET:
				break;
			case UPDATE:
				sfputr(sp, "UPDATE", -1);
				break;
			case MAKING:
				sfputr(sp, "MAKING", -1);
				break;
			case TOUCH:
				sfputr(sp, "TOUCH", -1);
				break;
			case EXISTS:
				sfputr(sp, "EXISTS", -1);
				break;
			case IGNORE:
				sfputr(sp, "IGNORE", -1);
				break;
			case FAILED:
				sfputr(sp, "FAILED", -1);
				break;
			case OLDRULE:
				sfputr(sp, "OLDRULE", -1);
				break;
#if DEBUG
			default:
				sfprintf(sp, "STATUS=%d", r->status);
				break;
#endif
			}
		if (r->mark)
		{
			sfputr(sp, " |mark", '|');
			if (r->mark & M_bind)
				sfputr(sp, "bind", '|');
			if (r->mark & M_compile)
				sfputr(sp, "compile", '|');
			if (r->mark & M_directory)
				sfputr(sp, "directory", '|');
			if (r->mark & M_generate)
				sfputr(sp, "generate", '|');
			if (r->mark & M_mark)
				sfputr(sp, "mark", '|');
			if (r->mark & M_metarule)
				sfputr(sp, "metarule", '|');
			if (r->mark & M_scan)
				sfputr(sp, "scan", '|');
			if (r->mark & M_waiting)
				sfputr(sp, "waiting", '|');
		}
		sfputc(sp, '\n');
	}
	else
	{
		if (r->property & P_accept)
			sfputr(sp, internal.accept->name, ' ');
		if (r->property & P_after)
			sfputr(sp, internal.after->name, ' ');
		if (r->property & P_always)
			sfputr(sp, internal.always->name, ' ');
		if (r->property & P_attribute)
		{
			if (r->attribute)
				sfputr(sp, internal.attribute->name, ' ');
			else if (r->scan)
				sfputr(sp, internal.scan->name, ' ');
		}
		if (r->property & P_before)
			sfputr(sp, internal.before->name, ' ');
		if (r->property & P_force)
			sfputr(sp, internal.force->name, ' ');
		if (r->property & P_foreground)
			sfputr(sp, internal.foreground->name, ' ');
		if (r->property & P_functional)
			sfputr(sp, internal.functional->name, ' ');
		if (r->property & P_joint)
			sfputr(sp, internal.joint->name, ' ');
		if (r->property & P_make)
			sfputr(sp, internal.make->name, ' ');
		if (r->property & P_multiple)
			sfputr(sp, internal.multiple->name, ' ');
		if (r->action && !*r->action)
			sfputr(sp, internal.null->name, ' ');
		if (r->property & P_archive)
			sfputr(sp, internal.archive->name, ' ');
		if (r->property & P_command)
			sfputr(sp, internal.command->name, ' ');
		if (r->property & P_ignore)
			sfputr(sp, internal.ignore->name, ' ');
		if (r->property & P_immediate)
			sfputr(sp, internal.immediate->name, ' ');
		if (r->property & P_implicit)
			sfputr(sp, internal.implicit->name, ' ');
		if (r->property & P_local)
			sfputr(sp, internal.local->name, ' ');
		if (r->property & P_operator)
			sfputr(sp, internal.op->name, ' ');
		if (r->property & P_parameter)
			sfputr(sp, internal.parameter->name, ' ');
		if (r->property & P_repeat)
			sfputr(sp, internal.repeat->name, ' ');
		if (r->semaphore)
			for (n = r->semaphore - 1; n; n--)
				sfputr(sp, internal.semaphore->name, ' ');
		if (r->property & P_terminal)
			sfputr(sp, internal.terminal->name, ' ');
		if ((r->property & (P_metarule|P_use))
			== P_use) sfputr(sp, internal.use->name, ' ');
		if (r->property & P_virtual)
			sfputr(sp, internal.virt->name, ' ');
	}
	if (p = r->prereqs)
	{
		if (dumpall || !state.list)
			sfputr(sp, " prerequisites:", ' ');
		for (; p; p = p->next)
			dumpname(sp, p->rule, " ");
		sfputc(sp, '\n');
	}
	else if (!dumpall && state.list)
		sfputc(sp, '\n');
	if (r->action && *r->action)
	{
		if (dumpall || !state.list)
			sfputr(sp, " action:", '\n');
		dumpaction(sp, NiL, r->action, "\t");
	}
	if ((dumpall || !state.list) && (r->property & P_statevar) && r->statedata)
		sfprintf(sp, " state: %s\n", r->statedata);
	return 0;
}

/*
 * list a single variable and its value
 */

static int
listvar(const char* s, char* u, void* h)
{
	register Var_t*		v = (Var_t*)u;
	register char*		t;
	register char*		q;
	register Sfio_t*	sp = (Sfio_t*)h;

	if (dumpall || !(v->property & V_import) && (!state.list || !isintvar(v->name)))
	{
		if (!dumpall && state.list)
		{
			for (q = (char*)s; istype(*q, C_ID1|C_ID2); q++);
			if (*q)
				q = "\"";
		}
		else
			q = null;
		sfprintf(sp, "%s%s%s ", q, s, q);
		if (dumpall || !state.list)
		{
			sfputr(sp, "[", ' ');
			if (v->property & V_append)
				sfputr(sp, "append", ' ');
			if (v->property & V_auxiliary)
				sfputr(sp, "auxiliary", ' ');
			if (v->property & V_builtin)
				sfputr(sp, "builtin", ' ');
			if (v->property & V_free)
				sfputr(sp, "free", ' ');
			if (v->property & V_frozen)
				sfputr(sp, "frozen", ' ');
			if (v->property & V_functional)
				sfputr(sp, "functional", ' ');
			if (v->property & V_import)
				sfputr(sp, "import", ' ');
			if (v->property & V_local_D)
				sfputr(sp, "local_D", ' ');
			if (v->property & V_local_E)
				sfputr(sp, "local_E", ' ');
			if (v->property & V_oldvalue)
			{
				sfputr(sp, "oldvalue", -1);
				if (t = getold(s))
					sfprintf(sp, "=`%s'", t);
				sfputc(sp, ' ');
			}
			if (v->property & V_readonly)
				sfputr(sp, "readonly", ' ');
			if (v->property & V_restored)
				sfputr(sp, "restored", ' ');
			if (v->property & V_retain)
				sfputr(sp, "retain", ' ');
			if (v->property & V_scan)
				sfputr(sp, "scan", ' ');
			if (v->property & V_scope)
				sfputr(sp, "scope", ' ');
			sfputr(sp, "]", ' ');
		}
		sfprintf(sp, "= %s", t = v->value);
		if ((v->property & V_auxiliary) && (v = auxiliary(v->name, 0)))
		{
			if (!dumpall && state.list)
				sfprintf(sp, "\n%s%s%s ", q, s, q);
			else if (*t)
				sfputc(sp, ' ');
			sfprintf(sp, "&= %s", v->value);
		}
		sfputc(sp, '\n');
	}
	return 0;
}

/*
 * dump rules and variables
 */

void
dump(Sfio_t* sp, int verbose)
{
	static int	dumping;

	if (!dumping++)
	{
		if (state.vardump || state.ruledump)
			state.list = 0;
		if (verbose)
			hashdump(NiL, (error_info.trace <= -20) ? HASH_BUCKET : 0);
		if (state.list)
			sfprintf(sp, "/* %s */\n\n", version);
		if (!dumpall && (state.list || state.vardump))
		{
			sfprintf(sp, "\n/* Variables */\n\n");
			hashwalk(table.var, 0, listvar, sp);
		}
		if (!dumpall && (state.list || state.ruledump))
		{
			sfprintf(sp, "\n/* Rules */\n");
			hashwalk(table.rule, 0, listrule, sp);
		}
		sfsync(sp);
		dumping--;
	}
}

/*
 * dump regression prefix,name,value
 */

void
dumpregress(register Sfio_t* sp, const char* prefix, const char* name, register char* value)
{
	register int	c;
	register int	n;
	int*		rp;
	char*		bp;
	char*		np;

	static int	index;

	sfprintf(sp, "%s%s %s ", state.mam.label, prefix, name ? name : "-");
	n = -1;
	if (value)
		for (;;)
		{
			switch (c = *value++)
			{
			case 0:
				break;
			case '\n':
				sfprintf(sp, "\n%s%s %s ", state.mam.label, prefix, name ? name : "-");
				n = 1;
				continue;
			case '\t':
				c = ' ';
				/*FALLTHROUGH*/
			case ' ':
				if (n < 0 || *value == ' ' || *value == '\t' || *value == 0 || *value == '\n')
					continue;
				/*FALLTHROUGH*/
			case '\'':
			case '"':
			case '=':
			case ':':
				sfputc(sp, c);
				n = 1;
				continue;
			case '/':
				if (n)
				{
					bp = np = value - 1;
					for (;;)
					{
						switch (*value++)
						{
						case ' ':
						case '\t':
						case '\n':
						case '\'':
						case '"':
						case ':':
						case 0:
							break;
						case '/':
							np = value - 1;
							/*FALLTHROUGH*/
						default:
							continue;
						}
						break;
					}
					c = *--value;
					*value = 0;
					if (strmatch(np, "*.*"))
					{
						*value = c;
						c = *(value = np);
						*value = 0;
					}
					if (!(rp = getreg(bp)))
					{
						rp = newof(0, int, 1, 0);
						*rp = ++index;
						putreg(0, rp);
					}
					sfprintf(sp, "${PATH_%d}", *rp);
					*value = c;
					n = 0;
				}
				else
					sfputc(sp, c);
				continue;
			case '-':
			case '+':
				sfputc(sp, c);
				if (n)
				{
					if (!(c = *value++))
						break;
					sfputc(sp, c);
					n = isalnum(c) ? 1 : 0;
				}
				continue;
			default:
				sfputc(sp, c);
				n = 0;
				continue;
			}
			break;
		}
	sfputc(sp, '\n');
}

/*
 * dump an action placing prefix at the beginning of each line
 */

void
dumpaction(Sfio_t* sp, const char* name, register char* action, register const char* prefix)
{
	register char*	s;
	char*		mamlabel;
	char*		sep;

	if (prefix)
	{
		mamlabel = null;
		name = null;
		sep = null;
	}
	else
	{
		if (state.mam.regress)
		{
			dumpregress(sp, "exec", name, action);
			return;
		}
		if (state.mam.out)
		{
			mamlabel = state.mam.label;
			prefix = "exec";
			if (!name)
				name = "-";
			sep = " ";
		}
		else
		{
			while (isspace(*action))
				action++;
			if (!*action)
				return;
			mamlabel = null;
			prefix = "+";
			name = null;
			sep = null;
		}
	}
	for (;;)
	{
		if (s = strchr(action, '\n'))
			*s = 0;
		sfprintf(sp, "%s%s %s%s%s\n", mamlabel, prefix, name, sep, action);
		if (!s)
			break;
		*s++ = '\n';
		action = s;
	}
	sfsync(sp);
}

/*
 * dump variable info
 */

void
dumpvar(Sfio_t* sp, register Var_t* v)
{
	dumpall++;
	listvar(v->name, (char*)v, sp);
	sfsync(sp);
	dumpall--;
}

/*
 * dump rule info
 */

void
dumprule(Sfio_t* sp, register Rule_t* r)
{
	register int		i;
	register Rule_t*	z;

	dumpall++;
	z = 0;
	while ((r->dynamic & D_alias) && r != z)
	{
		listrule(r->name, (char*)r, sp);
		z = r;
		if (!(r = getrule(r->name)))
			r = z;
	}
	if (r == z)
		error(2, "%s: alias loop", r->name);
	else
		listrule(r->name, (char*)r, sp);
	if (!(r->property & P_state))
		for (i = RULE; i <= STATERULES; i++)
			if (z = staterule(i, r, NiL, -1))
				listrule(z->name, (char*)z, sp);
	sfsync(sp);
	dumpall--;
}
