/*
 * 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 2006 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 *
 * Copyright (c) 2016 by Delphix. All rights reserved.
 * Copyright 2019 RackTop Systems.
 */

/*
 *	doname.c
 *
 *	Figure out which targets are out of date and rebuild them
 */

/*
 * Included files
 */
#include <alloca.h>		/* alloca() */
#include <fcntl.h>
#include <mk/defs.h>
#include <mksh/i18n.h>		/* get_char_semantics_value() */
#include <mksh/macro.h>		/* getvar(), expand_value() */
#include <mksh/misc.h>		/* getmem() */
#include <poll.h>
#include <libintl.h>
#include <signal.h>
#include <stropts.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>	/* uname() */
#include <sys/wait.h>
#include <unistd.h>		/* close() */

/*
 * Defined macros
 */
#	define LOCALHOST "localhost"

#define MAXRULES 100

// Sleep for .1 seconds between stat()'s
const int	STAT_RETRY_SLEEP_TIME = 100000;

/*
 * typedefs & structs
 */

/*
 * Static variables
 */
static char	hostName[MAXNAMELEN] = "";
static char	userName[MAXNAMELEN] = "";


static int	second_pass = 0;

/*
 * File table of contents
 */
extern	Doname		doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic);
extern	Doname		doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic);
static	Boolean		check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals);
void		dynamic_dependencies(Name target);
static	Doname		run_command(register Property line, Boolean print_machine);
extern	Doname		execute_serial(Property line);
extern	Name		vpath_translation(register Name cmd);
extern	void		check_state(Name temp_file_name);
static	void		read_dependency_file(register Name filename);
static	void		check_read_state_file(void);
static	void		do_assign(register Name line, register Name target);
static	void		build_command_strings(Name target, register Property line);
static	Doname		touch_command(register Property line, register Name target, Doname result);
extern	void		update_target(Property line, Doname result);
static	Doname		sccs_get(register Name target, register Property *command);
extern	void		read_directory_of_file(register Name file);
static	void		add_pattern_conditionals(register Name target);
extern	void		set_locals(register Name target, register Property old_locals);
extern	void		reset_locals(register Name target, register Property old_locals, register Property conditional, register int index);
extern	Boolean		check_auto_dependencies(Name target, int auto_count, Name *automatics);
static	void		delete_query_chain(Chain ch);

// From read2.cc
extern	Name		normalize_name(register wchar_t *name_string, register int length);



/*
 * DONE.
 *
 *	doname_check(target, do_get, implicit, automatic)
 *
 *	Will call doname() and then inspect the return value
 *
 *	Return value:
 *				Indication if the build failed or not
 *
 *	Parameters:
 *		target		The target to build
 *		do_get		Passed thru to doname()
 *		implicit	Passed thru to doname()
 *		automatic	Are we building a hidden dependency?
 *
 *	Global variables used:
 *		build_failed_seen	Set if -k is on and error occurs
 *		continue_after_error	Indicates that -k is on
 *		report_dependencies	No error msg if -P is on
 */
Doname
doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic)
{
	int first_time = 1;
	Doname rv = build_failed;

	(void) fflush(stdout);
try_again:
	switch (doname(target, do_get, implicit, automatic)) {
	case build_ok:
		second_pass = 0;
		rv = build_ok;
		break;
	case build_running:
		second_pass = 0;
		rv = build_running;
		break;
	case build_failed:
		if (!continue_after_error) {
			fatal(
			    gettext("Target `%s' not remade because of errors"),
			    target->string_mb);
		}
		build_failed_seen = true;
		second_pass = 0;
		rv = build_failed;
		break;
	case build_dont_know:
		/*
		 * If we can't figure out how to build an automatic
		 * (hidden) dependency, we just ignore it.
		 * We later declare the target to be out of date just in
		 * case something changed.
		 * Also, don't complain if just reporting the dependencies
		 * and not building anything.
		 */
		if (automatic || (report_dependencies_level > 0)) {
			second_pass = 0;
			rv = build_dont_know;
			break;
		}
		if(first_time) {
			first_time = 0;
			second_pass = 1;
			goto try_again;
		}
		second_pass = 0;
		if (continue_after_error && !svr4) {
			warning(gettext("Don't know how to make target `%s'"),
				target->string_mb);
			build_failed_seen = true;
			rv = build_failed;
			break;
		}
		fatal(gettext("Don't know how to make target `%s'"),
		    target->string_mb);
		break;
	}
	return (rv);
}


void
enter_explicit_rule_from_dynamic_rule(Name target, Name source)
{
	Property line, source_line;
	Dependency dependency;

	source_line = get_prop(source->prop, line_prop);
	line = maybe_append_prop(target, line_prop);
	line->body.line.sccs_command = false;
	line->body.line.target = target;
	if (line->body.line.command_template == NULL) {
		line->body.line.command_template = source_line->body.line.command_template;
		for (dependency = source_line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			enter_dependency(line, dependency->name, false);
		}
		line->body.line.less = target;
	}
	line->body.line.percent = NULL;
}



Name
find_dyntarget(Name target)
{
	Dyntarget		p;
	int			i;
	String_rec		string;
	wchar_t			buffer[STRING_BUFFER_LENGTH];
	wchar_t			*pp, * bufend;
	wchar_t			tbuffer[MAXPATHLEN];
	Wstring			wcb(target);

	for (p = dyntarget_list; p != NULL; p = p->next) {
		INIT_STRING_FROM_STACK(string, buffer);
		expand_value(p->name, &string, false);
		i = 0;
		pp = string.buffer.start;
		bufend = pp + STRING_BUFFER_LENGTH;
		while((*pp != nul_char) && (pp < bufend)) {
			if(iswspace(*pp)) {
				tbuffer[i] = nul_char;
				if(i > 0) {
					if (wcb.equal(tbuffer)) {
						enter_explicit_rule_from_dynamic_rule(target, p->name);
						return(target);
					}
				}
				pp++;
				i = 0;
				continue;
			}
			tbuffer[i] = *pp;
			i++;
			pp++;
			if(*pp == nul_char) {
				tbuffer[i] = nul_char;
				if(i > 0) {
					if (wcb.equal(tbuffer)) {
						enter_explicit_rule_from_dynamic_rule(target, p->name);
						return(target);
					}
				}
				break;
			}
		}
	}
	return(NULL);
}

/*
 * DONE.
 *
 *	doname(target, do_get, implicit)
 *
 *	Chases all files the target depends on and builds any that
 *	are out of date. If the target is out of date it is then rebuilt.
 *
 *	Return value:
 *				Indiates if build failed or nt
 *
 *	Parameters:
 *		target		Target to build
 *		do_get		Run sccs get is nessecary
 *		implicit	doname is trying to find an implicit rule
 *
 *	Global variables used:
 *		assign_done	True if command line assgnment has happened
 *		commands_done	Preserved for the case that we need local value
 *		debug_level	Should we trace make's actions?
 *		default_rule	The rule for ".DEFAULT", used as last resort
 *		empty_name	The Name "", used when looking for single sfx
 *		keep_state	Indicates that .KEEP_STATE is on
 *		parallel	True if building in parallel
 *		recursion_level	Used for tracing
 *		report_dependencies make -P is on
 */
