/*
 * $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: makemk.c 1.36 90/05/17 $"

#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include <sys/stat.h>
#include "def_rules.h"
#include "allocate.h"			/* perm_{alloc,str}, free_list() */
#include "symbols.h"			/* symbol table defns */

extern	char	*strcpy();
extern	void	do_product_objs();	/* substitute %OBJS in makefile */
extern	void	do_lintlibs();		/* substitute %LINT_LIBS in makefile */
extern	void	do_cfiles();		/* substitute %CFILES in makefile */
extern	void	do_archives();		/* substitute %ARCHIVES in makefile */
extern	void	do_products();		/* substitute %PRODUCTS in makefile */
extern	void	do_lintlib_consrules();	/* write high-level lint rules */
extern	void	do_archive_rules();	/* write archive rules in makefile */
extern	void	print_cfile_names();	/* print all C-file source names */
extern	void	print_modconfig_names();/* print mod_config file names */
extern	boolean	is_file();

struct hdrdir {
	char *hd_name;			/* name of header directory */
	opaque_ptr hd_subdirs;		/* subdirectories */
};

static	opaque_ptr hdrdirs = (opaque_ptr)NULL;

static	char	*crule;
static	char	*srule;
static	char	*hrule;
static	char	*lnrule;
static	char	*grule;

static	void	do_objs();
static	void	do_sfiles();
static	void	do_hfiles();
static	void	do_binaries();
static	void	do_rules();
static	void	do_space();
static	void	do_mach();
static	void	do_mkopts();
struct	llist	*find_list();

char	*arch_type;				/* architecture type */
struct	list_head	obj_space_list;
struct	list_head	sfile_list;
struct	list_head	hfile_list;
struct	list_head	crules_list;
struct	list_head	hrules_list;
struct	list_head	srules_list;
struct	list_head	nrules_list;
struct	list_head	space_list;
struct	list_head	bfiles_space_list;
struct	list_head	bfiles_stubs_list;
struct	list_head	bin_list;
struct	list_head	mkopts_list;
struct	list_head	conf_list;
struct	list_head	confh_list;
struct	llist		*hfile_llist;

static	int	hfiles = 0;	/* number of header files so far */
static	int	hf_cnt = 0;	/* number of header group so far */

struct mk_pp {
	char	*tok;
	void	(*func)();
} mk_pp[] = {
	{	"%ARCH_TYPE"	,	do_mach },
	{	"%MKOPTS"	,	do_mkopts },
	{	"%OBJS"		,	do_objs },
	{	"%HFILES"	,	do_hfiles },
	{	"%SPACE"	,	do_space },
	{	"%LINT_LIBS"	,	do_lintlibs },
	{	"%CFILES"	,	do_cfiles },
	{	"%SFILES"	,	do_sfiles },
	{	"%BINARIES"	,	do_binaries },
	{	"%ARCHIVES"	,	do_archives },
	{	"%PRODUCTS"	,	do_products },
	{	"%RULES"	,	do_rules },
};

#define	MAX_PP	(sizeof mk_pp/sizeof(struct mk_pp))

/*
 * print a line list to a file.
 */
void
print_lines(fp, lp , full)
register FILE *fp;
register struct list *lp;
register boolean full;
{
	while(lp) {
		if ((!full) && (strncmp(lp->l_text, "$(UTS)", 6) == 0))
			fprintf(fp, "../..%s\n", &lp->l_text[6]);
		else
			fprintf(fp, "%s\n", lp->l_text);
		lp = lp->l_next;
	}
}

/*
 *  print list in form X = \n a\\n b\\n .....
 */
void
print_list(fp, lp, name)
	register FILE		*fp;
	register struct list	*lp;
	char			*name;
{
	fprintf(fp, "%s = ", name);
	while(lp) {
		fprintf(fp, " \\\n	%s", lp->l_text);
		lp = lp->l_next;
	}
	fprintf(fp,"\n");
}

