#ident "@(#)doname.c 1.1 94/10/31 Copyright 1986,1987,1988 Sun Micro"

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

/*
 * Included files
 */
#include "defs.h"
#include <sys/file.h>

/*
 * Defined macros
 */

/*
 * typedefs & structs
 */

/*
 * Static variables
 */

/*
 * File table of contents
 */
extern	Doname		doname_check();
extern	Doname		doname();
extern	Boolean		check_dependencies();
extern	void		dynamic_dependencies();
extern	Doname		run_command();
extern	Doname		execute_serial();
extern	Name		vpath_translation();
extern	void		check_state();
extern	void		read_dependency_file();
extern	void		check_read_state_file();
extern	void		do_assign();
extern	void		build_command_strings();
extern	Doname		touch_command();
extern	void		update_target();
extern	Doname		sccs_get();
extern	void		read_directory_of_file();
extern	void		add_pattern_conditionals();
extern	void		set_locals();
extern	void		reset_locals();
extern	Boolean		check_auto_dependencies();

/*
 *	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(target, do_get, implicit, automatic)
	register Name		target;
	register Boolean	do_get;
	register Boolean	implicit;
	register Boolean	automatic;
{
	(void) fflush(stdout);
	switch (doname(target, do_get, implicit)) {
	case build_ok:
		return build_ok;
	case build_running:
		return build_running;
	case build_failed:
		if (!continue_after_error) {
			fatal("Target `%s' not remade because of errors",
			      target->string);
		}
		build_failed_seen = true;
		return build_failed;
	case build_dont_know:
		/* If we cant 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_only) {
			return build_dont_know;
		}
		if (continue_after_error) {
			warning("Don't know how to make target `%s'",
				target->string);
			build_failed_seen = true;
			return build_failed;
		}
		fatal("Don't know how to make target `%s'", target->string);
		break;
	}
#ifdef lint
	return build_failed;
#endif
}

/*
 *	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(target, do_get, implicit)
	register Name		target;
	register Boolean	do_get;
	register Boolean	implicit;
{
	Doname			result = build_dont_know;
	Chain			out_of_date_list = NULL;
	Chain			target_group;
	Property		old_locals;
	register Property	line;
	Property		command = NULL;
	register Dependency	dependency;
	Name			less = NULL;
	Name			true_target = target;
	Name			*automatics;
	register int		auto_count;
	Boolean			rechecking_target = false;
	Boolean			saved_commands_done;
	Boolean			restart = false;
	Boolean			save_parallel = parallel;
	Boolean			doing_subtree = false;
	
	if (target->state == build_running) {
		return build_running;
	}
	line = get_prop(target->prop, line_prop);
#ifdef PARALLEL
	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;
			}
		}
	}
#endif PARALLEL
	/* 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 dont need to do it again */
	/* Unless it depends on conditional macros or a delayed assignment */
	/* has been done when KEEP_STATE is on */
	if ((target->state == build_ok) &&
	    (!keep_state ||
	     (!target->depends_on_conditional &&
	      !assign_done))) {
		return build_ok;
	}
	if (target->state == build_subtree) {
		/* A dynamic macro subtree is being built (in parallel) */
		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;
#ifdef PARALLEL
	} else if (parallel &&
		   keep_state &&
		   (target->conditional_cnt > 0)) {
		add_subtree(target, recursion_level, do_get, implicit);
		target->state = build_running;
		return build_running;
#endif PARALLEL
	}
	/* 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) {
				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) {
				*p++ = dependency->name;
			}
		}
	}
	if (debug_level > 1) {
		(void) printf("%*sdoname(%s)\n",
			      recursion_level,
			      "",
			      true_target->string);
	}
	recursion_level++;
	/* avoid infinite loops */
	if (target->state == build_in_progress) {
		warning("Infinite loop: Target `%s' depends on itself",
			target->string);
		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);
	}
	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)) {
			return build_running;
		}
		line->body.line.query = out_of_date_list;
	}

	if (doing_subtree) {
		parallel = false;
	}

