/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: products.c 1.10 90/05/17 $"

#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include <sys/stat.h>
#ifdef SVAE
#include <dir.h>
#else
#include <dirent.h>
#endif
#include <fcntl.h>
#include "symbols.h"			/* symbol table functions */
#include "allocate.h"			/* perm_str(), perm_to_tail(), */
					/* and perm_alloc() */

#define	SYSFILE_HASHENTS	20	/* Number of sys_config hash entries */

extern	size_t	strlen();
extern	int	strcmp();
extern	char	*strcpy();


struct sys_config {			/* sys_config file information */
	boolean sci_parsed;		/* TRUE if already parsed; else FALSE */
	boolean sci_fullinit;		/* TRUE if full parse init needed */
	boolean sci_description;	/* description directive seen */
	boolean sci_trace;		/* trace flag */
	boolean sci_select;		/* select flag */
	long sci_offset;		/* Offset within file */
	int sci_lineno;			/* ...import and checkfor directives */
	struct sys_config *sci_parent;	/* ptr to parent of sys_config */
	char *sci_archive;		/* name of archive */
	char *sci_lintlib_basename;	/* lint library base name */
	struct {
		int count;		/* #of C-source files */
		struct llist *list;	/* first C-file source list */
		struct llist *last;	/* last C-file source list */
	} sci_cfiles;
	struct list_head sci_objects;	/* object file list for product */
	struct pmodule *sci_pmodset;	/* pmodule set for product */
	char *sci_modulespath;		/* full pathname of modules file */
	char *sci_product;		/* name of product */
	char sci_configpath[MAXPATH];	/* full pathname of sys_config file */
};

/*
 *  Initialize a sys_config data structure.
 */
#define SYS_CONFIG_INIT(sci, product_name, sys_modulespath, sys_configpath) \
	(sci)->sci_parsed = FALSE; \
	(sci)->sci_fullinit = TRUE; \
	(sci)->sci_description = FALSE; \
	(sci)->sci_trace = FALSE; \
	(sci)->sci_select = selected(product_name); \
	(sci)->sci_offset = 0; \
	(sci)->sci_lineno = 1; \
	(sci)->sci_parent = (struct sys_config*)NULL; \
	(sci)->sci_archive = (char *)NULL; \
	(sci)->sci_lintlib_basename = (char *)NULL; \
	(sci)->sci_cfiles.count = 0; \
	(sci)->sci_cfiles.list = (struct llist *)NULL; \
	(sci)->sci_cfiles.last = (struct llist *)NULL; \
	(sci)->sci_objects.l_head = (struct list *)NULL; \
	(sci)->sci_objects.l_last = (struct list *)NULL; \
	(sci)->sci_pmodset = (struct pmodule *)NULL; \
	(sci)->sci_modulespath = (sys_modulespath); \
	(sci)->sci_product = (product_name); \
	(void)strcpy((sci)->sci_configpath, (sys_configpath));

struct file_ref {			/* selected parameter file reference */
	struct file_ref *fr_next;	/* next parameter file reference */
	char *fr_filename;		/* name of selected parameter file */
};
 
/*
 *  System configuration file set.
 */
static boolean import_failure;		/* boolean IMPORT failure flag */
static opaque_ptr next_symbol;		/* ptr to 1st imported symbol of */
					/* import list, or current include */
static opaque_ptr opaque_sysconfig = (opaque_ptr)NULL;
					/* current sys_config module */
static struct sys_config *next_data;	/* ptr to data for 1st imported symbol*/
					/* of import list or current include */
static FILE *current_sysfp;		/* current sys_config file stream */
static opaque_ptr sysfiles;		/* system configuration files */
static struct sys_config *sys_basep;	/* base OS sys_config info */
static struct sys_config *sys_configp;	/* current system configuration file */
static struct sys_config *sys_productp;	/* current product */

/*
 *  List of files for which all parameter declarations and directives
 *  are to be selected.
 */
static struct file_ref *file_refs = (struct file_ref *)NULL;
 
/*
 *  Miscellaneous:
 */
static boolean base_archive_known;	/* system archive known flag */
static boolean returning_for_include;	/* returning-for-include flag */


/*
 *  Enter another system configuration file in the set of files (and hence all
 *  module configuration files for the indicated system configuration file)
 *  in the list of "interesting" files for parameter directives.
 */
void
select_sysconfig(sys_configname)
register char *sys_configname;		/* selected parameter file reference */
{
	extern void selecting_params();
	register struct file_ref *file_refp;

	file_refp = file_refs;		/* check for duplicates */
	if (file_refp == (struct file_ref *)NULL) {
		selecting_params();	/* tell parameters.c we're specific */
	} else {
		do {
			if (strcmp(file_refp->fr_filename, sys_configname) == 0)
				return;
			file_refp = file_refp->fr_next;
		} while (file_refp != (struct file_ref *)NULL);
	}
	/*
	 *  Insert sys_config file into list.
	 */
	file_refp = (struct file_ref *)perm_alloc(sizeof(struct file_ref));
	file_refp->fr_next = file_refs;
	file_refp->fr_filename = sys_configname;
	file_refs = file_refp;
}

/*
 *  Return TRUE iff the name sys_config file is in the list of files
 *  to be culled for configurable parameter directives.
 */
static boolean
selected(sys_configname)
register char *sys_configname;		/* sys_config name to be checked */
{
	register struct file_ref *file_prevrefp;
	register struct file_ref *file_refp;

	if (sys_configname == (char *)NULL) {
		/* Inherit parent's select flag */
		return (sys_configp->sci_select);
	}
	file_prevrefp = (struct file_ref *)NULL;
	file_refp = file_refs;
	while (file_refp != (struct file_ref *)NULL) {
		if (strcmp(file_refp->fr_filename, sys_configname)==0) {
			/*
			 *  We found the file.  Remove it from the
			 *  select list...and return.
			 */
			if (file_prevrefp == (struct file_ref *)NULL)
				file_refs = file_refp->fr_next;
			else
				file_prevrefp->fr_next = file_refp->fr_next;
			return(TRUE);	/* sys_config file selected */
		}
		file_prevrefp = file_refp;
		file_refp = file_refp->fr_next;
	}
	return(FALSE);
}

/*
 *  Show the remaining sys_config file references that do not exist.
 */