/*
 *  Insert a header subdirectory into the header directory tree.
 */
static void
new_header_dir(subdirs, next_componentp)
opaque_ptr subdirs;				/* ptr to subdirs table */
register char *next_componentp;			/* ptr to hdrdir path */
{
	register struct hdrdir *hdp;		/* ptr to hdr dir data */
	register char component_termch;		/* component termination char */
	char *curr_componentp;			/* ptr to curr component */

	for (;;) {
		component_termch = *next_componentp;
		while (component_termch == '/') {	/* skip leading '/'s */
			component_termch = *++next_componentp;
		}
		if (component_termch == '\0')		/* no path components */
			return;
		curr_componentp = next_componentp;	/* 1st component char */
		component_termch = *++next_componentp;
		while (component_termch != '\0') {	/* scan for 0 or '/'  */
			if (component_termch == '/') {	/* component end found*/
				*next_componentp = '\0';/* zap terminating '/'*/
				break;
			}
			component_termch = *++next_componentp;
		}
		hdp = (struct hdrdir *)search_symtab(subdirs, curr_componentp);
		if (hdp == (struct hdrdir *)NULL) {
			hdp = (struct hdrdir*)perm_alloc(sizeof(struct hdrdir));
			hdp->hd_name = perm_str(curr_componentp);
			hdp->hd_subdirs = create_symtab(4);
			if (insert_symtab(subdirs, hdp->hd_name,
					 (opaque_ptr)hdp) != (opaque_ptr)NULL) {
				fatal("Header component collision.\n");
				exit(1);
			}
		}
		subdirs = hdp->hd_subdirs;
		if (component_termch == '/')		/* restore terminator */
			*next_componentp++ = component_termch;
	}
}

/*
 *  Make a header subdirectory.
 */
static void
mk_hdrsubdirs(subdirs, pathbuf, pathlen)
register opaque_ptr subdirs;			/* current subdirectory */
char pathbuf[MAXPATH];				/* directory path buffer */
int pathlen;					/* directory path length */
{
	extern boolean mkdirectory();
	register opaque_ptr opaque_hdrdirp;	/* current header dir */
	struct hdrdir *hdrdir_datap;		/* ptr to hdr dir data */
	register struct hdrdir *hdp;		/* ptr to hdr dir data */
	char *hdrdir_component;			/* header directory component */

	opaque_hdrdirp = first_symtab(subdirs,
				      &hdrdir_component, &hdrdir_datap);
	while (opaque_hdrdirp != (opaque_ptr)NULL) {
		hdp = hdrdir_datap;
		pathbuf[pathlen] = '/';
		(void)strcpy(&pathbuf[pathlen+1], hdp->hd_name);
		if (DEBUG_LEVEL(1))
			printf("Making directory: [%s].\n", pathbuf);
		if (mkdirectory(pathbuf, FALSE)) {
			mk_hdrsubdirs(hdp->hd_subdirs, pathbuf,
				      pathlen+1+strlen(hdp->hd_name));
		} else
			Trouble = TRUE;
		opaque_hdrdirp = next_symtab(opaque_hdrdirp,
					     &hdrdir_component, &hdrdir_datap);
	}
}

/*
 *  Make the necessary header directories.
 */
void
mk_hdrdirs()
{
	char pathbuf[MAXPATH];			/* directory path buffer */
	if (hdrdirs == (opaque_ptr)NULL)	/* No header directories */
		return;
	(void)sprintf(pathbuf, "%s/%s", Anchor_dir, LOCAL_HEADERS);
	if (mkdirectory(pathbuf, FALSE)) {
		if (DEBUG_LEVEL(1))
			printf("Making directory: [%s].\n", pathbuf);
		mk_hdrsubdirs(hdrdirs, pathbuf, strlen(pathbuf));
	} else
		Trouble = TRUE;
}

/*
 * scan the skeleton makefile for the special pre-warning
 * character "%" . Then token substitute.
 * Table mk_pp defines token name and action routine.
 */
