/*
 * 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 2003 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Included files
 */
#include <mk/defs.h>
#include <mksh/misc.h>		/* get_prop() */

/*
 * File table of contents
 */
void   print_dependencies(register Name target, register Property line);
static void	print_deps(register Name target, register Property line);
static void	print_more_deps(Name target, Name name);
static void	print_filename(Name name);
static Boolean	should_print_dep(Property line);
static void	print_forest(Name target);
static void	print_deplist(Dependency head);
void		print_value(register Name value, Daemon daemon);
static void	print_rule(register Name target);
static	void	print_rec_info(Name target);
static Boolean	is_out_of_date(Property line);
extern void depvar_print_results (void);

/*
 *	print_dependencies(target, line)
 *
 *	Print all the dependencies of a target. First print all the Makefiles.
 *	Then print all the dependencies. Finally, print all the .INIT
 *	dependencies.
 *
 *	Parameters:
 *		target		The target we print dependencies for
 *		line		We get the dependency list from here
 *
 *	Global variables used:
 *		done		The Name ".DONE"
 *		init		The Name ".INIT"
 *		makefiles_used	List of all makefiles read
 */
void
print_dependencies(register Name target, register Property line)
{
	Dependency	dp;
	static Boolean	makefiles_printed = false;

	if (target_variants) {
		depvar_print_results();
	}

	if (!makefiles_printed) {
		/*
		 * Search the makefile list for the primary makefile,
		 * then print it and its inclusions.  After that go back
		 * and print the default.mk file and its inclusions.
		 */
		for (dp = makefiles_used; dp != NULL; dp = dp->next) {
			if (dp->name == primary_makefile) {
				break;
			}
		}
		if (dp) {
			print_deplist(dp);
			for (dp = makefiles_used; dp != NULL; dp = dp->next) {
				if (dp->name == primary_makefile) {
					break;
				}
				(void)printf(" %s", dp->name->string_mb);
			}
		}
		(void) printf("\n");
		makefiles_printed = true;
	}
	print_deps(target, line);
/*
	print_more_deps(target, init);
	print_more_deps(target, done);
 */
	if (target_variants) {
		print_forest(target);
	}
}

/*
 *	print_more_deps(target, name)
 *
 *	Print some special dependencies.
 *	These are the dependencies for the .INIT and .DONE targets.
 *
 *	Parameters:
 *		target		Target built during make run
 *		name		Special target to print dependencies for
 *
 *	Global variables used:
 */
static void
print_more_deps(Name target, Name name)
{
	Property	line;
	register Dependency	dependencies;

	line = get_prop(name->prop, line_prop);
	if (line != NULL && line->body.line.dependencies != NULL) {
		(void) printf("%s:\t", target->string_mb);
		print_deplist(line->body.line.dependencies);
		(void) printf("\n");
		for (dependencies= line->body.line.dependencies;
		     dependencies != NULL;
		     dependencies= dependencies->next) {
	                 print_deps(dependencies->name,
				 get_prop(dependencies->name->prop, line_prop));
		}
	}
}

/*
 *	print_deps(target, line, go_recursive)
 *
 *	Print a regular dependency list.  Append to this information which
 *	indicates whether or not the target is recursive.
 *
 *	Parameters:
 *		target		target to print dependencies for
 *		line		We get the dependency list from here
 *		go_recursive	Should we show all dependencies recursively?
 *
 *	Global variables used:
 *		recursive_name	The Name ".RECURSIVE", printed
 */
static void
print_deps(register Name target, register Property line)
{
	register Dependency	dep;

	if ((target->dependency_printed) ||
	    (target == force)) {
		return;
	}
	target->dependency_printed = true;

	/* only print entries that are actually derived and are not leaf
	 * files and are not the result of sccs get.
	 */
	if (should_print_dep(line)) {
		if ((report_dependencies_level == 2) ||
		    (report_dependencies_level == 4)) {
			if (is_out_of_date(line)) {
			        (void) printf("1 ");
			} else {
			        (void) printf("0 ");
			}
		}
		print_filename(target);
		(void) printf(":\t");
		print_deplist(line->body.line.dependencies);
		print_rec_info(target);
		(void) printf("\n");
		for (dep = line->body.line.dependencies;
		     dep != NULL;
		     dep = dep->next) {
			print_deps(dep->name,
			           get_prop(dep->name->prop, line_prop));
		}
	}
}

