/*
 *	SCCS: @(#)rrproc.c	1.7 (97/10/22)
 *
 *	UniSoft Ltd., London, England
 *
 * (C) Copyright 1996 X/Open Company Limited
 *
 * All rights reserved.  No part of this source code may be reproduced,
 * stored in a retrieval system, or transmitted, in any form or by any
 * means, electronic, mechanical, photocopying, recording or otherwise,
 * except as stated in the end-user licence agreement, without the prior
 * permission of the copyright owners.
 * A copy of the end-user licence agreement is contained in the file
 * Licence which accompanies this distribution.
 * 
 * X/Open and the 'X' symbol are trademarks of X/Open Company Limited in
 * the UK and other countries.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/************************************************************************

SCCS:   	@(#)rrproc.c	1.7 97/10/22 TETware release 3.3
NAME:		rrproc.c
PRODUCT:	TETware
AUTHOR:		Andrew Dingwall, UniSoft Ltd.
DATE CREATED:	September 1996

DESCRIPTION:
	tcc rerun and resume processing functions

MODIFICATIONS:
	Geoff Clare, UniSoft Ltd., Oct 1996
	Added <string.h>.

	Andrew Dingwall, UniSoft Ltd., June 1997
	fixed a problem with the way in which the resume_iclist is
	extended after a resume point is found part-way through a test
	case in EXEC mode

	Andrew Dingwall, UniSoft Ltd., October 1997
	added a work-around for LINUX getopt() which scribbles on the argv
	that is passed to it

	Aaron Plattner, April 2010
	Fixed warnings when compiled with GCC's -Wall option.

************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <ctype.h>
#include <errno.h>
#  include <unistd.h>
#include "dtmac.h"
#include "error.h"
#include "ltoa.h"
#include "dtetlib.h"
#include "scentab.h"
#include "dirtab.h"
#include "tcc.h"
#include "tet_jrnl.h"

/* rescodes and modes of operation from the command-line */
static int *rrcodes;
static int lrrcodes, nrrcodes;
static int rrmodes;

/* name and handle for the old journal file */
static char *jfname;
static FILE *jfp;

/*
** the scenario resume point when in RESUME mode
**
** information about where to resume a loop is stored in the related
** loop directive node in the scenario tree
*/
struct scentab *resume_scen;	/* the resume point */
int resume_mode;		/* mode in which to resume */
char *resume_iclist;		/* iclist to use when resuming in EXEC mode */
int resume_found;		/* set by the execution engine when the resume
				   point has been found */

/* structure of a broken out journal line */
struct jline {
	long jl_lineno;			/* line number in the journal file */
	long jl_offset;			/* offset of line from start of file */
	int jl_id;			/* the line's ID from field 1 */
	char *jl_flds2[5];		/* subfields in field 2 */
	char *jl_fld3;			/* the text field */
	long jl_ref;			/* reference from scenario ref */
	int jl_sys;			/* sysid from scenario ref */
};


/* static function declarations */
static void badynopt PROTOLIST((int));
static void clear_exiclists PROTOLIST((struct scentab *));
static void clear_itcount PROTOLIST((struct scentab *));
static void clear_scflags PROTOLIST((struct scentab *, int));
static void exiclist_addupdate PROTOLIST((struct scentab *, int));
static void exiclist_set PROTOLIST((struct scentab *, char *));
static char *prjnlid PROTOLIST((int));
static void remove_unneeded_tcs PROTOLIST((struct scentab *));
static void rrp_checkloopstart PROTOLIST((struct scentab *, struct jline *));
static int rrp_cl2 PROTOLIST((struct scentab *, struct scentab *, int));
static struct scentab *rrp_findscen PROTOLIST((struct jline *));
static struct scentab *rrp_fs2 PROTOLIST((struct scentab *, long));
static struct jline *rrp_getline PROTOLIST((char *));
static int rrp_procendline PROTOLIST((struct scentab *, struct jline *, int));
static int rrp_procic PROTOLIST((struct scentab *, int, char *, int));
static int rrp_proctc PROTOLIST((struct scentab *, char *, int));
static int rrp_proctp PROTOLIST((char *, int));
static void rrp_tccstart PROTOLIST((struct jline *));
static void rrp_tcsync PROTOLIST((struct scentab *, struct jline *));
static char **rrp_ts2 PROTOLIST((char *, int *));
static void set_scflags PROTOLIST((struct scentab *, int));


/*
**	rrproc() - prune the scenario tree in rerun or resume mode
*/