scan_skel() 
{
	extern	char	*strtok();
	extern	char	rcs_id[];

	char	line[BUFSIZ];
	FILE	*skel_makefile_fp;		/* Skeleton makefile */
	FILE	*makefile_fp;			/* Makefile */

	register char	*t;
	register int	i;

	if (DEBUG_LEVEL(0))
		printf("\n\tGenerating makefile\n");
	if ((skel_makefile_fp = fopen(Smk_file, "r")) == (FILE *)NULL) {
		fatal("unable to open %s",Smk_file);
		exit(1);
	}	
	SET_FILE(Mk_file+Anchor_len+1);
	if (Lflag != 2) {
		/*
		 * Initialise the Makefile.
		 * write preamble.
		 */
		if ((makefile_fp = fopen(Mk_file, "w+")) == (FILE *)NULL) {
			fatal("cannot create %s\n",Mk_file);
			exit(1);
		}
		fprintf(makefile_fp,"#\n");
		fprintf(makefile_fp,
			"# This makefile was produced by config %s\n",rcs_id);
		fprintf(makefile_fp,"# from the skeleton makefile:\n");
		t = Smk_file;
		i = *t;
		while (i == '.' || i == '/')
			i = *++t;
		fprintf(makefile_fp,"#  %s\n", t);
		fprintf(makefile_fp,"#\n");
	}
	if (Lflag) {
		FILE	*lf;
		/*
		 * creat file list.
		 */
		if ((lf = fopen(Files_file, "w+")) == (FILE *)NULL) {
			fatal("cannot create %s\n",Files_file);
			exit(1);
		}
		print_cfile_names(lf);
		print_lines(lf, sfile_list.l_head, TRUE);
		print_lines(lf, space_list.l_head, TRUE);
		if (ferror(lf) || fclose(lf)) {
			unlink(Files_file);
			unlink(Mk_file);	/* prevent kernel build */
			fflush(stdout);
			fprintf(stderr, "Error writing file: [%s].\n",
				Files_file);
			Trouble = TRUE;
		}

		/*
		 *  Create the list of files needed for a binary distribution
		 *  (with the exception of the headers).
		 */
		if ((lf = fopen(Bfiles_file, "w+")) == (FILE *)NULL) {
			fatal("cannot create %s\n",Bfiles_file);
			exit(1);
		}
		print_modconfig_names(lf);
		print_lines(lf, space_list.l_head, TRUE);
		print_lines(lf, bfiles_space_list.l_head, TRUE);
		print_lines(lf, bfiles_stubs_list.l_head, TRUE);
		if (ferror(lf) || fclose(lf)) {
			unlink(Bfiles_file);
			unlink(Mk_file);	/* prevent kernel build */
			fflush(stdout);
			fprintf(stderr, "Error writing file: [%s].\n",
				Bfiles_file);
			Trouble = TRUE;
		}

		/*
		 * done ?
		 */
		if (Lflag == 2)
			exit(0);
	}
	while(fgets(line, BUFSIZ, skel_makefile_fp) != (char *)NULL) {
		if (*line == '%') {
			t = strtok(line, ": \t\n");
			for (i=0; i<MAX_PP; i++) {
				if (strcmp(t, mk_pp[i].tok) == 0) {
					(*mk_pp[i].func)(makefile_fp);
					break;
				}
			}
		} else 
			fputs(line, makefile_fp);
	}
	if (ferror(makefile_fp) || fclose(makefile_fp)) {
		unlink(Mk_file);		/* prevent kernel build */
		fflush(stdout);
		fprintf(stderr, "Error writing file: [%s].\n", Mk_file);
		Trouble = TRUE;
	}
	(void)fclose(skel_makefile_fp);
}

/*
 * substitute %OBJS
 */
