/*
   Find all the kernel source and target files and build the database of all
   the permanent kernel files.  It also generates files that are constructed
   from .prepend, base and .append files.
 */

/* Copyright, compile requirements.

   (c) Keith Owens <kaos@ocs.com.au> February 2002.
   Released under GPL version 2, (you know the rest).
 */

/* Run requirements.

   The environment must contain the correct variables, see pp_getenv.c.
 */

/* Processing.

   Walk all the KBUILD_SRCTREE_nnn source trees.

   Kernel source pathnames are converted to be relative to the top level
   directory of a source tree.

   Some special file suffices are recognised.  '*.append' and '*.prepend' files
   in any shadow source tree are logically appended to the corresponding file in
   the lower source trees.  If more than one append file exists for a target
   file then the data from the highest source tree is appended last.  If more
   than one prepend file exists for a target file then the data from the highest
   source tree is prepended first.

   Append and prepend files are not allowed in the base or object directories,
   they can only appear in the shadow trees.  Allowing append/prepend in the
   base directory significantly complicates the makefile processing when source
   and object are the same tree and allowing these files in the base provides no
   advantages.

   It is legal to mix append/prepend suffixes with the complete replacement of
   the same file.  Data will only be added if append/prepend appears in a higher
   tree than the one that supplies the complete file replacement.  In other
   words, when a source tree completely replaces a file then kbuild will ignore
   any append/prepend for that file in the same tree and in all lower trees.

   With separate source and object trees, none of the source trees can contain
   include/asm.  A separate source tree can be used to compile for more than one
   target architecture at the same time so the asm symlink must be held in the
   object directory.

   Outputs are all in the object tree.

     .tmp_db_main.  The database used by all pre-processor code.

     .tmp_filelist_new.  A list of the files in the database.  Fields printed:
       filename, relative to root of the kernel tree.
       absolute filename.
       entry type.
       primary flag.
       index.
       mtime.
       shadow_source flag.

     Files suffixed with .prepend or .append are copied from the source trees to
     the current directory under the same relative name, with the relevant
     contents prepended or appended.
 */

/* Parameters.

   --norun		Do not unlink or write anything.
   --verbose		Display what pp_makefile1 is doing.
 */

#include "pp_makefile.h"
#include "pp_db.h"

#include <dirent.h>
#include PP_GETOPT_H
#include <limits.h>
#include <signal.h>

const char *program = "pp_makefile1";

int errors;
int norun;
int verbose;
int signalled;

static const DB_TOKEN db_null_token = DB_TOKEN_NULL;

static inline int
prepend_name(const char *name, int l)
{
	return ((l > 8 && strcmp(name + l - 8, ".prepend") == 0));
}

static inline int
append_name(const char *name, int l)
{
	return ((l > 7 && strcmp(name + l - 7, ".append") == 0));
}

/* Data passed to my_ftw routines */

struct ftw_data {
	PPDB *db;
	int index;
	const char *treename;
};

/* Some versions of glibc nftw() are buggy and do not cope with a start
 * directory of ".".  Even if it is not buggy, nftw() is slow because it uses
 * path names that are relative to the start directory, the kernel takes longer
 * to stat() each file name when it has multiple components.  my_ftw() replaces
 * nftw and uses a similar algorithm to find so it runs much faster.  my_ftw()
 * also supports a prune return code.
 */

