/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */


/*
 *	parallel.cc
 *
 *	Deal with the parallel processing
 */

/*
 * Included files
 */
#include <errno.h>		/* errno */
#include <fcntl.h>
#include <mk/defs.h>
#include <mksh/dosys.h>		/* redirect_io() */
#include <mksh/macro.h>		/* expand_value() */
#include <mksh/misc.h>		/* getmem() */
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
#include <netdb.h>
#include <libintl.h>



/*
 * Defined macros
 */
#define MAXRULES		100

/*
 * This const should be in avo_dms/include/AvoDmakeCommand.h
 */
const int local_host_mask = 0x20;


/*
 * typedefs & structs
 */


/*
 * Static variables
 */
static	Boolean		just_did_subtree = false;
static	char		local_host[MAXNAMELEN] = "";
static	char		user_name[MAXNAMELEN] = "";
static	int		pmake_max_jobs = 0;
static	pid_t		process_running = -1;
static	Running		*running_tail = &running_list;
static	Name		subtree_conflict;
static	Name		subtree_conflict2;


/*
 * File table of contents
 */
static	void		delete_running_struct(Running rp);
static	Boolean		dependency_conflict(Name target);
static	Doname		distribute_process(char **commands, Property line);
static	void		doname_subtree(Name target, Boolean do_get, Boolean implicit);
static	void		dump_out_file(char *filename, Boolean err);
static	void		finish_doname(Running rp);
static	void		maybe_reread_make_state(void);
static	void		process_next(void);
static	void		reset_conditionals(int cnt, Name *targets, Property *locals);
static	pid_t           run_rule_commands(char *host, char **commands);
static	Property	*set_conditionals(int cnt, Name *targets);
static	void		store_conditionals(Running rp);


/*
 *	execute_parallel(line, waitflg)
 *
 *	DMake 2.x:
 *	parallel mode: spawns a parallel process to execute the command group.
 *
 *	Return value:
 *				The result of the execution
 *
 *	Parameters:
 *		line		The command group to execute
 */
Doname
execute_parallel(Property line, Boolean waitflg, Boolean local)
{
	int			argcnt;
	int			cmd_options = 0;
	char			*commands[MAXRULES + 5];
	char			*cp;
	Name			dmake_name;
	Name			dmake_value;
	int			ignore;
	Name			make_machines_name;
	char			**p;
	Property		prop;
	Doname			result = build_ok;
	Cmd_line		rule;
	Boolean			silent_flag;
	Name			target = line->body.line.target;
	Boolean			wrote_state_file = false;

	if ((pmake_max_jobs == 0) &&
	    (dmake_mode_type == parallel_mode)) {
		if (local_host[0] == '\0') {
			(void) gethostname(local_host, MAXNAMELEN);
		}
		MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS");
		dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
		if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
		    ((dmake_value = prop->body.macro.value) != NULL)) {
			pmake_max_jobs = atoi(dmake_value->string_mb);
			if (pmake_max_jobs <= 0) {
				warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero."));
				warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
				pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
			}
		} else {
			/*
			 * For backwards compatibility w/ PMake 1.x, when
			 * DMake 2.x is being run in parallel mode, DMake
			 * should parse the PMake startup file
			 * $(HOME)/.make.machines to get the pmake_max_jobs.
			 */
			MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE");
			dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
			if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
			    ((dmake_value = prop->body.macro.value) != NULL)) {
				make_machines_name = dmake_value;
			} else {
				make_machines_name = NULL;
			}
			if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
				pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
			}
		}
	}

	if ((dmake_mode_type == serial_mode) ||
	    ((dmake_mode_type == parallel_mode) && (waitflg))) {
		return (execute_serial(line));
	}

	{
		p = commands;
	}

	argcnt = 0;
	for (rule = line->body.line.command_used;
	     rule != NULL;
	     rule = rule->next) {
		if (posix && (touch || quest) && !rule->always_exec) {
			continue;
		}
		if (vpath_defined) {
			rule->command_line =
			  vpath_translation(rule->command_line);
		}
		
		silent_flag = false;
		ignore = 0;

		if (rule->command_line->hash.length > 0) {
			if (++argcnt == MAXRULES) {
				return build_serial;
			}
			{
				if (rule->silent && !silent) {
					silent_flag = true;
				}
				if (rule->ignore_error) {
					ignore++;
				}
				/* XXX - need to add support for + prefix */
				if (silent_flag || ignore) {
					*p = getmem((silent_flag ? 1 : 0) +
						    ignore +
						    (strlen(rule->
						           command_line->
						           string_mb)) +
						    1);
					cp = *p++;
					if (silent_flag) {
						*cp++ = (int) at_char;
					}
					if (ignore) {
						*cp++ = (int) hyphen_char;
					}
					(void) strcpy(cp, rule->command_line->string_mb);
				} else {
					*p++ = rule->command_line->string_mb;
				}
			}
		}
	}
	if ((argcnt == 0) ||
	    (report_dependencies_level > 0)) {
		return build_ok;
	}
	{
		*p = NULL;

		Doname res = distribute_process(commands, line);
		if (res == build_running) {
			parallel_process_cnt++;
		}

		/*
		 * Return only those memory that were specially allocated
		 * for part of commands.
		 */
		for (int i = 0; commands[i] != NULL; i++) {
			if ((commands[i][0] == (int) at_char) ||
			    (commands[i][0] == (int) hyphen_char)) {
				retmem_mb(commands[i]);
			}
		}
		return res;
	}
}



#include <unistd.h>	/* sysconf(_SC_NPROCESSORS_ONLN) */
#include <sys/ipc.h>		/* ftok() */
#include <sys/shm.h>		/* shmget(), shmat(), shmdt(), shmctl() */
#include <semaphore.h>		/* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
#include <sys/loadavg.h>	/* getloadavg() */

/*
 *	adjust_pmake_max_jobs (int pmake_max_jobs)
 *
 *	Parameters:
 * 		pmake_max_jobs	- max jobs limit set by user
 *
 *	External functions used:
 *		sysconf()
 * 		getloadavg()
 */