static void
do_objs(makefile_fp)
register FILE *makefile_fp;
{
	do_product_objs(makefile_fp);
	if (DEBUG_LEVEL(0))
		printf("Writing SPACE_OBJS\n");
	print_list(makefile_fp, obj_space_list.l_head, "SPACE_OBJS");
}


/*
 * substitute %SFILES
 */
static void
do_sfiles(makefile_fp)
register FILE *makefile_fp;
{
	if (DEBUG_LEVEL(0))
		printf("Do_sfiles\n");
	print_list(makefile_fp, sfile_list.l_head, "SFILES");
}

/*
 * substitute %HFILES
 */
static void
do_hfiles(makefile_fp)
register FILE *makefile_fp;
{
	register struct	llist	*ll;

	if (DEBUG_LEVEL(0))
		printf("Do_hfiles\n");
	print_list(makefile_fp, hfile_list.l_head, "HFILES");
	for(ll = hfile_llist; ll != (struct llist *)NULL; ll=ll->ll_next) {
		print_list(makefile_fp, ll->ll_list.l_head, ll->ll_name);
	}
}

/*
 * substitute %BINARIES
 */
static void
do_binaries(makefile_fp)
register FILE *makefile_fp;
{
	if (DEBUG_LEVEL(0))
		printf("Do_binaries\n");
	print_list(makefile_fp, bin_list.l_head, "BINARIES");
}

/*
 * substitute %RULES
 */
static void
do_rules(makefile_fp)
register FILE *makefile_fp;
{
	if (DEBUG_LEVEL(0))
		printf("Do_rules\n");
	print_cat(makefile_fp, crules_list.l_head, "\n");
	print_cat(makefile_fp, srules_list.l_head, "\n");
	print_cat(makefile_fp, hrules_list.l_head, "\n");
	do_lintlib_consrules(makefile_fp);
	print_cat(makefile_fp, nrules_list.l_head, "\n");
	do_archive_rules(makefile_fp);
}

/*
 * substitute %SPACE
 */
static void
do_space(makefile_fp)
register FILE *makefile_fp;
{
	if (DEBUG_LEVEL(0))
		printf("Do_space\n");
	print_list(makefile_fp, space_list.l_head, "SPACE");
}


/*
 * substitute %MAKEOPTS
 */
static void
do_mkopts(makefile_fp)
register FILE *makefile_fp;
{
	if (DEBUG_LEVEL(0))
		printf("Do_mkopts\n");
	print_cat(makefile_fp, mkopts_list.l_head, "\n");
}


/*
 * substitute %ARCH_TYPE
 */
static void
do_mach(makefile_fp)
register FILE *makefile_fp;
{
	fprintf(makefile_fp,"ARCH_TYPE = %s\n",arch_type); /* last one wins */
	fprintf(makefile_fp,"MACH = %s\n",machine_type);
	fprintf(makefile_fp,"UTS = ..\n");
	fprintf(makefile_fp,"CONFIG_T = %s\n",configuration_name);
}


/*
 *  add to the %SPACE_OBJS list
 */
add_objs_space(name)
	char	*name;
{
	if (DEBUG_LEVEL(6))
		printf("Add objs_space %s\n",name);
	perm_to_tail(&obj_space_list, name);
}


/*
 *  Add the indicated C-module to the end of the %CRULES list.
 */
add_crules(isspacefile, c_name, o_name, depend, pmodp)
	boolean isspacefile;
	char *c_name;
	char *o_name;
	char *depend;
	struct pmodule *pmodp;
{
	if (DEBUG_LEVEL(6))
		printf("Add c rule %s\n", c_name);
	perm_to_tail(&crules_list,
		     ms(isspacefile ? grule : crule, o_name, c_name, T_ddirl,
			depend, pmodp->pmod_common.cmod_name) );
}


/*
 * add a string to the end of the %SRULES list.
 */
add_srules(name)
	char	*name;
{
	if (DEBUG_LEVEL(6))
		printf("Add s rule %s\n",name);
	perm_to_tail(&srules_list, name);
}