Doname
doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic)
{
	Doname			result = build_dont_know;
	Chain			out_of_date_list = NULL;
	Chain			target_group;
	Property		old_locals = NULL;
	register Property	line;
	Property		command = NULL;
	register Dependency	dependency;
	Name			less = NULL;
	Name			true_target = target;
	Name			*automatics = NULL;
	register int		auto_count;
	Boolean			rechecking_target = false;
	Boolean			saved_commands_done;
	Boolean			restart = false;
	Boolean			save_parallel = parallel;
	Boolean			doing_subtree = false;

	Boolean			recheck_conditionals = false;

	if (target->state == build_running) {
		return build_running;
	}
	line = get_prop(target->prop, line_prop);
	if (line != NULL) {
		/*
		 * If this target is a member of target group and one of the
		 * other members of the group is running, mark this target
		 * as running.
		 */
		for (target_group = line->body.line.target_group;
		     target_group != NULL;
		     target_group = target_group->next) {
			if (is_running(target_group->name)) {
				target->state = build_running;
				add_pending(target,
					    recursion_level,
					    do_get,
					    implicit,
					    false);
				return build_running;
			}
		}
	}
	/*
	 * If the target is a constructed one for a "::" target,
	 * we need to consider that.
	 */
	if (target->has_target_prop) {
		true_target = get_prop(target->prop,
				       target_prop)->body.target.target;
		if (true_target->colon_splits > 0) {
			/* Make sure we have a valid time for :: targets */
			Property        time;

			time = get_prop(true_target->prop, time_prop);
			if (time != NULL) {
				true_target->stat.time = time->body.time.time;
			}
		}
	}
	(void) exists(true_target);
	/*
	 * If the target has been processed, we don't need to do it again,
	 * unless it depends on conditional macros or a delayed assignment,
	 * or it has been done when KEEP_STATE is on.
	 */
	if (target->state == build_ok) {
		if((!keep_state || (!target->depends_on_conditional && !assign_done))) {
			return build_ok;
		} else {
			recheck_conditionals = true;
		}
	}
	if (target->state == build_subtree) {
		/* A dynamic macro subtree is being built */
		target->state = build_dont_know;
		doing_subtree = true;
		if (!target->checking_subtree) {
			/*
			 * This target has been started before and therefore
			 * not all dependencies have to be built.
			 */
			restart = true;
		}
	} else if (target->state == build_pending) {
		target->state = build_dont_know;
		restart = true;
/*
	} else if (parallel &&
		   keep_state &&
		   (target->conditional_cnt > 0)) {
	    if (!parallel_ok(target, false)) {
		add_subtree(target, recursion_level, do_get, implicit);
		target->state = build_running;
		return build_running;
	    }
 */
	}
	/*
	 * If KEEP_STATE is on, we have to rebuild the target if the
	 * building of it caused new automatic dependencies to be reported.
	 * This is where we restart the build.
	 */
	if (line != NULL) {
		line->body.line.percent = NULL;
	}
recheck_target:
	/* Init all local variables */
	result = build_dont_know;
	out_of_date_list = NULL;
	command = NULL;
	less = NULL;
	auto_count = 0;
	if (!restart && line != NULL) {
		/*
		 * If this target has never been built before, mark all
		 * of the dependencies as never built.
		 */
		for (dependency = line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			dependency->built = false;
		}
	}
	/* Save the set of automatic depes defined for this target */
	if (keep_state &&
	    (line != NULL) &&
	    (line->body.line.dependencies != NULL)) {
		Name *p;

		/*
		 * First run thru the dependency list to see how many
		 * autos there are.
		 */
		for (dependency = line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			if (dependency->automatic && !dependency->stale) {
				auto_count++;
			}
		}
		/* Create vector to hold the current autos */
		automatics =
		  (Name *) alloca((int) (auto_count * sizeof (Name)));
		/* Copy them */
		for (p = automatics, dependency = line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			if (dependency->automatic && !dependency->stale) {
				*p++ = dependency->name;
			}
		}
	}
	if (debug_level > 1) {
		(void) printf("%*sdoname(%s)\n",
			      recursion_level,
			      "",
			      target->string_mb);
	}
	recursion_level++;
	/* Avoid infinite loops */
	if (target->state == build_in_progress) {
		warning(gettext("Infinite loop: Target `%s' depends on itself"),
			target->string_mb);
		return build_ok;
	}
	target->state = build_in_progress;

	/* Activate conditional macros for the target */
	if (!target->added_pattern_conditionals) {
		add_pattern_conditionals(target);
		target->added_pattern_conditionals = true;
	}
	if (target->conditional_cnt > 0) {
		old_locals = (Property) alloca(target->conditional_cnt *
					       sizeof (Property_rec));
		set_locals(target, old_locals);
	}

/*
 * after making the call to dynamic_dependecies unconditional we can handle
 * target names that are same as file name. In this case $$@ in the
 * dependencies did not mean anything. WIth this change it expands it
 * as expected.
 */
	if (!target->has_depe_list_expanded)
	{
		dynamic_dependencies(target);
	}

/*
 *	FIRST SECTION -- GO THROUGH DEPENDENCIES AND COLLECT EXPLICIT
 *	COMMANDS TO RUN
 */
	if ((line = get_prop(target->prop, line_prop)) != NULL) {
		if (check_dependencies(&result,
				       line,
				       do_get,
				       target,
				       true_target,
				       doing_subtree,
				       &out_of_date_list,
				       old_locals,
				       implicit,
				       &command,
				       less,
				       rechecking_target,
				       recheck_conditionals)) {
			return build_running;
		}
		if (line->body.line.query != NULL) {
			delete_query_chain(line->body.line.query);
		}
		line->body.line.query = out_of_date_list;
	}


/*
 * If the target is a :: type, do not try to find the rule for the target,
 * all actions will be taken by separate branches.
 * Else, we try to find an implicit rule using various methods,
 * we quit as soon as one is found.
 *
 * [tolik, 12 Sep 2002] Do not try to find implicit rule for the target
 * being rechecked - the target is being rechecked means that it already
 * has explicit dependencies derived from an implicit rule found
 * in previous step.
 */
	if (target->colon_splits == 0 && !rechecking_target) {
		/* Look for percent matched rule */
		if ((result == build_dont_know) &&
		    (command == NULL)) {
			switch (find_percent_rule(
					target,
					&command,
					recheck_conditionals)) {
			case build_failed:
				result = build_failed;
				break;
			case build_running:
				target->state = build_running;
				add_pending(target,
					    --recursion_level,
					    do_get,
					    implicit,
					    false);
				if (target->conditional_cnt > 0) {
					reset_locals(target,
						     old_locals,
						     get_prop(target->prop,
							     conditional_prop),
						     0);
				}
				return build_running;
			case build_ok:
				result = build_ok;
				break;
			}
		}
		/* Look for double suffix rule */
		if (result == build_dont_know) {
			Property member;

			if (target->is_member &&
			    ((member = get_prop(target->prop, member_prop)) !=
			     NULL)) {
			        switch (find_ar_suffix_rule(target,
						member->body.
						member.member,
						&command,
						recheck_conditionals)) {
				case build_failed:
					result = build_failed;
					break;
				case build_running:
					target->state = build_running;
					add_pending(target,
						    --recursion_level,
						    do_get,
						    implicit,
						    false);
				    if (target->conditional_cnt > 0) {
					    reset_locals(target,
							 old_locals,
							 get_prop(target->prop,
							     conditional_prop),
							 0);
				    }
					return build_running;
				default:
					/* ALWAYS bind $% for old style */
					/* ar rules */
					if (line == NULL) {
						line =
						  maybe_append_prop(target,
								    line_prop);
					}
					line->body.line.percent =
					  member->body.member.member;
					break;
				}
			} else {
				switch (find_double_suffix_rule(target,
						&command,
						recheck_conditionals)) {
				case build_failed:
					result = build_failed;
					break;
				case build_running:
					target->state = build_running;
					add_pending(target,
						    --recursion_level,
						    do_get,
						    implicit,
						    false);
					if (target->conditional_cnt > 0) {
						reset_locals(target,
							     old_locals,
							     get_prop(target->
								      prop,
								      conditional_prop),
							     0);
					}
					return build_running;
				}
			}
		}
		/* Look for single suffix rule */

/* /tolik/
 * I commented !implicit to fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules.
 * This caused problem with SVR4 tilde rules (infinite recursion). So I made some changes in "implicit.cc"
 */
/* /tolik, 06.21.96/
 * Regression! See BugId 1255360
 * If more than one percent rules are defined for the same target then
 * the behaviour of 'make' with my previous fix may be different from one
 * of the 'old make'.
 * The global variable second_pass (maybe it should be an argument to doname())
 * is intended to avoid this regression. It is set in doname_check().
 * First, 'make' will work as it worked before. Only when it is
 * going to say "don't know how to make target" it sets second_pass to true and
 * run 'doname' again but now trying to use Single Suffix Rules.
 */
		if ((result == build_dont_know) && !automatic && (!implicit || second_pass) &&
		    ((line == NULL) ||
		     ((line->body.line.target != NULL) &&
		      !line->body.line.target->has_regular_dependency))) {
			switch (find_suffix_rule(target,
						 target,
						 empty_name,
						 &command,
						 recheck_conditionals)) {
			case build_failed:
				result = build_failed;
				break;
			case build_running:
				target->state = build_running;
				add_pending(target,
					    --recursion_level,
					    do_get,
					    implicit,
					    false);
				if (target->conditional_cnt > 0) {
					reset_locals(target,
						     old_locals,
						     get_prop(target->prop,
							     conditional_prop),
						     0);
				}
				return build_running;
			}
		}
		/* Try to sccs get */
		if ((command == NULL) &&
		    (result == build_dont_know) &&
		    do_get) {
			result = sccs_get(target, &command);
		}

		/* Use .DEFAULT rule if it is defined. */
		if ((command == NULL) &&
		    (result == build_dont_know) &&
		    (true_target->colons == no_colon) &&
		    default_rule &&
		    !implicit) {
			/* Make sure we have a line prop */
			line = maybe_append_prop(target, line_prop);
			command = line;
			Boolean out_of_date;
			if (true_target->is_member) {
				out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time,
									line->body.line.dependency_time);
			} else {
				out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time,
								    line->body.line.dependency_time);
			}
			if (build_unconditional || out_of_date) {
				line->body.line.is_out_of_date = true;
				if (debug_level > 0) {
					(void) printf(gettext("%*sBuilding %s using .DEFAULT because it is out of date\n"),
						      recursion_level,
						      "",
						      true_target->string_mb);
				}
			}
			line->body.line.sccs_command = false;
			line->body.line.command_template = default_rule;
			line->body.line.target = true_target;
			line->body.line.star = NULL;
			line->body.line.less = true_target;
			line->body.line.percent = NULL;
		}
	}

	/* We say "target up to date" if no cmd were executed for the target */
	if (!target->is_double_colon_parent) {
		commands_done = false;
	}

	silent = silent_all;
	ignore_errors = ignore_errors_all;
	if  (posix)
	{
	  if  (!silent)
	  {
            silent = (Boolean) target->silent_mode;
	  }
	  if  (!ignore_errors)
	  {
            ignore_errors = (Boolean) target->ignore_error_mode;
	  }
	}

	int doname_dyntarget = 0;
r_command:
	/* Run commands if any. */
	if ((command != NULL) &&
	    (command->body.line.command_template != NULL)) {
		if (result != build_failed) {
			result = run_command(command,
					     (Boolean) ((parallel || save_parallel) && !silent));
		}
		switch (result) {
		case build_running:
			add_running(target,
				    true_target,
				    command,
				    --recursion_level,
				    auto_count,
				    automatics,
				    do_get,
				    implicit);
			target->state = build_running;
			if ((line = get_prop(target->prop,
					     line_prop)) != NULL) {
				if (line->body.line.query != NULL) {
					delete_query_chain(line->body.line.query);
				}
				line->body.line.query = NULL;
			}
			if (target->conditional_cnt > 0) {
				reset_locals(target,
					     old_locals,
					     get_prop(target->prop,
						     conditional_prop),
					     0);
			}
			return build_running;
		case build_serial:
			add_serial(target,
				   --recursion_level,
				   do_get,
				   implicit);
			target->state = build_running;
			line = get_prop(target->prop, line_prop);
			if (line != NULL) {
				if (line->body.line.query != NULL) {
					delete_query_chain(line->body.line.query);
				}
				line->body.line.query = NULL;
			}
			if (target->conditional_cnt > 0) {
				reset_locals(target,
					     old_locals,
					     get_prop(target->prop,
						     conditional_prop),
					     0);
			}
			return build_running;
		case build_ok:
			/* If all went OK set a nice timestamp */
			if (true_target->stat.time == file_doesnt_exist) {
				true_target->stat.time = file_max_time;
			}
			break;
		}
	} else {
		/*
		 * If no command was found for the target, and it doesn't
		 * exist, and it is mentioned as a target in the makefile,
		 * we say it is extremely new and that it is OK.
		 */
		if (target->colons != no_colon) {
			if (true_target->stat.time == file_doesnt_exist){
				true_target->stat.time = file_max_time;
			}
			result = build_ok;
		}
		/*
		 * Trying dynamic targets.
		 */
		if(!doname_dyntarget) {
			doname_dyntarget = 1;
			Name dtarg = find_dyntarget(target);
			if(dtarg!=NULL) {
				if (!target->has_depe_list_expanded) {
					dynamic_dependencies(target);
				}
				if ((line = get_prop(target->prop, line_prop)) != NULL) {
					if (check_dependencies(&result,
					                       line,
					                       do_get,
					                       target,
					                       true_target,
					                       doing_subtree,
					                       &out_of_date_list,
					                       old_locals,
					                       implicit,
					                       &command,
					                       less,
					                       rechecking_target,
					                       recheck_conditionals))
					{
						return build_running;
					}
					if (line->body.line.query != NULL) {
						delete_query_chain(line->body.line.query);
					}
					line->body.line.query = out_of_date_list;
				}
				goto r_command;
			}
		}
		/*
		 * If the file exists, it is OK that we couldnt figure
		 * out how to build it.
		 */
		(void) exists(target);
		if ((target->stat.time != file_doesnt_exist) &&
		    (result == build_dont_know)) {
			result = build_ok;
		}
	}

	/*
	 * Some of the following is duplicated in the function finish_doname.
	 * If anything is changed here, check to see if it needs to be
	 * changed there.
	 */
	if ((line = get_prop(target->prop, line_prop)) != NULL) {
		if (line->body.line.query != NULL) {
			delete_query_chain(line->body.line.query);
		}
		line->body.line.query = NULL;
	}
	target->state = result;
	parallel = save_parallel;
	if (target->conditional_cnt > 0) {
		reset_locals(target,
			     old_locals,
			     get_prop(target->prop, conditional_prop),
			     0);
	}
	recursion_level--;
	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 =
			  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, rechecking all dependencies\n"),
				      recursion_level,
				      "",
				      true_target->string_mb);
		}
		rechecking_target = true;
		saved_commands_done = commands_done;
		goto recheck_target;
	}

	if (rechecking_target && !commands_done) {
		commands_done = saved_commands_done;
	}

	return result;
}