void rrproc(codelist, old_journal_file)
char *codelist, *old_journal_file;
{
	register char *p, **ap;
	register int n;
	int rrerrors = 0;
	char **argv = (char **) 0;
	int largv = 0;
	int argc, sel;
	char buf[LBUFLEN];
	struct jline *jlp;
	struct scentab *ep;

	jfname = old_journal_file;

	TRACE4(tet_Ttcc, 4, "rrproc(): rerun/resume = %s, codelist = \"%s\", old journal file = \"%s\"",
		prtccmode(tcc_modes & (TCC_RERUN | TCC_RESUME)),
		codelist, jfname);

	/*
	**	decode the codelist from the command-line
	*/

	/* count the comma-separated fields */
	n = 1;
	for (p = codelist; *p; p++)
		if (*p == ',')
			n++;

	/*
	** get an argv big enough for all the fields
	** and split the codelist into fields
	*/
	RBUFCHK((char **) &argv, &largv, n * sizeof *argv);
	sprintf(buf, "%.*s", (int) sizeof buf - 1, codelist);
	argc = split(buf, argv, n, ',');

	/*
	** process each field in the codelist -
	** if it a mode of operation, set the corresponding bit in rrmodes;
	** otherwise, if it is a valid result code name, append the
	** corresponding result code value to the list at *rrcodes
	*/
	for (ap = argv; ap < argv + argc; ap++) {
		if (**ap == 'b' && *(*ap + 1) == '\0')
			rrmodes |= TCC_BUILD;
		else if (**ap == 'e' && *(*ap + 1) == '\0')
			rrmodes |= TCC_EXEC;
		else if (**ap == 'c' && *(*ap + 1) == '\0')
			rrmodes |= TCC_CLEAN;
		else if ((n = tet_getrescode(*ap, (int *) 0)) < 0) {
			error(0, *ap, "is not a valid result code name or mode of operation");
			rrerrors++;
		}
		else {
			RBUFCHK((char **) &rrcodes, &lrrcodes,
				(int) ((nrrcodes + 1) * sizeof *rrcodes));
			*(rrcodes + nrrcodes++) = n;
		}
	}

	/* free the argv */
	TRACE2(tet_Tbuf, 6, "free codelist argv = %s", tet_i2x(argv));
	free((char *) argv);
	largv = argc = 0;
	argv = (char **) 0;

	/* exit now if there have been any codelist errors */
	if (rrerrors)
		tcc_exit(2);

#ifndef NOTRACE
	TRACE3(tet_Ttcc, 8, "rrmodes = %s, nrrcodes = %s",
		prtccmode(rrmodes), tet_i2a(nrrcodes));
	if (rrcodes && tet_Ttcc >= 8) {
		int *ip;
		for (ip = rrcodes; ip < rrcodes + nrrcodes; ip++)
			TRACE3(tet_Ttcc, 8, "rrcode %s = %s",
				tet_i2a(*ip), tet_getresname(*ip, (int *) 0));
	}
#endif

	/*
	**	process the old journal file
	*/

	/* open the old journal file */
	if ((jfp = fopen(jfname, "r")) == (FILE *) 0)
		fatal(errno, "can't open", jfname);

	/* read the first line in the file - should be a TCC Start line */
	if (
		(jlp = rrp_getline(buf)) == (struct jline *) 0 ||
		jlp->jl_id != TET_JNL_TCC_START
	) {
		fprintf(stderr, "TCC: old journal file does not start with a TCC Start line: %s\n",
			jfname);
		tcc_exit(1);
	}

	/*
	** inspect the command line arguments on the TCC start line
	** in the old journal file
	*/
	rrp_tccstart(jlp);

	/* clear the NEEDED flag in each node in the scenario tree */
	clear_scflags(sctree->sc_child, SCF_NEEDED);

	/* clear the iteration counts in each looping directive in the tree */
	clear_itcount(sctree->sc_child);

	/*
	** in RERUN mode, clear the default exec IC list in each test case
	** node in the scenario tree;
	** the exec IC lists will be rebuilt later to reflect the ICs
	** which should actually be rerun
	*/
	if (tcc_modes & TCC_RERUN)
		clear_exiclists(sctree->sc_child);

	/* read each journal line in turn and process it */
	sel = 0;
	while ((jlp = rrp_getline(buf)) != (struct jline *) 0) {
		switch (jlp->jl_id) {
		case TET_JNL_BUILD_START:
		case TET_JNL_INVOKE_TC:
		case TET_JNL_CLEAN_START:
			ep = rrp_findscen(jlp);
			rrp_checkloopstart(ep, jlp);
			rrp_tcsync(ep, jlp);
			if ((tcc_modes & TCC_RESUME) == 0 || !resume_scen)
				sel += rrp_proctc(ep, buf, jlp->jl_id);
			break;
		case TET_JNL_BUILD_END:
		case TET_JNL_TC_END:
		case TET_JNL_CLEAN_END:
		case TET_JNL_IC_START:
		case TET_JNL_IC_END:
		case TET_JNL_TP_START:
		case TET_JNL_TP_RESULT:
			if ((tcc_modes & TCC_RESUME) == 0 || !resume_scen) {
				fprintf(stderr, "TCC: found %s outside the scope of a Test Case at line %ld in old journal file %s\n",
					prjnlid(jlp->jl_id), jlp->jl_lineno,
					jfname);
				tcc_exit(1);
			}
			break;
		case TET_JNL_RPT_START:
		case TET_JNL_TLOOP_START:
			ep = rrp_findscen(jlp);
			clear_itcount(ep->sc_child);
			break;
		case TET_JNL_SEQ_START:
		case TET_JNL_RND_START:
		case TET_JNL_PRL_START:
		case TET_JNL_VAR_START:
#ifndef TET_LITE	/* -START-LITE-CUT- */
		case TET_JNL_RMT_START:
		case TET_JNL_DIST_START:
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
		case TET_JNL_RPT_END:
		case TET_JNL_SEQ_END:
		case TET_JNL_RND_END:
		case TET_JNL_PRL_END:
		case TET_JNL_VAR_END:
		case TET_JNL_TLOOP_END:
#ifndef TET_LITE	/* -START-LITE-CUT- */
		case TET_JNL_RMT_END:
		case TET_JNL_DIST_END:
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
			rrp_findscen(jlp);
			break;
		}
	}

	fclose(jfp);

	/*
	** see if any test cases have been selected (in RERUN mode) or if
	** a resume point has been found (in RESUME mode)
	*/
	if ((tcc_modes & TCC_RERUN) && !sel) {
		printf("TCC: rerun codelist does not select any test cases to rerun\n");
		fflush(stdout);
		tcc_exit(0);
	}
	if ((tcc_modes & TCC_RESUME) && !resume_scen) {
		printf("TCC: resume codelist does not identify a resume point in the scenario\n");
		fflush(stdout);
		tcc_exit(0);
	}

	/* remove all of the un-needed test cases from the scenario tree */
	TRACE1(tet_Ttcc, 4,
		"rrproc(): remove un-needed test cases from the scenario tree");
	remove_unneeded_tcs(sctree->sc_child);

	/* free allocated memory and return */
	if (rrcodes) {
		TRACE2(tet_Tbuf, 6, "free rrcodes = %s", tet_i2x(rrcodes));
		free((char *) rrcodes);
		rrcodes = (int *) 0;
		lrrcodes = nrrcodes = 0;
	}

	TRACE1(tet_Ttcc, 4, "rrproc() RETURN");
}

/*
**	rrp_tccstart() - process the TCC start line in the old journal file
*/

static void rrp_tccstart(jlp)
struct jline *jlp;
{
	int argc, c, modes;
	char **argv, **ap1, **ap2;
	char **gargv = (char **) 0;
	int lgargv = 0;
	extern int optind;
	extern char *optarg;

	/* ensure that the tcc version number matches our version number */
	if (strcmp(jlp->jl_flds2[0], tcc_version)) {
		fprintf(stderr, "TCC version %s: old journal file was generated by a different version of tcc (%s)\n",
			tcc_version, jlp->jl_flds2[0]);
		tcc_exit(1);
	}

	/*
	** get the tcc command line arguments -
	** ensure that this is not a RESUME mode journal;
	** get the modes of operation and any -y or -n options from the journal
	**
	** we must pass a copy of the argv to getopt(), since getopt()
	** modifies some of the pointers in the argv on LINUX (at least);
	** also, getopt() on LINUX seems to modify *(argv - 1),
	** so the copy must include an extra element on the front of the argv
	*/
	argc = 0;
	argv = rrp_ts2(jlp->jl_fld3, &argc);
	RBUFCHK((char **) &gargv, &lgargv,
		(int) ((argc + 2) * sizeof *gargv));
	ap2 = gargv;
	*ap2++ = (char *) 0;
	for (ap1 = argv; ap1 < argv + argc; ap1++)
		*ap2++ = *ap1;
	*ap2 = (char *) 0;
	modes = 0;
	optind = 1;
	while ((c = GETOPT(argc, gargv + 1, tcc_options)) != EOF)
		switch (c) {
		case 'b':
			modes |= TCC_BUILD;
			break;
		case 'c':
			modes |= TCC_CLEAN;
			break;
		case 'e':
			modes |= TCC_EXEC;
			break;
		case 'm':
			if (tcc_modes & TCC_RESUME) {
				fprintf(stderr, "TCC: can't RESUME using an old journal file which has been generated by tcc in RESUME mode\n");
				tcc_exit(1);
			}
			break;
		case 'n':
			if (*optarg)
				nostr(optarg, YN_OJFILE);
			else
				badynopt(c);
			break;
		case 'y':
			if (*optarg)
				yesstr(optarg, YN_OJFILE);
			else
				badynopt(c);
			break;
		case '?':
			fprintf(stderr, "TCC: TCC Start line in old journal file contains an invalid or incomplete command-line option\n");
			tcc_exit(1);
			break;
		}