static int
my_ftw2(const char *rel_dir,
	int (*funcptr) (const char *file, const struct stat * statbuf,
			const struct stat * symlink_statbuf, void *userdata),
	void *userdata, char *name, int lname, int name_size,
	struct stat *statbuf, struct stat *symlink_statbuf,
	const char *treename)
{
	DIR *d;
	struct dirent *ent;
	int ret = 0, ldir = strlen(rel_dir), lent, topdir = lname == 0;

	if (lname + ldir + 2 > name_size) {
		fprintf(stderr, "name too long: %s%s%s\n", treename, name,
			rel_dir);
		return (-ENAMETOOLONG);
	}
	memcpy(name + lname, rel_dir, ldir);
	if (ldir) {
		memcpy(name + lname + ldir, "/", 2);
		lname += ldir + 1;
	}

	/* Special processing for top level directory */
	if (topdir
	    && (ret = (*funcptr) (name, statbuf, symlink_statbuf, userdata))) {
		if (ret == 1) {
			ret = 0;	/* prune the lot */
		}
		return (ret);
	}

	if ((ret = chdir(rel_dir))) {
		fprintf(stderr, "cannot chdir(%s%s): %m\n", treename, name);
		return (ret);
	}

	if (!(d = opendir("."))) {
		ret = errno;
		fprintf(stderr, "cannot opendir(%s%s): %m\n", treename, name);
		return (ret);
	}

	while ((ent = readdir(d))) {
		if (strcmp(ent->d_name, ".") == 0 ||
		    strcmp(ent->d_name, "..") == 0) {
			continue;
		}
		lent = strlen(ent->d_name);
		if (lname + lent + 1 >= name_size) {
			fprintf(stderr, "name too long: %s%s%s\n", treename,
				name, ent->d_name);
			return (-ENAMETOOLONG);
		}
		memcpy(name + lname, ent->d_name, lent);
		name[lname + lent] = '\0';
		if ((ret = lstat(ent->d_name, statbuf))) {
			if (errno == ENOENT) {
				ret = 0;
				continue;	/* deleted under us */
			}
			fprintf(stderr, "lstat(%s%s) failed: %m\n", treename,
				name);
			break;
		}
		if (S_ISLNK(statbuf->st_mode)) {
			if (stat(ent->d_name, symlink_statbuf)) {
				fprintf(stderr,
					"stat on symbolic link %s%s failed, %m\n",
					treename, name);
				return (1);
			}
			if (S_ISDIR(symlink_statbuf->st_mode)) {
				fprintf(stderr,
					"Ignoring symbolic link %s%s, it points to a directory\n",
					treename, name);
				/* Feature or bug in kbuild?, well, it causes
				 * a bug for us, so change return(1) to
				 * continue, just skips over symlinks to
				 * directories, totally legal behavior
				 * SRN
				 */
				
				/* return (1); */
				continue;
			}
		}
		/* User functions expect trailing '/' for directories */
		if (S_ISDIR(statbuf->st_mode)) {
			if (lname + lent + 2 >= name_size) {
				fprintf(stderr, "name too long: %s%s\n",
					treename, name);
				return (-ENAMETOOLONG);
			}
			memcpy(name + lname + lent, "/", 2);
		}
		ret = (*funcptr) (name, statbuf, symlink_statbuf, userdata);
		if (ret == 0) {
			if (S_ISDIR(statbuf->st_mode)) {
				ret =
				    my_ftw2(ent->d_name, funcptr, userdata,
					    name, lname, name_size, statbuf,
					    symlink_statbuf, treename);
			}
		} else if (ret == 1) {
			ret = 0;	/* pruned */
		} else {
			break;
		}
	}
	closedir(d);
	name[lname] = '\0';
	if (ret == 0) {
		if ((ret = chdir(".."))) {
			fprintf(stderr, "cannot chdir(%s%s..): %m\n", treename,
				name);
			return (ret);
		}
	}
	return (ret);
}

static int
my_ftw(const char *rel_dir,
       int (*funcptr) (const char *file, const struct stat * statbuf,
		       const struct stat * symlink_statbuf, void *userdata),
       const char *treename, void *userdata)
{
	int ret, cwd;
	struct stat statbuf, symlink_statbuf;
	char name[PATH_MAX + 1];
	cwd = open(".", O_RDONLY);
	if (cwd < 0) {
		perror("cannot open current directory");
		return (errno);
	}
	if ((ret = lstat(rel_dir, &statbuf))) {
		fprintf(stderr, "lstat(%s%s) failed: %m\n", treename, rel_dir);
	} else
	    if ((ret =
		 my_ftw2(rel_dir, funcptr, userdata, name, 0, sizeof (name),
			 &statbuf, &symlink_statbuf, treename))) {
		if (ret == 1) {
			ret = 0;	/* pruned */
		}
	}
	fchdir(cwd);
	close(cwd);
	return (ret);
}

/* Add directory names to the database.  Update the parent even for existing
 * entries, walk_fs_db clears and rebuilds the parent/children links.
 */

static DB_REC_DIRECTORY *
walk_fs_add_db_dir(PPDB * db, const char *relname)
{
	DB_REC_DIRECTORY *d;
	DB_TOKEN token;
	VERBOSE(4, "    walk_fs_add_db_dir %s\n", relname + 1);
	token = findnew_db_directory(db, relname);
	d = read_db_directory(db, token);
	d->flags.explicit = 1;
	return (d);
}

/* Add a file to the database.  Anything in the object tree is both a target and
 * a source file, generated files (targets) can be read (sources).  Anything in
 * a source tree is only a source file.  Update the parent even for existing
 * entries, walk_fs_db clears and rebuilds the parent/children links.
 *
 * If the filename contains /include/asm-$(ARCH)/ then create another entry with
 * /asm-$(ARCH)/ replaced with /asm/.
 */