/*
 * DONE.
 *
 *	check_dependencies(result, line, do_get,
 *			target, true_target, doing_subtree, out_of_date_tail,
 *			old_locals, implicit, command, less, rechecking_target)
 *
 *	Return value:
 *				True returned if some dependencies left running
 *
 *	Parameters:
 *		result		Pointer to cell we update if build failed
 *		line		We get the dependencies from here
 *		do_get		Allow use of sccs get in recursive doname()
 *		target		The target to chase dependencies for
 *		true_target	The real one for :: and lib(member)
 *		doing_subtree	True if building a conditional macro subtree
 *		out_of_date_tail Used to set the $? list
 *		old_locals	Used for resetting the local macros
 *		implicit	Called when scanning for implicit rules?
 *		command		Place to stuff command
 *		less		Set to $< value
 *
 *	Global variables used:
 *		command_changed	Set if we suspect .make.state needs rewrite
 *		debug_level	Should we trace actions?
 *		force		The Name " FORCE", compared against
 *		recursion_level	Used for tracing
 *		rewrite_statefile Set if .make.state needs rewriting
 *		wait_name	The Name ".WAIT", compared against
 */
static Boolean
check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals)
{
	Boolean			dependencies_running;
	register Dependency	dependency;
	Doname			dep_result;
	Boolean			dependency_changed = false;

	line->body.line.dependency_time = file_doesnt_exist;
	if (line->body.line.query != NULL) {
		delete_query_chain(line->body.line.query);
	}
	line->body.line.query = NULL;
	line->body.line.is_out_of_date = false;
	dependencies_running = false;
	/*
	 * Run thru all the dependencies and call doname() recursively
	 * on each of them.
	 */
	for (dependency = line->body.line.dependencies;
	     dependency != NULL;
	     dependency = dependency->next) {
		Boolean this_dependency_changed = false;

		if (!dependency->automatic &&
		    (rechecking_target || target->rechecking_target)) {
			/*
			 * We only bother with the autos when rechecking
			 */
			continue;
		}

		if (dependency->name == wait_name) {
			/*
			 * The special target .WAIT means finish all of
			 * the prior dependencies before continuing.
			 */
			if (dependencies_running) {
				break;
			}
		} else if ((!parallel_ok(dependency->name, false)) &&
			   (dependencies_running)) {
			/*
			 * If we can't execute the current dependency in
			 * parallel, hold off the dependency processing
			 * to preserve the order of the dependencies.
			 */
			break;
		} else {
			timestruc_t	depe_time = file_doesnt_exist;


			if (true_target->is_member) {
				depe_time = exists(dependency->name);
			}
			if (dependency->built ||
			    (dependency->name->state == build_failed)) {
				dep_result = (Doname) dependency->name->state;
			} else {
				dep_result = doname_check(dependency->name,
							  do_get,
							  false,
							  (Boolean) dependency->automatic);
			}
			if (true_target->is_member || dependency->name->is_member) {
				/* should compare only secs, cause lib members does not have nsec time resolution */
				if (depe_time.tv_sec != dependency->name->stat.time.tv_sec) {
					this_dependency_changed =
					  dependency_changed =
					    true;
				}
			} else {
				if (depe_time != dependency->name->stat.time) {
					this_dependency_changed =
					  dependency_changed =
					    true;
				}
			}
			dependency->built = true;
			switch (dep_result) {
			case build_running:
				dependencies_running = true;
				continue;
			case build_failed:
				*result = build_failed;
				break;
			case build_dont_know:
/*
 * If make can't figure out how to make a dependency, maybe the dependency
 * is out of date. In this case, we just declare the target out of date
 * and go on. If we really need the dependency, the make'ing of the target
 * will fail. This will only happen for automatic (hidden) dependencies.
 */
				if(!recheck_conditionals) {
					line->body.line.is_out_of_date = true;
				}
				/*
				 * Make sure the dependency is not saved
				 * in the state file.
				 */
				dependency->stale = true;
				rewrite_statefile =
				  command_changed =
				    true;
				if (debug_level > 0) {
					(void) printf(gettext("Target %s rebuilt because dependency %s does not exist\n"),
						     true_target->string_mb,
						     dependency->name->string_mb);
				}
				break;
			}
			if (dependency->name->depends_on_conditional) {
				target->depends_on_conditional = true;
			}
			if (dependency->name == force) {
				target->stat.time =
				  dependency->name->stat.time;
			}
			/*
			 * Propagate new timestamp from "member" to
			 * "lib.a(member)".
			 */
			(void) exists(dependency->name);

			/* Collect the timestamp of the youngest dependency */
			line->body.line.dependency_time =
			  MAX(dependency->name->stat.time,
			      line->body.line.dependency_time);

			/* Correction: do not consider nanosecs for members */
			if(true_target->is_member || dependency->name->is_member) {
				line->body.line.dependency_time.tv_nsec = 0;
			}

			if (debug_level > 1) {
				(void) printf(gettext("%*sDate(%s)=%s \n"),
					      recursion_level,
					      "",
					      dependency->name->string_mb,
					      time_to_string(dependency->name->
							     stat.time));
				if (dependency->name->stat.time > line->body.line.dependency_time) {
					(void) printf(gettext("%*sDate-dependencies(%s) set to %s\n"),
						      recursion_level,
						      "",
						      true_target->string_mb,
						      time_to_string(line->body.line.
								     dependency_time));
				}
			}

			/* Build the $? list */
			if (true_target->is_member) {
				if (this_dependency_changed == true) {
					true_target->stat.time = dependency->name->stat.time;
					true_target->stat.time.tv_sec--;
				} else {
					/* Dina:
					 * The next statement is commented
					 * out as a fix for bug #1051032.
					 * if dependency hasn't changed
					 * then there's no need to invalidate
					 * true_target. This statemnt causes
					 * make to take much longer to process
					 * an already-built archive. Soren
					 * said it was a quick fix for some
					 * problem he doesn't remember.
					true_target->stat.time = file_no_time;
					 */
					(void) exists(true_target);
				}
			} else {
				(void) exists(true_target);
			}
			Boolean out_of_date;
			if (true_target->is_member || dependency->name->is_member) {
				out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time,
							                dependency->name->stat.time);
			} else {
				out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time,
							            dependency->name->stat.time);
			}
			if ((build_unconditional || out_of_date) &&
			    (dependency->name != force) &&
			    (dependency->stale == false)) {
				*out_of_date_tail = ALLOC(Chain);
				if (dependency->name->is_member &&
				    (get_prop(dependency->name->prop,
					      member_prop) != NULL)) {
					(*out_of_date_tail)->name =
					  get_prop(dependency->name->prop,
						   member_prop)->
						     body.member.member;
				} else {
					(*out_of_date_tail)->name =
					  dependency->name;
				}
				(*out_of_date_tail)->next = NULL;
				out_of_date_tail = &(*out_of_date_tail)->next;
				if (debug_level > 0) {
					if (dependency->name->stat.time == file_max_time) {
						(void) printf(gettext("%*sBuilding %s because %s does not exist\n"),
							      recursion_level,
							      "",
							      true_target->string_mb,
							      dependency->name->string_mb);
					} else {
						(void) printf(gettext("%*sBuilding %s because it is out of date relative to %s\n"),
							      recursion_level,
							      "",
							      true_target->string_mb,
							      dependency->name->string_mb);
					}
				}
			}
			if (dependency->name == force) {
				force->stat.time =
				  file_max_time;
				force->state = build_dont_know;
			}
		}
	}
	if (dependencies_running) {
		if (doing_subtree) {
			if (target->conditional_cnt > 0) {
				reset_locals(target,
					     old_locals,
					     get_prop(target->prop,
						      conditional_prop),
					     0);
			}
			return true;
		} else {
			target->state = build_running;
			add_pending(target,
				    --recursion_level,
				    do_get,
				    implicit,
				    false);
			if (target->conditional_cnt > 0) {
				reset_locals(target,
					     old_locals,
					     get_prop(target->prop,
						      conditional_prop),
					     0);
			}
			return true;
		}
	}
	/*
	 * Collect the timestamp of the youngest double colon target
	 * dependency.
	 */
	if (target->is_double_colon_parent) {
		for (dependency = line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			Property        tmp_line;

			if ((tmp_line = get_prop(dependency->name->prop, line_prop)) != NULL) {
				if(tmp_line->body.line.dependency_time != file_max_time) {
					target->stat.time =
					  MAX(tmp_line->body.line.dependency_time,
					      target->stat.time);
				}
			}
		}
	}
	if ((true_target->is_member) && (dependency_changed == true)) {
		true_target->stat.time = file_no_time;
	}
	/*
	 * After scanning all the dependencies, we check the rule
	 * if we found one.
	 */
	if (line->body.line.command_template != NULL) {
		if (line->body.line.command_template_redefined) {
			warning(gettext("Too many rules defined for target %s"),
				target->string_mb);
		}
		*command = line;
		/* Check if the target is out of date */
		Boolean out_of_date;
		if (true_target->is_member) {
			out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time,
				                                line->body.line.dependency_time);
		} else {
			out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time,
				                            line->body.line.dependency_time);
		}
		if (build_unconditional || out_of_date){
			if(!recheck_conditionals) {
				line->body.line.is_out_of_date = true;
			}
		}
		line->body.line.sccs_command = false;
		line->body.line.target = true_target;
		if(gnu_style) {

			// set $< for explicit rule
			if(line->body.line.dependencies != NULL) {
				less = line->body.line.dependencies->name;
			}

			// set $* for explicit rule
			Name			target_body;
			Name			tt = true_target;
			Property		member;
			register wchar_t	*target_end;
			register Dependency	suffix;
			register int		suffix_length;
			Wstring			targ_string;
			Wstring			suf_string;

			if (true_target->is_member &&
			    ((member = get_prop(target->prop, member_prop)) !=
			     NULL)) {
				tt = member->body.member.member;
			}
			targ_string.init(tt);
			target_end = targ_string.get_string() + tt->hash.length;
			for (suffix = suffixes; suffix != NULL; suffix = suffix->next) {
				suffix_length = suffix->name->hash.length;
				suf_string.init(suffix->name);
				if (tt->hash.length < suffix_length) {
					continue;
				} else if (!IS_WEQUALN(suf_string.get_string(),
						(target_end - suffix_length),
						suffix_length)) {
					continue;
				}
				target_body = GETNAME(
					targ_string.get_string(),
					(int)(tt->hash.length - suffix_length)
				);
				line->body.line.star = target_body;
			}

			// set result = build_ok so that implicit rules are not used.
			if(*result == build_dont_know) {
				*result = build_ok;
			}
		}
		if (less != NULL) {
			line->body.line.less = less;
		}
	}

	return false;
}