	/*
	** in RESUME mode, check that the operation modes in the journal match
	** the current set
	*/
	if (
		(tcc_modes & TCC_RESUME) &&
		(tcc_modes & (TCC_BUILD | TCC_EXEC | TCC_CLEAN)) != modes
	) {
		fprintf(stderr, "TCC: set of operation modes in old journal file (%s) does not match the set selected for this run (%s)\n",
			prtccmode(modes),
			prtccmode(tcc_modes & (TCC_BUILD | TCC_EXEC | TCC_CLEAN)));
		tcc_exit(1);
	}

	/* free the storage allocated in this function */
	TRACE2(tet_Tbuf, 6, "free gargv = %s", tet_i2x(gargv));

	/* free the storage allocated by rrp_ts2() */
	for (ap1 = argv; ap1 < argv + argc; ap1++)
		if (*ap1) {
			TRACE2(tet_Tbuf, 6, "free rrp_ts2 arg = %s",
				tet_i2x(*ap1));
			free(*ap1);
		}
	TRACE2(tet_Tbuf, 6, "free rrp_ts2 argv = %s", tet_i2x(argv));
	free((char *) argv);

	/*
	** finally, prune the scenario tree using -y/-n options from the
	** old journal file
	*/
	ynproc(YN_OJFILE);
}

/*
**	rrp_ts2() - extend the rrp_tccstart() processing
**
**	fld3 points to the 3rd field of the TCC Start line
**
**	the return value points to an argv which contains the tcc
**	command-line arguments;
**	the number of arguments is returned indirectly through *argcp
*/

static char **rrp_ts2(fld3, argcp)
char *fld3;
int *argcp;
{
	static char key[] = "Command line: ";
	register char *p1, *p2;
	register int new, quotes;
	register char **ap;
	char **argv = (char **) 0;
	int largv = 0;
	int nargv;
	char buf[TET_JNL_LEN];

	/* find the start of the tcc command line */
	for (p1 = fld3; *p1; p1++)
		if (!strncmp(p1, key, sizeof key - 1))
			break;
	if (!*p1) {
		fprintf(stderr, "TCC: can't find tcc command in TCC Start line in old journal file\n");
		tcc_exit(1);
	}

	/* count the number of words in the command-line */
	p1 += sizeof key - 1;
	nargv = 1;
	for (p2 = p1; *p2; p2++)
		if (isspace(*p2))
			nargv++;

	/* allocate memory for the argv */
	RBUFCHK((char **) &argv, &largv, (int) (nargv * sizeof *argv));
	ap = argv;

	/*
	** build the argv from the command line extracted from the old
	** journal file, interpreting the escape and quoting conventions
	** used when the line was written
	*/
	new = 1;
	quotes = 0;
	for (p2 = buf; *p1; p1++) {
		if (p2 >= &buf[sizeof buf - 2]) {
			if (ap < argv + nargv)
				*ap++ = rstrstore(buf);
			p2 = buf;
			new = 1;
			continue;
		}
		if (*p1 == '\\' && *(p1 + 1)) {
			*p2++ = *++p1;
			new = 0;
			continue;
		}
		if (!quotes && isspace(*p1)) {
			if (!new) {
				*p2 = '\0';
				if (ap < argv + nargv)
					*ap++ = rstrstore(buf);
				p2 = buf;
				new = 1;
			}
			continue;
		}
		new = 0;
		if (*p1 == '"')
			quotes = !quotes;
		else
			*p2++ = *p1;
	}

	if (!new) {
		*p2 = '\0';
		if (ap < argv + nargv)
			*ap++ = rstrstore(buf);
	}

	*argcp = ap - argv;
	return(argv);
}

/*
**	badynopt() - report a bad -y or -n option in the old journal file
*/

static void badynopt(c)
int c;
{
	fprintf(stderr, "TCC: TCC Start line in old journal file contains a bad format -%c option\n", c);
	tcc_exit(1);
}

/*
**	rrp_findscen() - return a pointer to the scenario element
**		which matches the journal line scenario reference
*/

static struct scentab *rrp_findscen(jlp)
struct jline *jlp;
{
	register struct scentab *ep;
	int sctype, scdir;
	char *directive;

	/* find the scenario element corresponding to the scenario reference */
	if ((ep = rrp_fs2(sctree->sc_child, jlp->jl_ref)) == (struct scentab *) 0) {
		fprintf(stderr, "TCC: the specified scenario does not contain an element which corresponds to reference %ld-%d at line %ld in old journal file %s\n",
			jlp->jl_ref, jlp->jl_sys, jlp->jl_lineno, jfname);
		tcc_exit(1);
	}

	/*
	** determine what type of scenario element corresponds to this
	** journal line id
	*/
	switch (jlp->jl_id) {
	case TET_JNL_INVOKE_TC:
	case TET_JNL_TC_END:
	case TET_JNL_BUILD_START:
	case TET_JNL_BUILD_END:
	case TET_JNL_CLEAN_START:
	case TET_JNL_CLEAN_END:
		sctype = SC_TESTCASE;
		scdir = -1;
		break;
	case TET_JNL_PRL_START:
	case TET_JNL_PRL_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_PARALLEL;
		break;
	case TET_JNL_SEQ_START:
	case TET_JNL_SEQ_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_SEQUENTIAL;
		break;
	case TET_JNL_VAR_START:
	case TET_JNL_VAR_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_VARIABLE;
		break;
	case TET_JNL_RPT_START:
	case TET_JNL_RPT_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_REPEAT;
		break;
	case TET_JNL_TLOOP_START:
	case TET_JNL_TLOOP_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_TIMED_LOOP;
		break;
	case TET_JNL_RND_START:
	case TET_JNL_RND_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_RANDOM;
		break;
#ifndef TET_LITE	/* -START-LITE-CUT- */
	case TET_JNL_RMT_START:
	case TET_JNL_RMT_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_REMOTE;
		break;
	case TET_JNL_DIST_START:
	case TET_JNL_DIST_END:
		sctype = SC_DIRECTIVE;
		scdir = SD_DISTRIBUTED;
		break;
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
	default:
		/* this "can't happen" */
		fatal(0, "journal line id unexpected:", prjnlid(jlp->jl_id));
		/* NOTREACHED */
	}