/*
 * add a string to the end of the %HRULES list.
 */
add_hrules(name)
	char	*name;
{
	if (DEBUG_LEVEL(6))
		printf("Add h rule %s\n",name);
	perm_to_tail(&hrules_list, name);
}


/*
 * add a string to the end of the %LNRULES list.
 */
add_lnrules(lintlib_basename, lintlib_targetname, lintlib_dependencies)
	char *lintlib_basename;
	char *lintlib_targetname;
	char *lintlib_dependencies;
{
	if (DEBUG_LEVEL(6))
		printf("Add ln rule %s\n", lintlib_targetname);
	perm_to_tail(&nrules_list, ms(lnrule, lintlib_targetname, "", "",
				      lintlib_dependencies, lintlib_basename) );
}

/*
 *  add to the %SPACE list
 *  generate the corresponding %CRULE
 */

add_space(c_name, o_name)
	register char	*c_name;
	char		*o_name;
{
	char	dependencies[512];

	if (DEBUG_LEVEL(6))
		printf("Add space %s\n", c_name);
	perm_to_tail(&space_list, c_name);
	(void)sprintf(dependencies, "%s %s", c_name, CONFIG_H);
	add_crules(TRUE, c_name, o_name, dependencies, Pmodp);
}


/*
 *  Add to the "binary files" space list when the space file
 *  is for a module that is not configured (deconfigured) in
 *  the current configuration.
 */

add_bfiles_space(spacelist_head)
	struct list_head spacelist_head;
{
	char pathbuf[MAXPATH];
	register struct list *listp;

	listp = spacelist_head.l_head;
	while (listp != (struct list *)NULL) {
		if (DEBUG_LEVEL(6))
			printf("Add bfiles space: %s\n", listp->l_text);
		(void)sprintf(pathbuf, "%s/%s", T_dirl, listp->l_text);
		perm_to_tail(&bfiles_space_list, pathbuf);
		listp = listp->l_next;
	}
	free_list(spacelist_head.l_head);
}

/*
 *  Add to the "binary files" stubs file list.
 */

add_bfiles_stubs(name)
	register char	*name;
{
	char pathbuf[MAXPATH];

	if (DEBUG_LEVEL(6))
		printf("Add bfiles stubs: %s\n",name);
	(void)sprintf(pathbuf, "%s/%s", T_dirl, name);
	perm_to_tail(&bfiles_stubs_list, pathbuf);
}

/*
 * Add to the %BINARIES list.
 */
add_bin(name)
	register char 	*name;
{
	if (DEBUG_LEVEL(6))
		printf("Add_bin %s\n", name);
	perm_to_tail(&bin_list, name);
}

/*
 *  add to the %SFILE list
 *  generate the corresponding %CRULE
 */
add_sfiles(name, o_name, full_pathname)
	register char	*name;
	char		*o_name;
	char		*full_pathname;
{
	register char	*depends;

	if (DEBUG_LEVEL(6))
		printf("Add sfile %s-->%s\n",name,o_name);

	perm_to_tail(&sfile_list, name);
	depends = depend(full_pathname, name);
	add_srules( ms(srule, o_name, name, T_ddirl, depends,
		       Pmodp->pmod_common.cmod_name) );
}

/*
 *  add to the conf.c file.
 */
add_conf(name)
	char	*name;
{

	if (DEBUG_LEVEL(6))
		printf("Add conf \"%s\"\n",name);
	perm_to_tail(&conf_list, name);
}
	
/*
 *  add to the tmp/config.h file.
 */
add_confh(name)
	char	*name;
{

	if (DEBUG_LEVEL(6))
		printf("Add confh \"%s\"\n",name);
	perm_to_tail(&confh_list, name);
}
	

/*
 *  add to the %MAKEOPTS list
 */