static int
adjust_pmake_max_jobs (int pmake_max_jobs)
{
	static int	ncpu = 0;
	double		loadavg[3];
	int		adjustment;
	int		adjusted_max_jobs;

	if (ncpu <= 0) {
		if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) {
			ncpu = 1;
		}
	}
	if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs);
	adjustment = ((int)loadavg[LOADAVG_1MIN]);
	if (adjustment < 2) return(pmake_max_jobs);
	if (ncpu > 1) {
		adjustment = adjustment / ncpu;
	}
	adjusted_max_jobs = pmake_max_jobs - adjustment;
	if (adjusted_max_jobs < 1) adjusted_max_jobs = 1;
	return(adjusted_max_jobs);
}

/*
 *  M2 adjust mode data and functions
 *
 *  m2_init()		- initializes M2 shared semaphore
 *  m2_acquire_job()	- decrements M2 semaphore counter
 *  m2_release_job()	- increments M2 semaphore counter
 *  m2_fini()		- destroys M2 semaphore and shared memory*
 *
 *  Environment variables:
 *	__DMAKE_M2_FILE__
 *
 *  External functions:
 *	ftok(), shmget(), shmat(), shmdt(), shmctl()
 *	sem_init(), sem_trywait(), sem_post(), sem_destroy()
 *	creat(), close(), unlink()
 *	getenv(), putenv()
 *
 *  Static variables:
 *	m2_file		- tmp file name to create ipc key for shared memory
 *	m2_shm_id	- shared memory id
 *	m2_shm_sem	- shared memory semaphore
 */

static char	m2_file[MAXPATHLEN];
static int	m2_shm_id = -1;
static sem_t*	m2_shm_sem = 0;

static int
m2_init() {
	char	*var;
	key_t	key;

	if ((var = getenv("__DMAKE_M2_FILE__")) == 0) {
		/* compose tmp file name */
		sprintf(m2_file, "%s/dmake.m2.%d.XXXXXX", tmpdir, getpid());

		/* create tmp file */
		int fd = mkstemp(m2_file);
		if (fd < 0) {
			return -1;
		} else {
			close(fd);
		}
	} else {
		/* using existing semaphore */
		strcpy(m2_file, var);
	}

	/* combine IPC key */
	if ((key = ftok(m2_file, 38)) == (key_t) -1) {
		return -1;
	}

	/* create shared memory */
	if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
		return -1;
	}

	/* attach shared memory */
	if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
		return -1;
	}

	/* root process */
	if (var == 0) {
		/* initialize semaphore */
		if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
			return -1;
		}

		/* alloc memory for env variable */
		if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
			return -1;
		}

		/* put key to env */
		sprintf(var, "__DMAKE_M2_FILE__=%s", m2_file);
		if (putenv(var)) {
			return -1;
		}
	}
	return 0;
}

static void
m2_fini() {
	if (m2_shm_id >= 0) {
		struct shmid_ds stat;

		/* determine the number of attached processes */
		if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
			if (stat.shm_nattch <= 1) {
				/* destroy semaphore */
				if (m2_shm_sem != 0) {
					(void) sem_destroy(m2_shm_sem);
				}

				/* destroy shared memory */
				(void) shmctl(m2_shm_id, IPC_RMID, &stat);

				/* remove tmp file created for the key */
				(void) unlink(m2_file);
			} else {
				/* detach shared memory */
				if (m2_shm_sem != 0) {
					(void) shmdt((char*) m2_shm_sem);
				}
			}
		}

		m2_shm_id = -1;
		m2_shm_sem = 0;
	}
}

static int
m2_acquire_job() {
	if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
		if (sem_trywait(m2_shm_sem) == 0) {
			return 1;
		}
		if (errno == EAGAIN) {
			return 0;
		}
	}
	return -1;
}

static int
m2_release_job() {
	if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
		if (sem_post(m2_shm_sem) == 0) {
			return 0;
		}
	}
	return -1;
}

/*
 *  job adjust mode
 *
 *  Possible values:
 *    ADJUST_M1		- adjustment by system load (default)
 *    ADJUST_M2		- fixed limit of jobs for the group of nested dmakes
 *    ADJUST_NONE	- no adjustment - fixed limit of jobs for the current dmake
 */
static enum {
	ADJUST_UNKNOWN,
	ADJUST_M1,
	ADJUST_M2,
	ADJUST_NONE
} job_adjust_mode = ADJUST_UNKNOWN;

/*
 *  void job_adjust_fini()
 *
 *  Description:
 *	Cleans up job adjust data.
 *
 *  Static variables:
 *	job_adjust_mode	Current job adjust mode
 */
void
job_adjust_fini() {
	if (job_adjust_mode == ADJUST_M2) {
		m2_fini();
	}
}

/*
 *  void job_adjust_error()
 *
 *  Description:
 *	Prints warning message, cleans up job adjust data, and disables job adjustment
 *
 *  Environment:
 *	DMAKE_ADJUST_MAX_JOBS
 *
 *  External functions:
 *	putenv()
 *
 *  Static variables:
 *	job_adjust_mode	Current job adjust mode
 */
static void
job_adjust_error() {
	if (job_adjust_mode != ADJUST_NONE) {
		/* cleanup internals */
		job_adjust_fini();

		/* warning message for the user */
		warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment."));

		/* switch off job adjustment for the children */
		putenv(strdup("DMAKE_ADJUST_MAX_JOBS=NO"));

		/* and for this dmake */
		job_adjust_mode = ADJUST_NONE;
	}
}

/*
 *  void job_adjust_init()
 *
 *  Description:
 *	Parses DMAKE_ADJUST_MAX_JOBS env variable
 *	and performs appropriate initializations.
 *
 *  Environment:
 *	DMAKE_ADJUST_MAX_JOBS
 *	  DMAKE_ADJUST_MAX_JOBS == "NO"	- no adjustment
 *	  DMAKE_ADJUST_MAX_JOBS == "M2"	- M2 adjust mode
 *	  other				- M1 adjust mode
 *
 *  External functions:
 *	getenv()
 *
 *  Static variables:
 *	job_adjust_mode	Current job adjust mode
 */