	/*
	** ensure that the scenario element matches the journal id -
	** if it doesn't, it is likely that the old journal file was not
	** generated from this scenario
	*/
	if (
		sctype != ep->sc_type ||
		(sctype == SC_DIRECTIVE && scdir != ep->sc_directive)
	) {
		if (ep->sc_type == SC_DIRECTIVE)
			if ((ep->sc_flags & SCF_IMPLIED) &&
				ep->sc_directive == SD_SEQUENTIAL)
					directive = "implied sequential";
			else
				directive = prscdir(ep->sc_directive);
		else
			directive = "";
		fprintf(stderr, "TCC: %s line %ld in old journal file %s\n",
			prjnlid(jlp->jl_id), jlp->jl_lineno, jfname);
		fprintf(stderr, "does not match %s%s%s element at line %d in scenario file %s\n",
			directive, (ep->sc_type == SC_DIRECTIVE) ? " " : "",
			prsctype(ep->sc_type), ep->sc_lineno, ep->sc_fname);
		tcc_exit(1);
	}

	return(ep);
}

/*
**	rrp_fs2() - extend the rrp_findscen processing
**
**	search the scenario tree at this level and below for an element which
**	matches the specified reference
*/

static struct scentab *rrp_fs2(ep, ref)
register struct scentab *ep;
register long ref;
{
	register struct scentab *ep2;

	for (; ep; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		if (ep->sc_ref == ref)
			return(ep);
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			if ((ep2 = rrp_fs2(ep->sc_child, ref)) != (struct scentab *) 0)
				return(ep2);
			break;
		}
	}

	return((struct scentab *) 0);
}

/*
**	rrp_tcsync() - check that the test case name on a BUILD/TC/CLEAN
**		start line matches the test case name in the corresponding
**		scenario element
**
**	there is no return if a mismatch is found - it is likely that the
**	old journal file was not generated from the current scenario
*/

static void rrp_tcsync(ep, jlp)
struct scentab *ep;
struct jline *jlp;
{
	int is_testcase_start_id = 0;

	switch (jlp->jl_id) {
	case TET_JNL_BUILD_START:
	case TET_JNL_INVOKE_TC:
	case TET_JNL_CLEAN_START:
		is_testcase_start_id = 1;
	}

	ASSERT(ep->sc_type == SC_TESTCASE);
	ASSERT(is_testcase_start_id);

	if (strcmp(ep->sc_tcname, jlp->jl_flds2[1])) {
		fprintf(stderr, "TCC: the test case name (%s) on %s line %ld in old journal file %s\n",
			jlp->jl_flds2[1], prjnlid(jlp->jl_id),
			jlp->jl_lineno, jfname);
		fprintf(stderr, "does not match the test case name (%s) at line %d of scenario file %s\n",
			ep->sc_tcname, ep->sc_lineno, ep->sc_fname);
		tcc_exit(1);
	}
}

/*
**	rrp_proctc() - process journal lines associated with a TEST CASE
**		scenario element
**
**	we've just read a BUILD, TC or CLEAN start line -
**	process journal lines up to and including the matching END line
**
**	return 1 if this TC is selected by the RERUN/RESUME options,
**	zero otherwise
*/

static int rrp_proctc(ep, buf, startid)
struct scentab *ep;
char *buf;
int startid;
{
	register struct jline *jlp;
	register int done, sel;
	register struct scentab *ep2;
	int mode, endid;

	ASSERT(ep->sc_type == SC_TESTCASE);

	/* determine the matching END id */
	switch (startid) {
	case TET_JNL_BUILD_START:
		endid = TET_JNL_BUILD_END;
		mode = TCC_BUILD;
		break;
	case TET_JNL_INVOKE_TC:
		endid = TET_JNL_TC_END;
		mode = TCC_EXEC;
		break;
	case TET_JNL_CLEAN_START:
		endid = TET_JNL_CLEAN_END;
		mode = TCC_CLEAN;
		break;
	default:
		/* this "can't happen" */
		fatal(0, "unexpected jnl id:", prjnlid(startid));
		/* NOTREACHED */
	}

	TRACE4(tet_Ttcc, 8, "rrp_proctc(): examine TC %s ref %s in %s mode",
		ep->sc_tcname, tet_l2a(ep->sc_ref), prtccmode(mode));

	/*
	** process each line associated with this test case
	** until the corresponding END line is found
	*/
	done = 0;
	sel = 0;
	while (!done && (jlp = rrp_getline(buf)) != (struct jline *) 0) {

		/*
		** if this is an END line, ensure that it matches the
		** START line which defined the processing mode for this TC;
		** then process it
		**
		** punt an IC start line down a level
		**
		** check for lines out of context
		**
		** back up over other lines and return - the backed-up line
		** will be re-read at the level above
		*/
		switch (jlp->jl_id) {
		case TET_JNL_BUILD_END:
		case TET_JNL_TC_END:
		case TET_JNL_CLEAN_END:
			if (endid != jlp->jl_id) {
				fprintf(stderr, "TCC: expected %s at line %ld in old journal file %s\n",
					prjnlid(endid), jlp->jl_lineno, jfname);
				fprintf(stderr,
					"instead, found %s line\n",
					prjnlid(jlp->jl_id));
				tcc_exit(1);
			}
			sel += rrp_procendline(ep, jlp, mode);
			done = 1;
			break;
		case TET_JNL_IC_START:
			sel += rrp_procic(ep, atoi(jlp->jl_flds2[1]), buf, mode);
			break;
		case TET_JNL_IC_END:
		case TET_JNL_TP_START:
		case TET_JNL_TP_RESULT:
			fprintf(stderr, "TCC: found %s outside the scope of an IC at line %ld in old journal file %s\n",
				prjnlid(jlp->jl_id), jlp->jl_lineno, jfname);
			tcc_exit(1);
			break;
		default:
			if (fseek(jfp, jlp->jl_offset, SEEK_SET) < 0)
				fatal(errno, "seek error on", jfname);
			done = 1;
			break;
		}
	}

	/* return now if this test case has not been selected */
	if (!sel)
		return(0);

	/*
	** here if this test case has been selected -
	** mark the test case as needed
	*/
	ep->sc_flags |= SCF_NEEDED;

	/*
	** in RESUME mode:
	**
	**	the resume mode has already been set
	**	adjust the resume mode if necessary
	**
	**	if the resume point is within the scope of a PARALLEL
	**	or RANDOM directive, move it back to the start of the
	**	outermost PARALLEL directive or innermost RANDOM directive
	**
	**	mark the rest of the scenario as needed
	**
	**	remember the iteration count in each of the enclosing
	**	REPEAT or TIMED_LOOP directives
	*/
	if (tcc_modes & TCC_RESUME) {

		if (resume_mode == TCC_EXEC && (tcc_modes & TCC_CLEAN))
			resume_mode = TCC_BUILD;

		for (ep2 = ep->sc_parent; ep2; ep2 = ep2->sc_parent) {
			ASSERT(ep2->sc_magic == SC_MAGIC);
			if (
				ep2->sc_type == SC_DIRECTIVE &&
				ep2->sc_directive == SD_RANDOM
			) {
				TRACE2(tet_Ttcc, 6, "resume point moved to start of enclosing RANDOM directive at %s",
					tet_i2x(ep2));
				resume_scen = ep2;
				if (resume_iclist) {
					TRACE2(tet_Tbuf, 6,
						"RND free resume_iclist = %s",
						tet_i2x(resume_iclist));
					free(resume_iclist);
					resume_iclist = (char *) 0;
				}
				break;
			}
		}

		for (ep2 = ep->sc_parent; ep2; ep2 = ep2->sc_parent) {
			ASSERT(ep2->sc_magic == SC_MAGIC);
			if (
				ep2->sc_type == SC_DIRECTIVE &&
				ep2->sc_directive == SD_PARALLEL
			) {
				TRACE2(tet_Ttcc, 6, "resume point moved to start of enclosing PARALLEL directive at %s",
					tet_i2x(ep2));
				resume_scen = ep2;
				if (resume_iclist) {
					TRACE2(tet_Tbuf, 6,
						"PAR free resume_iclist = %s",
						tet_i2x(resume_iclist));
					free(resume_iclist);
					resume_iclist = (char *) 0;
				}
			}
		}

		set_scflags(resume_scen->sc_child, SCF_NEEDED);
		for (ep2 = resume_scen; ep2; ep2 = ep2->sc_parent) {
			ASSERT(ep2->sc_magic == SC_MAGIC);
			if (ep2->sc_type != SC_SCENARIO)
				set_scflags(ep2->sc_forw, SCF_NEEDED);
		}

		if (resume_mode == TCC_EXEC) {
			for (ep2 = ep->sc_parent; ep2; ep2 = ep2->sc_parent) {
				ASSERT(ep2->sc_magic == SC_MAGIC);
				if (ep2->sc_type != SC_DIRECTIVE)
					continue;
				if (
					ep2->sc_directive == SD_REPEAT ||
					ep2->sc_directive == SD_TIMED_LOOP
				) {
					TRACE4(tet_Ttcc, 6, "resume execution at iteration %s of enclosing %s directive at %s",
						tet_i2a(ep2->sc_itcount),
						prscdir(ep2->sc_directive),
						tet_i2x(ep2));
					ep2->sc_rescount = ep2->sc_itcount;
				}
			}
		}

	}

	return(1);
}