static void
show_nonexistent()
{
	register struct file_ref *file_refp;

	file_refp = file_refs;
	while (file_refp != (struct file_ref *)NULL) {
		fprintf(stderr, "Sys_config file: %s.%s %s.\n",
			file_refp->fr_filename, configuration_name,
			"specified via \"-f\" does not exist");
		file_refp = file_refp->fr_next;
	}
}

/*
 *  Create global names for system files.
 *  Create build directory.
 */
void
set_mach()
{
	extern char *getcwd();
	extern boolean is_dir();
	extern boolean is_same_file();
	extern boolean mkdirectory();
	char sys_configuration_name[MAXPATH];
	char site_configuration_name[MAXPATH];
	char modules_pathbuf[MAXPATH];
	char pathbuf[MAXPATH];
	struct sys_config *sci;
#ifdef SVAE
	struct direct *dir;
#else
	struct dirent *dir;
#endif
	DIR *mach_dir;
	char *p;
	char site_config_present;
	int configuration_name_len;
	int i;

	if (DEBUG_LEVEL(0)) {
		printf("Set_mach: type \"%s\" configuration_name \"%s\"\n",
				machine_type, configuration_name);
	}

	/*
	 * look for a ./uts directory beneath us
	 */
	(void)strcpy(Anchor_dir, SRC_ANCHOR);
	if (!is_dir(Anchor_dir)) {
		if (getcwd(pathbuf, MAXPATH) == (char *)NULL) {
			perror("getcwd:");
			fatal("Unable to get current working directory\n");
			exit(1);
		}
		(void)sprintf(Anchor_dir, "/%s/", SRC_ANCHOR);
		if ((i=index(pathbuf, Anchor_dir)) != -1) {
			p = pathbuf + i + strlen(SRC_ANCHOR) + 1;
			*p++ = '\0';
			/*
			 *  At this time, 'pathbuf' is the absolute pathname
			 *  of the "uts" directory.
			 */
			i = *p++;
			while (i == '/')		/* consume "/"s */
				i = *p++;
			if (i == '\0') {		/* "xxxx/uts///////" */
				Anchor_dir[0] = '.';
				Anchor_dir[1] = '\0';
			} else {			/* "xxxx/uts/xxxxxx" */
				Anchor_dir[0] = '\0';
				do {
					do {
						i = *p++;
					} while (i != '\0' && i != '/');
					(void)strcat(Anchor_dir, "../");
					while (i == '/')
						i = *p++;
				} while (i);
				Anchor_dir[strlen(Anchor_dir)-1] = '\0';
			}
			if (!is_same_file(pathbuf, Anchor_dir)) {
				fatal("set_mach(): directory parse error.\n");
				exit(1);
			}
		} else {
			Anchor_dir[0] = '.';
			Anchor_dir[1] = '\0';
		}
	}
	Anchor_len = strlen(Anchor_dir);

	/*
	 * Define all magic file names here.
	 */
	(void)sprintf(Mach_dir, "%s/%s", Anchor_dir, machine_type);
	(void)sprintf(Build_dir,"%s/%s.%s", Anchor_dir, machine_type,
		      configuration_name);
	/* define the default */
	(void)sprintf(Smk_file,"%s/%s.%s", Mach_dir, MAKEFILE,
		      configuration_name);
	(void)sprintf(Mk_file,"%s/%s", Build_dir, MAKEFILE);
	(void)sprintf(Files_file,"%s/%s", Build_dir, FILES);
	(void)sprintf(Bfiles_file,"%s/%s", Build_dir, BFILES);
	(void)sprintf(site_configuration_name, "%s.%s",
		      SITE_CONFIG, configuration_name);

	/*
	 *  Initialize "miscellaneous" variables:
	 */
	returning_for_include = FALSE;
	base_archive_known = FALSE;

	/*
	 *  Make "system.std" the very first sys configuration file in the
	 *  list of system configuration files to be parsed.
	 */
	sysfiles = create_symtab(SYSFILE_HASHENTS);
	next_symbol = (opaque_ptr)NULL;
	(void)sprintf(sys_configuration_name, "%s.%s",
		      SYS_CONFIG, configuration_name);
	(void)sprintf(modules_pathbuf, "%s/%s", Mach_dir, SYS_MODULES);
	(void)sprintf(pathbuf, "%s/%s", Mach_dir, sys_configuration_name);
	sci = (struct sys_config *)
		perm_alloc( sizeof(struct sys_config) + strlen(pathbuf) + 1
			   - MAXPATH );
	sys_basep = sci;
	p = perm_str(modules_pathbuf);
	SYS_CONFIG_INIT(sci, perm_str(SYS_CONFIG), p, pathbuf);
	if (insert_symtab(sysfiles, sci->sci_configpath+strlen(Mach_dir)+1,
			  (opaque_ptr)sci) != (opaque_ptr)NULL) {
		fatal("internal error in set_mach\n");
		exit(1);
	}

	/*
	 *  Scan the machine architecture directory for configuration
	 *  and module list files.
	 */
	if ((mach_dir = opendir(Mach_dir)) == NULL){
		fatal("unable to access %s\n",Mach_dir);
		exit(1);
	}
	site_config_present = 0;
	configuration_name_len = strlen(configuration_name);
	while((dir=readdir(mach_dir)) != NULL) {
		if (strcmp(dir->d_name, site_configuration_name) == 0)
			site_config_present = 1;
		else if (strcmp(dir->d_name, sys_configuration_name) != 0 &&
			 (i = strlen(dir->d_name)) > configuration_name_len &&
			 dir->d_name[0] != '.' &&	/* No hidden files */
			 dir->d_name[(i -= configuration_name_len)-1] == '.' &&
			 strcmp(p = dir->d_name+i, configuration_name) == 0) {
			/*
			 *  XXXXX.configtyp
			 *
			 *  A layered product configuration file has been
			 *  found matching the requested configuration.
			 */
			(void)sprintf(pathbuf, "%s/%s", Mach_dir, dir->d_name);
			*--p = '\0';	/* trim to product name only */
			(void)sprintf(modules_pathbuf, "%s/%s.%s",
				      Mach_dir, dir->d_name, SYS_MODULES_EXT);
			sci = (struct sys_config *)
			       perm_alloc(  sizeof(struct sys_config)
					  + strlen(pathbuf) + 1
					  - MAXPATH  );
			p = perm_str(modules_pathbuf);
			SYS_CONFIG_INIT(sci, perm_str(dir->d_name), p, pathbuf);
			if (insert_symtab(sysfiles,
					  sci->sci_configpath
						+ strlen(Mach_dir) + 1,
					  (opaque_ptr)sci
						 ) != (opaque_ptr)NULL) {
				/*
				 *  No way should this ever happen.
				 *  It means somehow that we found the
				 *  same file listed TWICE in the same
				 *  directory.
				 */
				fatal("internal error in set_mach\n");
				exit(1);
			}
		}
	}
	closedir(mach_dir);

	if (site_config_present) {
		/*
		 *  The special "site.<configuration>" file is present.
		 *  This file *MUST* be parsed last.
		 */
		(void)sprintf(pathbuf, "%s/%s",
			      Mach_dir, site_configuration_name);
		sci = (struct sys_config *)
			perm_alloc(  sizeof(struct sys_config)
				   + strlen(pathbuf) + 1
				   - MAXPATH  );
		SYS_CONFIG_INIT(sci, SITE_CONFIG, (char *)NULL, pathbuf);
		if (insert_symtab(sysfiles,
				  sci->sci_configpath+strlen(Mach_dir)+1,
				  (opaque_ptr)sci) != (opaque_ptr)NULL) {
			fatal("internal error in set_mach\n");
			exit(1);
		}
	}
	if (file_refs != (struct file_ref *)NULL) {
		show_nonexistent();
		exit(1);
	}
	if (DEBUG_LEVEL(12)) {
		printf("Mach_dir: %s\n", Mach_dir);
		printf("Build_dir: %s\n", Build_dir);
		printf("Smk_file: %s\n", Smk_file);
		printf("Mk_file: %s\n", Mk_file);
	}

	/*
	 * Create the machine_type.config_type directory
	 */
	(void)mkdirectory(Build_dir, TRUE);	/* aborts on failure */

	if (DEBUG_LEVEL(0))
		printf("Machine now set up\n");
}