add_mkopts(name)
	char	*name;
{

	if (DEBUG_LEVEL(6))
		printf("Add mkopts \"%s\"\n",name);
	
	perm_to_tail(&mkopts_list, name);
}

/*
 * Add (list of binaries) to the %BINARIES list.
 */
add_binaries(binlist_head)
	struct list_head binlist_head;
{
	char pathbuf_obj[MAXPATH];
	register struct list *binaryp;

	if (DEBUG_LEVEL(6))
		printf("Add_binaries: entry\n");

	for(binaryp = binlist_head.l_head;
	    binaryp != (struct list *)NULL; binaryp = binaryp->l_next) {
		(void)sprintf(pathbuf_obj, "%s/%s", Build_dir, binaryp->l_text);
		if (! is_file(pathbuf_obj)) {
			fatal(" missing object file \"%s\"\n", binaryp->l_text);
			exit(1);
		}
		add_bin(binaryp->l_text);
	}
	free_list(binlist_head.l_head);
	if (DEBUG_LEVEL(6))
		printf("Add_binaries: exit\n");
}

/*
 *  add (list of headers) to the %HFILES list (of lists),
 *  generate the corresponding %HRULE
 */
add_headers(hdrlist_head)
	struct list_head hdrlist_head;
{
	register struct	list	*headerp;
	register char		*name;
	char			list_name[MAXPATH];
	char			dependency_name[MAXPATH];
	char			pathbuf_hdr[MAXPATH];

	for (headerp = hdrlist_head.l_head;
	     headerp != (struct list *)NULL; headerp = headerp->l_next) {
		if (Vers.version >= 107) {
			name = sname(headerp->l_text);
			if (hdrdirs == (opaque_ptr)NULL)
				hdrdirs = create_symtab(4);
			if (name != headerp->l_text) {
				*(name-1) = '\0';
				new_header_dir(hdrdirs, headerp->l_text);
				*(name-1) = '/';
			}
		} else
			name = headerp->l_text;
		(void)sprintf(pathbuf_hdr, "%s/%s", Module_dir, name);
		if (! is_file(pathbuf_hdr)) {
			fatal(" missing headers file \"%s\"\n",headerp->l_text);
			exit(1);
		}
		(void)sprintf(list_name, "HF_%s_%d",
			      Pmodp->pmod_common.cmod_name, hf_cnt);
		(void)sprintf(dependency_name, "H_%s_%d",
			      Pmodp->pmod_common.cmod_name, hf_cnt);
		add_hf(headerp->l_text, list_name, dependency_name);
	}
	free_list(hdrlist_head.l_head);
}


/*
 *  For now just save the list since this module may not be configured.
 *  Add (list of space files) to the %SPACE list
 */
struct list_head Spacefile_listhead;

add_spacefiles(spacelist_head)
	struct list_head spacelist_head;
{
	if (Spacefile_listhead.l_head == (struct list *)NULL)
		Spacefile_listhead.l_head = spacelist_head.l_head;
	else
		Spacefile_listhead.l_last->l_next = spacelist_head.l_head;
	Spacefile_listhead.l_last = spacelist_head.l_last;
}

really_add_spacefiles()
{
	char pathbuf_hdr[MAXPATH];
	char pathbuf_obj[MAXPATH];
	register struct	list *spacep;
	register char *p;

	for (spacep = Spacefile_listhead.l_head;
	     spacep != (struct list *)NULL; spacep = spacep->l_next) {
		(void)sprintf(pathbuf_hdr, "%s/%s", Module_dir, spacep->l_text);
		if (! is_file(pathbuf_hdr)) {
			fatal(" missing space file \"%s\"\n", spacep->l_text);
			exit(1);
		}
		(void)sprintf(pathbuf_hdr, "%s/%s", T_dirl, spacep->l_text);
		(void)strcpy(pathbuf_obj, spacep->l_text);
		if ((p=strrchr(pathbuf_obj, '.')) == (char *)NULL) {
			fatal("syntax error in file name %s",spacep->l_text);
			exit(1);
		}
		*++p = 'o';
		*++p = '\0';
		add_space(pathbuf_hdr, pathbuf_obj);
		add_objs_space(pathbuf_obj);
	}
	if (Spacefile_listhead.l_head != (struct list *)NULL)
		free_list(Spacefile_listhead.l_head);
}