/*
**	rrp_procic() - process journal lines associated with an IC
**
**	we've just read an IC start line -
**	process journal lines up to and including the matching IC end line
**
**	return 1 if a result code below this IC selects this IC, zero otherwise
*/

static int rrp_procic(ep, icno, buf, mode)
struct scentab *ep;
int icno;
char *buf;
int mode;
{
	register struct jline *jlp;
	register int done, sel;
	int len, needlen, tmplen;
	char *p;

	TRACE3(tet_Ttcc, 8, "rrp_procic(): examine IC %s in %s mode",
		tet_i2a(icno), prtccmode(mode));

	/*
	** process each line associated with this IC
	** until the corresponding END line is found
	*/
	done = 0;
	sel = 0;
	while (!done && (jlp = rrp_getline(buf)) != (struct jline *) 0) {
		switch (jlp->jl_id) {
		case TET_JNL_TP_START:
			TRACE2(tet_Ttcc, 8, "examine result of TP %s",
				jlp->jl_flds2[1]);
			sel += rrp_proctp(buf, mode);
			break;
		case TET_JNL_IC_END:
			done = 1;
			break;
		case TET_JNL_TP_RESULT:
			fprintf(stderr, "TCC: found %s outside the scope of a TP at line %ld in old journal file %s\n",
				prjnlid(jlp->jl_id), jlp->jl_lineno, jfname);
			tcc_exit(1);
			break;
		default:
			if (fseek(jfp, jlp->jl_offset, SEEK_SET) < 0)
				fatal(errno, "seek error on", jfname);
			done = 1;
			break;
		}
	}

	/*
	** if we already have a resume point, append this IC number
	** to the resume_iclist and return
	*/
	if ((tcc_modes & TCC_RESUME) && resume_scen) {
		if (mode == TCC_EXEC && resume_iclist) {
			len = strlen(resume_iclist) + 1;
			tmplen = len;
			p = tet_i2a(icno);
			needlen = len + strlen(p) + 1;
			RBUFCHK(&resume_iclist, &tmplen, needlen);
			sprintf(resume_iclist + len - 1, ",%s", p);
		}
		return(sel);
	}

	/* return now if this IC has not been selected */
	if (!sel)
		return(0);

	/*
	**	here if this IC is selected
	*/

	/* in RESUME mode, remember the resume point */
	if (tcc_modes & TCC_RESUME) {
		TRACE4(tet_Ttcc, 6,
			"rrp_procic(): resume at TC %s (%s) in %s mode",
			ep->sc_tcname, tet_i2x(ep), prtccmode(mode));
		resume_scen = ep;
		resume_mode = mode;
		if (mode == TCC_EXEC) {
			ASSERT(resume_iclist == (char *) 0);
			sprintf(buf, "%d", icno);
			resume_iclist = rstrstore(buf);
			TRACE2(tet_Ttcc, 6, "\tresume at IC %s",
				resume_iclist);
		}
	}

	/* in RERUN mode, update the IC list if so required */
	if ((tcc_modes & TCC_RERUN) && mode == TCC_EXEC)
		exiclist_addupdate(ep, icno);

	return(1);
}

/*
**	rrp_proctp() - process journal lines associated with a TP
**
**	we've just read an TP start line -
**	the next line should be the matching TP result line
**
**	return 1 if the result selects the enclosing IC, zero otherwise
*/

static int rrp_proctp(buf, mode)
char *buf;
int mode;
{
	struct jline *jlp;
	register int *ip, result, sel;

	/* read the TP result line */
	if ((jlp = rrp_getline(buf)) == (struct jline *) 0)
		return(0);

	sel = 0;
	switch (jlp->jl_id) {
	case TET_JNL_TP_RESULT:
		break;
	default:
		if (rrmodes & mode) {
			TRACE2(tet_Ttcc, 8, "missing TP result line selects this TP in %s mode",
				prtccmode(mode));
			sel = 1;
		}
		if (fseek(jfp, jlp->jl_offset, SEEK_SET) < 0)
			fatal(errno, "seek error on", jfname);
		return(sel);
	}

	/* see if the result selects the enclosing IC */
	result = atol(jlp->jl_flds2[2]);
	if (mode == TCC_EXEC)
		for (ip = rrcodes; ip < rrcodes + nrrcodes; ip++)
			if (*ip == result) {
				TRACE2(tet_Ttcc, 8, "result code %s selects this TP in EXEC mode",
					tet_i2a(result));
				sel = 1;
				break;
			}

	if (!sel && result && (rrmodes & mode)) {
		TRACE2(tet_Ttcc, 8,
			"non-zero result code selects this TP in %s mode",
			prtccmode(mode));
		sel = 1;
	}

	return(sel);
}