/* Do not try to find rule for target if target is :: type */
/* All actions will be taken by separate branches */
/* Else we try to find an implicit rule using various methods */
/* We quit doing that as soon as one is found */
	if (target->colon_splits == 0) {
		/* Look for percent matched rule */
		if ((result == build_dont_know) && (command == NULL)) {
			switch (find_percent_rule(target, &command)) {
			case build_failed:
				result = build_failed;
				break;
#ifdef PARALLEL
			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;
#endif PARALLEL
			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)) {
				case build_failed:
					result = build_failed;
					break;
#ifdef PARALLEL
				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;
#endif PARALLEL
				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)) {
				case build_failed:
					result = build_failed;
					break;
#ifdef PARALLEL
				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;
#endif PARALLEL
				}
			}
		}
		/* Look for single suffix rule */
		if ((result == build_dont_know) && !implicit &&
		    ((line == NULL) ||
		     ((line->body.line.target != NULL) &&
		      !line->body.line.target->has_regular_dependency))) {
			switch (find_suffix_rule(target,
						 target,
						 empty_name,
						 &command)) {
			case build_failed:
				result = build_failed;
				break;
#ifdef PARALLEL
			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;
#endif PARALLEL
			}
		}
		/* 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;
			if (OUT_OF_DATE(true_target->stat.time,
					line->body.line.dependency_time)) {
				line->body.line.is_out_of_date = true;
				if (debug_level > 0) {
					(void) printf("%*sBuilding %s using .DEFAULT because it is out of date\n",
						      recursion_level,
						      "",
						      true_target->string);
				}
			}
			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;
	}
	/* Run commands if any. */
	if ((command != NULL) &&
	    (command->body.line.command_template != NULL)) {
		if (!report_dependencies_only) {
			if (result != build_failed) {
				result = run_command(command);
			}
			switch (result) {
#ifdef PARALLEL
			case build_running:
				add_running(target,
					    true_target,
					    --recursion_level,
					    auto_count,
					    automatics,
					    do_get,
					    implicit);
				target->state = build_running;
				if ((line = get_prop(target->prop,
						     line_prop)) != NULL) {
					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) {
					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;
#endif PARALLEL
			case build_ok:
				/* If all went OK set a nice timestamp */
				if (true_target->stat.time ==
				    (int) file_doesnt_exist) {
					true_target->stat.time =
					  (int) file_max_time;
				}
				break;
			}
		} else {
			result = build_ok;
		}
	} else {
		/* If no command was found for the target and it doesnt */
		/* 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 == (int) file_doesnt_exist){
				true_target->stat.time = (int) file_max_time;
			}
			result = build_ok;
		}
		/* If the file exists it is OK that we couldnt figure */
		/* out how to build it */
		(void) exists(target);
		if ((target->stat.time != (int) 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) {
		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 != (int) file_max_time) &&
		    ((member = get_prop(target->prop, member_prop)) != NULL) &&
		    (exists(member->body.member.member) >
		     (int) 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("%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n",
				      recursion_level,
				      "",
				      true_target->string);
		}
		rechecking_target = true;
		saved_commands_done = commands_done;
		goto recheck_target;
	}

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

/*
 *	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(result, line, do_get,
		   target, true_target, doing_subtree, out_of_date_tail,
		   old_locals, implicit, command, less, rechecking_target)
	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			dependencies_running;
	register Dependency	dependency;
	Doname			dep_result;
	Boolean			dependency_changed = false;

	line->body.line.dependency_time = (int) file_doesnt_exist;
	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 them */
	for (dependency = line->body.line.dependencies;
	     dependency != NULL;
	     dependency = dependency->next) {
		Boolean this_dependency_changed = false;

		if (!dependency->automatic && 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 {
			time_t	depe_time = 0;


			if (true_target->is_member) {
				depe_time = exists(dependency->name);
			}
			if (dependency->built ||
			    (dependency->name->state == build_failed)) {
				dep_result = dependency->name->state;
			} else {
				dep_result = doname_check(dependency->name,
							  do_get,
							  false,
							  dependency->
							  automatic);
			}
			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 cant 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 makeing of the target will fail */
/* This will only happen for automatic (hidden) dependencies */
				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("Target %s rebuilt because dependency %s does not exist\n",
						     true_target->string,
						     dependency->name->string);
				}
				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);
			if ((dependency->name->stat.time >
			     line->body.line.dependency_time) &&
			    (debug_level > 1)) {
				(void) printf("%*sDate(%s)=%s Date-dependencies(%s)=%s\n",
					      recursion_level,
					      "",
					      dependency->name->string,
					      time_to_string(dependency->name->
							     stat.time),
					      true_target->string,
					      time_to_string(line->body.line.
							     dependency_time));
			}
			/* Collect the timestamp of the youngest dependency */
			line->body.line.dependency_time =
			  MAX(dependency->name->stat.time,
			      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 - 1;
				} else {
					true_target->stat.time =
					  (time_t) file_no_time;
					(void) exists(true_target);
				}
			} else {
				(void) exists(true_target);
			}
			if ((true_target->stat.time <=
			     dependency->name->stat.time) &&
			    (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 ==
					    (time_t) file_max_time) {
						(void) printf("%*sBuilding %s because %s does not exist\n",
							      recursion_level,
							      "",
							      true_target->string,
							      dependency->name->string);
					} else {
						(void) printf("%*sBuilding %s because it is out of date relative to %s\n",
							      recursion_level,
							      "",
							      true_target->string,
							      dependency->name->string);
					}
				}
			}
			if (dependency->name == force) {
				force->stat.time =
				  (int) file_max_time;
				force->state = build_dont_know;
			}
		}
	}