static int
walk_fs_add_db_file(PPDB * db, const char *relname, const struct stat *statbuf,
		    int index, int symlink)
{
	DB_REC_SOURCE *s;
	DB_TOKEN token;
	int i;
	int l = strlen(relname);
	char name2[l + 1];
	memcpy(name2, relname, l + 1);
	VERBOSE(5, "    walk_fs_add_db_file %s\n", relname + 1);
	for (i = 0; i < 2; ++i) {
		if (i == 1) {
			char *p = strstr(name2, "/include/asm-");
			if (p && strncmp(p + 13, arch, arch_len) == 0
			    && p[13 + arch_len] == '/') {
				strcpy(p + 12, p + 13 + arch_len);
			} else {
				break;
			}
		}
		if (common_src_obj || index == objtree_treename_index) {
			DB_REC_TARGET *t;
			token = findnew_db_target(db, name2);
			t = read_db_target(db, token);
			t->mtime = statbuf->st_mtime;
			t->flags.symlink = symlink;
		}
		token = findnew_db_source(db, name2, index);
		s = read_db_source(db, token);
		s->index = index;
		s->mtime = statbuf->st_mtime;
		if (!common_src_obj) {
			if (index != objtree_treename_index
			    && !prepend_name(relname, l)
			    && !append_name(relname, l)) {
				s->flags.shadow_source = 1;
			}
			if (i == 1) {
				s->flags.shadow_source = 0;
			}
		}
	}
	return (0);
}

/* Verify that a component of a generated file is valid and add it to the
 * database.
 */

static int
walk_fs_add_db_component(PPDB * db, const char *relname,
			 const struct stat *statbuf, int index)
{
	int l = strlen(relname);
	char basename[l + 1];
	VERBOSE(3, "    walk_fs_add_db_component %s\n", relname + 1);
	if (index == 0) {
		fprintf(stderr,
			"prepend/append files are not supported in the base tree, ignoring %s\n",
			relname);
		return (1);
	}
	if (index == objtree_treename_index) {
		fprintf(stderr,
			"prepend/append files are not supported in the object tree, ignoring %s\n",
			relname);
		return (1);
	}
	if (!S_ISREG(statbuf->st_mode)) {
		fprintf(stderr,
			"prepend/append is only supported on regular files, ignoring %s\n",
			relname);
		return (1);
	}
	memcpy(basename, relname, l + 1);
	if (prepend_name(basename, l)) {
		basename[l -= 8] = '\0';
	} else if (append_name(basename, l)) {
		basename[l -= 7] = '\0';
	}
	if (prepend_name(basename, l) || append_name(basename, l)) {
		fprintf(stderr,
			"nested prepend/append is not allowed, ignoring %s\n",
			relname);
		return (1);
	}
	return (walk_fs_add_db_file(db, relname, statbuf, index, 0));
}

static int
walk_fs_db(const char *filename, const struct stat *statbuf,
	   const struct stat *symlink_statbuf, void *userdata)
{
	const char *relname = filename + 1;	/* skip leading '.' */
	int relname_len = strlen(relname);
	struct ftw_data *ftw_data = userdata;
	PPDB *db = ftw_data->db;
	int index = ftw_data->index;
	const char *treename = ftw_data->treename;
	int symlink = 0;

	/* Ignore top level directory "." */
	if (!*relname) {
		return (0);
	}

	if (signalled) {
		return (1);	/* Prune everything after a signal */
	}

	/* Ignore .tmp files.  They must all be explicitly handled by by the
	 * makefiles.  This ignores all files and directories where any qualifier
	 * starts with '.tmp', not just the first or last qualifier.
	 */
	if (strstr(relname, "/.tmp")) {
		return (1);
	}

	/* If source is separate from object then none of the source directories can
	 * contain "/include/asm/".  All asm symlinks must be done in the object tree,
	 * there is no guarantee that an asm link in a source tree is for the current
	 * architecture.
	 */
	if ((relname_len == 12 && strcmp(relname, "/include/asm") == 0) ||
	    (relname_len == 13 && strcmp(relname, "/include/asm/") == 0)) {
		if (!common_src_obj) {
			fprintf(stderr,
				"Error: source tree %s contains include/asm\n",
				treename);
			++errors;
		}
		return (1);
	}

	/* Symlinks are tricky.  A symlink for a read only source file is always
	 * valid.  A symlink for a target is valid as long as that target is not
	 * shipped under the same name (issrcfile kludge).  Symlinks for
	 * directories are not allowed, the existing kbuild does not support them.
	 * Also directory symlinks are ambiguous with separate source and object
	 * trees.
	 *
	 * issrcfile processing in pp_makefile4 will unlink shipped targets that
	 * are referenced by a symlink.
	 */

	if (S_ISLNK(statbuf->st_mode)) {
		symlink = 1;
		statbuf = symlink_statbuf;
	}

	if (S_ISREG(statbuf->st_mode) || S_ISDIR(statbuf->st_mode)) {
		if (prepend_name(relname, relname_len)
		    || append_name(relname, relname_len)) {
			return (walk_fs_add_db_component
				(db, relname, statbuf, index));
		} else if (S_ISDIR(statbuf->st_mode)) {
			walk_fs_add_db_dir(db, relname);
			return (0);
		} else {
			walk_fs_add_db_file(db, relname, statbuf, index,
					    symlink);
			return (0);
		}
	}

	fprintf(stderr, "Unexpected type for kernel file %s, giving up\n",
		filename);
	exit(1);
}