/*
**	rrp_procendline() - process a BUILD/TC/CLEAN end line
**
**	return 1 if the status code selects this test case, zero otherwise
*/

static int rrp_procendline(ep, jlp, mode)
struct scentab *ep;
struct jline *jlp;
int mode;
{
	int status = atoi(jlp->jl_flds2[1]);

	TRACE5(tet_Ttcc, 8, "rrp_procendline(): examine TC %s ref %s completion status %s in %s mode",
		ep->sc_tcname, tet_l2a(jlp->jl_ref), tet_i2a(status),
		prtccmode(mode));

	/* see if the tool completion status selects this TC */
	if (!status || (rrmodes & mode) == 0)
		return(0);

	/* here if the tool completion status selects this TC */
	TRACE2(tet_Ttcc, 8,
		"non-zero completion status selects this TC in %s mode",
		prtccmode(mode));

	/* in RERUN mode, restore the original exec IC list if necessary */
	if ((tcc_modes & TCC_RERUN) && mode == TCC_EXEC)
		exiclist_set(ep, ep->sc_sciclist);

	/*
	** in RESUME mode, remember the resume point if we don't already
	** have one
	*/
	if (tcc_modes & TCC_RESUME) {
		if (resume_scen) {
			if (mode == TCC_EXEC && resume_scen == ep && resume_iclist) {
				TRACE2(tet_Tbuf, 6,
					"ENDLINE free resume_iclist = %s",
					tet_i2x(resume_iclist));
				free(resume_iclist);
				resume_iclist = (char *) 0;
			}
		}
		else {
			resume_scen = ep;
			resume_mode = mode;
		}
	}

	return(1);
}

/*
**	rrp_getline() - read a line from the old journal file
**
**	return a pointer to a structure containing the broken-out fields
**	from the journal line
**
**	return (struct jline *) 0 on EOF or error
*/

static struct jline *rrp_getline(buf)
char *buf;
{
	static char fmt1[] = "ignored badly formatted journal line number %ld in";
	static char fmt2[] = "expected %d subfields in field 2, observed";
	static long lcount;
	static struct jline jline;
	register char *p;
	char line[LBUFLEN];
	char *flds[3];
	int nflds2, expflds;

	/*
	** loop until we find a line in a valid format whose id is one
	** of the types that we want
	*/
	for (;;) {
		jline.jl_offset = ftell(jfp);
		if (fgets(line, sizeof line, jfp) == (char *) 0)
			return((struct jline *) 0);
		lcount++;
		for (p = line; *p; p++)
			if (*p == '\n') {
				*p = '\0';
				break;
			}

		/* split the line into 3 |-separated fields */
		if (jnlproc_split(line, flds, buf) < 0 || !isdigit(*flds[0])) {
			sprintf(line, fmt1, lcount);
			error(0, line, jfname);
			continue;
		}

		/* store the line count and the ID from field 1 */
		jline.jl_lineno = lcount;
		jline.jl_id = atoi(flds[0]);

		/*
		** discard lines that we don't want -
		** determine how many subfields to expect in field 2
		*/
		switch (jline.jl_id) {
		case TET_JNL_SEQ_START:
		case TET_JNL_RND_START:
		case TET_JNL_VAR_START:
			expflds = 0;
			break;
		case TET_JNL_PRL_START:
		case TET_JNL_RPT_START:
		case TET_JNL_TLOOP_START:
#ifndef TET_LITE	/* -START-LITE-CUT- */
		case TET_JNL_RMT_START:
		case TET_JNL_DIST_START:
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
			expflds = 1;
			break;
		case TET_JNL_TCC_START:
		case TET_JNL_INVOKE_TC:
		case TET_JNL_TC_END:
		case TET_JNL_BUILD_START:
		case TET_JNL_BUILD_END:
		case TET_JNL_TP_START:
		case TET_JNL_CLEAN_START:
		case TET_JNL_CLEAN_END:
			expflds = 3;
			break;
		case TET_JNL_IC_START:
		case TET_JNL_IC_END:
		case TET_JNL_TP_RESULT:
			expflds = 4;
			break;
		default:
			continue;
		}

		/* break out the 2nd field into subfields */
		nflds2 = tet_getargs(flds[1], jline.jl_flds2, 5);

		/* check the number of subfields in the 2nd field */
		if (expflds != nflds2) {
			sprintf(line, fmt2, expflds);
			error(0, line, tet_i2a(nflds2));
			sprintf(line, fmt1, lcount);
			error(0, line, jfname);
			continue;
		}

		/* zero out excess subfield pointers */
		while (nflds2 < 5)
			jline.jl_flds2[nflds2++] = (char *) 0;

		/* check the format of some of the subfields */
		switch (jline.jl_id) {
		case TET_JNL_PRL_START:
		case TET_JNL_RPT_START:
		case TET_JNL_TLOOP_START:
#ifndef TET_LITE	/* -START-LITE-CUT- */
		case TET_JNL_RMT_START:
		case TET_JNL_DIST_START:
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
			if (!isdigit(*jline.jl_flds2[0])) {
				sprintf(line, fmt1, lcount);
				error(0, line, jfname);
				continue;
			}
			break;
		case TET_JNL_BUILD_START:
		case TET_JNL_INVOKE_TC:
		case TET_JNL_CLEAN_START:
			if (
				!isdigit(*jline.jl_flds2[0]) ||
				!(
					*jline.jl_flds2[1] == '/' ||
					*jline.jl_flds2[1] == '@'
				)
			) {
				sprintf(line, fmt1, lcount);
				error(0, line, jfname);
				continue;
			}
			break;
		case TET_JNL_TC_END:
		case TET_JNL_BUILD_END:
		case TET_JNL_CLEAN_END:
			if (
				!isdigit(*jline.jl_flds2[0]) ||
				!(
					isdigit(*jline.jl_flds2[1]) ||
					*jline.jl_flds2[1] == '-'
				)
			) {
				sprintf(line, fmt1, lcount);
				error(0, line, jfname);
				continue;
			}
			break;
		case TET_JNL_TP_START:
			if (
				!isdigit(*jline.jl_flds2[0]) ||
				!isdigit(*jline.jl_flds2[1])
			) {
				sprintf(line, fmt1, lcount);
				error(0, line, jfname);
				continue;
			}
			break;
		case TET_JNL_IC_START:
		case TET_JNL_IC_END:
		case TET_JNL_TP_RESULT:
			if (
				!isdigit(*jline.jl_flds2[0]) ||
				!isdigit(*jline.jl_flds2[1]) ||
				!isdigit(*jline.jl_flds2[2])
			) {
				sprintf(line, fmt1, lcount);
				error(0, line, jfname);
				continue;
			}
			break;
		}

		/* store the 3rd field */
		jline.jl_fld3 = flds[2];

		/* decode the scenario reference on lines which have it */
		jline.jl_ref = 0L;
		jline.jl_sys = 0;
		switch (jline.jl_id) {
		case TET_JNL_INVOKE_TC:
		case TET_JNL_TC_END:
		case TET_JNL_BUILD_START:
		case TET_JNL_BUILD_END:
		case TET_JNL_CLEAN_START:
		case TET_JNL_CLEAN_END:
		case TET_JNL_SEQ_START:
		case TET_JNL_RND_START:
		case TET_JNL_PRL_START:
		case TET_JNL_VAR_START:
		case TET_JNL_RPT_START:
		case TET_JNL_TLOOP_START:
#ifndef TET_LITE	/* -START-LITE-CUT- */
		case TET_JNL_RMT_START:
		case TET_JNL_DIST_START:
#endif /* !TET_LITE */	/* -END-LITE-CUT- */
			for (p = flds[2]; *p; p++)
				if (sscanf(p, tcc_scenref_fmt, &jline.jl_ref, &jline.jl_sys) > 0)
					break;
			break;
		}

		TRACE3(tet_Ttcc, 10,
			"rrp_getline(): return line no %s, id = %s",
			tet_l2a(jline.jl_lineno), prjnlid(jline.jl_id));
		return(&jline);
	}

}