#ifdef PARALLEL
	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;
		}
	}
#endif PARALLEL
	if ((true_target->is_member) && (dependency_changed == true)) {
		true_target->stat.time = (time_t) 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("Too many rules defined for target %s",
				target->string);
		}
		*command = line;
		/* Check if the target is out of date */
		if (OUT_OF_DATE(true_target->stat.time,
				line->body.line.dependency_time)){
			line->body.line.is_out_of_date = true;
		}
		line->body.line.sccs_command = false;
		line->body.line.target = true_target;
		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
 */
static void
dynamic_dependencies(target)
	Name			target;
{
	char			pattern[MAXPATHLEN];
	register char		*p;
	Property		line;
	register Dependency	dependency;
	register Dependency	*remove;
	String_rec		string;
	char			buffer[MAXPATHLEN];
	register Boolean	set_at = false;
	register char		*start;
	Dependency		new_depe;
	register Boolean	reuse_cell;
	Dependency		first_member;
	Name			directory;
	Name			lib;
	Name			member;
	Property		prop;
	Name			true_target = target;
	char			*library;

	target->has_depe_list_expanded = true;
	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;
		}
		/* 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 (isspace(*start)) {
			start++;
		}
		/* Remove the cell (later) if the macro was empty */
		if (start[0] == (int) nul_char) {
			dependency->name = NULL;
		}
		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 (isspace(*start)) {
					start++;
				}
				break;
			}
		}
		do {
		    /* First skip whitespace */
			for (p = start; *p != (int) nul_char; p++) {
				if ((*p == (int) nul_char) ||
				    isspace(*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 */
				dependency->name = GETNAME(start, p - start);
				if ((debug_level > 0) &&
				    (first_member == NULL)) {
					(void) printf("%*sDynamic dependency `%s' for target `%s'\n",
						      recursion_level,
						      "",
						      dependency->name->string,
						      true_target->string);
				}
				for (start = p; isspace(*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_string(lib->string,
					      &string,
					      (int) lib->hash.length);
				append_char((int) parenleft_char, &string);
				append_string(first_member->name->string,
					      &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("%*sDynamic dependency `%s' for target `%s'\n",
						      recursion_level,
						      "",
						      first_member->name->
						      string,
						      true_target->string);
				}
				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;
				}
			}
		}
	}
	/* 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) {
			if ((start = strchr(dependency->name->string,
					    (int) parenleft_char)) != NULL) {
				/* lib(*) type pattern */
				library = buffer;
				(void) strncpy(buffer,
					       dependency->name->string,
					       start-dependency->name->string);
				buffer[start-dependency->name->string] =
				  (int) nul_char;
				(void) strncpy(pattern,
					       start+1,
					       (int) (dependency->name->
						      hash.length -
						     (start-dependency->name->
						      string)-2));
				pattern[dependency->name->hash.length -
					(start-dependency->name->string) - 2] =
					  (int) nul_char;
			} else {
				library = NULL;
				(void) strncpy(pattern,
					       dependency->name->string,
					       (int) dependency->name->
					       hash.length);
				pattern[dependency->name->hash.length] =
				  (int) nul_char;
			}
			start = strrchr(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);
}