/*============================================================================*/

/* Clear old data from the database.  mtime is cleared on source records.
 * Parent information is cleared on files and directories to mark them as
 * disconnected.  The children list on directories is cleared to mark them as
 * disconnected.  The walk process will set mtime and reconnect those entries
 * that still exist.
 *
 * The CONFIG_SETTINGS directory is a special case, we always preserve its
 * entries, even if they do not exist.  Config settings are created for all
 * config references during the build phase, but not all split files exist.
 * Preserving CONFIG_SETTINGS retains the references from the previous build.
 */

static int
clear_old_data_run(PPDB * db, kvpair kv, void *userdata)
{
	DB_TOKEN token;
	const char *relname;
	static const char config[] = CONFIG_SETTINGS;
	memcpy(&token, (void *) (kv.key.dptr), sizeof (token));
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = (DB_REC_SOURCE *) (kv.val.dptr);
		relname = read_db_text(db, s->relname);
		if (memcmp(relname, config, sizeof (config) - 1) == 0) {
			return (0);
		}
		s->mtime = 0;
		s->parent = db_null_token;
	}
	if (token.type == DB_RECTYPE_TARGET) {
		DB_REC_TARGET *t = (DB_REC_TARGET *) (kv.val.dptr);
		relname = read_db_text(db, t->relname);
		if (memcmp(relname, config, sizeof (config) - 1) == 0) {
			return (0);
		}
		t->mtime = 0;
		t->parent = db_null_token;
	}
	if (token.type == DB_RECTYPE_DIRECTORY) {
		DB_REC_DIRECTORY *d = (DB_REC_DIRECTORY *) (kv.val.dptr);
		relname = read_db_text(db, d->relname);
		if (memcmp(relname, config, sizeof (config) - 1) == 0 ||
		    (memcmp(relname, config, sizeof (config) - 2) == 0
		     && relname[sizeof (config) - 2] == '\0')) {
			return (0);
		}
		d->parent = db_null_token;
		if (!DB_IS_NULL_TOKEN(d->children)) {
			empty_db_child_list(db, d->children);
		}
	}
	return (0);
}

static int
clear_old_data(PPDB * db)
{
	return (run_db(db, clear_old_data_run, NULL));
}

/*============================================================================*/

/* Remove information from deleted files.   Keep the records on the assumption
 * that these files will be recreated.
 */

static int
deleted_files_run(PPDB * db, kvpair kv, void *userdata)
{
	DB_TOKEN token;
	int i;
	memcpy(&token, (void *) (kv.key.dptr), sizeof (token));
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = (DB_REC_SOURCE *) (kv.val.dptr);
		if (s->mtime == 0) {
			memset(&(s->flags), 0, sizeof (s->flags));
			if (!DB_IS_NULL_TOKEN(s->component)) {
				empty_db_ccd_list(db, s->component);
			}
			if (!DB_IS_NULL_TOKEN(s->new_component)) {
				empty_db_ccd_list(db, s->new_component);
			}
			if (!DB_IS_NULL_TOKEN(s->config)) {
				empty_db_ccd_list(db, s->config);
			}
		}
	}
	if (token.type == DB_RECTYPE_TARGET) {
		DB_REC_TARGET *t = (DB_REC_TARGET *) (kv.val.dptr);
		if (t->mtime == 0) {
			DB_REC_TARGET temp;
			temp.flags = t->flags;
			memset(&(t->flags), 0, sizeof (t->flags));
			t->flags.ignore = temp.flags.ignore;
			t->flags.symlink = temp.flags.symlink;
			if (!DB_IS_NULL_TOKEN(t->dependency)) {
				empty_db_ccd_list(db, t->dependency);
			}
			if (!DB_IS_NULL_TOKEN(t->objlink)) {
				empty_db_target_list(db, t->objlink);
			}
			if (!DB_IS_NULL_TOKEN(t->command)) {
				empty_db_text(db, t->command);
			}
			for (i = 0;
			     i <
			     sizeof (t->acld_flags) / sizeof (t->acld_flags[0]);
			     ++i) {
				if (!DB_IS_NULL_TOKEN(t->acld_flags[i])) {
					empty_db_text(db, t->acld_flags[i]);
				}
			}
		}
	}
	return (0);
}