/*
**	rrp_checkloopstart() - see if this Test Case element is at the start of
**		a loop in EXEC mode
**
**	if it is, increment the enclosing directive's iteration count
*/

static void rrp_checkloopstart(ep, jlp)
register struct scentab *ep;
struct jline *jlp;
{
	register struct scentab *parent;

	/* return now if this is not an EXEC start */
	if (jlp->jl_id != TET_JNL_INVOKE_TC)
		return;

#ifndef TET_LITE	/* -START-LITE-CUT- */
	/*
	** look back up the tree to see if we are within the scope of
	** a REMOTE or DISTRIBUTED directive - return if this line is not
	** for the first system in the list
	*/
	for (parent = ep->sc_parent; parent; parent = parent->sc_parent) {
		ASSERT(parent->sc_magic == SC_MAGIC);
		if (
			parent->sc_type == SC_DIRECTIVE && (
				parent->sc_directive == SD_REMOTE ||
				parent->sc_directive == SD_DISTRIBUTED
			)
		) {
			ASSERT(parent->sc_sys);
			if (jlp->jl_sys != *parent->sc_sys)
				return;
		}
	}
#endif /* !TET_LITE */	/* -END-LITE-CUT- */

	/*
	** look back up the tree to see if we are within the scope
	** of a looping directive
	*/
	for (parent = ep->sc_parent; parent; parent = parent->sc_parent) {
		ASSERT(parent->sc_magic == SC_MAGIC);
		if (
			parent->sc_type == SC_DIRECTIVE && (
				parent->sc_directive == SD_REPEAT ||
				parent->sc_directive == SD_TIMED_LOOP
			)
		) {
			break;
		}
	}

	/* return now if not */
	if (!parent)
		return;

	/*
	** see if this is the first TC within the looping directive's scope
	** and increment the directive's iteration count if it is
	**
	** for a TIMED_LOOP, remember the iteration count high water mark
	** in sc_count for use in loop_test() in the execution engine
	*/
	if (rrp_cl2(parent->sc_child, ep, 0)) {
		parent->sc_itcount++;
		if (parent->sc_directive == SD_TIMED_LOOP &&
			parent->sc_itcount > parent->sc_count)
				parent->sc_count = parent->sc_itcount;
	}
}

/*
**	rrp_cl2() - extend the rrp_loopcount() processing
**
**	return 1 if tc is the first Test Case node on or below the level
**	pointed to by ep, or zero otherwise
*/

static int rrp_cl2(ep, tc, random)
register struct scentab *ep, *tc;
int random;
{
	for (; ep && ep != tc; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			switch (ep->sc_directive) {
			case SD_RANDOM:
				random = 1;
				/* fall through */
			default:
				if (rrp_cl2(ep->sc_child, tc, random))
					return(1);
				break;
			}
			break;
		case SC_TESTCASE:
			if (!random)
				return(0);
			break;
		}
	}

	return((ep && ep == tc) ? 1 : 0);
}

/*
**	exiclist_addupdate() - add an IC number to a scenario element's
**		EXEC IC list if it is not already there
*/

static void exiclist_addupdate(ep, icno)
struct scentab *ep;
int icno;
{
	struct ics {
		int ic_start;
		int ic_end;
	} *ics = (struct ics *) 0;
	int lics = 0;
	register struct ics *icp;
	register char **fldp;
	register char *p;
	char *iclist;
	char **flds1 = (char **) 0;
	int lflds1 = 0;
	int nflds1;
	char *flds2[2];
	int nics, len, needlen;

	/*
	** if the existing EXEC IC list is empty, just set it to the new
	** IC number and return
	*/
	if (ep->sc_exiclist == (char *) 0) {
		exiclist_set(ep, rstrstore(tet_i2a(icno)));
		return;
	}

	/*
	** here if there is an existing IC list;
	** split the list into comma-separated fields
	**
	** a scratchpad is allocated because split() trashes its input string
	*/
	iclist = rstrstore(ep->sc_exiclist);
	nflds1 = 1;
	for (p = iclist; *p; p++)
		if (*p == ',')
			nflds1++;
	RBUFCHK((char **) &flds1, &lflds1, (int) (nflds1 * sizeof *flds1));
	nflds1 = split(iclist, flds1, nflds1, ',');

	/*
	** now, each field contains an IC number or an IC number range;
	** split each field into IC start and end numbers
	*/
	RBUFCHK((char **) &ics, &lics, (int) (nflds1 * sizeof *ics));
	icp = ics;
	for (fldp = flds1; fldp < flds1 + nflds1; fldp++) {
		switch (split(*fldp, flds2, 2, '-')) {
		case 0:
			continue;
		case 1:
			icp->ic_start = atoi(flds2[0]);
			icp->ic_end = icp->ic_start;
			break;
		default:
			icp->ic_start = atoi(flds2[0]);
			icp->ic_end = atoi(flds2[1]);
			break;
		}
		icp++;
	}
	nics = icp - ics;

	/* look for the IC number in each field */
	for (icp = ics; icp < ics + nics; icp++)
		if (icno >= icp->ic_start && icno <= icp->ic_end)
			break;

	/* free the scratchpad */
	TRACE2(tet_Tbuf, 6, "free tmp iclist = %s", tet_i2x(iclist));
	free(iclist);
	iclist = (char *) 0;

	/* if icno was not found, append it to the EXEC IC list */
	if (icp >= ics + nics) {
		len = 0;
		p = tet_i2a(icno);
		needlen = strlen(ep->sc_exiclist) + strlen(p) + 2;
		RBUFCHK(&iclist, &len, needlen);
		sprintf(iclist, "%s,%s", ep->sc_exiclist, p);
		exiclist_set(ep, iclist);
	}

	/* finally, free the other allocated storage and return */
	TRACE2(tet_Tbuf, 6, "free flds1 = %s", tet_i2x(flds1));
	free((char *) flds1);
	TRACE2(tet_Tbuf, 6, "free ics = %s", tet_i2x(ics));
	free((char *) ics);
}