/*
 *  Parse the next product (if there is one).
 *  This is done as follows:
 *	Parse the "modules" file for the product;
 *	Parse the sys_config file for the product;
 *	Parse the mod_config file(s) for the product.
 *  Return FALSE if no more system configuration files to parse.
 *  Otherwise, return TRUE indicating that we should be called
 *	again.
 */
boolean
next_sysconfig()
{
	extern void read_modules();	/* read modules list for product */
	extern void parse_modules();	/* parse product mod_config files */
	extern void check_description();/* WARN if no description */
	static void define_archive();	/* define archive for sys_config */
	static void add_lintrules();	/* add the necessary lint rules */
	register struct sys_config *sci;/* current sys_config */
	register char *pathname;	/* ptr to pathname of sys_config file */
	register FILE *modfp;		/* modules file for product */
	char *filename;			/* ptr to filename of sys_config file */

	if (opaque_sysconfig == (opaque_ptr)NULL) {
		opaque_sysconfig = first_symtab(sysfiles, &filename,
						&sys_configp);
	} else if (next_symbol != (opaque_ptr)NULL) {
		/*
		 *  Either we're going to parse the first file of an
		 *  import list, or we're going to parse the file that
		 *  we just mentioned in an include directive.
		 */
		opaque_sysconfig = next_symbol;
		next_symbol = (opaque_ptr)NULL;
		sys_configp = next_data;
	} else {
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	if (opaque_sysconfig == (opaque_ptr)NULL)
		return(FALSE);
	/*
	 *  Parse modules list for product if we are not returning to this
	 *  product from previous IMPORT directives, and there exists a
	 *  "modules" file for the product (the include'd sys_config files
	 *  and the site.<configuration> files don't have a "modules" file).
	 */
	sci = sys_configp;
	while (sci->sci_parent != (struct sys_config *)NULL)
		sci = sci->sci_parent;
	sys_productp = sci;			/* Establish current product */
	if (DEBUG_LEVEL(0)) {
		printf("Current product configuration file: [%s].\n",
			sys_productp->sci_configpath);
	}
	sci = sys_configp;			/* Recover current sys_config */
	if (sci->sci_offset == 0) {
		if (DEBUG_LEVEL(0))
			printf("Parsing sys_config file: [%s].\n",
				sci->sci_configpath);
		if (sci->sci_modulespath != (char *)NULL) {
			pathname = sci->sci_modulespath;
			if (DEBUG_LEVEL(0))
				printf("Modules file: [%s].\n", pathname);
			if ((modfp = fopen(pathname, "r")) == (FILE *)NULL) {
				fprintf(stderr,"FATAL: unable to open: [%s].\n",
					pathname);
				exit(1);
			}	
			read_modules(pathname, modfp, (void *)sys_productp);
			(void)fclose(modfp);
		}
	} else if (DEBUG_LEVEL(0)) {
		/*
		 *  Returning to a file from an IMPORT or INCLUDE.
		 */
		printf("Returning to sys_config: [%s] line#: %d.\n",
			sci->sci_configpath, sci->sci_lineno);
	}
	/*
	 *  Parse product system configuration file, and assorted
	 *  module configuration files.
	 */
	sci = sys_configp;
	pathname = sci->sci_configpath;
	if ((current_sysfp = fopen(pathname, "r")) == (FILE *)NULL) {
		fprintf(stderr, "FATAL: unable to open: [%s].\n", pathname);
		exit(1);
	}	
	(void)fseek(current_sysfp, sci->sci_offset, 0);
	/*
	 *  Initialize parser.  Note that if we are returning from an
	 *  included file or are entering an included file, then we do
	 *  NOT fully initialize the parser.
	 */
	lexinit_sys(pathname+Anchor_len+1, current_sysfp,
		    sci->sci_fullinit, sci->sci_select, sci->sci_description,
		    sci->sci_trace, sci->sci_lineno);
	if (returning_for_include) {
		returning_for_include = FALSE;
		return(TRUE);
	}
	if (yyparse())			/* parse product sys_config file */
		exit(1);
	/*
	 *  The file stream: current_sysfp is closed....
	 */
	if (next_symbol != (opaque_ptr)NULL) {	/* Handle import or include */
		if (DEBUG_LEVEL(0))
			printf ("Handling sys_config IMPORT.\n");
		return(TRUE);
	}
	if (Vers.version == 0) {
		fatal("No VERSION directive specified.\n");
		exit(1);
	}
	check_description();		/* WARN if no description */
	if (!base_archive_known) {
		/*
		 *  Since 'base_archive_name' is NULL, we know we've just
		 *  parsed "system.<configuration>."  The system sys_config
		 *  file is *REQUIRED* to define an archive (unless the
		 *  version is 1.6 or lower)....
		 */
		if (sys_productp->sci_archive == (char *)NULL) {
			if (Vers.version >= 107) {
				fatal("No ARCHIVE defined.\n");
				exit(1);
			}
			define_archive(BASE_PRODUCT_NAME);
		}
		base_archive_known = TRUE;
	}
	/*
	 *  If we have not just finished an include file, then parse
	 *  the modules declared for the product.
	 */
	if (sci == sys_productp) {
		parse_modules(sci->sci_pmodset, sci->sci_select);
		add_lintrules();	/* generate the needed lint rules */
	}
	return(TRUE);			/* parse mod_config files */
}

/*
 *  Return the name of the current product being parsed.
 */
char *
current_product_name()
{
	return (sys_productp->sci_product);
}

/*
 *  Add the given module into the mod_config set for the current product.
 */
void
add_pmodset(pmodp)
register struct pmodule *pmodp;
{
	register struct pmodule *walkpmodp;
	register struct pmodule *nextpmodp;

	walkpmodp = sys_productp->sci_pmodset;
	if (walkpmodp != (struct pmodule *)NULL) {
		nextpmodp = walkpmodp->pmod_next;
		while (nextpmodp != (struct pmodule *)NULL) {
			walkpmodp = nextpmodp;
			nextpmodp = walkpmodp->pmod_next;
		}
		pmodp->pmod_prev = walkpmodp;
		walkpmodp->pmod_next = pmodp;
	} else
		sys_productp->sci_pmodset = pmodp;
}

/*
 *  Set the mod_config set for the current product.
 */
void
set_pmodset(pmodp)
register struct pmodule *pmodp;
{
	sys_productp->sci_pmodset = pmodp;
}

/*
 *  Return TRUE if the given module is part of the current product.
 *  Otherwise, FALSE is returned.
 */
boolean
in_current_product(pmodp)
register struct pmodule *pmodp;
{
	if (pmodp->pmod_product == (void *)sys_productp)
		return(TRUE);
	return(FALSE);
}

/* 
 *  Import the indicated product.
 *  Return TRUE if non-trivial import, else FALSE.
 */
boolean
import_sys(product_name, required)
	char *product_name;	
	int required;
{
	char product_fullname[MAXPATH];
	char error_message[MAXPATH];
	register struct sys_config *sci;	/* sys_config information ptr */
	opaque_ptr new_next_symbol;		/* opaque imported symbol */

	(void)sprintf(product_fullname, "%s.%s",
		      product_name, configuration_name);
	if (DEBUG_LEVEL(0)) {
		printf("Import_sys: [%s] from: [%s]\n",
			product_fullname, sys_configp->sci_configpath);
	}
	sci = (struct sys_config *)
	       search_symtab3(sysfiles, product_fullname, &new_next_symbol);
	if (sci == (struct sys_config *)NULL) {
		/*
		 *  No configuration file present for product.configuration.
		 */
		if (required) {
			(void)sprintf(error_message,
				"%s: [%s]\n\t%s: [%s]\n\t%s",
				"no configuration file for",
				product_name,
				"found for configuration",
				configuration_name,
				"satisfying IMPORT directive.");
			yyerror (error_message);
			import_failure = TRUE;
			return(TRUE);		/* Non-trivial import */
		}
		return(FALSE);			/* File not present */
	}
	/*
	 *  The named product has a system configuration file.
	 */
	if (sci->sci_parsed)			/* if already parsed, */
		return(FALSE);			/* then don't import  */
	if (sci->sci_offset) {			/* circular import */
		(void)sprintf(error_message, "%s: [%s]\n\t%s: [%s]",
			"circular import of",
			product_name,
			"encountered for configuration",
			configuration_name);
		yyerror (error_message);
		return(FALSE);
	}
	if (sci == sys_configp) {		/* self referential import! */
		(void)sprintf(error_message, "%s: [%s]\n\t%s: [%s]",
			"self import of",
			product_name,
			"encountered for configuration",
			configuration_name);
		yyerror (error_message);
		return(FALSE);
	}
	if (DEBUG_LEVEL(0)) {
		printf("Import_mod: [%s] from: [%s] is non-trivial\n",
			product_fullname, sys_configp->sci_configpath);
	}
	before_symtab(sysfiles,			/* move file ahead */
		      opaque_sysconfig, new_next_symbol);
	if (next_symbol == (opaque_ptr)NULL) {	/* remember 1st import */
		next_symbol = new_next_symbol;
		next_data = sci;
	}
	return(TRUE);				/* Non-trivial import */
}

/*
 *  Do sys_config imports.
 */
void
do_sys_imports(lineno, description, trace)
	int lineno;		/* line# */
	boolean description;	/* description seen flag */
	boolean trace;		/* trace flag */
{
	if (import_failure)	/* One or more IMPORTed products missing */
		exit(1);
	if (DEBUG_LEVEL(0)) {
		printf("Processing sys_config imports for: [%s] at line#: %d\n",
			sys_configp->sci_configpath, lineno);
	}
	sys_configp->sci_description = description;
	sys_configp->sci_trace = trace;			/* save trace flag */
	sys_configp->sci_offset = ftell(current_sysfp);	/* save offset */
	sys_configp->sci_lineno = lineno;		/* save lineno */
	/*
	 *  Set the "full-init" flag, since upon completion of the parsing
	 *  of an imported file, we MUST fully reinitialize the parser.
	 */
	sys_configp->sci_fullinit = TRUE;
	fclose(current_sysfp);
}

/*
 *  Include a sys_config file within another.
 */
boolean
include_sys(new_sysconfig, lineno, description, trace)
	char *new_sysconfig;	/* name of sys_config file to be included */
	int lineno;		/* current line# */
	int description;	/* boolean description seen flag */
	int trace;		/* boolean trace flag */
{
	char pathbuf[MAXPATH];
	opaque_ptr new_next_symbol;		/* opaque included symbol */
	register struct sys_config *sci;	/* sys_config information ptr */

	if (DEBUG_LEVEL(0)) {
		printf("Including: [%s] from: [%s] at line#: %d\n",
			new_sysconfig, sys_configp->sci_configpath, lineno);
	}
	sci = (struct sys_config *)
	       search_symtab3(sysfiles, new_sysconfig, &new_next_symbol);
	if (sci == (struct sys_config *)NULL) {
		(void)sprintf(pathbuf, "%s/%s", Mach_dir, new_sysconfig);
		sci = (struct sys_config *)
			perm_alloc( sizeof(struct sys_config)
				   + strlen(pathbuf) + 1
				   - MAXPATH );
		SYS_CONFIG_INIT(sci, (char*)NULL, (char*)NULL, pathbuf);
		if (insert_symtab(sysfiles, new_sysconfig,
				  (opaque_ptr)sci) != (opaque_ptr)NULL) {
			/*
			 *  No way should this ever happen.
			 *  It means somehow that we found the
			 *  same file listed TWICE in the same
			 *  directory.
			 */
			fatal("sys_config collision in include_sys\n");
			exit(1);
		}
		if ((struct sys_config *)
		     search_symtab3(sysfiles, new_sysconfig,
				    &new_next_symbol) != sci) {
			fatal("sys_config search failure in include_sys\n");
			exit(1);
		}
	} else {
		/*
		 *  It is illegal (for several *very* good reasons) to
		 *  include a system configuration file that was already
		 *  in the set of files to be parsed by virtue of it
		 *  having the same .<configuration> suffix as is presently
		 *  being configured....
		 *
		 *	--OR--
		 *
		 *  has already been included in another sys_config file....
		 */
		yyerror("Illegal include of system configuration file: [%s].\n",
			new_sysconfig);
		return(FALSE);
	}
	sys_configp->sci_description = description;	/* save descr flag */
	sys_configp->sci_fullinit = FALSE;		/* no more full-init */
	sys_configp->sci_trace = trace;			/* save trace flag */
	sys_configp->sci_offset = ftell(current_sysfp);	/* save offset */
	sys_configp->sci_lineno = lineno;		/* save lineno */
	sci->sci_parent = sys_configp;
	sci->sci_fullinit = FALSE;			/* no more full-init */
	before_symtab(sysfiles, opaque_sysconfig,	/* more file ahead */
		      new_next_symbol);
	next_symbol = new_next_symbol;
	next_data = sci;
	fclose(current_sysfp);
	returning_for_include = TRUE;
	if (next_sysconfig())
		return(TRUE);
	fatal("internal error in include_sys().\n");
	exit(1);
}

/*
 *  Called upon completing the parsing of a sys_config file.
 *	Returns FALSE if we just finished parsing a sys_config file
 *		included by another; else TRUE.
 */
boolean
end_sys()
{
	fclose(current_sysfp);			/* Close current file */
	sys_configp->sci_parsed = TRUE;		/* Mark as parsed */
	/*
	 *  We have just finished parsing a sys_config file included
	 *  by another sys_config file.
	 */
	if (DEBUG_LEVEL(0)) {
		printf ("End of sys_config: [%s]\n",
			sys_configp->sci_configpath);
	}
	if (sys_configp->sci_parent != (struct sys_config *)NULL) {
		returning_for_include = TRUE;
		if (next_sysconfig())
			return(FALSE);
	}
	return(TRUE);
}


/*
 *  Return TRUE if the given product is present in the configuration.
 *  Otherwise, FALSE is returned.
 */
boolean
ifproduct(product_name)
char *product_name;
{
	char product_fullname[MAXPATH];

	(void)sprintf(product_fullname, "%s.%s",
		      product_name, configuration_name);
	if (search_symtab(sysfiles, product_fullname) == (opaque_ptr)NULL)
		return(FALSE);
	return(TRUE);
}


/*
 *  Return TRUE if the given product is NOT present in the configuration.
 *  Otherwise, FALSE is returned (the product IS present).
 */
boolean
ifnproduct(product_name)
char *product_name;
{
	if (ifproduct(product_name))
		return(FALSE);
	return(TRUE);
}


/*
 *  Define archive for current product.
 */
void
define_archive(name)
register char *name;			/* archive name */
{
	char error_message[1024];
	char archive_name[512];
	char lintlib_basename[512];
	register int len;

	if (sys_productp->sci_archive != (char *)NULL) {
		(void)sprintf(error_message,
		"ARCHIVE already defined for product: [%s]\n\tas: [%s].\n",
			sys_productp->sci_product,
			sys_productp->sci_archive);
		yyerror(error_message);
	} else {
		len = strlen(name);
		if (len < 2 || name[len-2] != '.' || name[len-1] != 'a') {
			/*
			 *  No explicit library suffix.
			 *  Append "_<configuration_name>.a".
			 */
			if (strcmp(name, sys_productp->sci_product) &&
			    sys_productp != sys_basep) {
				/*
				 *  The defined archive name does not match
				 *  the product name.  For painful historical
				 *  reasons, the base kernel product is allowed
				 *  to a have a system configuration file name
				 *  of "system," and an archive and lint
				 *  library name based on "unix."  Sigh....
				 *
				 *  We used to be more flexible here, but
				 *  the time has come to enforce order.
				 */
				(void)sprintf(error_message,
					"%s: [%s].\n\t%s: [%s].\n\t%s.\n",
	"Non-standard ARCHIVE name defined for product",
					sys_productp->sci_product,
	"The ARCHIVE name with implicit suffix was specified as", name,
	"ARCHIVE names with implicit suffixes must match the product name");
				yyerror(error_message);
				name = sys_productp->sci_product;
			}
			if (strcmp(configuration_name, MFG_CONF)) {
				(void)sprintf(archive_name, "%s_%s.a",
					      name, DEF_CONF);
				(void)sprintf(lintlib_basename, "%s_%s",
					      name, DEF_CONF);
			} else {
				(void)sprintf(archive_name, "%s_%s.a",
					      name, configuration_name);
				(void)sprintf(lintlib_basename, "%s_%s",
					      name, configuration_name);
			}
		} else {
			/*
			 *  If you explicitly specify the suffix, then
			 *  you get to say whatever you want....
			 */
			(void)strcpy(archive_name, name);
			name[len-2] = '\0';	/* Truncate ".a" */
			(void)strcpy(lintlib_basename, name);
		}
		sys_productp->sci_archive = perm_str(archive_name);
		sys_productp->sci_lintlib_basename = perm_str(lintlib_basename);
	}
}


/*
 *  Add the named C-source module to the list of modules to be built for
 *  the current product.  This involves additions to the %CFILES and
 *  %RULES lists.
 */
void
add_cfiles(c_name, o_name, full_pathname)
        char   *c_name;
        char   *o_name;
        char   *full_pathname;
{
	char lintlib_basename[512];	/* lint library base name */
	register struct llist *llistp;
	register int libno;

        if (DEBUG_LEVEL(6))
                printf("Add cfile %s-->%s\n", c_name, o_name);
        /*
         * add to the CFILES list and add the corresponding compilation
         * rule.
         */
	add_crules(FALSE, c_name, o_name, depend(full_pathname, c_name), Pmodp);
	if ((libno = sys_productp->sci_cfiles.count) % LLIB_SIZE == 0) {
		libno = libno / LLIB_SIZE;
		if (sys_productp == sys_basep) {/* base is special case */
			(void)sprintf(lintlib_basename, "CFILE_%s%d",
				      BASE_PRODUCT_NAME, libno);
		} else {
			(void)sprintf(lintlib_basename, "CFILE_%s%d", 
				      sys_productp->sci_product, libno);
		}
		llistp = (struct llist *)perm_alloc(sizeof(struct llist));
		llistp->ll_list.l_head = (struct list *)NULL;
		llistp->ll_list.l_last = (struct list *)NULL;
		llistp->ll_name = perm_str(lintlib_basename);
		llistp->ll_next = (struct llist *)NULL;
		if (sys_productp->sci_cfiles.list == (struct llist *)NULL)
			sys_productp->sci_cfiles.list = llistp;
		else
			sys_productp->sci_cfiles.last->ll_next = llistp;
		sys_productp->sci_cfiles.last = llistp;
	}
        perm_to_tail(&sys_productp->sci_cfiles.last->ll_list, c_name);
	sys_productp->sci_cfiles.count++;
}


/*
 *  Add the necessary lint rules for the current product.
 */
static void
add_lintrules()
{
	char lintlib_targetname[512];	/* lint library target name */
	char lintlib_dependencies[512];	/* lint library dependencies */
	register struct llist *llistp;

        if (DEBUG_LEVEL(6))
                printf("Add lintrules.\n");
	if (sys_productp->sci_cfiles.count > LLIB_SIZE) {
		/*
		 *  The current product has a large number of C-source files
		 *  requiring the use constituent lint libraries to avoid
		 *  argument overflow on exec, and in the kernel makefile.
		 */
		llistp = sys_productp->sci_cfiles.list;
		do {
			(void)sprintf(lintlib_targetname, "llib-l$(LL)%s.ln",
				      llistp->ll_name);
			(void)sprintf(lintlib_dependencies, "$(%s)",
				      llistp->ll_name);
			add_lnrules( llistp->ll_name, lintlib_targetname,
				     lintlib_dependencies );
			llistp = llistp->ll_next;
		} while (llistp != (struct llist *)NULL);
	} else if (sys_productp->sci_cfiles.count > 0) {
		/*
		 *  The current product does not have a large number of
		 *  C-source files requiring the use constituent lint
		 *  libraries.  The lint library for this product can
		 *  be generated directly.
		 */
		llistp = sys_productp->sci_cfiles.list;
		/*
		 *  Historical note:  Interim implementation formatted
		 *		      the final library name as:
		 *
		 * 		"llib-l$(LL)%s.ln"
		 */
		(void)sprintf(lintlib_targetname, "llib-l%s.ln",
			      sys_productp->sci_lintlib_basename);
		(void)sprintf(lintlib_dependencies, "$(%s)",
			      llistp->ll_name);
		add_lnrules( llistp->ll_name, lintlib_targetname,
			     lintlib_dependencies );
	}
}


/*
 *  Add to the objects list for the current product.
 */
void
add_objs(name)
char *name;
{
	char error_message[1024];

	if (sys_productp->sci_archive == (char *)NULL) {
		if (Vers.version >= 107) {
			(void)sprintf(error_message,
				"%s: [%s].\n\tObject [%s] %s.\n",
				"No ARCHIVE yet defined for product",
				sys_productp->sci_product,
				name, "not added to objects list for product.");
			yyerror (error_message);
			return;
		}
		define_archive(sys_productp->sci_product);
	}
	if (DEBUG_LEVEL(6))
		printf ("Add_objs: [%s] for product: [%s]\n",
			name, sys_productp->sci_product);
	perm_to_tail(&sys_productp->sci_objects, name);
}


/*
 *  Substitute %OBJS_<product> in the makefile.
 */
void
do_product_objs(makefile_fp)
register FILE *makefile_fp;
{
	extern void print_list();
	char buff[1024];
	char *filename;			/* ptr to filename of sys_config file */
	register struct sys_config *sci;

	if (DEBUG_LEVEL(0))
		printf("Do_product_objs\n");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_archive != (char *)NULL) {
			(void)sprintf(buff, "OBJS_%s", sci->sci_product);
			print_list(makefile_fp, sci->sci_objects.l_head, buff);
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
}

/*
 * substitute %LINT_LIBS
 */
void
do_lintlibs(makefile_fp)
register FILE *makefile_fp;
{
	static boolean file_in_machine_dir();
	char lintlib_name[512];		/* lint library name */
	char *filename;			/* ptr to sys_config filename */
	char *product_name;		/* ptr to product name */
	register struct sys_config *sci;
	register struct llist *llistp;

	if (DEBUG_LEVEL(0))
		printf("Do_lintlibs\n");
	fprintf(makefile_fp, "LLIBS =");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_lintlib_basename != (char *)NULL) {
			/*
			 *  Historical note:  Interim implementation formatted
			 *		      the final library name as:
			 *
			 * 		"llib-l$(LL)%s.ln"
			 */
			(void)sprintf(lintlib_name, "llib-l%s.ln",
				      sci->sci_lintlib_basename);
			if (sci->sci_cfiles.list != (struct llist *)NULL) {
				/*
				 *  There are C-source files.  A lint
				 *  library for the source is to built.
				 *  We still check however to see if the
				 *  product has an existing lint library
				 *  in the <machine> directory, in case
				 *  we have added source to an otherwise
				 *  purely binary distribution....
				 */
				fprintf(makefile_fp, " \\\n\t%s", lintlib_name);
			}
			/*
			 *  We check to see that the lint library is present
			 *  before referencing it.  The name of the lint
			 *  library becomes defined when the archive name
			 *  is defined, but its definition does not ne-
			 *  cessarily imply the presence of any actual
			 *  code....
			 */
			if (file_in_machine_dir(lintlib_name)) {
				fprintf(makefile_fp, " \\\n\t../%s/%s",
					machine_type, lintlib_name);
			}
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\n\n");
	/*
	 *  Now write the lists of any product constituent lint-libraries.
	 */
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_cfiles.count > LLIB_SIZE) {
			/*
			 *  There are a large number of C-source files.
			 *  Create the definitions for the constituent
			 *  lint libraries to avoid argument overflow
			 *  on exec, and in the kernel makefile.
			 */
			product_name = sci->sci_product;
			if (sci == sys_basep)
				product_name = BASE_PRODUCT_NAME;
			fprintf(makefile_fp, "LLIBS_%s =", product_name);
			llistp = sci->sci_cfiles.list;
			do {
				fprintf(makefile_fp, " \\\n\tllib-l$(LL)%s.ln",
					llistp->ll_name);
				llistp = llistp->ll_next;
			} while (llistp != (struct llist *)NULL);
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\n\n");
}


/*
 * substitute %CFILES
 */
void
do_cfiles(makefile_fp)
register FILE *makefile_fp;
{
	extern void print_list();	/* print the named list */
	char *filename;			/* ptr to filename of sys_config file */
	register struct llist *llistp;

	if (DEBUG_LEVEL(0))
		printf("Do_cfiles\n");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		llistp = sys_configp->sci_cfiles.list;
		while (llistp != (struct llist *)NULL) {
			print_list(makefile_fp, llistp->ll_list.l_head,
				   llistp->ll_name);
			fputs("\n", makefile_fp);
			llistp = llistp->ll_next;
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
}


/*
 * substitute %ARCHIVES
 */
void
do_archives(makefile_fp)
register FILE *makefile_fp;
{
	static void maybe_copy_archive();
	char *filename;			/* ptr to sys_config filename */
	register struct sys_config *sci;

	if (DEBUG_LEVEL(0))
		printf("Do_archives\n");
	if (!base_archive_known)
		return;
	/*
	 *  Write the 'ARCHIVE_SYS' definition.
	 */
	sci = sys_basep;
	if (sci->sci_objects.l_head == (struct list *)NULL) {
		/*
		 *  No objects.  Must be previously built archive.
		 *  Assume archive in "<machine>" directory.
		 */
		fprintf(makefile_fp, "ARCHIVE_SYS = ../%s/%s\n\n",
			machine_type, sci->sci_archive);
	} else {
		/*
		 *  There ARE objects.  This archive is to be built, or
		 *  new objects are to be inserted into a copy of the
		 *  system archive taken from the <machine> directory.
		 */
		fprintf(makefile_fp, "ARCHIVE_SYS = %s\n\n", sci->sci_archive);
	}
	/*
	 *  Note: we are careful to write the list in reverse order
	 *	  so that unix.a is searched last.
	 */
	fprintf(makefile_fp, "ARCHIVES = ");
	opaque_sysconfig = last_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_archive != (char *)NULL) {
			if (sci->sci_objects.l_head == (struct list *)NULL) {
				/*
				 *  No objects.  Must be previously built
				 *  archive.  Assume archive in "<machine>"
				 *  directory.
				 */
				fprintf(makefile_fp, "\\\n\t../%s/%s",
					machine_type, sci->sci_archive);
			} else {
				/*
				 *  There ARE objects.  This archive is to be
				 *  built, or new objects are to be inserted
				 *  into a copy of the product archive taken
				 *  from the <machine> directory.
				 */
				maybe_copy_archive(sci->sci_archive);
				fprintf(makefile_fp, "\\\n\t%s",
					sci->sci_archive);
			}
		}
		opaque_sysconfig = prev_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\n\n");
}

/*
 *  Substitute %PRODUCTS
 *  All products with source are included in the list.
 */
void
do_products(makefile_fp)
register FILE *makefile_fp;
{
	char *filename;			/* ptr to sys_config filename */
	register struct sys_config *sci;
	register boolean first_time;
	char *product_name;

	if (DEBUG_LEVEL(0))
		printf("Do_products\n");
	first_time = TRUE;
	fprintf(makefile_fp, "PRODUCTS = \"");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_cfiles.list != (struct llist *)NULL) {
			/*
			 *  Handle the special case of having the base kernel
			 *  product defined in the system configuration file:
			 *  "system.std," use the name of "unix" instead of
			 *  "system."
			 */
			product_name = sci->sci_product;
			if (sci == sys_basep)
				product_name = BASE_PRODUCT_NAME;
			if (first_time) {
				first_time = FALSE;
				fprintf(makefile_fp, "%s", product_name);
			} else
				fprintf(makefile_fp, " %s", product_name);
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\"\n\n");
}