/*
 *	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(line)
	register Property	line;
{
	register Doname		result = build_ok;
	register Boolean	remember_only = false;
	register Name		target = line->body.line.target;
	char			string[MAXPATHLEN];

	/* 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) {
		build_command_strings(target, line);
	}
	/* Never mind */
	if (!line->body.line.is_out_of_date) {
		return build_ok;
	}
	/* If quest is on we just need to know that something is out of date */
	if (quest) {
		exit(1);
	}
	/* We actually had to do something this time */
	rewrite_statefile = commands_done = true;
/* If this is a sccs command we have do do some extra checking and */
/* possibly complain. If the file cannt be gotten because it is checked out */
/* we complain and then behave as if the command was executed even when we */
/* ignore the command */
	if (!touch &&
	    line->body.line.sccs_command &&
	    (target->stat.time != (int) file_doesnt_exist) &&
	    ((target->stat.mode & 0222) != 0)) {
		fatal("%s is writable so it cannot be sccs gotten",
		      target->string);
		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);
		(void) sprintf(string,
			       "%s/.make.dependency.%d.%d %s",
			       temp_file_directory,
			       getpid(),
			       file_number++,
			       target->string);
		(void) SETVAR(sunpro_dependencies,
			      GETNAME(string, FIND_LENGTH),
			      false);
		*strchr(string, (int) space_char) = (int) nul_char;
		temp_file_name = GETNAME(string, FIND_LENGTH);
		temp_file_name->stat.is_file = true;
	} 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 this is an sccs get command we need to ignore we skip all the */
	/* command execution stuff */
	if (remember_only) {
		/* Empty block!!! */
	} else if (touch) {
		result = touch_command(line, target, result);
	} else {
		/* If this is not a touch run we need to execute the */
		/* proper command for the target */
#ifdef PARALLEL
		if (parallel) {
			if (!parallel_ok(target)) {
				/*
				 * 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_serial(line);
				} else {
					current_target = NULL;
					current_line = NULL;
					line->body.line.command_used = NULL;
					return build_serial;
				}
			} else {
				result = execute_parallel(line);
				switch (result) {
				case build_running:
					return build_running;
				case build_serial:
					if (parallel_process_cnt == 0) {
						result = execute_serial(line);
					} else {
						current_target = NULL;
						current_line = NULL;
						target->parallel = false;
						line->body.line.command_used =
						  			NULL;
						return build_serial;
					}
				}
			}
		} else {
#endif PARALLEL
			result = execute_serial(line);
#ifdef PARALLEL
		}
#endif PARALLEL
	}
	temp_file_name = NULL;
	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(line)
	Property		line;
{
	Cmd_line		rule;
	Name			target = line->body.line.target;
	Doname			result = build_ok;

	target->has_recursive_dependency = false;
	for (rule = line->body.line.command_used;
	     rule != NULL;
	     rule = rule->next) {
		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)) {
			(void) printf("%s\n", rule->command_line->string);
		}
		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_only) {
				/* Execute command line */
				setvar_envvar();
				result = dosys(rule->command_line,
					       rule->ignore_error,
					       rule->make_refd,
					       BOOLEAN(rule->silent &&
						       rule->ignore_error),
					       target);
				check_state(result, temp_file_name);
			}
		} else {
			result = build_ok;
		}
		if (result == build_failed) {
			if (silent) {
				(void) printf("The following command caused the error:\n%s\n",
					      rule->command_line->string);
			}
			if (!rule->ignore_error) {
				if (!continue_after_error) {
					fatal("Command failed for target `%s'",
					      target->string);
				}
				/* Make sure a failing command is not saved */
				/* in .make.state */
				line->body.line.command_used = NULL;
				break;
			} else {
				result = build_ok;
			}
	        }
	}
	temp_file_name = 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(cmd)
	register Name		cmd;
{
	char			buffer[STRING_BUFFER_LENGTH];
	String_rec		new_cmd;
	char			*p;
	char			*start;

	if ((cmd == NULL) || (cmd->hash.length == 0)) {
		return cmd;
	}
	INIT_STRING_FROM_STACK(new_cmd, buffer);
	p = cmd->string;
	while (*p != (int) nul_char) {
		while (isspace(*p) && (*p != (int) nul_char)) {
			append_char(*p++, &new_cmd);
		}
		start = p;
		while (!isspace(*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_string(cmd->string,
				      &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(result, temp_file_name)
 *
 *	Reads and checks the state changed by the previously executed command.
 *
 *	Parameters:
 *		result		Result of the build
 *		temp_file_name	The auto dependency temp file
 *
 *	Global variables used:
 */
void
check_state(result, temp_file_name)
	Doname		result;
	Name		temp_file_name;
{
	if (result == build_ok) {
		/* 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);
	}
}

/*
 *	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(filename)
	register Name		filename;
{
	register Makefile_type	save_makefile_type;

	if (filename == NULL) {
		return;
	}
	filename->stat.time = (int) file_no_time;
	if (exists(filename) > (int) 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()
{
	register time_t		previous = make_state->stat.time;
	register Makefile_type	save_makefile_type;
	register Property	makefile;

	make_state->stat.time = (int) file_no_time;
	if ((exists(make_state) == (int) 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;
	}
	(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(line, target)
	register Name		line;
	register Name		target;
{
	register char		*equal;
	register char		*string = line->string;
	register Boolean	append = false;
	register Name		name;

	/* 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 (isspace(*string)) {
		string++;
	}
	equal = string;
	/* Find "+=" or "=" */
	while (!isspace(*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("= expected in rule `%s' for target `%s'",
		      line->string,
		      target->string);
	case plus_char:
		append = true;
		equal++;
		break;
	}
	equal++;
	/* Skip over whitespace in front of value */
	while (isspace(*equal)) {
		equal++;
	}
	/* Enter new macro value */
	enter_equal(name,
		    GETNAME(equal, line->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(target, line)
	Name			target;
	register Property	line;
{
	String_rec		command_line;
	register Cmd_line	template = line->body.line.command_template;
	register Cmd_line	*insert = &line->body.line.command_used;
	register Cmd_line	used = *insert;
	char			buffer[STRING_BUFFER_LENGTH];
	char			*start;
	Name			new;
	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;

	if (less_name == NULL) {
		less_name = GETNAME("<", FIND_LENGTH);
		percent_name = GETNAME("%", FIND_LENGTH);
		star = GETNAME("*", 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 */
	(void) SETVAR(c_at, target, false);
	(void) SETVAR(star, line->body.line.star, false);
	(void) SETVAR(less_name, line->body.line.less, false);
	(void) SETVAR(percent_name, line->body.line.percent, false);
	/* $? is seldom used and it is expensive to build */
	/* so we store the list form and build the string on demand */
	(void) setvar_daemon(query,
			     (Name) line->body.line.query,
			     false,
			     chain_daemon);

/* 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 (;
	     template != NULL;
	     template = 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;
		/* Expand the macros in the command line */
		INIT_STRING_FROM_STACK(command_line, buffer);
		make_word_mentioned =
		  query_mentioned =
		    false;
		expand_value(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 (;
		     isspace(*start) ||
		     (char_semantics[*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;
			}
		}
		/* 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 = GETNAME(start, FIND_LENGTH);
		/* Compare it with the old one for command consistency */
		if (used->command_line != new) {
			if (keep_state &&
			    !used->ignore_command_dependency) {
				if (debug_level > 0) {
					if (used->command_line != NULL
					    && *used->command_line->string !=
					    (int) nul_char) {
						(void) printf("%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n",
							      recursion_level,
							      "",
							      target->string,
							      new->string,
							      recursion_level,
							      "",
							      used->
							      command_line->
							      string);
					} else {
						(void) printf("%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n",
							      recursion_level,
							      "",
							      target->string,
							      new->string,
							      recursion_level,
							      "");
					}
				}
				command_changed =
				  line->body.line.is_out_of_date =
				    true;
			}
			used->command_line = new;
		}
		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("%*sBuilding %s because new command shorter than old\n",
					      recursion_level,
					      "",
					      target->string);
			}
			command_changed =
			  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("%*sBuilding %s because new command longer than old\n",
				      recursion_level,
				      "",
				      target->string);
		}
		command_changed = 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 (conditional_macro_used) {
		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(line, target, result)
	register Property	line;
	register Name		target;
	Doname			result;
{
	Name			name;
	register Chain		target_group;
	String_rec		touch_string;
	char			buffer[MAXPATHLEN];
	Name			touch_cmd;

	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);
			append_string("touch ", &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_string(touch_cmd->string,
				      &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);
			}
			/* Run the touch command, or simulate it */
			if (!do_not_exec_rule) {
				result = dosys(touch_cmd,
					       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(line, result)
	Property		line;
	Doname			result;
{
	Name			target = line->body.line.target;
	Chain			target_group;
	Property		line2;

	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[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 = (int) file_max_time;
		} else {
			/* If we really built the target we read the */
			/* new timestamp */
			target->stat.time = (int) file_no_time;
			(void) exists(target);
		}
		/* 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(target, command)
	register Name		target;
	register Property	*command;
{
	register time_t		sccs_time;
	register Property	line;

	/* 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;
	case no_sccs:
		/* If there is no SCCS/s.* but the plain file exists we say */
		/* thing are OK */
		if (target->stat.time > (int) 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 != (int) file_doesnt_exist) &&
	    (sccs_get_rule != NULL)) {
		/* 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("%*sSccs getting %s because s. file is younger than source file\n",
					      recursion_level,
					      "",
					      target->string);
			}
		}
		line->body.line.sccs_command = true;
		line->body.line.command_template = sccs_get_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(file)
	register Name		file;
{
	register Name		directory = dot;
	register char		*p = strrchr(file->string, (int) slash_char);
	register int		length = p - file->string;
static	Name			usr_include;
static	Name			usr_include_sys;

	if (usr_include == NULL) {
		usr_include = GETNAME("/usr/include", FIND_LENGTH);
		usr_include_sys = GETNAME("/usr/include/sys",
					       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_EQUALN(usr_include->string,
			      file->string,
			      length)) {
			directory = usr_include;
		} else
		  if ((usr_include_sys->hash.length == length) &&
		      IS_EQUALN(usr_include_sys->string,
				file->string,
				length)) {
			directory = usr_include_sys;
		} else {
			directory = GETNAME(file->string, length);
		}
	}
	(void) read_dir(directory,
			(char *) NULL,
			(Property) NULL,
			(char *) 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(target)
	register Name		target;
{
	register Property	conditional;
	Property		new_prop;
	Property		*previous;
	Name_rec		dummy;
	char			*pattern;
	char			*percent;
	int			length;

	length = strlen(target->string);
	for (conditional = get_prop(conditionals->prop, conditional_prop);
	     conditional != NULL;
	     conditional = get_prop(conditional->next, conditional_prop)) {
		pattern = conditional->body.conditional.target->string;
		if (pattern[1] != 0) {
			percent = strchr(pattern, (int) percent_char);
			if (!IS_EQUALN(target->string,
				       pattern,
				       percent-pattern) ||
			    !IS_EQUAL(target->string+length-strlen(percent+1),
				      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(target, old_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;

	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("%*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,
				      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(target, old_locals, conditional, index)
	register Name		target;
	register Property	old_locals;
	register Property	conditional;
	register int		index;
{
	register Property	this_conditional;
	Chain			cond_chain;

	/* 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("Internal error: reset target not at head of condtional_targets chain");
		} else {
			cond_chain = conditional_targets->next;
			retmem((char *) 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("%*sdeactivating conditional value: %s= %s\n",
				      recursion_level,
				      "",
				      conditional->body.conditional.name->
				      string,
				      old_locals[index].body.macro.value->
				      string);
		} else {
			(void) printf("%*sdeactivating conditional value: %s =\n",
				      recursion_level,
				      "",
				      conditional->body.conditional.name->
				      string);
		}
	}
}

/*
 *	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(target, auto_count, automatics)
	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) {
				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;
	}
}