static void
job_adjust_init() {
	if (job_adjust_mode == ADJUST_UNKNOWN) {
		/* default mode */
		job_adjust_mode = ADJUST_M1;

		/* determine adjust mode */
		if (char *var = getenv("DMAKE_ADJUST_MAX_JOBS")) {
			if (strcasecmp(var, "NO") == 0) {
				job_adjust_mode = ADJUST_NONE;
			} else if (strcasecmp(var, "M2") == 0) {
				job_adjust_mode = ADJUST_M2;
			}
		}

		/* M2 specific initialization */
		if (job_adjust_mode == ADJUST_M2) {
			if (m2_init()) {
				job_adjust_error();
			}
		}
	}
}


/*
 *	distribute_process(char **commands, Property line)
 *
 *	Parameters:
 *		commands	argv vector of commands to execute
 *
 *	Return value:
 *				The result of the execution
 *
 *	Static variables used:
 *		process_running	Set to the pid of the process set running
 *		job_adjust_mode	Current job adjust mode
 */
static Doname
distribute_process(char **commands, Property line)
{
	static unsigned	file_number = 0;
	wchar_t		string[MAXPATHLEN];
	char		mbstring[MAXPATHLEN];
	int		filed;
	int		res;
	int		tmp_index;
	char		*tmp_index_str_ptr;

	/* initialize adjust mode, if not initialized */
	if (job_adjust_mode == ADJUST_UNKNOWN) {
		job_adjust_init();
	}

	/* actions depend on adjust mode */
	switch (job_adjust_mode) {
	case ADJUST_M1:
		while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) {
			await_parallel(false);
			finish_children(true);
		}
		break;
	case ADJUST_M2:
		if ((res = m2_acquire_job()) == 0) {
			if (parallel_process_cnt > 0) {
				await_parallel(false);
				finish_children(true);

				if ((res = m2_acquire_job()) == 0) {
					return build_serial;
				}
			} else {
				return build_serial;
			}
		}
		if (res < 0) {
			/* job adjustment error */
			job_adjust_error();

			/* no adjustment */
			while (parallel_process_cnt >= pmake_max_jobs) {
				await_parallel(false);
				finish_children(true);
			}
		}
		break;
	default:
		while (parallel_process_cnt >= pmake_max_jobs) {
			await_parallel(false);
			finish_children(true);
		}
	}

	setvar_envvar();
	/*
	 * Tell the user what DMake is doing.
	 */
	if (!silent && output_mode != txt2_mode) {
		/*
		 * Print local_host --> x job(s).
		 */
		(void) fprintf(stdout,
		               gettext("%s --> %d %s\n"),
		               local_host,
		               parallel_process_cnt + 1,
		               (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs"));

		/* Print command line(s). */
		tmp_index = 0;
		while (commands[tmp_index] != NULL) {
		    /* No @ char. */
		    /* XXX - need to add [2] when + prefix is added */
		    if ((commands[tmp_index][0] != (int) at_char) &&
		        (commands[tmp_index][1] != (int) at_char)) {
			tmp_index_str_ptr = commands[tmp_index];
			if (*tmp_index_str_ptr == (int) hyphen_char) {
				tmp_index_str_ptr++;
			}
                        (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
		    }
		    tmp_index++;
		}
		(void) fflush(stdout);
	}

	(void) sprintf(mbstring,
		        "%s/dmake.stdout.%d.%d.XXXXXX",
			tmpdir,
		        getpid(),
	                file_number++);

	mktemp(mbstring);

	stdout_file = strdup(mbstring);
	stderr_file = NULL;

	if (!out_err_same) {
		(void) sprintf(mbstring,
			        "%s/dmake.stderr.%d.%d.XXXXXX",
				tmpdir,
			        getpid(),
		                file_number++);

		mktemp(mbstring);

		stderr_file = strdup(mbstring);
	}

	process_running = run_rule_commands(local_host, commands);

	return build_running;
}

/*
 *	doname_parallel(target, do_get, implicit)
 *
 *	Processes the given target and finishes up any parallel
 *	processes left running.
 *
 *	Return value:
 *				Result of target build
 *
 *	Parameters:
 *		target		Target to build
 *		do_get		True if sccs get to be done
 *		implicit	True if this is an implicit target
 */
Doname
doname_parallel(Name target, Boolean do_get, Boolean implicit)
{
	Doname		result;

	result = doname_check(target, do_get, implicit, false);
	if (result == build_ok || result == build_failed) {
		return result;
	}
	finish_running();
	return (Doname) target->state;
}

/*
 *	doname_subtree(target, do_get, implicit)
 *
 *	Completely computes an object and its dependents for a
 *	serial subtree build.
 *
 *	Parameters:
 *		target		Target to build
 *		do_get		True if sccs get to be done
 *		implicit	True if this is an implicit target
 *
 *	Static variables used:
 *		running_tail	Tail of the list of running processes
 *
 *	Global variables used:
 *		running_list	The list of running processes
 */
static void
doname_subtree(Name target, Boolean do_get, Boolean implicit)
{
	Running		save_running_list;
	Running		*save_running_tail;

	save_running_list = running_list;
	save_running_tail = running_tail;
	running_list = NULL;
	running_tail = &running_list;
	target->state = build_subtree;
	target->checking_subtree = true;
	while(doname_check(target, do_get, implicit, false) == build_running) {
		target->checking_subtree = false;
		finish_running();
		target->state = build_subtree;
	}
	target->checking_subtree = false;
	running_list = save_running_list;
	running_tail = save_running_tail;
}

/*
 *	finish_running()
 *
 *	Keeps processing until the running_list is emptied out.
 *
 *	Parameters:
 *
 *	Global variables used:
 *		running_list	The list of running processes
 */
void
finish_running(void)
{
	while (running_list != NULL) {
		{
			await_parallel(false);
			finish_children(true);
		}
		if (running_list != NULL) {
			process_next();
		}
	}
}

/*
 *	process_next()
 *
 *	Searches the running list for any targets which can start processing.
 *	This can be a pending target, a serial target, or a subtree target.
 *
 *	Parameters:
 *
 *	Static variables used:
 *		running_tail		The end of the list of running procs
 *		subtree_conflict	A target which conflicts with a subtree
 *		subtree_conflict2	The other target which conflicts
 *
 *	Global variables used:
 *		commands_done		True if commands executed
 *		debug_level		Controls debug output
 *		parallel_process_cnt	Number of parallel process running
 *		recursion_level		Indentation for debug output
 *		running_list		List of running processes
 */
static void
process_next(void)
{
	Running		rp;
	Running		*rp_prev;
	Property	line;
	Chain		target_group;
	Dependency	dep;
	Boolean		quiescent = true;
	Running		*subtree_target;
	Boolean		saved_commands_done;
	Property	*conditionals;

	subtree_target = NULL;
	subtree_conflict = NULL;
	subtree_conflict2 = NULL;
	/*
	 * If nothing currently running, build a serial target, if any.
	 */
start_loop_1:
	for (rp_prev = &running_list, rp = running_list;
	     rp != NULL && parallel_process_cnt == 0;
	     rp = rp->next) {
		if (rp->state == build_serial) {
			*rp_prev = rp->next;
			if (rp->next == NULL) {
				running_tail = rp_prev;
			}
			recursion_level = rp->recursion_level;
			rp->target->state = build_pending;
			(void) doname_check(rp->target,
					    rp->do_get,
					    rp->implicit,
					    false);
			quiescent = false;
			delete_running_struct(rp);
			goto start_loop_1;
		} else {
			rp_prev = &rp->next;
		}
	}
	/*
	 * Find a target to build.  The target must be pending, have all
	 * its dependencies built, and not be in a target group with a target
	 * currently building.
	 */
start_loop_2:
	for (rp_prev = &running_list, rp = running_list;
	     rp != NULL;
	     rp = rp->next) {
		if (!(rp->state == build_pending ||
		      rp->state == build_subtree)) {
			quiescent = false;
			rp_prev = &rp->next;
		} else if (rp->state == build_pending) {
			line = get_prop(rp->target->prop, line_prop);
			for (dep = line->body.line.dependencies;
			     dep != NULL;
			     dep = dep->next) {
				if (dep->name->state == build_running ||
				    dep->name->state == build_pending ||
				    dep->name->state == build_serial) {
					break;
				}
			}
			if (dep == NULL) {
				for (target_group = line->body.line.target_group;
				     target_group != NULL;
				     target_group = target_group->next) {
					if (is_running(target_group->name)) {
						break;
					}
				}
				if (target_group == NULL) {
					*rp_prev = rp->next;
					if (rp->next == NULL) {
						running_tail = rp_prev;
					}
					recursion_level = rp->recursion_level;
					rp->target->state = rp->redo ?
					  build_dont_know : build_pending;
					saved_commands_done = commands_done;
					conditionals =
						set_conditionals
						    (rp->conditional_cnt,
						     rp->conditional_targets);
					rp->target->dont_activate_cond_values = true;
					if ((doname_check(rp->target,
							  rp->do_get,
							  rp->implicit,
							  rp->target->has_target_prop ? true : false) !=
					     build_running) &&
					    !commands_done) {
						commands_done =
						  saved_commands_done;
					}
					rp->target->dont_activate_cond_values = false;
					reset_conditionals
						(rp->conditional_cnt,
						 rp->conditional_targets,
						 conditionals);
					quiescent = false;
					delete_running_struct(rp);
					goto start_loop_2;
				} else {
					rp_prev = &rp->next;
				}
			} else {
				rp_prev = &rp->next;
			}
		} else {
			rp_prev = &rp->next;
		}
	}
	/*
	 * If nothing has been found to build and there exists a subtree
	 * target with no dependency conflicts, build it.
	 */
	if (quiescent) {
start_loop_3:
		for (rp_prev = &running_list, rp = running_list;
		     rp != NULL;
		     rp = rp->next) {
			if (rp->state == build_subtree) {
				if (!dependency_conflict(rp->target)) {
					*rp_prev = rp->next;
					if (rp->next == NULL) {
						running_tail = rp_prev;
					}
					recursion_level = rp->recursion_level;
					doname_subtree(rp->target,
						       rp->do_get,
						       rp->implicit);
					quiescent = false;
					delete_running_struct(rp);
					goto start_loop_3;
				} else {
					subtree_target = rp_prev;
					rp_prev = &rp->next;
				}
			} else {
				rp_prev = &rp->next;
			}
		}
	}
	/*
	 * If still nothing found to build, we either have a deadlock
	 * or a subtree with a dependency conflict with something waiting
	 * to build.
	 */
	if (quiescent) {
		if (subtree_target == NULL) {
			fatal(gettext("Internal error: deadlock detected in process_next"));
		} else {
			rp = *subtree_target;
			if (debug_level > 0) {
				warning(gettext("Conditional macro conflict encountered for %s between %s and %s"),
					subtree_conflict2->string_mb,
					rp->target->string_mb,
					subtree_conflict->string_mb);
			}
			*subtree_target = (*subtree_target)->next;
			if (rp->next == NULL) {
				running_tail = subtree_target;
			}
			recursion_level = rp->recursion_level;
			doname_subtree(rp->target, rp->do_get, rp->implicit);
			delete_running_struct(rp);
		}
	}
}

/*
 *	set_conditionals(cnt, targets)
 *
 *	Sets the conditional macros for the targets given in the array of
 *	targets.  The old macro values are returned in an array of
 *	Properties for later resetting.
 *
 *	Return value:
 *					Array of conditional macro settings
 *
 *	Parameters:
 *		cnt			Number of targets
 *		targets			Array of targets
 */
static Property *
set_conditionals(int cnt, Name *targets)
{
	Property	*locals, *lp;
	Name		*tp;

	locals = (Property *) getmem(cnt * sizeof(Property));
	for (lp = locals, tp = targets;
	     cnt > 0;
	     cnt--, lp++, tp++) {
		*lp = (Property) getmem((*tp)->conditional_cnt *
					sizeof(struct _Property));
		set_locals(*tp, *lp);
	}
	return locals;
}

/*
 *	reset_conditionals(cnt, targets, locals)
 *
 *	Resets the conditional macros as saved in the given array of
 *	Properties.  The resets are done in reverse order.  Afterwards the
 *	data structures are freed.
 *
 *	Parameters:
 *		cnt			Number of targets
 *		targets			Array of targets
 *		locals			Array of dependency macro settings
 */
static void
reset_conditionals(int cnt, Name *targets, Property *locals)
{
	Name		*tp;
	Property	*lp;

	for (tp = targets + (cnt - 1), lp = locals + (cnt - 1);
	     cnt > 0;
	     cnt--, tp--, lp--) {
		reset_locals(*tp,
			     *lp,
			     get_prop((*tp)->prop, conditional_prop),
			     0);
		retmem_mb((caddr_t) *lp);
	}
	retmem_mb((caddr_t) locals);
}

/*
 *	dependency_conflict(target)
 *
 *	Returns true if there is an intersection between
 *	the subtree of the target and any dependents of the pending targets.
 *
 *	Return value:
 *					True if conflict found
 *
 *	Parameters:
 *		target			Subtree target to check
 *
 *	Static variables used:
 *		subtree_conflict	Target conflict found
 *		subtree_conflict2	Second conflict found
 *
 *	Global variables used:
 *		running_list		List of running processes
 *		wait_name		.WAIT, not a real dependency
 */
static Boolean
dependency_conflict(Name target)
{
	Property	line;
	Property	pending_line;
	Dependency	dp;
	Dependency	pending_dp;
	Running		rp;

	/* Return if we are already checking this target */
	if (target->checking_subtree) {
		return false;
	}
	target->checking_subtree = true;
	line = get_prop(target->prop, line_prop);
	if (line == NULL) {
		target->checking_subtree = false;
		return false;
	}
	/* Check each dependency of the target for conflicts */
	for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) {
		/* Ignore .WAIT dependency */
		if (dp->name == wait_name) {
			continue;
		}
		/*
		 * For each pending target, look for a dependency which
		 * is the same as a dependency of the subtree target.  Since
		 * we can't build the subtree until all pending targets have
		 * finished which depend on the same dependency, this is
		 * a conflict.
		 */
		for (rp = running_list; rp != NULL; rp = rp->next) {
			if (rp->state == build_pending) {
				pending_line = get_prop(rp->target->prop,
							line_prop);
				if (pending_line == NULL) {
					continue;
				}
				for(pending_dp = pending_line->
				    			body.line.dependencies;
				    pending_dp != NULL;
				    pending_dp = pending_dp->next) {
					if (dp->name == pending_dp->name) {
						target->checking_subtree
						  		= false;
						subtree_conflict = rp->target;
						subtree_conflict2 = dp->name;
						return true;
					}
				}
			}
		}
		if (dependency_conflict(dp->name)) {
			target->checking_subtree = false;
			return true;
		}
	}
	target->checking_subtree = false;
	return false;
}

/*
 *	await_parallel(waitflg)
 *
 *	Waits for parallel children to exit and finishes their processing.
 *	If waitflg is false, the function returns after update_delay.
 *
 *	Parameters:
 *		waitflg		dwight
 */
void
await_parallel(Boolean waitflg)
{
	Boolean		nohang;
	pid_t		pid;
	int		status;
	Running		rp;
	int		waiterr;

	nohang = false;
	for ( ; ; ) {
		if (!nohang) {
			(void) alarm((int) update_delay);
		}
		pid = waitpid((pid_t)-1,
			      &status,
			      nohang ? WNOHANG : 0);
		waiterr = errno;
		if (!nohang) {
			(void) alarm(0);
		}
		if (pid <= 0) {
			if (waiterr == EINTR) {
				if (waitflg) {
					continue;
				} else {
					return;
				}
			} else {
				return;
			}
		}
		for (rp = running_list;
		     (rp != NULL) && (rp->pid != pid);
		     rp = rp->next) {
			;
		}
		if (rp == NULL) {
			fatal(gettext("Internal error: returned child pid not in running_list"));
		} else {
			rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
		}
		nohang = true;
		parallel_process_cnt--;

		if (job_adjust_mode == ADJUST_M2) {
			if (m2_release_job()) {
				job_adjust_error();
			}
		}
	}
}

/*
 *	finish_children(docheck)
 *
 *	Finishes the processing for all targets which were running
 *	and have now completed.
 *
 *	Parameters:
 *		docheck		Completely check the finished target
 *
 *	Static variables used:
 *		running_tail	The tail of the running list
 *
 *	Global variables used:
 *		continue_after_error  -k flag
 *		fatal_in_progress  True if we are finishing up after fatal err
 *		running_list	List of running processes
 */
void
finish_children(Boolean docheck)
{
	int		cmds_length;
	Property	line;
	Property	line2;
	struct stat	out_buf;
	Running		rp;
	Running		*rp_prev;
	Cmd_line	rule;
	Boolean		silent_flag;

	for (rp_prev = &running_list, rp = running_list;
	     rp != NULL;
	     rp = rp->next) {
bypass_for_loop_inc_4:
		/*
		 * If the state is ok or failed, then this target has
		 * finished building.
		 * In parallel_mode, output the accumulated stdout/stderr.
		 * Read the auto dependency stuff, handle a failed build,
		 * update the target, then finish the doname process for
		 * that target.
		 */
		if (rp->state == build_ok || rp->state == build_failed) {
			*rp_prev = rp->next;
			if (rp->next == NULL) {
				running_tail = rp_prev;
			}
			if ((line2 = rp->command) == NULL) {
				line2 = get_prop(rp->target->prop, line_prop);
			}


			/*
			 * Check if there were any job output
			 * from the parallel build.
			 */
			if (rp->stdout_file != NULL) {
				if (stat(rp->stdout_file, &out_buf) < 0) {
					fatal(gettext("stat of %s failed: %s"),
					    rp->stdout_file,
					    errmsg(errno));
				}

				if ((line2 != NULL) &&
				    (out_buf.st_size > 0)) {
					cmds_length = 0;
					for (rule = line2->body.line.command_used,
						 silent_flag = silent;
					     rule != NULL;
					     rule = rule->next) {
						cmds_length += rule->command_line->hash.length + 1;
						silent_flag = BOOLEAN(silent_flag || rule->silent);
					}
					if (out_buf.st_size != cmds_length || silent_flag ||
					    output_mode == txt2_mode) {
						dump_out_file(rp->stdout_file, false);
					}
				}
				(void) unlink(rp->stdout_file);
				retmem_mb(rp->stdout_file);
				rp->stdout_file = NULL;
			}

			if (!out_err_same && (rp->stderr_file != NULL)) {
				if (stat(rp->stderr_file, &out_buf) < 0) {
					fatal(gettext("stat of %s failed: %s"),
					    rp->stderr_file,
					    errmsg(errno));
				}
				if ((line2 != NULL) &&
				    (out_buf.st_size > 0)) {
					dump_out_file(rp->stderr_file, true);
				}
				(void) unlink(rp->stderr_file);
				retmem_mb(rp->stderr_file);
				rp->stderr_file = NULL;
			}
			
			check_state(rp->temp_file);
			if (rp->temp_file != NULL) {
				free_name(rp->temp_file);
			}
			rp->temp_file = NULL;
			if (rp->state == build_failed) {
				line = get_prop(rp->target->prop, line_prop);
				if (line != NULL) {
					line->body.line.command_used = NULL;
				}
				if (continue_after_error ||
				    fatal_in_progress ||
				    !docheck) {
					warning(gettext("Command failed for target `%s'"),
						rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
					build_failed_seen = true;
				} else {
					/*
					 * XXX??? - DMake needs to exit(),
					 * but shouldn't call fatal().
					 */
#ifdef PRINT_EXIT_STATUS
					warning("I'm in finish_children. rp->state == build_failed.");
#endif

					fatal(gettext("Command failed for target `%s'"),
						rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
				}
			}
			if (!docheck) {
				delete_running_struct(rp);
				rp = *rp_prev;
				if (rp == NULL) {
					break;
				} else {
					goto bypass_for_loop_inc_4;
				}
			}
			update_target(get_prop(rp->target->prop, line_prop),
				      rp->state);
			finish_doname(rp);
			delete_running_struct(rp);
			rp = *rp_prev;
			if (rp == NULL) {
				break;
			} else {
				goto bypass_for_loop_inc_4;
			}
		} else {
			rp_prev = &rp->next;
		}
	}
}

/*
 *	dump_out_file(filename, err)
 *
 *	Write the contents of the file to stdout, then unlink the file.
 *
 *	Parameters:
 *		filename	Name of temp file containing output
 *
 *	Global variables used:
 */
static void
dump_out_file(char *filename, Boolean err)
{
	int		chars_read;
	char		copybuf[BUFSIZ];
	int		fd;
	int		out_fd = (err ? 2 : 1);

	if ((fd = open(filename, O_RDONLY)) < 0) {
		fatal(gettext("open failed for output file %s: %s"),
		      filename,
		      errmsg(errno));
	}
	if (!silent && output_mode != txt2_mode) {
		(void) fprintf(err ? stderr : stdout,
		               err ?
				gettext("%s --> Job errors\n") :
				gettext("%s --> Job output\n"),
		               local_host);
		(void) fflush(err ? stderr : stdout);
	}
	for (chars_read = read(fd, copybuf, BUFSIZ);
	     chars_read > 0;
	     chars_read = read(fd, copybuf, BUFSIZ)) {
		/*
		 * Read buffers from the source file until end or error.
		 */
		if (write(out_fd, copybuf, chars_read) < 0) {
			fatal(gettext("write failed for output file %s: %s"),
			      filename,
			      errmsg(errno));
		}
	}
	(void) close(fd);
	(void) unlink(filename);
}

/*
 *	finish_doname(rp)
 *
 *	Completes the processing for a target which was left running.
 *
 *	Parameters:
 *		rp		Running list entry for target
 *
 *	Global variables used:
 *		debug_level	Debug flag
 *		recursion_level	Indentation for debug output
 */
static void
finish_doname(Running rp)
{
	int		auto_count = rp->auto_count;
	Name		*automatics = rp->automatics;
	Doname		result = rp->state;
	Name		target = rp->target;
	Name		true_target = rp->true_target;
	Property	*conditionals;

	recursion_level = rp->recursion_level;
	if (result == build_ok) {
		if (true_target == NULL) {
			(void) printf("Target = %s\n", target->string_mb);
			(void) printf(" State = %d\n", result);
			fatal("Internal error: NULL true_target in finish_doname");
		}
		/* If all went OK, set a nice timestamp */
		if (true_target->stat.time == file_doesnt_exist) {
			true_target->stat.time = file_max_time;
		}
	}
	target->state = result;
	if (target->is_member) {
		Property member;

		/* Propagate the timestamp from the member file to the member */
		if ((target->stat.time != file_max_time) &&
		    ((member = get_prop(target->prop, member_prop)) != NULL) &&
		    (exists(member->body.member.member) > file_doesnt_exist)) {
			target->stat.time =
/*
			  exists(member->body.member.member);
 */
			  member->body.member.member->stat.time;
		}
	}
	/*
	 * Check if we found any new auto dependencies when we
	 * built the target.
	 */
	if ((result == build_ok) && check_auto_dependencies(target,
							    auto_count,
							    automatics)) {
		if (debug_level > 0) {
			(void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
				      recursion_level,
				      "",
				      true_target->string_mb);
		}
		target->rechecking_target = true;
		target->state = build_running;

		/* [tolik, Tue Mar 25 1997]
		 * Fix for bug 4038824:
		 *       command line options set by conditional macros get dropped
		 * rp->conditional_cnt and rp->conditional_targets must be copied
		 * to new 'rp' during add_pending(). Set_conditionals() stores
		 * rp->conditional_targets to the global variable 'conditional_targets'
		 * Add_pending() will use this variable to set up 'rp'.
		 */
		conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
		add_pending(target,
			    recursion_level,
			    rp->do_get,
			    rp->implicit,
			    true);
		reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
	}
}

/*
 *	new_running_struct()
 *
 *	Constructor for Running struct. Creates a structure and initializes
 *      its fields.
 *
 */
static Running new_running_struct()
{
	Running		rp;

	rp = ALLOC(Running);
	rp->target = NULL;
	rp->true_target = NULL;
	rp->command = NULL;
	rp->sprodep_value = NULL;
	rp->sprodep_env = NULL;
	rp->auto_count = 0;
	rp->automatics = NULL;
	rp->pid = -1;
	rp->job_msg_id = -1;
	rp->stdout_file = NULL;
	rp->stderr_file = NULL;
	rp->temp_file = NULL;
	rp->next = NULL;
	return rp;
}

/*
 *	add_running(target, true_target, command, recursion_level, auto_count,
 *					automatics, do_get, implicit)
 *
 *	Adds a record on the running list for this target, which
 *	was just spawned and is running.
 *
 *	Parameters:
 *		target		Target being built
 *		true_target	True target for target
 *		command		Running command.
 *		recursion_level	Debug indentation level
 *		auto_count	Count of automatic dependencies
 *		automatics	List of automatic dependencies
 *		do_get		Sccs get flag
 *		implicit	Implicit flag
 *
 *	Static variables used:
 *		running_tail	Tail of running list
 *		process_running	PID of process
 *
 *	Global variables used:
 *		current_line	Current line for target
 *		current_target	Current target being built
 *		stderr_file	Temporary file for stdout
 *		stdout_file	Temporary file for stdout
 *		temp_file_name	Temporary file for auto dependencies
 */
void
add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
{
	Running		rp;
	Name		*p;

	rp = new_running_struct();
	rp->state = build_running;
	rp->target = target;
	rp->true_target = true_target;
	rp->command = command;
	rp->recursion_level = recursion_level;
	rp->do_get = do_get;
	rp->implicit = implicit;
	rp->auto_count = auto_count;
	if (auto_count > 0) {
		rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
		for (p = rp->automatics; auto_count > 0; auto_count--) {
			*p++ = *automatics++;
		}
	} else {
		rp->automatics = NULL;
	}
	{
		rp->pid = process_running;
		process_running = -1;
		childPid = -1;
	}
	rp->job_msg_id = job_msg_id;
	rp->stdout_file = stdout_file;
	rp->stderr_file = stderr_file;
	rp->temp_file = temp_file_name;
	rp->redo = false;
	rp->next = NULL;
	store_conditionals(rp);
	stdout_file = NULL;
	stderr_file = NULL;
	temp_file_name = NULL;
	current_target = NULL;
	current_line = NULL;
	*running_tail = rp;
	running_tail = &rp->next;
}

/*
 *	add_pending(target, recursion_level, do_get, implicit, redo)
 *
 *	Adds a record on the running list for a pending target
 *	(waiting for its dependents to finish running).
 *
 *	Parameters:
 *		target		Target being built
 *		recursion_level	Debug indentation level
 *		do_get		Sccs get flag
 *		implicit	Implicit flag
 *		redo		True if this target is being redone
 *
 *	Static variables used:
 *		running_tail	Tail of running list
 */
void
add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
{
	Running		rp;
	rp = new_running_struct();
	rp->state = build_pending;
	rp->target = target;
	rp->recursion_level = recursion_level;
	rp->do_get = do_get;
	rp->implicit = implicit;
	rp->redo = redo;
	store_conditionals(rp);
	*running_tail = rp;
	running_tail = &rp->next;
}

/*
 *	add_serial(target, recursion_level, do_get, implicit)
 *
 *	Adds a record on the running list for a target which must be
 *	executed in serial after others have finished.
 *
 *	Parameters:
 *		target		Target being built
 *		recursion_level	Debug indentation level
 *		do_get		Sccs get flag
 *		implicit	Implicit flag
 *
 *	Static variables used:
 *		running_tail	Tail of running list
 */
void
add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
{
	Running		rp;

	rp = new_running_struct();
	rp->target = target;
	rp->recursion_level = recursion_level;
	rp->do_get = do_get;
	rp->implicit = implicit;
	rp->state = build_serial;
	rp->redo = false;
	store_conditionals(rp);
	*running_tail = rp;
	running_tail = &rp->next;
}

/*
 *	add_subtree(target, recursion_level, do_get, implicit)
 *
 *	Adds a record on the running list for a target which must be
 *	executed in isolation after others have finished.
 *
 *	Parameters:
 *		target		Target being built
 *		recursion_level	Debug indentation level
 *		do_get		Sccs get flag
 *		implicit	Implicit flag
 *
 *	Static variables used:
 *		running_tail	Tail of running list
 */
void
add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
{
	Running		rp;

	rp = new_running_struct();
	rp->target = target;
	rp->recursion_level = recursion_level;
	rp->do_get = do_get;
	rp->implicit = implicit;
	rp->state = build_subtree;
	rp->redo = false;
	store_conditionals(rp);
	*running_tail = rp;
	running_tail = &rp->next;
}

/*
 *	store_conditionals(rp)
 *
 *	Creates an array of the currently active targets with conditional
 *	macros (found in the chain conditional_targets) and puts that
 *	array in the Running struct.
 *
 *	Parameters:
 *		rp		Running struct for storing chain
 *
 *	Global variables used:
 *		conditional_targets  Chain of current dynamic conditionals
 */
static void
store_conditionals(Running rp)
{
	int		cnt;
	Chain		cond_name;

	if (conditional_targets == NULL) {
		rp->conditional_cnt = 0;
		rp->conditional_targets = NULL;
		return;
	}
	cnt = 0;
	for (cond_name = conditional_targets;
	     cond_name != NULL;
	     cond_name = cond_name->next) {
		cnt++;
	}
	rp->conditional_cnt = cnt;
	rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
	for (cond_name = conditional_targets;
	     cond_name != NULL;
	     cond_name = cond_name->next) {
		rp->conditional_targets[--cnt] = cond_name->name;
	}
}

/*
 *	parallel_ok(target, line_prop_must_exists)
 *
 *	Returns true if the target can be run in parallel
 *
 *	Return value:
 *				True if can run in parallel
 *
 *	Parameters:
 *		target		Target being tested
 *
 *	Global variables used:
 *		all_parallel	True if all targets default to parallel
 *		only_parallel	True if no targets default to parallel
 */
Boolean
parallel_ok(Name target, Boolean line_prop_must_exists)
{
	Boolean		assign;
	Boolean		make_refd;
	Property	line;
	Cmd_line	rule;

	assign = make_refd = false;
	if (((line = get_prop(target->prop, line_prop)) == NULL) &&
	    line_prop_must_exists) {
		return false;
	}
	if (line != NULL) {
		for (rule = line->body.line.command_used;
		     rule != NULL;
		     rule = rule->next) {
			if (rule->assign) {
				assign = true;
			} else if (rule->make_refd) {
				make_refd = true;
			}
		}
	}
	if (assign) {
		return false;
	} else if (target->parallel) {
		return true;
	} else if (target->no_parallel) {
		return false;
	} else if (all_parallel) {
		return true;
	} else if (only_parallel) {
		return false;
	} else if (make_refd) {
		return false;
	} else {
		return true;
	}
}

/*
 *	is_running(target)
 *
 *	Returns true if the target is running.
 *
 *	Return value:
 *				True if target is running
 *
 *	Parameters:
 *		target		Target to check
 *
 *	Global variables used:
 *		running_list	List of running processes
 */
Boolean
is_running(Name target)
{
	Running		rp;

	if (target->state != build_running) {
		return false;
	}
	for (rp = running_list;
	     rp != NULL && target != rp->target;
	     rp = rp->next);
	if (rp == NULL) {
		return false;
	} else {
		return (rp->state == build_running) ? true : false;
	}
}

/*
 * This function replaces the makesh binary.
 */
 

static pid_t
run_rule_commands(char *host, char **commands)
{
	Boolean		always_exec;
	Name		command;
	Boolean		ignore;
	int		length;
	Doname		result;
	Boolean		silent_flag;
	wchar_t		*tmp_wcs_buffer;

	childPid = fork();
	switch (childPid) {
	case -1:	/* Error */
		fatal(gettext("Could not fork child process for dmake job: %s"),
		      errmsg(errno));
		break;
	case 0:		/* Child */
		/* To control the processed targets list is not the child's business */
		running_list = NULL;
		if(out_err_same) {
			redirect_io(stdout_file, (char*)NULL);
		} else {
			redirect_io(stdout_file, stderr_file);
		}
		for (commands = commands;
		     (*commands != (char *)NULL);
		     commands++) {
			silent_flag = silent;
			ignore = false;
			always_exec = false;
			while ((**commands == (int) at_char) ||
			       (**commands == (int) hyphen_char) ||
			       (**commands == (int) plus_char)) {
				if (**commands == (int) at_char) {
					silent_flag = true;
				}
				if (**commands == (int) hyphen_char) {
					ignore = true;
				}
				if (**commands == (int) plus_char) {
					always_exec = true;
				}
				(*commands)++;
			}
			if ((length = strlen(*commands)) >= MAXPATHLEN) {
				tmp_wcs_buffer = ALLOC_WC(length + 1);
				(void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
				command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
				retmem(tmp_wcs_buffer);
			} else {
				MBSTOWCS(wcs_buffer, *commands);
				command = GETNAME(wcs_buffer, FIND_LENGTH);
			}
			if ((command->hash.length > 0) &&
			    !silent_flag) {
				(void) printf("%s\n", command->string_mb);
			}
			result = dosys(command,
			               ignore,
			               false,
			               false, /* bugs #4085164 & #4990057 */
			               /* BOOLEAN(silent_flag && ignore), */
			               always_exec, 
			               (Name) NULL);
			if (result == build_failed) {
				if (silent_flag) {
					(void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb);
				}
				if (!ignore) {
					_exit(1);
				}
			}
		}
		_exit(0);
		break;
	default:
		break;
	}
	return childPid;
}

static void
maybe_reread_make_state(void)
{
	/* Copying dosys()... */
	if (report_dependencies_level == 0) {
		make_state->stat.time = file_no_time;
		(void) exists(make_state);
		if (make_state_before == make_state->stat.time) {
			return;
		}
		makefile_type = reading_statefile;
		if (read_trace_level > 1) {
			trace_reader = true;
		}
		temp_file_number++;
		(void) read_simple_file(make_state,
					false,
					false,
					false,
					false,
					false,
					true);
		trace_reader = false;
	}
}


static void
delete_running_struct(Running rp)
{
	if ((rp->conditional_cnt > 0) &&
	    (rp->conditional_targets != NULL)) {
		retmem_mb((char *) rp->conditional_targets);
	}
/**/
	if ((rp->auto_count > 0) &&
	    (rp->automatics != NULL)) {
		retmem_mb((char *) rp->automatics);
	}
/**/
	if(rp->sprodep_value) {
		free_name(rp->sprodep_value);
	}
	if(rp->sprodep_env) {
		retmem_mb(rp->sprodep_env);
	}
	retmem_mb((char *) rp);

}