/*
 *	dynamic_dependencies(target)
 *
 *	Checks if any dependency contains a macro ref
 *	If so, it replaces the dependency with the expanded version.
 *	Here, "$@" gets translated to target->string. That is
 *	the current name on the left of the colon in the
 *	makefile.  Thus,
 *		xyz:	s.$@.c
 *	translates into
 *		xyz:	s.xyz.c
 *
 *	Also, "$(@F)" translates to the same thing without a preceeding
 *	directory path (if one exists).
 *	Note, to enter "$@" on a dependency line in a makefile
 *	"$$@" must be typed. This is because make expands
 *	macros in dependency lists upon reading them.
 *	dynamic_dependencies() also expands file wildcards.
 *	If there are any Shell meta characters in the name,
 *	search the directory, and replace the dependency
 *	with the set of files the pattern matches
 *
 *	Parameters:
 *		target		Target to sanitize dependencies for
 *
 *	Global variables used:
 *		c_at		The Name "@", used to set macro value
 *		debug_level	Should we trace actions?
 *		dot		The Name ".", used to read directory
 *		recursion_level	Used for tracing
 */
void
dynamic_dependencies(Name target)
{
	wchar_t			pattern[MAXPATHLEN];
	register wchar_t	*p;
	Property		line;
	register Dependency	dependency;
	register Dependency	*remove;
	String_rec		string;
	wchar_t			buffer[MAXPATHLEN];
	register Boolean	set_at = false;
	register wchar_t	*start;
	Dependency		new_depe;
	register Boolean	reuse_cell;
	Dependency		first_member;
	Name			directory;
	Name			lib;
	Name			member;
	Property		prop;
	Name			true_target = target;
	wchar_t			*library;

	if ((line = get_prop(target->prop, line_prop)) == NULL) {
		return;
	}
	/* If the target is constructed from a "::" target we consider that */
	if (target->has_target_prop) {
		true_target = get_prop(target->prop,
				       target_prop)->body.target.target;
	}
	/* Scan all dependencies and process the ones that contain "$" chars */
	for (dependency = line->body.line.dependencies;
	     dependency != NULL;
	     dependency = dependency->next) {
		if (!dependency->name->dollar) {
			continue;
		}
		target->has_depe_list_expanded = true;

		/* The make macro $@ is bound to the target name once per */
		/* invocation of dynamic_dependencies() */
		if (!set_at) {
			(void) SETVAR(c_at, true_target, false);
			set_at = true;
		}
		/* Expand this dependency string */
		INIT_STRING_FROM_STACK(string, buffer);
		expand_value(dependency->name, &string, false);
		/* Scan the expanded string. It could contain whitespace */
		/* which mean it expands to several dependencies */
		start = string.buffer.start;
		while (iswspace(*start)) {
			start++;
		}
		/* Remove the cell (later) if the macro was empty */
		if (start[0] == (int) nul_char) {
			dependency->name = NULL;
		}

/* azv 10/26/95 to fix bug BID_1170218 */
		if ((start[0] == (int) period_char) &&
		    (start[1] == (int) slash_char)) {
			start += 2;
		}
/* azv */

		first_member = NULL;
		/* We use the original dependency cell for the first */
		/* dependency from the expansion */
		reuse_cell = true;
		/* We also have to deal with dependencies that expand to */
		/* lib.a(members) notation */
		for (p = start; *p != (int) nul_char; p++) {
			if ((*p == (int) parenleft_char)) {
				lib = GETNAME(start, p - start);
				lib->is_member = true;
				first_member = dependency;
				start = p + 1;
				while (iswspace(*start)) {
					start++;
				}
				break;
			}
		}
		do {
		    /* First skip whitespace */
			for (p = start; *p != (int) nul_char; p++) {
				if ((*p == (int) nul_char) ||
				    iswspace(*p) ||
				    (*p == (int) parenright_char)) {
					break;
				}
			}
			/* Enter dependency from expansion */
			if (p != start) {
				/* Create new dependency cell if */
				/* this is not the first dependency */
				/* picked from the expansion */
				if (!reuse_cell) {
					new_depe = ALLOC(Dependency);
					new_depe->next = dependency->next;
					new_depe->automatic = false;
					new_depe->stale = false;
					new_depe->built = false;
					dependency->next = new_depe;
					dependency = new_depe;
				}
				reuse_cell = false;
				/* Internalize the dependency name */
				// tolik. Fix for bug 4110429: inconsistent expansion for macros that
				// include "//" and "/./"
				//dependency->name = GETNAME(start, p - start);
				dependency->name = normalize_name(start, p - start);
				if ((debug_level > 0) &&
				    (first_member == NULL)) {
					(void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"),
						      recursion_level,
						      "",
						      dependency->name->string_mb,
						      true_target->string_mb);
				}
				for (start = p; iswspace(*start); start++);
				p = start;
			}
		} while ((*p != (int) nul_char) &&
			 (*p != (int) parenright_char));
		/* If the expansion was of lib.a(members) format we now */
		/* enter the proper member cells */
		if (first_member != NULL) {
			/* Scan the new dependencies and transform them from */
			/* "foo" to "lib.a(foo)" */
			for (; 1; first_member = first_member->next) {
				/* Build "lib.a(foo)" name */
				INIT_STRING_FROM_STACK(string, buffer);
				APPEND_NAME(lib,
					      &string,
					      (int) lib->hash.length);
				append_char((int) parenleft_char, &string);
				APPEND_NAME(first_member->name,
					      &string,
					      FIND_LENGTH);
				append_char((int) parenright_char, &string);
				member = first_member->name;
				/* Replace "foo" with "lib.a(foo)" */
				first_member->name =
				  GETNAME(string.buffer.start, FIND_LENGTH);
				if (string.free_after_use) {
					retmem(string.buffer.start);
				}
				if (debug_level > 0) {
					(void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"),
						      recursion_level,
						      "",
						      first_member->name->
						      string_mb,
						      true_target->string_mb);
				}
				first_member->name->is_member = lib->is_member;
				/* Add member property to member */
				prop = maybe_append_prop(first_member->name,
							 member_prop);
				prop->body.member.library = lib;
				prop->body.member.entry = NULL;
				prop->body.member.member = member;
				if (first_member == dependency) {
					break;
				}
			}
		}
	}
	Wstring wcb;
	/* Then scan all the dependencies again. This time we want to expand */
	/* shell file wildcards */
	for (remove = &line->body.line.dependencies, dependency = *remove;
	     dependency != NULL;
	     dependency = *remove) {
		if (dependency->name == NULL) {
			dependency = *remove = (*remove)->next;
			continue;
		}
		/* If dependency name string contains shell wildcards */
		/* replace the name with the expansion */
		if (dependency->name->wildcard) {
			wcb.init(dependency->name);
			if ((start = (wchar_t *) wcschr(wcb.get_string(),
					   (int) parenleft_char)) != NULL) {
				/* lib(*) type pattern */
				library = buffer;
				(void) wcsncpy(buffer,
					      wcb.get_string(),
					      start - wcb.get_string());
				buffer[start-wcb.get_string()] =
				  (int) nul_char;
				(void) wcsncpy(pattern,
					      start + 1,
(int) (dependency->name->hash.length-(start-wcb.get_string())-2));
				pattern[dependency->name->hash.length -
					(start-wcb.get_string()) - 2] =
					  (int) nul_char;
			} else {
				library = NULL;
				(void) wcsncpy(pattern,
					      wcb.get_string(),
					      (int) dependency->name->hash.length);
				pattern[dependency->name->hash.length] =
				  (int) nul_char;
			}
			start = (wchar_t *) wcsrchr(pattern, (int) slash_char);
			if (start == NULL) {
				directory = dot;
				p = pattern;
			} else {
				directory = GETNAME(pattern, start-pattern);
				p = start+1;
			}
			/* The expansion is handled by the read_dir() routine*/
			if (read_dir(directory, p, line, library)) {
				*remove = (*remove)->next;
			} else {
				remove = &dependency->next;
			}
		} else {
			remove = &dependency->next;
		}
        }

	/* Then unbind $@ */
	(void) SETVAR(c_at, (Name) NULL, false);
}

/*
 * DONE.
 *
 *	run_command(line)
 *
 *	Takes one Cmd_line and runs the commands from it.
 *
 *	Return value:
 *				Indicates if the command failed or not
 *
 *	Parameters:
 *		line		The command line to run
 *
 *	Global variables used:
 *		commands_done	Set if we do run command
 *		current_line	Set to the line we run a command from
 *		current_target	Set to the target we run a command for
 *		file_number	Used to form temp file name
 *		keep_state	Indicates that .KEEP_STATE is on
 *		make_state	The Name ".make.state", used to check timestamp
 *		parallel	True if currently building in parallel
 *		parallel_process_cnt Count of parallel processes running
 *		quest		Indicates that make -q is on
 *		rewrite_statefile Set if we do run a command
 *		sunpro_dependencies The Name "SUNPRO_DEPENDENCIES", set value
 *		temp_file_directory Used to form temp fie name
 *		temp_file_name	Set to the name of the temp file
 *		touch		Indicates that make -t is on
 */
static Doname
run_command(register Property line, Boolean)
{
	register Doname		result = build_ok;
	register Boolean	remember_only = false;
	register Name		target = line->body.line.target;
	wchar_t			*string;
	char			tmp_file_path[MAXPATHLEN];

	if (!line->body.line.is_out_of_date && target->rechecking_target) {
		target->rechecking_target = false;
		return build_ok;
	}

	/*
	 * Build the command if we know the target is out of date,
	 * or if we want to check cmd consistency.
	 */
	if (line->body.line.is_out_of_date || keep_state) {
		/* Hack for handling conditional macros in DMake. */
		if (!line->body.line.dont_rebuild_command_used) {
			build_command_strings(target, line);
		}
	}
	/* Never mind */
	if (!line->body.line.is_out_of_date) {
		return build_ok;
	}
	/* If quest, then exit(1) because the target is out of date */
	if (quest) {
		if (posix) {
			result = execute_parallel(line, true);
		}
		exit_status = 1;
		exit(1);
	}
	/* We actually had to do something this time */
	rewrite_statefile = commands_done = true;
	/*
	 * If this is an sccs command, we have to do some extra checking
	 * and possibly complain. If the file can't be gotten because it's
	 * checked out, we complain and behave as if the command was
	 * executed eventhough we ignored the command.
	 */
	if (!touch &&
	    line->body.line.sccs_command &&
	    (target->stat.time != file_doesnt_exist) &&
	    ((target->stat.mode & 0222) != 0)) {
		fatal(gettext("%s is writable so it cannot be sccs gotten"),
		      target->string_mb);
		target->has_complained = remember_only = true;
	}
	/*
	 * If KEEP_STATE is on, we make sure we have the timestamp for
	 * .make.state. If .make.state changes during the command run,
	 * we reread .make.state after the command. We also setup the
	 * environment variable that asks utilities to report dependencies.
	 */
	if (!touch &&
	    keep_state &&
	    !remember_only) {
		(void) exists(make_state);
		if((strlen(temp_file_directory) == 1) &&
			(temp_file_directory[0] == '/')) {
		   tmp_file_path[0] = '\0';
		} else {
		   strcpy(tmp_file_path, temp_file_directory);
		}
		sprintf(mbs_buffer,
				"%s/.make.dependency.%08x.%d.%d",
			        tmp_file_path,
			        hostid,
			        getpid(),
			        file_number++);
		MBSTOWCS(wcs_buffer, mbs_buffer);
		Boolean fnd;
		temp_file_name = getname_fn(wcs_buffer, FIND_LENGTH, false, &fnd);
		temp_file_name->stat.is_file = true;
		int len = 2*MAXPATHLEN + strlen(target->string_mb) + 2;
		wchar_t *to = string = ALLOC_WC(len);
		for (wchar_t *from = wcs_buffer; *from != (int) nul_char; ) {
			if (*from == (int) space_char) {
				*to++ = (int) backslash_char;
			}
			*to++ = *from++;
		}
		*to++ = (int) space_char;
		MBSTOWCS(to, target->string_mb);
		Name sprodep_name = getname_fn(string, FIND_LENGTH, false, &fnd);
		(void) SETVAR(sunpro_dependencies,
			      sprodep_name,
			      false);
		retmem(string);
	} else {
		temp_file_name = NULL;
	}

	/*
	 * In case we are interrupted, we need to know what was going on.
	 */
	current_target = target;
	/*
	 * We also need to be able to save an empty command instead of the
	 * interrupted one in .make.state.
	 */
	current_line = line;
	if (remember_only) {
		/* Empty block!!! */
	} else if (touch) {
		result = touch_command(line, target, result);
		if (posix) {
			result = execute_parallel(line, true);
		}
	} else {
		/*
		 * If this is not a touch run, we need to execute the
		 * proper command(s) for the target.
		 */
		if (parallel) {
			if (!parallel_ok(target, true)) {
				/*
				 * We are building in parallel, but
				 * this target must be built in serial.
				 */
				/*
				 * If nothing else is building,
				 * do this one, else wait.
				 */
				if (parallel_process_cnt == 0) {
					result = execute_parallel(line, true, target->localhost);
				} else {
					current_target = NULL;
					current_line = NULL;
/*
					line->body.line.command_used = NULL;
 */
					line->body.line.dont_rebuild_command_used = true;
					return build_serial;
				}
			} else {
				result = execute_parallel(line, false);
				switch (result) {
				case build_running:
					return build_running;
				case build_serial:
					if (parallel_process_cnt == 0) {
						result = execute_parallel(line, true, target->localhost);
					} else {
						current_target = NULL;
						current_line = NULL;
						target->parallel = false;
						line->body.line.command_used =
						    NULL;
						return build_serial;
					}
				}
			}
		} else {
			result = execute_parallel(line, true, target->localhost);
		}
	}
	temp_file_name = NULL;
	if (report_dependencies_level == 0){
		update_target(line, result);
	}
	current_target = NULL;
	current_line = NULL;
	return result;
}

