--- /dev/null	2015-05-20 11:04:11.000000000 -0400
+++ new/usr/src/cmd/make/bin/make/common/state.cc	2015-05-20 11:04:11.492237430 -0400
@@ -0,0 +1,463 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * @(#)state.cc 1.27 06/12/12
+ */
+
+#pragma	ident	"@(#)state.cc	1.27	06/12/12"
+
+/*
+ *	state.c
+ *
+ *	This file contains the routines that write the .make.state file
+ */
+
+/*
+ * Included files
+ */
+#include <mk/defs.h>
+#include <mksh/misc.h>		/* errmsg() */
+#include <setjmp.h>		/* setjmp() */
+#include <unistd.h>		/* getpid() */
+#include <errno.h>		/* errno    */
+#include <locale.h>		/* MB_CUR_MAX    */
+
+/*
+ * Defined macros
+ */
+#define LONGJUMP_VALUE 17
+#define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \
+					longjmp(long_jump, LONGJUMP_VALUE);}
+#define XPUTC(ch, fd) { \
+	if (putc((int) ch, fd) == EOF) \
+		longjmp(long_jump, LONGJUMP_VALUE); \
+	}
+#define XFPUTS(string, fd) fputs(string, fd)
+
+/*
+ * typedefs & structs
+ */
+
+/*
+ * Static variables
+ */
+
+/*
+ * File table of contents
+ */
+static char * escape_target_name(Name np)
+{
+	if(np->dollar) {
+		int len = strlen(np->string_mb);
+		char * buff = (char*)malloc(2 * len);
+		int pos = 0;
+		wchar_t wc;
+		int pp = 0;
+		while(pos < len) {
+			int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX);
+			if(n < 0) { // error - this shouldn't happen
+				(void)free(buff);
+				return strdup(np->string_mb);
+			}
+			if(wc == dollar_char) {
+				buff[pp] = '\\'; pp++;
+				buff[pp] = '$'; pp++;
+			} else {
+				for(int j=0;j<n;j++) {
+					buff[pp] = np->string_mb[pos+j]; pp++;
+				}
+			}
+			pos += n;
+		}
+		buff[pp] = '\0';
+		return buff;
+	} else {
+		return strdup(np->string_mb);
+	}
+}
+
+static	void		print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump);
+
+/*
+ *	write_state_file(report_recursive, exiting)
+ *
+ *	Write a new version of .make.state
+ *
+ *	Parameters:
+ *		report_recursive	Should only be done at end of run
+ *		exiting			true if called from the exit handler
+ *
+ *	Global variables used:
+ *		built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
+ *		command_changed	If no command changed we do not need to write
+ *		current_make_version The Name "<current version>", written
+ *		do_not_exec_rule If -n is on we do not write statefile
+ *		hashtab		The hashtable that contains all names
+ *		keep_state	If .KEEP_STATE is no on we do not write file
+ *		make_state	The Name ".make.state", used for opening file
+ *		make_version	The Name ".MAKE_VERSION", written
+ *		recursive_name	The Name ".RECURSIVE", written
+ *		rewrite_statefile Indicates that something changed
+ */
+
+void
+#ifdef NSE
+write_state_file(int report_recursive, Boolean exiting)
+#else
+write_state_file(int, Boolean exiting)
+#endif
+{
+	register FILE		*fd;
+	int			lock_err;
+	char			buffer[MAXPATHLEN];
+	char			make_state_tempfile[MAXPATHLEN];
+	jmp_buf			long_jump;
+	register int		attempts = 0;
+	Name_set::iterator	np, e;
+	register Property	lines;
+	register int		m;
+	Dependency		dependency;
+	register Boolean	name_printed;
+	Boolean			built_this_run = false;
+	char			*target_name;
+	int			line_length;
+	register Cmd_line	cp;
+
+
+	if (!rewrite_statefile ||
+	    !command_changed ||
+	    !keep_state ||
+	    do_not_exec_rule ||
+	    (report_dependencies_level > 0)) {
+		return;
+	}
+	/* Lock the file for writing. */
+ 	make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(NOCATGETS(".lock")) + 1);
+ 	(void) sprintf(make_state_lockfile,
+ 	               NOCATGETS("%s.lock"),
+ 	               make_state->string_mb);
+	if (lock_err = file_lock(make_state->string_mb, 
+				 make_state_lockfile, 
+				 (int *) &make_state_locked, 0)) {
+ 		retmem_mb(make_state_lockfile);
+		make_state_lockfile = NULL;
+		
+		/*
+		 * We need to make sure that we are not being
+		 * called by the exit handler so we don't call
+		 * it again.
+		 */
+		
+		if (exiting) {
+			(void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid());
+			report_pwd = true;
+			warning(catgets(catd, 1, 60, "Writing to %s"), buffer);
+			int fdes = mkstemp(buffer);
+			if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
+				fprintf(stderr,
+					catgets(catd, 1, 61, "Could not open statefile `%s': %s"),
+					buffer,
+					errmsg(errno));
+				return;
+			}
+		} else {
+			report_pwd = true;
+			fatal(catgets(catd, 1, 62, "Can't lock .make.state"));
+		}
+	}
+
+	(void) sprintf(make_state_tempfile,
+	               NOCATGETS("%s.tmp"),
+	               make_state->string_mb);
+	/* Delete old temporary statefile (in case it exists) */
+	(void) unlink(make_state_tempfile);
+	if ((fd = fopen(make_state_tempfile, "w")) == NULL) {
+		lock_err = errno; /* Save it! unlink() can change errno */
+		(void) unlink(make_state_lockfile);
+ 		retmem_mb(make_state_lockfile);
+		make_state_lockfile = NULL;
+		make_state_locked = false;
+		fatal(catgets(catd, 1, 59, "Could not open temporary statefile `%s': %s"),
+		      make_state_tempfile,
+		      errmsg(lock_err));
+	}
+#ifdef NSE
+	if (nse) {
+		(void) fchmod(fileno(fd), 0666); 
+	}
+#endif
+	/*
+	 * Set a trap for failed writes. If a write fails, the routine
+	 * will try saving the .make.state file under another name in /tmp.
+	 */
+	if (setjmp(long_jump)) {
+		(void) fclose(fd);
+		if (attempts++ > 5) {
+			if ((make_state_lockfile != NULL) &&
+			    make_state_locked) {
+				(void) unlink(make_state_lockfile);
+ 				retmem_mb(make_state_lockfile);
+				make_state_lockfile = NULL;
+				make_state_locked = false;
+			}
+			fatal(catgets(catd, 1, 63, "Giving up on writing statefile"));
+		}
+		sleep(10);
+		(void) sprintf(buffer, NOCATGETS("%s/.make.state.%d.XXXXXX"), tmpdir, getpid());
+		int fdes = mkstemp(buffer);
+		if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) {
+			fatal(catgets(catd, 1, 64, "Could not open statefile `%s': %s"),
+			      buffer,
+			      errmsg(errno));
+		}
+		warning(catgets(catd, 1, 65, "Initial write of statefile failed. Trying again on %s"),
+			buffer);
+	}
+
+	/* Write the version stamp. */
+	XFWRITE(make_version->string_mb,
+		strlen(make_version->string_mb),
+		fd);
+	XPUTC(colon_char, fd);
+	XPUTC(tab_char, fd);
+	XFWRITE(current_make_version->string_mb,
+		strlen(current_make_version->string_mb),
+		fd);
+	XPUTC(newline_char, fd);
+
+	/*
+	 * Go through all the targets, dump their dependencies and
+	 * command used.
+	 */
+	for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
+		/*
+		 * If the target has no command used nor dependencies,
+		 * we can go to the next one.
+		 */
+		if ((lines = get_prop(np->prop, line_prop)) == NULL) {
+			continue;
+		}
+		/* If this target is a special target, don't print. */
+		if (np->special_reader != no_special) {
+			continue;
+		}
+		/*
+		 * Find out if any of the targets dependencies should
+		 * be written to .make.state.
+		 */
+		for (m = 0, dependency = lines->body.line.dependencies;
+		     dependency != NULL;
+		     dependency = dependency->next) {
+			if (m = !dependency->stale
+			    && (dependency->name != force)
+#ifndef PRINT_EXPLICIT_DEPEN
+			    && dependency->automatic
+#endif
+			    ) {
+				break;
+			}
+		}
+		/* Only print if dependencies listed. */
+		if (m || (lines->body.line.command_used != NULL)) {
+			name_printed = false;
+			/*
+			 * If this target was built during this make run,
+			 * we mark it.
+			 */
+			built_this_run = false;
+			if (np->has_built) {
+				built_this_run = true;
+				XFWRITE(built_last_make_run->string_mb,
+					strlen(built_last_make_run->string_mb),
+					fd);
+				XPUTC(colon_char, fd);
+				XPUTC(newline_char, fd);
+			}
+			/* If the target has dependencies, we dump them. */
+			target_name = escape_target_name(np);
+			if (np->has_long_member_name) {
+				target_name =
+				  get_prop(np->prop, long_member_name_prop)
+				    ->body.long_member_name.member_name->
+				      string_mb;
+			}
+			if (m) {
+				XFPUTS(target_name, fd);
+				XPUTC(colon_char, fd);
+				XFPUTS("\t", fd);
+				name_printed = true;
+				line_length = 0;
+				for (dependency =
+				     lines->body.line.dependencies;
+				     dependency != NULL;
+				     dependency = dependency->next) {
+					print_auto_depes(dependency,
+							 fd,
+							 built_this_run,
+							 &line_length,
+							 target_name,
+							 long_jump);
+				}
+				XFPUTS("\n", fd);
+			}
+			/* If there is a command used, we dump it. */
+			if (lines->body.line.command_used != NULL) {
+				/*
+				 * Only write the target name if it
+				 * wasn't done for the dependencies.
+				 */
+				if (!name_printed) {
+					XFPUTS(target_name, fd);
+					XPUTC(colon_char, fd);
+					XPUTC(newline_char, fd);
+				}
+				/*
+				 * Write the command lines.
+				 * Prefix each textual line with a tab.
+				 */
+				for (cp = lines->body.line.command_used;
+				     cp != NULL;
+				     cp = cp->next) {
+					char		*csp;
+					int		n;
+
+					XPUTC(tab_char, fd);
+					if (cp->command_line != NULL) {
+						for (csp = cp->
+						           command_line->
+						           string_mb,
+						     n = strlen(cp->
+						                command_line->
+						                string_mb);
+						     n > 0;
+						     n--, csp++) {
+							XPUTC(*csp, fd);
+							if (*csp ==
+							    (int) newline_char) {
+								XPUTC(tab_char,
+								      fd);
+							}
+						}
+					}
+					XPUTC(newline_char, fd);
+				}
+			}
+			(void)free(target_name);
+		}
+	}
+	if (fclose(fd) == EOF) {
+		longjmp(long_jump, LONGJUMP_VALUE);
+	}
+	if (attempts == 0) {
+		if (unlink(make_state->string_mb) != 0 && errno != ENOENT) {
+			lock_err = errno; /* Save it! unlink() can change errno */
+			/* Delete temporary statefile */
+			(void) unlink(make_state_tempfile);
+			(void) unlink(make_state_lockfile);
+	 		retmem_mb(make_state_lockfile);
+			make_state_lockfile = NULL;
+			make_state_locked = false;
+			fatal(catgets(catd, 1, 356, "Could not delete old statefile `%s': %s"),
+			      make_state->string_mb,
+			      errmsg(lock_err));
+		}
+		if (rename(make_state_tempfile, make_state->string_mb) != 0) {
+			lock_err = errno; /* Save it! unlink() can change errno */
+			/* Delete temporary statefile */
+			(void) unlink(make_state_tempfile);
+			(void) unlink(make_state_lockfile);
+	 		retmem_mb(make_state_lockfile);
+			make_state_lockfile = NULL;
+			make_state_locked = false;
+			fatal(catgets(catd, 1, 357, "Could not rename `%s' to `%s': %s"),
+			      make_state_tempfile,
+			      make_state->string_mb,
+			      errmsg(lock_err));
+		}
+	}
+	if ((make_state_lockfile != NULL) && make_state_locked) {
+		(void) unlink(make_state_lockfile);
+ 		retmem_mb(make_state_lockfile);
+		make_state_lockfile = NULL;
+		make_state_locked = false;
+	}
+#ifdef NSE
+	if (report_recursive) {
+		report_recursive_done();
+	}
+#endif
+}
+
+/*
+ *	print_auto_depes(dependency, fd, built_this_run,
+ *			 line_length, target_name, long_jump)
+ *
+ *	Will print a dependency list for automatic entries.
+ *
+ *	Parameters:
+ *		dependency	The dependency to print
+ *		fd		The file to print it to
+ *		built_this_run	If on we prefix each line with .BUILT_THIS...
+ *		line_length	Pointer to line length var that we update
+ *		target_name	We need this when we restart line
+ *		long_jump	setjmp/longjmp buffer used for IO error action
+ *
+ *	Global variables used:
+ *		built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written
+ *		force		The Name " FORCE", compared against
+ */
+static void
+print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump)
+{
+	if (!dependency->automatic ||
+	    dependency->stale ||
+	    (dependency->name == force)) {
+		return;
+	}
+	XFWRITE(dependency->name->string_mb, 
+		strlen(dependency->name->string_mb),
+		fd);
+	/*
+	 * Check if the dependency line is too long.
+	 * If so, break it and start a new one.
+	 */
+	if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) {
+		*line_length = 0;
+		XPUTC(newline_char, fd);
+		if (built_this_run) {
+			XFPUTS(built_last_make_run->string_mb, fd);
+			XPUTC(colon_char, fd);
+			XPUTC(newline_char, fd);
+		}
+		XFPUTS(target_name, fd);
+		XPUTC(colon_char, fd);
+		XPUTC(tab_char, fd);
+	} else {
+		XFPUTS(" ", fd);
+	}
+	return;
+}
+
+