/*
 *  Write the lint library rules for the construction of any
 *  sub-constituent lint-libraries.
 */
void
do_lintlib_consrules(makefile_fp)
register FILE *makefile_fp;
{
	char lintlib_targetname[512];	/* lint library target name */
	char lintlib_dependencies[512];	/* lint library dependencies */
	char *filename;			/* ptr to sys_config filename */
	char *product_name;		/* ptr to product name */
	register struct sys_config *sci;

	if (DEBUG_LEVEL(0))
		printf("Do_lintlib_consrules\n");
	opaque_sysconfig = last_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_cfiles.count > LLIB_SIZE) {
			/*
			 *  There are a large number of C-source files.
			 *  Write the proper rules for the creation of
 			 *  the product lint libraries from their
			 *  constituent lint libraries.  We go to
			 *  these lengths to avoid argument overflow
			 *  on exec, and in the kernel makefile.
			 */
			product_name = sci->sci_product;
			if (sci == sys_basep)
				product_name = BASE_PRODUCT_NAME;
			/*
			 *  Historical note:  Interim implementation formatted
			 *		      the final library name as:
			 *
			 * 		"llib-l$(LL)%s.ln"
			 */
			(void)sprintf(lintlib_targetname, "llib-l%s.ln",
				      sci->sci_lintlib_basename);
			(void)sprintf(lintlib_dependencies, "$(LLIBS_%s)",
				      product_name);
			fprintf(makefile_fp, "%s: %s\n",
				lintlib_targetname, lintlib_dependencies);
			fprintf(makefile_fp, "\ttouch %s; \\\n",
				lintlib_targetname);
			fprintf(makefile_fp, "\tfor i in %s; \\\n",
				lintlib_dependencies);
			fputs("\tdo \\\n", makefile_fp);
			fprintf(makefile_fp, "\t\tcat $$i >> %s; \\\n",
				lintlib_targetname);
			fputs("\tdone\n\n", makefile_fp);
		}
		opaque_sysconfig = prev_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\n\n");
}