static int
deleted_files(PPDB * db)
{
	return (run_db(db, deleted_files_run, NULL));
}

/*============================================================================*/

/* Handle files that are constructed from components.  Run the database
 * truncating any new_component data, build new_component data for source files
 * that are built from components, compare against the existing component list
 * and rebuild the files if necessary.  Files built from components are always
 * built from source files and the result is a source file in the object
 * directory.
 */

/* Clear new_component entries from source records.  Truncate existing records
 * to 0 entries but keep the record, it will probably be reused.
 */

static int
clear_new_components(PPDB * db, kvpair kv, void *userdata)
{
	DB_TOKEN token;
	memcpy(&token, (void *) (kv.key.dptr), sizeof (token));
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = (DB_REC_SOURCE *) (kv.val.dptr);
		if (!DB_IS_NULL_TOKEN(s->new_component)) {
			empty_db_ccd_list(db, s->new_component);
		}
	}
	return (0);
}

/* Add a new component to the new_components list for a source file.  The source
 * filename is taken from the component name without the suffix, the generated
 * file is always in the object tree.
 */

static void
add_db_new_component(PPDB * db, const char *relname, int partsize,
		     DB_TOKEN component_token, int index, time_t mtime,
		     int sorted)
{
	DB_TOKEN s_token;
	DB_REC_SOURCE *s;
	DB_REC_CCD_LIST *c;
	DB_REC_CCD_1 c1;
	char name2[partsize + 1];

	memcpy(name2, relname, partsize);
	name2[partsize] = '\0';
	s_token = lookup_db_byname(db, name2, DB_RECTYPE_SOURCE,
				   objtree_treename_index);
	if (DB_IS_NULL_TOKEN(s_token)) {
		s_token = new_db_source(db, name2, objtree_treename_index);
		s = read_db_source(db, s_token);
		s->index = objtree_treename_index;
		s->mtime = 0;
	} else {
		s = read_db_source(db, s_token);
	}

	if (DB_IS_NULL_TOKEN(s->new_component)) {
		new_db_ccd_list(db, &(s->new_component));
	}
	c1.entry = component_token;
	c1.index = index;
	c1.mtime = mtime;
	c = add_db_ccd_list(db, s->new_component, &c1, NULL);
	c->flags.sorted = sorted;
	c->parent_index = s->index;
	c->parent_mtime = s->mtime;
}

static int
build_db_new_components(PPDB * db, kvpair kv, void *userdata)
{
	DB_TOKEN token;
	memcpy(&token, (void *) (kv.key.dptr), sizeof (token));
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = (DB_REC_SOURCE *) (kv.val.dptr);
		const char *relname = read_db_text(db, s->relname);
		int l = strlen(relname);
		if (prepend_name(relname, l)) {
			add_db_new_component(db, relname, l - 8, token,
					     s->index, s->mtime, 0);
		} else if (append_name(relname, l)) {
			add_db_new_component(db, relname, l - 7, token,
					     s->index, s->mtime, 0);
		}
	}
	return (0);
}

/* Sort a component list held in a ccd record, assuming that the component list
 * is prepend/append data.  Find the highest version of the source file,
 * excluding any version in the object tree.  If no such file exists then issue
 * a warning and empty the component list.  Otherwise remove all component
 * entries with an index <= the primary source file.  Add the primary source and
 * sort the resulting list in order prepend descending, primary, append
 * ascending.
 */

#define ORDER_PREPEND_START	0
#define ORDER_PREPEND_END	ORDER_PREPEND_START+MAX_SRCTREE
#define ORDER_NORMAL		ORDER_PREPEND_END+1
#define ORDER_APPEND_START	ORDER_NORMAL+1
#define ORDER_APPEND_END	ORDER_APPEND_START+MAX_SRCTREE

struct qsort_component_t {
	int order;
	DB_REC_CCD_1 ccd_1;
};