/*
 *	execute_serial(line)
 *
 *	Runs thru the command line for the target and
 *	executes the rules one by one.
 *
 *	Return value:
 *				The result of the command build
 *
 *	Parameters:
 *		line		The command to execute
 *
 *	Static variables used:
 *
 *	Global variables used:
 *		continue_after_error -k flag
 *		do_not_exec_rule -n flag
 *		report_dependencies -P flag
 *		silent		Don't echo commands before executing
 *		temp_file_name	Temp file for auto dependencies
 *		vpath_defined	If true, translate path for command
 */
Doname
execute_serial(Property line)
{
	int			child_pid = 0;
	Boolean			printed_serial;
	Doname			result = build_ok;
	Cmd_line		rule, cmd_tail, command = NULL;
	char			mbstring[MAXPATHLEN];
	int			filed;
	Name			target = line->body.line.target;

	target->has_recursive_dependency = false;
	// We have to create a copy of the rules chain for processing because
	// the original one can be destroyed during .make.state file rereading.
	for (rule = line->body.line.command_used;
	     rule != NULL;
	     rule = rule->next) {
		if (command == NULL) {
			command = cmd_tail = ALLOC(Cmd_line);
		} else {
			cmd_tail->next = ALLOC(Cmd_line);
			cmd_tail = cmd_tail->next;
		}
		*cmd_tail = *rule;
	}
	if (command) {
		cmd_tail->next = NULL;
	}
	for (rule = command; rule != NULL; rule = rule->next) {
		if (posix && (touch || quest) && !rule->always_exec) {
			continue;
		}
		if (vpath_defined) {
			rule->command_line =
			  vpath_translation(rule->command_line);
		}
		/* Echo command line, maybe. */
		if ((rule->command_line->hash.length > 0) &&
		    !silent &&
		    (!rule->silent || do_not_exec_rule) &&
		    (report_dependencies_level == 0)) {
			(void) printf("%s\n", rule->command_line->string_mb);
		}
		if (rule->command_line->hash.length > 0) {
			/* Do assignment if command line prefixed with "=" */
			if (rule->assign) {
				result = build_ok;
				do_assign(rule->command_line, target);
			} else if (report_dependencies_level == 0) {
				/* Execute command line. */
				setvar_envvar();
				result = dosys(rule->command_line,
				               (Boolean) rule->ignore_error,
				               (Boolean) rule->make_refd,
				               /* ds 98.04.23 bug #4085164. make should always show error messages */
				               false,
				               /* BOOLEAN(rule->silent &&
				                       rule->ignore_error), */
				               (Boolean) rule->always_exec,
				               target);
				check_state(temp_file_name);
			}
		} else {
			result = build_ok;
		}
		if (result == build_failed) {
			if (silent || rule->silent) {
				(void) printf(gettext("The following command caused the error:\n%s\n"),
				              rule->command_line->string_mb);
			}
			if (!rule->ignore_error && !ignore_errors) {
				if (!continue_after_error) {
					fatal(gettext("Command failed for target `%s'"),
					      target->string_mb);
				}
				/*
				 * Make sure a failing command is not
				 * saved in .make.state.
				 */
				line->body.line.command_used = NULL;
				break;
			} else {
				result = build_ok;
			}
		}
	}
	for (rule = command; rule != NULL; rule = cmd_tail) {
		cmd_tail = rule->next;
		free(rule);
	}
	command = NULL;
	if (temp_file_name != NULL) {
		free_name(temp_file_name);
	}
        temp_file_name = NULL;

	Property spro = get_prop(sunpro_dependencies->prop, macro_prop);
	if(spro != NULL) {
		Name val = spro->body.macro.value;
		if(val != NULL) {
			free_name(val);
			spro->body.macro.value = NULL;
		}
	}
	spro = get_prop(sunpro_dependencies->prop, env_mem_prop);
	if(spro) {
		char *val = spro->body.env_mem.value;
		if(val != NULL) {
			/*
			 * Do not return memory allocated for SUNPRO_DEPENDENCIES
			 * It will be returned in setvar_daemon() in macro.cc
			 */
			//	retmem_mb(val);
			spro->body.env_mem.value = NULL;
		}
	}

        return result;
}



/*
 *	vpath_translation(cmd)
 *
 *	Translates one command line by
 *	checking each word. If the word has an alias it is translated.
 *
 *	Return value:
 *				The translated command
 *
 *	Parameters:
 *		cmd		Command to translate
 *
 *	Global variables used:
 */
Name
vpath_translation(register Name cmd)
{
	wchar_t			buffer[STRING_BUFFER_LENGTH];
	String_rec		new_cmd;
	wchar_t			*p;
	wchar_t			*start;

	if (!vpath_defined || (cmd == NULL) || (cmd->hash.length == 0)) {
		return cmd;
	}
	INIT_STRING_FROM_STACK(new_cmd, buffer);

	Wstring wcb(cmd);
	p = wcb.get_string();

	while (*p != (int) nul_char) {
		while (iswspace(*p) && (*p != (int) nul_char)) {
			append_char(*p++, &new_cmd);
		}
		start = p;
		while (!iswspace(*p) && (*p != (int) nul_char)) {
			p++;
		}
		cmd = GETNAME(start, p - start);
		if (cmd->has_vpath_alias_prop) {
			cmd = get_prop(cmd->prop, vpath_alias_prop)->
						body.vpath_alias.alias;
			APPEND_NAME(cmd,
				      &new_cmd,
				      (int) cmd->hash.length);
		} else {
			append_string(start, &new_cmd, p - start);
		}
	}
	cmd = GETNAME(new_cmd.buffer.start, FIND_LENGTH);
	if (new_cmd.free_after_use) {
		retmem(new_cmd.buffer.start);
	}
	return cmd;
}

/*
 *	check_state(temp_file_name)
 *
 *	Reads and checks the state changed by the previously executed command.
 *
 *	Parameters:
 *		temp_file_name	The auto dependency temp file
 *
 *	Global variables used:
 */
void
check_state(Name temp_file_name)
{
	if (!keep_state) {
		return;
	}

	/*
	 * Then read the temp file that now might
	 * contain dependency reports from utilities
	 */
	read_dependency_file(temp_file_name);

	/*
	 * And reread .make.state if it
	 * changed (the command ran recursive makes)
	 */
	check_read_state_file();
	if (temp_file_name != NULL) {
		(void) unlink(temp_file_name->string_mb);
	}
}

/*
 *	read_dependency_file(filename)
 *
 *	Read the temp file used for reporting dependencies to make
 *
 *	Parameters:
 *		filename	The name of the file with the state info
 *
 *	Global variables used:
 *		makefile_type	The type of makefile being read
 *		read_trace_level Debug flag
 *		temp_file_number The always increasing number for unique files
 *		trace_reader	Debug flag
 */
static void
read_dependency_file(register Name filename)
{
	register Makefile_type	save_makefile_type;

	if (filename == NULL) {
		return;
	}
	filename->stat.time = file_no_time;
	if (exists(filename) > file_doesnt_exist) {
		save_makefile_type = makefile_type;
		makefile_type = reading_cpp_file;
		if (read_trace_level > 1) {
			trace_reader = true;
		}
		temp_file_number++;
		(void) read_simple_file(filename,
					false,
					false,
					false,
					false,
					false,
					false);
		trace_reader = false;
		makefile_type = save_makefile_type;
	}
}

/*
 *	check_read_state_file()
 *
 *	Check if .make.state has changed
 *	If it has we reread it
 *
 *	Parameters:
 *
 *	Global variables used:
 *		make_state	Make state file name
 *		makefile_type	Type of makefile being read
 *		read_trace_level Debug flag
 *		trace_reader	Debug flag
 */
static void
check_read_state_file(void)
{
	timestruc_t		previous = make_state->stat.time;
	register Makefile_type	save_makefile_type;
	register Property	makefile;

	make_state->stat.time = file_no_time;
	if ((exists(make_state) == file_doesnt_exist) ||
	    (make_state->stat.time == previous)) {
		return;
	}
	save_makefile_type = makefile_type;
	makefile_type = rereading_statefile;
	/* Make sure we clear the old cached contents of .make.state */
	makefile = maybe_append_prop(make_state, makefile_prop);
	if (makefile->body.makefile.contents != NULL) {
		retmem(makefile->body.makefile.contents);
		makefile->body.makefile.contents = NULL;
	}
	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;
	makefile_type = save_makefile_type;
}

/*
 *	do_assign(line, target)
 *
 *	Handles runtime assignments for command lines prefixed with "=".
 *
 *	Parameters:
 *		line		The command that contains an assignment
 *		target		The Name of the target, used for error reports
 *
 *	Global variables used:
 *		assign_done	Set to indicate doname needs to reprocess
 */