/*
 *  Write the archive construction rules.
 */
void
do_archive_rules(makefile_fp)
register FILE *makefile_fp;
{
	char *filename;			/* ptr to filename of sys_config file */
	register struct sys_config *sci;

	if (DEBUG_LEVEL(0))
		printf("Do_archive_rules\n");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		sci = sys_configp;
		if (sci->sci_archive != (char *)NULL &&
		    sci->sci_objects.l_head != (struct list *)NULL) {
			/*
			 *  This product has an archive and objects
			 *  to be placed in the archive.  Write an
			 *  archive dependency/construction rule.
			 */
			fprintf(makefile_fp,
			"%s::\t$& $(OBJS_%s)\n\techo $? | xargs $(AR) r %s\n",
				sci->sci_archive, sci->sci_product,
				sci->sci_archive);
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
	fprintf(makefile_fp, "\n\n");
}


/*
 *  Print all C-source file names to the given file stream.
 */
void
print_cfile_names(fp)
register FILE *fp;
{
	extern void print_lines();	/* print lines to the given file */
	char *filename;			/* ptr to filename of sys_config file */
	register struct llist *llistp;

	if (DEBUG_LEVEL(0))
		printf("Print_cfile_names\n");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		llistp = sys_configp->sci_cfiles.list;
		while (llistp != (struct llist *)NULL) {
			print_lines(fp, llistp->ll_list.l_head, TRUE);
			llistp = llistp->ll_next;
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
}


/*
 *  Print module configuration file names for all products.
 */
void
print_modconfig_names(bfile_fp)
register FILE *bfile_fp;
{
	char *filename;			/* ptr to filename of sys_config file */
	register struct pmodule *pmodp;

	if (DEBUG_LEVEL(0))
		printf("Print_modconfig_names\n");
	opaque_sysconfig = first_symtab(sysfiles, &filename, &sys_configp);
	while (opaque_sysconfig != (opaque_ptr)NULL) {
		pmodp = sys_configp->sci_pmodset;
		while (pmodp != (struct pmodule *)NULL) {
			if (!pmodp->pmod_internalmod) {
				fprintf(bfile_fp, "$(UTS)/%s/config\n",
					pmodp->pmod_common.cmod_relpath);
			}
			pmodp = pmodp->pmod_next;
		}
		opaque_sysconfig = next_symtab(opaque_sysconfig, &filename,
					       &sys_configp);
	}
}


/*
 *  Test for the existence of the named file in the machine directory.
 */
static boolean
file_in_machine_dir(filename)
char *filename;				/* name of the file */
{
	extern boolean is_file();
	char machpath[MAXPATH];

	(void)sprintf(machpath, "%s/%s", Mach_dir, filename);
	return (is_file(machpath));
}


/*
 *  If the indicated archive file exists in the machine directory,
 *  but not in the build directory, then copy the archive from the 
 *  machine directory to the build directory.
 *
 *  This is done for the case of a binary configuration, and the
 *  site has elected to add some new modules (e.g. device drivers)
 *  to the kernel.
 */
static void
maybe_copy_archive(archive_name)
char *archive_name;			/* archive file name */
{
	char frompath[MAXPATH];
	char topath[MAXPATH];
	char buf[BUFSIZ*2];
	int oldumask;
	register int fromfd;
	register int tofd;
	register int count;

	(void)sprintf(frompath, "%s/%s", Mach_dir, archive_name);
	(void)sprintf(topath, "%s/%s", Build_dir, archive_name);
	/*
	 *  We shouldn't have to do this....
	 *  If, for unfathomnable reasons, the umask is set to disallow
	 *  the owner write privilege, we make sure to override this
	 *  since the archive being copied in here must be writeable.
	 */
	oldumask = umask(0);
	(void)umask(oldumask & 0177);
	if ((fromfd = open(frompath, O_RDONLY)) == -1) {
		if (errno != ENOENT) {
			fflush(stdout);
			fprintf(stderr, "Unable to read archive: [%s].\n",
				frompath);
			perror("config");
			Trouble = TRUE;
		}
	} else if ((tofd = open(topath, O_WRONLY|O_CREAT|O_EXCL, 0644)) == -1) {
		if (errno != EEXIST) {
			fflush(stdout);
			fprintf(stderr, "Unable to write archive: [%s].\n",
				topath);
			perror("config");
			Trouble = TRUE;
		}
		(void)close(fromfd);
	} else {
		/*
		 *  We are going to copy the archive.
		 */
		if (DEBUG_LEVEL(0)) {
			printf("Copying archive: [%s] to [%s].\n",
				frompath, topath);
		}
		while ((count = read(fromfd, buf, sizeof(buf))) > 0) {
			if (write(tofd, buf, count) != count) {
				fflush(stdout);
				fprintf(stderr,
					"Error copying archive: [%s].\n",
					topath);
				perror("config");
				break;
			}
		}
		(void)close(fromfd);
		if (count < 0 || close(tofd) == -1) {
			fflush(stdout);
			fprintf(stderr, "Error copying archive: [%s].\n",
				topath);
			perror("config");
			Trouble = TRUE;
		}
	}
	(void)umask(oldumask);		/* restore original umask */
}