static Boolean
is_out_of_date(Property line)
{
	Dependency	dep;
	Property	line2;

	if (line == NULL) {
		return false;
	}
	if (line->body.line.is_out_of_date) {
		return true;
	}
	for (dep = line->body.line.dependencies;
	     dep != NULL;
	     dep = dep->next) {
		line2 = get_prop(dep->name->prop, line_prop);
		if (is_out_of_date(line2)) {
			line->body.line.is_out_of_date = true;
			return true;
		}
	}
	return false;
}

/*
 * Given a dependency print it and all its siblings.
 */
static void
print_deplist(Dependency head)
{
	Dependency	dp;

	for (dp = head; dp != NULL; dp = dp->next) {
		if ((report_dependencies_level != 2) ||
		    ((!dp->automatic) ||
		     (dp->name->is_double_colon))) {
			if (dp->name != force) {
				putwchar(' ');
				print_filename(dp->name);
			}
		}
	}
}

/*
 * Print the name of a file for the -P option.
 * If the file is a directory put on a trailing slash.
 */
static void
print_filename(Name name)
{
	(void) printf("%s", name->string_mb);
/*
	if (name->stat.is_dir) {
		putwchar('/');
	}
 */
}

/*
 *	should_print_dep(line)
 *
 *	Test if we should print the dependencies of this target.
 *	The line must exist and either have children dependencies
 *	or have a command that is not an SCCS command.
 *
 *	Return value:
 *				true if the dependencies should be printed
 *
 *	Parameters:
 *		line		We get the dependency list from here
 *
 *	Global variables used:
 */
static Boolean
should_print_dep(Property line)
{
	if (line == NULL) {
		return false;
	}
	if (line->body.line.dependencies != NULL) {
		return true;
	}
	if (line->body.line.sccs_command) {
		return false;
	}
	return true;
}

/*
 * Print out the root nodes of all the dependency trees
 * in this makefile.
 */
static void
print_forest(Name target)
{
	Name_set::iterator np, e;
	Property	line;

	for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
			if (np->is_target && !np->has_parent && np != target) {
				(void) doname_check(np, true, false, false);
				line = get_prop(np->prop, line_prop);
				printf("-\n");
				print_deps(np, line);
			}
	}
}


/*
 *	This is a set  of routines for dumping the internal make state
 *	Used for the -p option
 */
void
print_value(register Name value, Daemon daemon)
{
	Chain			cp;

	if (value == NULL)
		(void)printf("=\n");
	else
		switch (daemon) {
		    case no_daemon:
			(void)printf("= %s\n", value->string_mb);
			break;
		    case chain_daemon:
			for (cp= (Chain) value; cp != NULL; cp= cp->next)
				(void)printf(cp->next == NULL ? "%s" : "%s ",
					cp->name->string_mb);
			(void)printf("\n");
			break;
		};
}

static void
print_rule(register Name target)
{
	register Cmd_line	rule;
	register Property	line;

	if (((line= get_prop(target->prop, line_prop)) == NULL) ||
	    ((line->body.line.command_template == NULL) &&
	     (line->body.line.dependencies == NULL)))
		return;
	print_dependencies(target, line);
	for (rule= line->body.line.command_template; rule != NULL; rule= rule->next)
		(void)printf("\t%s\n", rule->command_line->string_mb);
}


/*
 *  If target is recursive,  print the following to standard out:
 *	.RECURSIVE subdir targ Makefile
 */
static void
print_rec_info(Name target)
{
	Recursive_make	rp;
	wchar_t		*colon;

	report_recursive_init();

	rp = find_recursive_target(target);

	if (rp) {
		/*
		 * if found,  print starting with the space after the ':'
		 */
		colon = (wchar_t *) wcschr(rp->oldline, (int) colon_char);
		(void) printf("%s", colon + 1);
	}
}