static void
do_assign(register Name line, register Name target)
{
	Wstring wcb(line);
	register wchar_t	*string = wcb.get_string();
	register wchar_t	*equal;
	register Name		name;
	register Boolean	append = false;

	/*
	 * If any runtime assignments are done, doname() must reprocess all
	 * targets in the future since the macro values used to build the
	 * command lines for the targets might have changed.
	 */
	assign_done = true;
	/* Skip white space. */
	while (iswspace(*string)) {
		string++;
	}
	equal = string;
	/* Find "+=" or "=". */
	while (!iswspace(*equal) &&
	       (*equal != (int) plus_char) &&
	       (*equal != (int) equal_char)) {
		equal++;
	}
	/* Internalize macro name. */
	name = GETNAME(string, equal - string);
	/* Skip over "+=" "=". */
	while (!((*equal == (int) nul_char) ||
		 (*equal == (int) equal_char) ||
		 (*equal == (int) plus_char))) {
		equal++;
	}
	switch (*equal) {
	case nul_char:
		fatal(gettext("= expected in rule `%s' for target `%s'"),
		      line->string_mb,
		      target->string_mb);
	case plus_char:
		append = true;
		equal++;
		break;
	}
	equal++;
	/* Skip over whitespace in front of value. */
	while (iswspace(*equal)) {
		equal++;
	}
	/* Enter new macro value. */
	enter_equal(name,
		    GETNAME(equal, wcb.get_string() + line->hash.length - equal),
		    append);
}

/*
 *	build_command_strings(target, line)
 *
 *	Builds the command string to used when
 *	building a target. If the string is different from the previous one
 *	is_out_of_date is set.
 *
 *	Parameters:
 *		target		Target to build commands for
 *		line		Where to stuff result
 *
 *	Global variables used:
 *		c_at		The Name "@", used to set macro value
 *		command_changed	Set if command is different from old
 *		debug_level	Should we trace activities?
 *		do_not_exec_rule Always echo when running -n
 *		empty_name	The Name "", used for empty rule
 *		funny		Semantics of characters
 *		ignore_errors	Used to init field for line
 *		is_conditional	Set to false befor evaling macro, checked
 *				after expanding macros
 *		keep_state	Indicates that .KEEP_STATE is on
 *		make_word_mentioned Set by macro eval, inits field for cmd
 *		query		The Name "?", used to set macro value
 *		query_mentioned	Set by macro eval, inits field for cmd
 *		recursion_level	Used for tracing
 *		silent		Used to init field for line
 */
static void
build_command_strings(Name target, register Property line)
{
	String_rec		command_line;
	register Cmd_line	command_template = line->body.line.command_template;
	register Cmd_line	*insert = &line->body.line.command_used;
	register Cmd_line	used = *insert;
	wchar_t			buffer[STRING_BUFFER_LENGTH];
	wchar_t			*start;
	Name			new_command_line;
	register Boolean	new_command_longer = false;
	register Boolean	ignore_all_command_dependency = true;
	Property		member;
	static Name		less_name;
	static Name		percent_name;
	static Name		star;
	Name			tmp_name;

	if (less_name == NULL) {
		MBSTOWCS(wcs_buffer, "<");
		less_name = GETNAME(wcs_buffer, FIND_LENGTH);
		MBSTOWCS(wcs_buffer, "%");
		percent_name = GETNAME(wcs_buffer, FIND_LENGTH);
		MBSTOWCS(wcs_buffer, "*");
		star = GETNAME(wcs_buffer, FIND_LENGTH);
	}

	/* We have to check if a target depends on conditional macros */
	/* Targets that do must be reprocessed by doname() each time around */
	/* since the macro values used when building the target might have */
	/* changed */
	conditional_macro_used = false;
	/* If we are building a lib.a(member) target $@ should be bound */
	/* to lib.a */
	if (target->is_member &&
	    ((member = get_prop(target->prop, member_prop)) != NULL)) {
		target = member->body.member.library;
	}
	/* If we are building a "::" help target $@ should be bound to */
	/* the real target name */
	/* A lib.a(member) target is never :: */
	if (target->has_target_prop) {
		target = get_prop(target->prop, target_prop)->
		  body.target.target;
	}
	/* Bind the magic macros that make supplies */
	tmp_name = target;
	if(tmp_name != NULL) {
		if (tmp_name->has_vpath_alias_prop) {
			tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)->
					body.vpath_alias.alias;
		}
	}
	(void) SETVAR(c_at, tmp_name, false);

	tmp_name = line->body.line.star;
	if(tmp_name != NULL) {
		if (tmp_name->has_vpath_alias_prop) {
			tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)->
					body.vpath_alias.alias;
		}
	}
	(void) SETVAR(star, tmp_name, false);

	tmp_name = line->body.line.less;
	if(tmp_name != NULL) {
		if (tmp_name->has_vpath_alias_prop) {
			tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)->
					body.vpath_alias.alias;
		}
	}
	(void) SETVAR(less_name, tmp_name, false);

	tmp_name = line->body.line.percent;
	if(tmp_name != NULL) {
		if (tmp_name->has_vpath_alias_prop) {
			tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)->
					body.vpath_alias.alias;
		}
	}
	(void) SETVAR(percent_name, tmp_name, false);

	/* $? is seldom used and it is expensive to build */
	/* so we store the list form and build the string on demand */
	Chain query_list = NULL;
	Chain *query_list_tail = &query_list;

	for (Chain ch = line->body.line.query; ch != NULL; ch = ch->next) {
		*query_list_tail = ALLOC(Chain);
		(*query_list_tail)->name = ch->name;
		if ((*query_list_tail)->name->has_vpath_alias_prop) {
			(*query_list_tail)->name =
				get_prop((*query_list_tail)->name->prop,
					vpath_alias_prop)->body.vpath_alias.alias;
		}
		(*query_list_tail)->next = NULL;
		query_list_tail = &(*query_list_tail)->next;
	}
	(void) setvar_daemon(query,
			     (Name) query_list,
			     false,
			     chain_daemon,
                             false,
                             debug_level);

	/* build $^ */
	Chain hat_list = NULL;
	Chain *hat_list_tail = &hat_list;

	for (Dependency dependency = line->body.line.dependencies;
		dependency != NULL;
		dependency = dependency->next) {
		/* skip automatic dependencies */
		if (!dependency->automatic) {
			if ((dependency->name != force) &&
				(dependency->stale == false)) {
				*hat_list_tail = ALLOC(Chain);

				if (dependency->name->is_member &&
					(get_prop(dependency->name->prop, member_prop) != NULL)) {
					(*hat_list_tail)->name =
							get_prop(dependency->name->prop,
								member_prop)->body.member.member;
				} else {
					(*hat_list_tail)->name = dependency->name;
				}

				if((*hat_list_tail)->name != NULL) {
					if ((*hat_list_tail)->name->has_vpath_alias_prop) {
						(*hat_list_tail)->name =
							get_prop((*hat_list_tail)->name->prop,
								vpath_alias_prop)->body.vpath_alias.alias;
					}
				}

				(*hat_list_tail)->next = NULL;
				hat_list_tail = &(*hat_list_tail)->next;
			}
		}
	}
	(void) setvar_daemon(hat,
			     (Name) hat_list,
			     false,
			     chain_daemon,
                             false,
                             debug_level);

/* We have two command sequences we need to handle */
/* The old one that we probably read from .make.state */
/* and the new one we are building that will replace the old one */
/* Even when KEEP_STATE is not on we build a new command sequence and store */
/* it in the line prop. This command sequence is then executed by */
/* run_command(). If KEEP_STATE is on it is also later written to */
/* .make.state. The routine replaces the old command line by line with the */
/* new one trying to reuse Cmd_lines */

	/* If there is no old command_used we have to start creating */
	/* Cmd_lines to keep the new cmd in */
	if (used == NULL) {
		new_command_longer = true;
		*insert = used = ALLOC(Cmd_line);
		used->next = NULL;
		used->command_line = NULL;
		insert = &used->next;
	}
	/* Run thru the template for the new command and build the expanded */
	/* new command lines */
	for (;
	     command_template != NULL;
	     command_template = command_template->next, insert = &used->next, used = *insert) {
		/* If there is no old command_used Cmd_line we need to */
		/* create one and say that cmd consistency failed */
		if (used == NULL) {
			new_command_longer = true;
			*insert = used = ALLOC(Cmd_line);
			used->next = NULL;
			used->command_line = empty_name;
		}
		/* Prepare the Cmd_line for the processing */
		/* The command line prefixes "@-=?" are stripped and that */
		/* information is saved in the Cmd_line */
		used->assign = false;
		used->ignore_error = ignore_errors;
		used->silent = silent;
		used->always_exec = false;
		/* Expand the macros in the command line */
		INIT_STRING_FROM_STACK(command_line, buffer);
		make_word_mentioned =
		  query_mentioned =
		    false;
		expand_value(command_template->command_line, &command_line, true);
		/* If the macro $(MAKE) is mentioned in the command */
		/* "make -n" runs actually execute the command */
		used->make_refd = make_word_mentioned;
		used->ignore_command_dependency = query_mentioned;
		/* Strip the prefixes */
		start = command_line.buffer.start;
		for (;
		     iswspace(*start) ||
		     (get_char_semantics_value(*start) & (int) command_prefix_sem);
		     start++) {
			switch (*start) {
			case question_char:
				used->ignore_command_dependency = true;
				break;
			case exclam_char:
				used->ignore_command_dependency = false;
				break;
			case equal_char:
				used->assign = true;
				break;
			case hyphen_char:
				used->ignore_error = true;
				break;
			case at_char:
				if (!do_not_exec_rule) {
					used->silent = true;
				}
				break;
			case plus_char:
				if(posix) {
				  used->always_exec  = true;
				}
				break;
			}
		}
		/* If all command lines of the template are prefixed with "?"*/
		/* the VIRTUAL_ROOT is not used for cmd consistency checks */
		if (!used->ignore_command_dependency) {
			ignore_all_command_dependency = false;
		}
		/* Internalize the expanded and stripped command line */
		new_command_line = GETNAME(start, FIND_LENGTH);
		if ((used->command_line == NULL) &&
		    (line->body.line.sccs_command)) {
			used->command_line = new_command_line;
			new_command_longer = false;
		}
		/* Compare it with the old one for command consistency */
		if (used->command_line != new_command_line) {
			Name vpath_translated = vpath_translation(new_command_line);
			if (keep_state &&
			    !used->ignore_command_dependency && (vpath_translated != used->command_line)) {
				if (debug_level > 0) {
					if (used->command_line != NULL
					    && *used->command_line->string_mb !=
					    '\0') {
						(void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n"),
							      recursion_level,
							      "",
							      target->string_mb,
							      vpath_translated->string_mb,
							      recursion_level,
							      "",
							      used->
							      command_line->
							      string_mb);
					} else {
						(void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n"),
							      recursion_level,
							      "",
							      target->string_mb,
							      vpath_translated->string_mb,
							      recursion_level,
							      "");
					}
				}
				command_changed = true;
                                line->body.line.is_out_of_date = true;
			}
			used->command_line = new_command_line;
		}
		if (command_line.free_after_use) {
			retmem(command_line.buffer.start);
		}
	}
	/* Check if the old command is longer than the new for */
	/* command consistency */
	if (used != NULL) {
		*insert = NULL;
		if (keep_state &&
		    !ignore_all_command_dependency) {
			if (debug_level > 0) {
				(void) printf(gettext("%*sBuilding %s because new command shorter than old\n"),
					      recursion_level,
					      "",
					      target->string_mb);
			}
			command_changed = true;
                        line->body.line.is_out_of_date = true;
		}
	}
	/* Check if the new command is longer than the old command for */
	/* command consistency */
	if (new_command_longer &&
	    !ignore_all_command_dependency &&
	    keep_state) {
		if (debug_level > 0) {
			(void) printf(gettext("%*sBuilding %s because new command longer than old\n"),
				      recursion_level,
				      "",
				      target->string_mb);
		}
		command_changed = true;
                line->body.line.is_out_of_date = true;
	}
	/* Unbind the magic macros */
	(void) SETVAR(c_at, (Name) NULL, false);
	(void) SETVAR(star, (Name) NULL, false);
	(void) SETVAR(less_name, (Name) NULL, false);
	(void) SETVAR(percent_name, (Name) NULL, false);
	(void) SETVAR(query, (Name) NULL, false);
        if (query_list != NULL) {
		delete_query_chain(query_list);
        }
	(void) SETVAR(hat, (Name) NULL, false);
        if (hat_list != NULL) {
		delete_query_chain(hat_list);
        }

	if (conditional_macro_used) {
		target->conditional_macro_list = cond_macro_list;
		cond_macro_list = NULL;
		target->depends_on_conditional = true;
	}
}