static int
qsort_component(const void *va, const void *vb)
{
	const struct qsort_component_t *a = va, *b = vb;
	if (a->order < b->order) {
		return (-1);
	} else if (a->order > b->order) {
		return (1);
	} else {
		return (0);
	}
}

/* Sort a component list */

static DB_REC_CCD_LIST *
sort_db_components(PPDB * db, DB_TOKEN token, DB_REC_SOURCE * s)
{
	DB_REC_FILENAME_LIST *f_list;
	DB_REC_FILENAME_1 *f_slot, high;
	DB_REC_CCD_LIST *c;
	DB_REC_CCD_1 *c1;
	const char *relname;
	int i;
	c = read_db_ccd_list(db, token);
	if (c->flags.sorted) {
		return (c);
	}
	relname = read_db_text(db, s->relname);
	f_list = read_db_filename_list(db, relname, 0);
	assert(f_list);
	memset(&high, 0, sizeof (high));

	/* Which primary source file do these components apply to? */
	for (i = 0, f_slot = f_list->list; i < f_list->count; ++i, ++f_slot) {
		if (f_slot->entry.type == DB_RECTYPE_SOURCE
		    && f_slot->index != objtree_treename_index) {
			if (!high.entry.type || high.index < f_slot->index) {
				high = *f_slot;
			}
		}
	}
	if (!high.entry.type && c->count) {
		fprintf(stderr,
			"%s: warning - no primary file for %s, .append/.prepend ignored\n",
			program, relname + 1);
		c->count = 0;
	}

	/* Discard any components below the primary source file */
	for (i = 0, c1 = c->list; i < c->count;) {
		if (c1->entry.type == DB_RECTYPE_SOURCE
		    && c1->index <= high.index) {
			memcpy(c1, c1 + 1, (c->count - i) * sizeof (*c1));
			--(c->count);
		} else {
			++i;
			++c1;
		}
	}

	/* Add the primary source file to the list */
	if (c->count) {
		DB_REC_SOURCE *s_primary;
		DB_REC_CCD_1 add;
		s_primary = read_db_source(db, high.entry);
		add.entry = high.entry;
		add.index = s_primary->index;
		add.mtime = s_primary->mtime;
		c = add_db_ccd_list(db, token, &add, NULL);
	}

	/* Construct qsort data, sort the component list then copy back to the
	 * original record.
	 */
	if (c->count) {
		struct qsort_component_t ccd[c->count];
		DB_REC_SOURCE *s_ccd;
		int l;
		for (i = 0, c1 = c->list; i < c->count; ++i, ++c1) {
			ccd[i].ccd_1 = *c1;
			s_ccd = read_db_source(db, c1->entry);
			relname = read_db_text(db, s_ccd->relname);
			l = strlen(relname);
			if (prepend_name(relname, l)) {
				ccd[i].order =
				    ORDER_PREPEND_START + (MAX_SRCTREE -
							   c1->index);
			} else if (append_name(relname, l)) {
				ccd[i].order = ORDER_APPEND_START + c1->index;
			} else {
				ccd[i].order = ORDER_NORMAL;
			}
		}
		qsort(ccd, c->count, sizeof (*ccd), qsort_component);
		for (i = 0, c1 = c->list; i < c->count; ++i, ++c1) {
			*c1 = ccd[i].ccd_1;
			assert(c1->entry.type == DB_RECTYPE_SOURCE);
			assert(c1->index >= 0 && c1->index < MAX_SRCTREE);
			assert(c1->mtime != 0);
		}
	}

	c->flags.sorted = 1;
	return (c);
}

/* Copy the component sources to the final generated file, which must not exist
 * yet.
 */

static void
copy_db_source(PPDB * db, DB_REC_SOURCE * s, int wfd, const char *outfile)
{
	const char *relname = read_db_text(db, s->relname);
	int rfd, rlen, wlen;
	int l = strlen(relname + 1) + l_srctree[s->index] + 1;
	char buf[4096], *pbuf, name[l];
	strcpy(name, srctree[s->index]);
	strcat(name, relname + 1);
	VERBOSE(3, "    copy_db_source %s\n", name);
	rfd = open(name, O_RDONLY);
	if (rfd < 0) {
		perror(name);
		++errors;
		return;
	}
	while ((rlen = read(rfd, buf, sizeof (buf))) != 0) {
		if (rlen < 0) {
			if (errno == EAGAIN) {
				continue;
			}
			perror(name);
			exit(1);
		} else {
			pbuf = buf;
			while (rlen > 0) {
				if ((wlen = write(wfd, pbuf, rlen)) > 0) {
					pbuf += wlen;
					rlen -= wlen;
				} else if (rlen < 0 && errno == EAGAIN) {
					continue;
				} else {
					perror(outfile);
					exit(1);
				}
			}
		}
	}
	if (close(rfd)) {
		perror(name);
		++errors;
		return;
	}
}