/*
 * Add "simple file name:h_file" to list "list_name" and dependency
 * name "dependency_name"
 * Keep no more than 20 files in each list to avoid Makefile limits.
 */
static
add_hf(h_file, list_name, dependency_name)
	char	*h_file;
	char	*list_name;
	char	*dependency_name;
{
	register struct	llist	*ll;
	char			sys_file[MAXPATH];
	char			buff[BUFSIZ];

	if ((ll=find_list(list_name, hfile_llist)) == (struct llist *)NULL) {
		ll = (struct llist *)perm_alloc(sizeof(struct llist));
		ll->ll_name = perm_str(list_name);
		ll->ll_list.l_head = (struct list *)NULL;
		ll->ll_list.l_last = (struct list *)NULL;
		ll->ll_next = hfile_llist;
		hfile_llist = ll;
		perm_to_tail(&hfile_list, dependency_name);
		(void)sprintf(buff,"%s:$(&) $(%s)\n", dependency_name,
			      ll->ll_name);
		add_hrules(buff);
	}
	if (Vers.version >= 107)
		(void)sprintf(sys_file,"%s/%s", INCLUDE_PATH, h_file);
	else
		(void)sprintf(sys_file,"%s/%s", INCLUDE_SYS_PATH, h_file);
	perm_to_tail(&ll->ll_list, sys_file);
	if (hfiles++ > 20) {
		hfiles = 0;
		hf_cnt++;
	}
	if (Vers.version >= 107)
		(void)sprintf(buff, "%s/%s", T_dirl, sname(h_file));
	else
		(void)sprintf(buff, "%s/%s", T_dirl, h_file);
	add_hrules( ms(hrule, sys_file, buff, T_ddirl, buff,
		       Pmodp->pmod_common.cmod_name) );
}

/*
 *  Keep a list of sub-modules for each primary module.
 *
 *  If the module is configured, then this is the list of modules named
 *  in all "modules { }" directives in the module configuration file.
 *
 *  If the module is not configured (deconfigured), then this is the
 *  list of stubs files named in all "stubs { }" directives in the
 *  module configuration file.
 */