/*
 *	touch_command(line, target, result)
 *
 *	If this is an "make -t" run we do this.
 *	We touch all targets in the target group ("foo + fie:") if any.
 *
 *	Return value:
 *				Indicates if the command failed or not
 *
 *	Parameters:
 *		line		The command line to update
 *		target		The target we are touching
 *		result		Initial value for the result we return
 *
 *	Global variables used:
 *		do_not_exec_rule Indicates that -n is on
 *		silent		Do not echo commands
 */
static Doname
touch_command(register Property line, register Name target, Doname result)
{
	Name			name;
	register Chain		target_group;
	String_rec		touch_string;
	wchar_t			buffer[MAXPATHLEN];
	Name			touch_cmd;
	Cmd_line		rule;

	for (name = target, target_group = NULL; name != NULL;) {
		if (!name->is_member) {
			/*
			 * Build a touch command that can be passed
			 * to dosys(). If KEEP_STATE is on, "make -t"
			 * will save the proper command, not the
			 * "touch" in .make.state.
			 */
			INIT_STRING_FROM_STACK(touch_string, buffer);
			MBSTOWCS(wcs_buffer, "touch ");
			append_string(wcs_buffer, &touch_string, FIND_LENGTH);
			touch_cmd = name;
			if (name->has_vpath_alias_prop) {
				touch_cmd = get_prop(name->prop,
						 vpath_alias_prop)->
						   body.vpath_alias.alias;
			}
			APPEND_NAME(touch_cmd,
				      &touch_string,
				      FIND_LENGTH);
			touch_cmd = GETNAME(touch_string.buffer.start,
					    FIND_LENGTH);
			if (touch_string.free_after_use) {
				retmem(touch_string.buffer.start);
			}
			if (!silent ||
			    do_not_exec_rule &&
			    (target_group == NULL)) {
				(void) printf("%s\n", touch_cmd->string_mb);
			}
			/* Run the touch command, or simulate it */
			if (!do_not_exec_rule) {
				result = dosys(touch_cmd,
					       false,
					       false,
					       false,
					       false,
					       name);
			} else {
				result = build_ok;
			}
		} else {
			result = build_ok;
		}
		if (target_group == NULL) {
			target_group = line->body.line.target_group;
		} else {
			target_group = target_group->next;
		}
		if (target_group != NULL) {
			name = target_group->name;
		} else {
			name = NULL;
		}
	}
	return result;
}

/*
 *	update_target(line, result)
 *
 *	updates the status of a target after executing its commands.
 *
 *	Parameters:
 *		line		The command line block to update
 *		result		Indicates that build is OK so can update
 *
 *	Global variables used:
 *		do_not_exec_rule Indicates that -n is on
 *		touch		Fake the new timestamp if we are just touching
 */
void
update_target(Property line, Doname result)
{
	Name			target;
	Chain			target_group;
	Property		line2;
	timestruc_t		old_stat_time;
	Property		member;

	/*
	 * [tolik] Additional fix for bug 1063790. It was fixed
	 * for serial make long ago, but DMake dumps core when
	 * target is a symlink and sccs file is newer then target.
	 * In this case, finish_children() calls update_target()
	 * with line==NULL.
	 */
	if(line == NULL) {
		/* XXX. Should we do anything here? */
		return;
	}

	target = line->body.line.target;

	if ((result == build_ok) && (line->body.line.command_used != NULL)) {
		if (do_not_exec_rule ||
		    touch ||
		    (target->is_member &&
		     (line->body.line.command_template != NULL) &&
		     (line->body.line.command_template->command_line->string_mb[0] == 0) &&
		     (line->body.line.command_template->next == NULL))) {
			/* If we are simulating execution we need to fake a */
			/* new timestamp for the target we didnt build */
			target->stat.time = file_max_time;
		} else {
			/*
			 * If we really built the target we read the new
			 * timestamp.
			 * Fix for bug #1110906: if .c file is newer than
			 * the corresponding .o file which is in an archive
			 * file, make will compile the .c file but it won't
			 * update the object in the .a file.
			 */
			old_stat_time = target->stat.time;
			target->stat.time = file_no_time;
			(void) exists(target);
			if ((target->is_member) &&
			    (target->stat.time == old_stat_time)) {
				member = get_prop(target->prop, member_prop);
				if (member != NULL) {
					target->stat.time = member->body.member.library->stat.time;
					target->stat.time.tv_sec++;
				}
			}
		}
		/* If the target is part of a group we need to propagate the */
		/* result of the run to all members */
		for (target_group = line->body.line.target_group;
		     target_group != NULL;
		     target_group = target_group->next) {
			target_group->name->stat.time = target->stat.time;
			line2 = maybe_append_prop(target_group->name,
						  line_prop);
			line2->body.line.command_used =
			  line->body.line.command_used;
			line2->body.line.target = target_group->name;
		}
	}
	target->has_built = true;
}

/*
 *	sccs_get(target, command)
 *
 *	Figures out if it possible to sccs get a file
 *	and builds the command to do it if it is.
 *
 *	Return value:
 *				Indicates if sccs get failed or not
 *
 *	Parameters:
 *		target		Target to get
 *		command		Where to deposit command to use
 *
 *	Global variables used:
 *		debug_level	Should we trace activities?
 *		recursion_level	Used for tracing
 *		sccs_get_rule	The rule to used for sccs getting
 */
static Doname
sccs_get(register Name target, register Property *command)
{
	register int		result;
	char			link[MAXPATHLEN];
	String_rec		string;
	wchar_t			name[MAXPATHLEN];
	register wchar_t	*p;
	timestruc_t		sccs_time;
	register Property	line;
	int			sym_link_depth = 0;

	/* For sccs, we need to chase symlinks. */
        while (target->stat.is_sym_link) {
		if (sym_link_depth++ > 90) {
			fatal(gettext("Can't read symbolic link `%s': Number of symbolic links encountered during path name traversal exceeds 90."),
			      target->string_mb);
		}
                /* Read the value of the link. */
                result = readlink_vroot(target->string_mb,
					link,
					sizeof(link),
					NULL,
					VROOT_DEFAULT);
                if (result == -1) {
                        fatal(gettext("Can't read symbolic link `%s': %s"),
                              target->string_mb, errmsg(errno));
		}
		link[result] = 0;
                /* Use the value to build the proper filename. */
                INIT_STRING_FROM_STACK(string, name);

		Wstring wcb(target);
                if ((link[0] != slash_char) &&
                    ((p = (wchar_t *) wcsrchr(wcb.get_string(), slash_char)) != NULL)) {
                        append_string(wcb.get_string(), &string, p - wcb.get_string() + 1);
		}
                append_string(link, &string, result);
                /* Replace the old name with the translated name. */
		target = normalize_name(string.buffer.start, string.text.p - string.buffer.start);
                (void) exists(target);
                if (string.free_after_use) {
                        retmem(string.buffer.start);
		}
        }

	/*
	 * read_dir() also reads the ?/SCCS dir and saves information
	 * about which files have SCSC/s. files.
	 */
	if (target->stat.has_sccs == DONT_KNOW_SCCS) {
		read_directory_of_file(target);
	}
	switch (target->stat.has_sccs) {
	case DONT_KNOW_SCCS:
		/* We dont know by now there is no SCCS/s.* */
		target->stat.has_sccs = NO_SCCS;
		/* FALLTHROUGH */
	case NO_SCCS:
		/*
		 * If there is no SCCS/s.* but the plain file exists,
		 * we say things are OK.
		 */
		if (target->stat.time > file_doesnt_exist) {
			return build_ok;
		}
		/* If we cant find the plain file, we give up. */
		return build_dont_know;
	case HAS_SCCS:
		/*
		 * Pay dirt. We now need to figure out if the plain file
		 * is out of date relative to the SCCS/s.* file.
		 */
		sccs_time = exists(get_prop(target->prop,
					    sccs_prop)->body.sccs.file);
		break;
	}

	if ((!target->has_complained &&
	    (sccs_time != file_doesnt_exist) &&
	    (sccs_get_rule != NULL))) {
		/* only checking */
		if (command == NULL) {
			return build_ok;
		}
		/*
		 * We provide a command line for the target. The line is a
		 * "sccs get" command from default.mk.
		 */
		line = maybe_append_prop(target, line_prop);
		*command = line;
		if (sccs_time > target->stat.time) {
			/*
			 * And only if the plain file is out of date do we
			 * request execution of the command.
			 */
			line->body.line.is_out_of_date = true;
			if (debug_level > 0) {
				(void) printf(gettext("%*sSccs getting %s because s. file is younger than source file\n"),
					      recursion_level,
					      "",
					      target->string_mb);
			}
		}
		line->body.line.sccs_command = true;
		line->body.line.command_template = sccs_get_rule;
		if(!svr4 && (!allrules_read || posix)) {
		   if((target->prop) &&
		      (target->prop->body.sccs.file) &&
		      (target->prop->body.sccs.file->string_mb)) {
		      if((strlen(target->prop->body.sccs.file->string_mb) ==
			strlen(target->string_mb) + 2) &&
		        (target->prop->body.sccs.file->string_mb[0] == 's') &&
		        (target->prop->body.sccs.file->string_mb[1] == '.')) {

		         line->body.line.command_template = get_posix_rule;
		      }
		   }
		}
		line->body.line.target = target;
		/*
		 * Also make sure the rule is build with $* and $<
		 * bound properly.
		 */
		line->body.line.star = NULL;
		line->body.line.less = NULL;
		line->body.line.percent = NULL;
		return build_ok;
	}
	return build_dont_know;
}

/*
 *	read_directory_of_file(file)
 *
 *	Reads the directory the specified file lives in.
 *
 *	Parameters:
 *		file		The file we need to read dir for
 *
 *	Global variables used:
 *		dot		The Name ".", used as the default dir
 */