static void
copy_db_sources(PPDB * db, DB_REC_SOURCE * s, DB_REC_CCD_LIST * c)
{
	DB_REC_CCD_1 *c1;
	DB_REC_SOURCE *s_slot;
	const char *relname = read_db_text(db, s->relname);
	int wfd, i;
	int l = strlen(relname + 1) + l_srctree[s->index] + 1;
	struct stat statbuf;
	char name[l];
	strcpy(name, srctree[s->index]);
	strcat(name, relname + 1);
	VERBOSE(2, "    copy_db_sources %s\n", name);
	if (!norun) {
		mkdir_p(name, program);
		wfd = open(name, O_CREAT | O_EXCL | O_WRONLY, 0666);
		if (wfd < 0) {
			perror(name);
			++errors;
			return;
		}
		for (i = 0, c1 = c->list; i < c->count; ++i, ++c1) {
			s_slot = read_db_source(db, c1->entry);
			copy_db_source(db, s_slot, wfd, name);
		}
		/* Code above only reads the db, so pointers are still valid */
		if (close(wfd) || stat(name, &statbuf)) {
			perror(name);
			++errors;
			c->parent_mtime = s->mtime = -1;
			return;
		}
		c->parent_mtime = s->mtime = statbuf.st_mtime;
	}
}

/* Update the files that are built from components.  All entries that have or
 * had components are now marked.  The generated file is unlinked and rebuilt
 * if :-
 *   The existing and new component lists differ.
 *   The timestamp on the generated file has changed since the last build.
 *   The components have been deleted but the generated file still exists.
 * The rebuild step is skipped if there are no components on this run.
 */

static void
update_db_components_source(PPDB * db, DB_TOKEN token)
{
	DB_REC_CCD_LIST *c, *nc;
	DB_REC_SOURCE *s;
	s = read_db_source(db, token);
	if (DB_IS_NULL_TOKEN(s->component)) {
		new_db_ccd_list(db, &(s->component));
	}
	s->flags.shadow_source = 1;
	if (DB_IS_NULL_TOKEN(s->new_component)) {
		new_db_ccd_list(db, &(s->new_component));
	}
	c = sort_db_components(db, s->component, s);
	nc = sort_db_components(db, s->new_component, s);
	if (c->count != nc->count || memcmp(c, nc, DB_SIZE_CCD_LIST(c)) != 0 ||
	    (c->parent_mtime != s->mtime) || (nc->count == 0 && s->mtime)) {
		unlink_db_files(db, read_db_text(db, s->relname));
		if (nc->count) {
			copy_db_sources(db, s, nc);
		}
	}
	if (!norun) {
		copy_db_data(db, s->component, s->new_component);
	}
}

static int
update_db_components(PPDB * db, kvpair kv, void *userdata)
{
	DB_TOKEN token;
	memcpy(&token, (void *) (kv.key.dptr), sizeof (token));
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = (DB_REC_SOURCE *) (kv.val.dptr);
		if (!DB_IS_NULL_TOKEN(s->component) ||
		    !DB_IS_NULL_TOKEN(s->new_component)) {
			update_db_components_source(db, token);
		}
	}
	return (0);
}

static int
handle_db_components(PPDB * db)
{
	run_db(db, clear_new_components, NULL);
	run_db(db, build_db_new_components, NULL);
	run_db(db, update_db_components, NULL);
	return (0);
}

/*============================================================================*/

/* Scan all the source and object trees */

static int
scan_trees(PPDB * db)
{
	int ret = 0, index;
	const char *treename;
	struct ftw_data ftw_data;
	ftw_data.db = db;
	for (index = 0; index <= objtree_treename_index; ++index) {
		if ((treename = srctree[index])) {
			if (signalled) {
				return (0);
			}
			VERBOSE(1, "    scan_trees %3d %s\n", index, treename);
			if ((ret = chdir(treename))) {
				fprintf(stderr, "chdir(%s) failed, %m\n",
					treename);
				return (ret);
			}
			ftw_data.index = index;
			ftw_data.treename = treename;
			if ((ret =
			     my_ftw(".", walk_fs_db, treename, &ftw_data))) {
				fprintf(stderr,
					"walk of %s failed, my_ftw return code %d\n",
					treename, ret);
				return (ret);
			}
		}
	}
	return (ret);
}