new_submodule(module_relpath, configured)
	register char *module_relpath;	/* relative pathname of module */
	boolean configured;		/* module is configured flag */
{
#	define SMOD_HASHENTS	200	/* #of sub-module hash entries */
	static char valtypes[] = "Type must be either: '.s' or '.c'";
	static struct smodule *static_smodp = (struct smodule *)NULL;
	static opaque_ptr smods = (opaque_ptr)NULL;
	char pathbuf[MAXPATH];
	register struct smodule *smodp;
	register char *ptr_type;
	struct smodule *othersmodp;
	char *mod_type_name;
	char *mod_name;

	mod_type_name = "ource";		/* [Ss]ource */
	if (!configured)
		mod_type_name = "tubs";	/* [Ss]tubs */
	if (smods == (opaque_ptr)NULL)
		smods = create_symtab(SMOD_HASHENTS);
	(void)sprintf(pathbuf, "%s/%s", Module_dir, module_relpath);
	if (! is_file(pathbuf)) {
		yyerror("Missing s%s module file: [%s].\n",
			mod_type_name, pathbuf);
		return;
	}
	if ((ptr_type = strrchr(module_relpath, '.')) == (char *)NULL) {
		yyerror("S%s module type unspecified: [%s].\n\t%s.\n",
			mod_type_name, module_relpath, valtypes);
		return;
	}
	(void)strncpy(pathbuf, module_relpath, ptr_type - module_relpath);
	pathbuf[ptr_type - module_relpath] = '\0';
	smodp = static_smodp;
	if (smodp == (struct smodule *)NULL) {
		smodp = (struct smodule *)perm_alloc(sizeof(struct smodule));
		static_smodp = smodp;
	}
	switch (smodp->smod_type = *++ptr_type) {
	case 'c':
	case 's':
		break;
	default:
		yyerror("Invalid S%s module type specified: [%s].\n\t%s.\n",
			mod_type_name, module_relpath, valtypes);
		return;
	}
	smodp->smod_next = (struct smodule *)NULL;
	smodp->smod_common.cmod_relpath = perm_str(module_relpath);
	smodp->smod_common.cmod_name = perm_str(pathbuf);
	smodp->smod_common.cmod_subo = (struct macro *)NULL;
	othersmodp = (struct smodule *)
		      insert_symtab(smods, smodp->smod_common.cmod_name,
				    (opaque_ptr)smodp);
	if (othersmodp == (struct smodule *)NULL) {
		if (Pmodp->pmod_fsubm == (struct smodule *)NULL)
			Pmodp->pmod_fsubm = smodp;
		else
			Pmodp->pmod_lsubm->smod_next = smodp;
		Pmodp->pmod_lsubm = smodp;
		if (DEBUG_LEVEL(12)) {
			printf("New s%s module: [%s] (%s) added to: [%s].\n",
				mod_type_name, smodp->smod_common.cmod_name,
				smodp->smod_common.cmod_relpath,
				Ffile);
		}
		static_smodp = (struct smodule *)NULL;
		Lcmodp = &smodp->smod_common;
	} else {
		fprintf(stderr,
			"S%s module: [%s] (%s) type: [%c] already configured\n",
			mod_type_name, smodp->smod_common.cmod_name,
			smodp->smod_common.cmod_relpath,
			smodp->smod_type);
		fprintf(stderr,"\tas s%s module: [%s] (%s) type: [%c].\n",
			mod_type_name, othersmodp->smod_common.cmod_name,
			othersmodp->smod_common.cmod_relpath,
			smodp->smod_type);
		/* MOREHERE to identify parent module and product */
		Trouble = TRUE;
	}
}

/*
 * Search a list for a matching "name".
 */
struct llist *
find_list(name, llist)
	register char		*name;
	register struct	llist	*llist;
{
	register struct	llist	*ll;

	for(ll=llist; ll!=(struct llist *)NULL; ll=ll->ll_next) {
		if (strcmp(name, ll->ll_name) == 0)
			return (ll);
	}
	return ((struct llist *)NULL);
}


/*
 * handles the %RULE keyword.
 */
set_def_rule(type, rule)
	char	*type;
	char	*rule;
{
	register char	*p;
	register char	*q;
	register char	*r;

	r = (char *)perm_alloc(strlen(rule)+3);
	q = r;
	for(p=rule; *p; p++) {
		if (*p == '\\') {
			switch (*++p) {
			case 'n':
				*q++ = '\n';
				break;
			case '\\':
				*q++ = '\\';
				break;
			case 't':
				*q++ = '\t';
				break;
			default:
				*q++ = *(p);
			}
		} else 
			*q++ = *p;
	}
	if (*(q-1) != '\n')
		*q++ = '\n';
	*q = '\0';

	if (DEBUG_LEVEL(6))
		printf("Set_def_rule: %s = \"%s\"\n",type,rule);
	switch(*type) {
	case	'g':
		def_grule = r;
		return;
	case	'c':
		def_crule = r;
		return;
	case	'h':
		def_hrule = r;
		return;
	case	's':
		def_srule = r;
		return;
	case	'n':
		def_lnrule = r;
		return;
	default:
		yyerror("syntax error");
	}
}

/*
 * default rules
 */
set_rules()
{
	crule = def_crule;
	srule = def_srule;
	hrule = def_hrule;
	lnrule = def_lnrule;
	grule = def_grule;
}