/*
**	exiclist_set() - set the EXEC IC list in a scenario element
*/

static void exiclist_set(ep, iclist)
register struct scentab *ep;
char *iclist;
{
	if (iclist != ep->sc_exiclist) {
		if (ep->sc_exiclist && ep->sc_exiclist != ep->sc_sciclist) {
			TRACE2(tet_Tbuf, 6, "free existing sc_exiclist = %s",
				tet_i2x(ep->sc_exiclist));
			free(ep->sc_exiclist);
		}
		ep->sc_exiclist = iclist;
	}
}


/*
**	remove_unneeded_tcs() - remove all unneeded test cases
**		from the scenario tree
*/

static void remove_unneeded_tcs(ep)
register struct scentab *ep;
{
	register struct scentab *forw;

	/* remove each test case which does not have the NEEDED flag set */
	for (; ep; ep = forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		forw = ep->sc_forw;
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			remove_unneeded_tcs(ep->sc_child);
			continue;
		case SC_TESTCASE:
			if ((ep->sc_flags & SCF_NEEDED) == 0) {
				TRACE3(tet_Ttcc, 6,
					"remove unneeded test case %s ref %s",
					ep->sc_tcname, tet_l2a(ep->sc_ref));
				scrm_lnode(ep);
			}
			break;
		default:
			continue;
		}
	}
}

/*
**	set_scflags() - set the specified flags in the scenario tree
*/

static void set_scflags(ep, flags)
register struct scentab *ep;
register int flags;
{
	/*
	** traverse the tree at this level, seting the specified flags
	** and descending directive trees
	*/
	for (; ep; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		ep->sc_flags |= flags;
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			TRACE2(tet_Ttcc, 7, "set_scflags(): descend tree below %s directive",
				prscdir(ep->sc_directive));
			set_scflags(ep->sc_child, flags);
			break;
		}
	}
}

/*
**	clear_scflags() - clear the specified flags in the scenario tree
*/

static void clear_scflags(ep, flags)
register struct scentab *ep;
register int flags;
{
	/*
	** traverse the tree at this level, clearing the specified flags
	** and descending directive trees
	*/
	for (; ep; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		ep->sc_flags &= ~flags;
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			TRACE2(tet_Ttcc, 7, "clear_scflags(): descend tree below %s directive",
				prscdir(ep->sc_directive));
			clear_scflags(ep->sc_child, flags);
			break;
		}
	}
}

/*
**	clear_exiclists() - clear all the default EXEC IC lists in the
**		scenario tree
**
**	this function should only be called in RERUN mode
*/

static void clear_exiclists(ep)
register struct scentab *ep;
{
	/*
	** traverse the tree at this level, clearing the EXEC IC lists
	** and descending directive trees
	*/
	for (; ep; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			TRACE2(tet_Ttcc, 7, "clear_exiclists(): descend tree below %s directive",
				prscdir(ep->sc_directive));
			clear_exiclists(ep->sc_child);
			break;
		case SC_TESTCASE:
			ep->sc_exiclist = (char *) 0;
			break;
		}
	}
}

/*
**	clear_itcount() - clear all the loop directive iteration counts in
**		the scenario tree
*/

static void clear_itcount(ep)
register struct scentab *ep;
{
	/*
	** traverse the tree at this level, clearing the loop directive
	** iteration counts and descending directive trees
	*/
	for (; ep; ep = ep->sc_forw) {
		ASSERT(ep->sc_magic == SC_MAGIC);
		switch (ep->sc_type) {
		case SC_DIRECTIVE:
			switch (ep->sc_directive) {
			case SD_REPEAT:
			case SD_TIMED_LOOP:
				ep->sc_itcount = 0;
				break;
			}
			TRACE2(tet_Ttcc, 7, "clear_itcount(): descend tree below %s directive",
				prscdir(ep->sc_directive));
			clear_itcount(ep->sc_child);
			break;
		}
	}
}

/*
**	prjnlid() - return a printable representation of a journal id value
*/

static char *prjnlid(id)
int id;
{
	static char text[] = "journal-id ";
	static char msg[sizeof text + LNUMSZ];

	switch (id) {
	case TET_JNL_TCC_START:
		return("TCC Start");
	case TET_JNL_INVOKE_TC:
		return("TC Start");
	case TET_JNL_TCM_START:
		return("TCM Start");
	case TET_JNL_CFG_START:
		return("Config Start");
	case TET_JNL_CFG_VALUE:
		return("Config Assignment");
	case TET_JNL_CFG_END:
		return("Config End");
	case TET_JNL_TC_MESSAGE:
		return("TCC Message");
	case TET_JNL_SCEN_OUT:
		return("Scenario Information");
	case TET_JNL_TC_END:
		return("TC End");
	case TET_USER_ABORT:
		return("User Abort");
	case TET_JNL_CAPTURED_OUTPUT:
		return("Captured Output");
	case TET_JNL_BUILD_START:
		return("Build Start");
	case TET_JNL_BUILD_END:
		return("Build End");
	case TET_JNL_TP_START:
		return("TP Start");
	case TET_JNL_TP_RESULT:
		return("TP Result");
	case TET_JNL_CLEAN_START:
		return("Clean Start");
	case TET_JNL_CLEAN_OUTPUT:
		return("Clean Output");
	case TET_JNL_CLEAN_END:
		return("Clean End");
	case TET_JNL_IC_START:
		return("IC Start");
	case TET_JNL_IC_END:
		return("IC End");
	case TET_JNL_TCM_INFO:
		return("TCM Message");
	case TET_JNL_TC_INFO:
		return("TC Info");
	case TET_JNL_PRL_START:
		return("Parallel Start");
	case TET_JNL_PRL_END:
		return("Parallel End");
	case TET_JNL_SEQ_START:
		return("Sequential Start");
	case TET_JNL_SEQ_END:
		return("Sequential End");
	case TET_JNL_VAR_START:
		return("Variable Start");
	case TET_JNL_VAR_END:
		return("Variable End");
	case TET_JNL_RPT_START:
		return("Repeat Start");
	case TET_JNL_RPT_END:
		return("Repeat End");
	case TET_JNL_TLOOP_START:
		return("Timed-loop Start");
	case TET_JNL_TLOOP_END:
		return("Timed-loop End");
	case TET_JNL_RND_START:
		return("Random Start");
	case TET_JNL_RND_END:
		return("Random End");
	case TET_JNL_RMT_START:
		return("Remote Start");
	case TET_JNL_RMT_END:
		return("Remote End");
	case TET_JNL_DIST_START:
		return("Distributed Start");
	case TET_JNL_DIST_END:
		return("Distributed End");
	case TET_JNL_TCC_END:
		return("TCC End");
	default:
		sprintf(msg, "%s%d", text, id);
		return(msg);
	}
}