void
read_directory_of_file(register Name file)
{

	Wstring file_string(file);
	wchar_t * wcb = file_string.get_string();
	wchar_t usr_include_buf[MAXPATHLEN];
	wchar_t usr_include_sys_buf[MAXPATHLEN];

	register Name		directory = dot;
	register wchar_t	*p = (wchar_t *) wcsrchr(wcb,
							(int) slash_char);
	register int		length = p - wcb;
	static Name		usr_include;
	static Name		usr_include_sys;

	if (usr_include == NULL) {
		MBSTOWCS(usr_include_buf, "/usr/include");
		usr_include = GETNAME(usr_include_buf, FIND_LENGTH);
		MBSTOWCS(usr_include_sys_buf, "/usr/include/sys");
		usr_include_sys = GETNAME(usr_include_sys_buf, FIND_LENGTH);
	}

	/*
	 * If the filename contains a "/" we have to extract the path
	 * Else the path defaults to ".".
	 */
	if (p != NULL) {
		/*
		 * Check some popular directories first to possibly
		 * save time. Compare string length first to gain speed.
		 */
		if ((usr_include->hash.length == length) &&
		    IS_WEQUALN(usr_include_buf,
			       wcb,
			       length)) {
			directory = usr_include;
		} else if ((usr_include_sys->hash.length == length) &&
		           IS_WEQUALN(usr_include_sys_buf,
		                      wcb,
		                      length)) {
			directory = usr_include_sys;
		} else {
			directory = GETNAME(wcb, length);
		}
	}
	(void) read_dir(directory,
			(wchar_t *) NULL,
			(Property) NULL,
			(wchar_t *) NULL);
}

/*
 *	add_pattern_conditionals(target)
 *
 *	Scan the list of conditionals defined for pattern targets and add any
 *	that match this target to its list of conditionals.
 *
 *	Parameters:
 *		target		The target we should add conditionals for
 *
 *	Global variables used:
 *		conditionals	The list of pattern conditionals
 */
static void
add_pattern_conditionals(register Name target)
{
	register Property	conditional;
	Property		new_prop;
	Property		*previous;
	Name_rec		dummy;
	wchar_t			*pattern;
	wchar_t			*percent;
	int			length;

	Wstring wcb(target);
	Wstring wcb1;

	for (conditional = get_prop(conditionals->prop, conditional_prop);
	     conditional != NULL;
	     conditional = get_prop(conditional->next, conditional_prop)) {
		wcb1.init(conditional->body.conditional.target);
		pattern = wcb1.get_string();
		if (pattern[1] != 0) {
			percent = (wchar_t *) wcschr(pattern, (int) percent_char);
			/* Check for possible buffer under-read */
			if ((length = wcb.length()-wcslen(percent+1)) <= 0) {
				continue;
			}
			if (!wcb.equaln(pattern, percent-pattern) ||
			    !IS_WEQUAL(wcb.get_string(length), percent+1)) {
				continue;
			}
		}
		for (previous = &target->prop;
		     *previous != NULL;
		     previous = &(*previous)->next) {
			if (((*previous)->type == conditional_prop) &&
			    ((*previous)->body.conditional.sequence >
			     conditional->body.conditional.sequence)) {
				break;
			}
		}
		if (*previous == NULL) {
			new_prop = append_prop(target, conditional_prop);
		} else {
			dummy.prop = NULL;
			new_prop = append_prop(&dummy, conditional_prop);
			new_prop->next = *previous;
			*previous = new_prop;
		}
		target->conditional_cnt++;
		new_prop->body.conditional = conditional->body.conditional;
	}
}

/*
 *	set_locals(target, old_locals)
 *
 *	Sets any conditional macros for the target.
 *	Each target carries a possibly empty set of conditional properties.
 *
 *	Parameters:
 *		target		The target to set conditional macros for
 *		old_locals	Space to store old values in
 *
 *	Global variables used:
 *		debug_level	Should we trace activity?
 *		is_conditional	We need to preserve this value
 *		recursion_level	Used for tracing
 */
void
set_locals(register Name target, register Property old_locals)
{
	register Property	conditional;
	register int		i;
	register Boolean	saved_conditional_macro_used;
	Chain			cond_name;
	Chain			cond_chain;

	if (target->dont_activate_cond_values) {
		return;
	}

	saved_conditional_macro_used = conditional_macro_used;

	/* Scan the list of conditional properties and apply each one */
	for (conditional = get_prop(target->prop, conditional_prop), i = 0;
	     conditional != NULL;
	     conditional = get_prop(conditional->next, conditional_prop),
	     i++) {
		/* Save the old value */
		old_locals[i].body.macro =
		  maybe_append_prop(conditional->body.conditional.name,
				    macro_prop)->body.macro;
		if (debug_level > 1) {
			(void) printf(gettext("%*sActivating conditional value: "),
				      recursion_level,
				      "");
		}
		/* Set the conditional value. Macros are expanded when the */
		/* macro is refd as usual */
		if ((conditional->body.conditional.name != virtual_root) ||
		    (conditional->body.conditional.value != virtual_root)) {
			(void) SETVAR(conditional->body.conditional.name,
				      conditional->body.conditional.value,
				      (Boolean) conditional->body.conditional.append);
		}
		cond_name = ALLOC(Chain);
		cond_name->name = conditional->body.conditional.name;
	}
	/* Put this target on the front of the chain of conditional targets */
	cond_chain = ALLOC(Chain);
	cond_chain->name = target;
	cond_chain->next = conditional_targets;
	conditional_targets = cond_chain;
	conditional_macro_used = saved_conditional_macro_used;
}

/*
 *	reset_locals(target, old_locals, conditional, index)
 *
 *	Removes any conditional macros for the target.
 *
 *	Parameters:
 *		target		The target we are retoring values for
 *		old_locals	The values to restore
 *		conditional	The first conditional block for the target
 *		index		into the old_locals vector
 *	Global variables used:
 *		debug_level	Should we trace activities?
 *		recursion_level	Used for tracing
 */
void
reset_locals(register Name target, register Property old_locals, register Property conditional, register int index)
{
	register Property	this_conditional;
	Chain			cond_chain;

	if (target->dont_activate_cond_values) {
		return;
	}

	/* Scan the list of conditional properties and restore the old value */
	/* to each one Reverse the order relative to when we assigned macros */
	this_conditional = get_prop(conditional->next, conditional_prop);
	if (this_conditional != NULL) {
		reset_locals(target, old_locals, this_conditional, index+1);
	} else {
		/* Remove conditional target from chain */
		if (conditional_targets == NULL ||
		    conditional_targets->name != target) {
			warning(gettext("Internal error: reset target not at head of condtional_targets chain"));
		} else {
			cond_chain = conditional_targets->next;
			retmem_mb((caddr_t) conditional_targets);
			conditional_targets = cond_chain;
		}
	}
	get_prop(conditional->body.conditional.name->prop,
		 macro_prop)->body.macro = old_locals[index].body.macro;
	if (conditional->body.conditional.name == virtual_root) {
		(void) SETVAR(virtual_root, getvar(virtual_root), false);
	}
	if (debug_level > 1) {
		if (old_locals[index].body.macro.value != NULL) {
			(void) printf(gettext("%*sdeactivating conditional value: %s= %s\n"),
				      recursion_level,
				      "",
				      conditional->body.conditional.name->
				      string_mb,
				      old_locals[index].body.macro.value->
				      string_mb);
		} else {
			(void) printf(gettext("%*sdeactivating conditional value: %s =\n"),
				      recursion_level,
				      "",
				      conditional->body.conditional.name->
				      string_mb);
		}
	}
}

/*
 *	check_auto_dependencies(target, auto_count, automatics)
 *
 *	Returns true if the target now has a dependency
 *	it didn't previously have (saved on automatics).
 *
 *	Return value:
 *				true if new dependency found
 *
 *	Parameters:
 *		target		Target we check
 *		auto_count	Number of old automatic vars
 *		automatics	Saved old automatics
 *
 *	Global variables used:
 *		keep_state	Indicates that .KEEP_STATE is on
 */
Boolean
check_auto_dependencies(Name target, int auto_count, Name *automatics)
{
	Name		*p;
	int		n;
	Property	line;
	Dependency	dependency;

	if (keep_state) {
		if ((line = get_prop(target->prop, line_prop)) == NULL) {
			return false;
		}
		/* Go thru new list of automatic depes */
		for (dependency = line->body.line.dependencies;
		     dependency != NULL;
		     dependency = dependency->next) {
			/* And make sure that each one existed before we */
			/* built the target */
			if (dependency->automatic && !dependency->stale) {
				for (n = auto_count, p = automatics;
				     n > 0;
				     n--) {
					if (*p++ == dependency->name) {
						/* If we can find it on the */
						/* saved list of autos we */
						/* are OK  */
						goto not_new;
					}
				}
				/* But if we scan over the old list */
				/* of auto. without finding it it is */
				/* new and we must check it */
				return true;
			}
		not_new:;
		}
		return false;
	} else {
		return false;
	}
}


// Recursively delete each of the Chain struct on the chain.

static void
delete_query_chain(Chain ch)
{
	if (ch == NULL) {
		return;
	} else {
		delete_query_chain(ch->next);
		retmem_mb((char *) ch);
	}
}

Doname
target_can_be_built(register Name target) {
	Doname		result = build_dont_know;
	Name		true_target = target;
	Property	line;

	if (target == wait_name) {
		return(build_ok);
	}
	/*
	 * If the target is a constructed one for a "::" target,
	 * we need to consider that.
	 */
	if (target->has_target_prop) {
		true_target = get_prop(target->prop,
				       target_prop)->body.target.target;
	}

	(void) exists(true_target);

	if (true_target->state == build_running) {
		return(build_running);
	}
	if (true_target->stat.time != file_doesnt_exist) {
		result = build_ok;
	}

	/* get line property for the target */
	line = get_prop(true_target->prop, line_prop);

	/* first check for explicit rule */
	if (line != NULL && line->body.line.command_template != NULL) {
		result = build_ok;
	}
	/* try to find pattern rule */
	if (result == build_dont_know) {
		result = find_percent_rule(target, NULL, false);
	}

	/* try to find double suffix rule */
	if (result == build_dont_know) {
		if (target->is_member) {
			Property member = get_prop(target->prop, member_prop);
			if (member != NULL && member->body.member.member != NULL) {
				result = find_ar_suffix_rule(target, member->body.member.member, NULL, false);
			} else {
				result = find_double_suffix_rule(target, NULL, false);
			}
		} else {
			result = find_double_suffix_rule(target, NULL, false);
		}
	}

	/* try to find suffix rule */
	if ((result == build_dont_know) && second_pass) {
		result = find_suffix_rule(target, target, empty_name, NULL, false);
	}

	/* check for sccs */
	if (result == build_dont_know) {
		result = sccs_get(target, NULL);
	}

	/* try to find dyn target */
	if (result == build_dont_know) {
		Name dtarg = find_dyntarget(target);
		if (dtarg != NULL) {
			result = target_can_be_built(dtarg);
		}
	}

	/* check whether target was mentioned in makefile */
	if (result == build_dont_know) {
		if (target->colons != no_colon) {
			result = build_ok;
		}
	}

	/* result */
	return result;
}