/*============================================================================*/

/* Add all the source treenames as pseudo file entries.  The prefix is
 * tmp_nametree rather than tmp_treename so it appears before the .tmp_shadow
 * entries.
 */

static int
add_extra_names(PPDB * db)
{
	DB_TOKEN token;
	int i;
	const char *treename;
	for (i = 0; i <= objtree_treename_index; ++i) {
		if ((treename = srctree[i])) {
			const char tmp_treename[] = "/.tmp_nametree";
			int l = sizeof (tmp_treename) + strlen(treename) + 3;
			char name[l + 1];
			snprintf(name, sizeof (name), "%s%s%03d",
				 tmp_treename, treename, i);
			token = findnew_db_directory(db, name);
			read_db_directory(db, token)->flags.explicit = 1;
		}
	}
	return (0);
}

/*============================================================================*/

/* Print a list of non-deleted filenames in the database */

static void
print_filename(FILE * f, const char *relname, char type, int primary, int index,
	       time_t mtime, int shadow_source)
{
	fprintf(f, "%s %s%s %c %d %d %ld %d\n", relname, srctree[index],
		relname + 1, type, primary, index, mtime, shadow_source);
}

static int
print_filenames_run(PPDB * db, DB_REC_CHILD_1 * c1, void *userdata)
{
	DB_TOKEN token = c1->entry;
	FILE *f = userdata;
	if (token.type == DB_RECTYPE_SOURCE) {
		DB_REC_SOURCE *s = read_db_source(db, token);
		if (s->mtime) {
			print_filename(f, read_db_text(db, s->relname),
				       token.type, c1->flags.primary, s->index,
				       s->mtime, s->flags.shadow_source);
		}
	}
	if (token.type == DB_RECTYPE_TARGET) {
		DB_REC_TARGET *t = read_db_target(db, token);
		if (t->mtime) {
			print_filename(f, read_db_text(db, t->relname),
				       token.type, 1, objtree_treename_index,
				       t->mtime, 0);
		}
	}
	if (token.type == DB_RECTYPE_DIRECTORY) {
		DB_REC_DIRECTORY *d = read_db_directory(db, token);
		if (d->flags.explicit) {
			print_filename(f, read_db_text(db, d->relname),
				       token.type, 1, objtree_treename_index, 0,
				       0);
		}
	}
	return (0);
}

static int
print_filenames(PPDB * db)
{
	FILE *f;
	int ret = 0;
	if (!(f = fopen(".tmp_filelist_new", "w"))) {
		perror(".tmp_filelist_new");
		ret = 1;
	} else {
		ret = run_db_child(db, print_filenames_run, f, "/");
		fclose(f);
	}
	return (ret);
}

/*============================================================================*/

static void
usage(void)
{
	fprintf(stderr, "usage: %s [--norun] [--verbose]\n", program);
	exit(1);
}

static int
parse(int argc, char **argv)
{
	int o;
	static struct option long_opts[] = {
		{"norun", 0, 0, 'n'},
		{"verbose", 0, 0, 'v'},
		{0, 0, 0, 0}
	};

	/* Parse the command line.  */
	while ((o = getopt_long(argc, argv, "nv", &long_opts[0], NULL)) != EOF) {
		switch (o) {
		case 'n':	/* --norun */
			norun = 1;
			break;
		case 'v':	/* --verbose */
			++verbose;
			break;
		default:
			usage();
			break;
		}
	}

	if (optind != argc) {
		usage();
	}

	return (errors);
}

static void
handler(int signum)
{
	if (++signalled > 2) {
		exit(2);
	}
}

int
main(int argc, char **argv)
{
	PPDB *db_main;
	int ret = 0;
	signal(SIGHUP, handler);
	signal(SIGINT, handler);
	signal(SIGQUIT, handler);
	signal(SIGPIPE, handler);
	signal(SIGTERM, handler);
	if (pp_getenv() || parse(argc, argv)) {
		return (1);
	}
	VERBOSE(1, "%s verbose %d\n", program, verbose);
	db_main = open_db_main(0);
	if (!db_main || signalled
	    || clear_old_data(db_main) || signalled
	    || scan_trees(db_main) || signalled
	    || deleted_files(db_main) || signalled
	    || add_extra_names(db_main) || signalled
	    || handle_db_components(db_main) || signalled
	    || sort_db_directories(db_main) || signalled) {
		ret = 1;
	}
	if (db_main && !signalled) {
		ret |= print_filenames(db_main);
	}
	if (db_main) {
		close_db_main(db_main);
	}
	pp_freeenv();
	return (errors || ret);
}
