/*
 * $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: util.c 1.27 90/04/13 $"

#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include <sys/stat.h>
#include "allocate.h"		/* perm_alloc(), perm_str() */

extern	char	*malloc();
extern	char	*realloc();

/*
 *  Conditionally create a directory (if it does not exist).
 *  Returns (only if 'abortonerr' was FALSE):
 *	TRUE if the directory already existed, or was created successfully.
 *	FALSE otherwise.
 */
boolean
mkdirectory(path, abortonerr)
register char *path;		/* pathname of directory to create */
boolean	abortonerr;		/* abort config if error */
{
	static char emsg[] = "Unable to create directory: [%s].\n";
	struct stat st;
	char path_cmd[MAXPATH+6];

	if (stat(path, &st) == - 1) {
		sprintf (path_cmd, "mkdir %s", path);
		if (system(path_cmd) < 0) {
			if (abortonerr)
				fatal(emsg, path);	/* no return */
			fflush(stdout);
			fprintf(stderr, emsg, path);
			return(FALSE);
		}
	} else if ((st.st_mode & S_IFDIR) == 0) {
		if (abortonerr)
			fatal(emsg, path);		/* no return */
		fflush(stdout);
		fprintf(stderr, emsg, path);
		return(FALSE);
	}
	return(TRUE);
}


/*
 * return TRUE if file exists.
 */
boolean
is_file(path)
register char *path;
{
	struct	stat	st;

	if ((stat(path, &st) != - 1) && ((st.st_mode & S_IFMT) == S_IFREG)) {
		if (DEBUG_LEVEL(12))
			printf("Is_file: [%s] returns TRUE.\n", path);
		return(TRUE);
	}
	if (DEBUG_LEVEL(12))
		printf("Is_file: [%s] returns FALSE.\n", path);
	return(FALSE);
}


/*
 * return TRUE if directory exists
 */
boolean
is_dir(path)
char	*path;
{
	struct	stat	st;

	if (stat(path, &st) != -1) {
		if ((st.st_mode & S_IFMT) == S_IFDIR) {
			if (DEBUG_LEVEL(12))
				printf("Is_dir: [%s] returns TRUE.\n", path);
			return(TRUE);
		}
	}
	if (DEBUG_LEVEL(12))
		printf("Is_dir: [%s] returns FALSE.\n", path);
	return(FALSE);
}

/*
 *  Return TRUE if the two pathnames refer to the same file.
 *  Otherwise FALSE is returned.
 */
boolean
is_same_file(path1, path2)
char	*path1;
char	*path2;
{
	struct	stat	st1;
	struct	stat	st2;

	if (stat(path1, &st1) != -1) {
		if (stat(path2, &st2) != -1) {
			if (st1.st_dev == st2.st_dev &&
			    st1.st_ino == st2.st_ino) {
				return(TRUE);
			}
			return(FALSE);
		}
		perror("stat: ");
		printf("is_same_file stat#2: \"%s\":0x%x\n", path2, path2);
	} else {
		perror("stat: ");
		printf("is_same_file stat#1: \"%s\":0x%x\n", path1, path1);
	}
	fatal("is_same_file(): Stat() failed.\n");
	exit(1);
}

#undef fatal

#undef fatal
/*VARARGS1*/
void
_fatal(string , args)
char	*string;
struct {
	int	arg[5];
} args;
{
	char	buff[1000];
	int		n;
	extern	int	yylineno;

	fflush(stdout);
	if (yylineno) {
		n = sprintf(buff, " %d: ", yylineno);
		(void)sprintf(&buff[n], string, args);
	} else
		(void)sprintf(buff, string, args);
	fatal(buff);
}
#define fatal _fatal

/*
 * Add a global sub-option macro defination.
 */
void
add_gmop(name, value)
char	*name;
char	*value;
{
	register struct	macro	*m;

	m = (struct macro *)perm_alloc(sizeof(struct macro));
	m->macro_name = perm_str(name);
	m->macro_value = perm_str(value);
	m->macro_next = Subo;			/* added to global */
	Subo = m;				/* sub-option list */
}

/*
 * Add a local sub-option macro defination.
 */
void
add_lmop(name, value)
char	*name;
char	*value;
{
	register struct	macro	*m;

	m = (struct macro *)perm_alloc(sizeof(struct macro));
	m->macro_name = perm_str(name);
	m->macro_value = perm_str(value);
	m->macro_next = Lcmodp->cmod_subo;	/* added to modules or files */
	Lcmodp->cmod_subo = m;			/* sub-option list */
}

/*
 * print a concatenated list to a file.
 */
void
print_cat(fp, lp, tail)
FILE		*fp;
struct list	*lp;
char		*tail;
{
	while(lp) {
		fputs(lp->l_text, fp);
		lp = lp->l_next;
	}
	fputs(tail, fp);
}

/*
 * convert string to upper case (in place).
 */
void
to_upper(s)
char	*s;
{
	register char ch;

	ch = *s;
	while (ch) {
		if (islower(ch))
			*s = toupper(ch);
		ch = *++s;
	}
}

/*
 * Macro substitute a rule string %(NAME)
 * If the rule does not start with a tab, then it is a continuation
 * of the dependencies.
 * Search the string for construct %(sub_option)
 * Built in sub_options are T D and S for target source and target directory.
 */
static	int		ms_cnt;
static	int		ms_bsize;
static	char 		*ms_buff;

char	*
ms(rule, targ, s_file, t_dir, dep, t_mod)
	char 	*rule;			/* rule:   READONLY */
	char	*targ;			/* target: READONLY */
	char	*s_file;		/* source file name: READONLY */
	char	*t_dir;			/* target directory: READONLY */
	char	*dep;			/* dependencies: READONLY */
	char	*t_mod;			/* module name:  READONLY */
{
	void		msext();

	char		*p;
	char		*m;
	struct	macro	*mac;
	char		tok[20];

	if (DEBUG_LEVEL(16))
		printf(" ms(%s)\n", rule);
	ms_cnt = 0;
	/*
	 * Allocate a buffer if needed.
	 */
	if (ms_bsize == 0) {
		ms_bsize = BUFSIZ;
		if ((ms_buff = malloc(ms_bsize)) == (char *)NULL){
			fatal("ms: unable to allocate\n");
			exit(1);
		}
	}
	/*
	 * if no target rule specified add one
	 */
	if (isspace(*rule)) {
		ms_cnt += sprintf(ms_buff, "%s: %s\n", targ, dep);
	}
	/*
	 * Seach for %(<x>).
	 */
	for(p=rule; *p ;) {
		/*
		 *  The following code reads:  "< 3"  (and not "< 2") because
		 *  the code later on assumes that it does not have to check
		 *  *before* storing a character into ms_buff.
		 */
		if ((ms_bsize-ms_cnt) < 3)
			msext();
		if ((ms_buff[ms_cnt++] = *p++) != '%')
			continue;
		if ((ms_buff[ms_cnt++] = *p++) != '(')
			continue;
		ms_cnt -= 2;
		for(m=tok; *p != ')';) {
			*m++ = *p++;
		}
		*m = '\0';
		p++;
		if (tok[1] == '\0') {
			/*
			 * Check for built in tokens.
			 */
			switch(tok[0]) {
			case 't':	/* simple name of target */
				for(m=sname(targ); *m;) {
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				continue;
			case 'T':	/* full name of target */
				for(m=targ; *m;) {
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				continue;
			case 'S':	/* full source file name */
				for(m=s_file; *m;) {
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				continue;
			case 'D':	/* dependencies */
				for(m=dep; *m;){
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				continue;
			case 'd':	/* Target directory */
				for(m=t_dir; *m;){
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				ms_buff[ms_cnt++] = '/';
				if (ms_cnt >= ms_bsize)
					msext();
				continue;
			case 'm':	/* module name */
				for(m=t_mod; *m;){
					ms_buff[ms_cnt++] = *m++;
					if (ms_cnt >= ms_bsize)
						msext();
				}
				continue;
			}
		}
		/*
		 * Check for library specifier sub opt token.
		 * Replace %(tok) with library substitution macros.
		 */
		mac = Lcmodp->cmod_subo;
		if (mac != (struct macro *)NULL) {
			if (DEBUG_LEVEL(12))
				printf("Lib subs_macros on %%(%s)\n", tok);

			/*
			 * search library list.
			 */
			do {
				if (strcmp(tok, mac->macro_name) == 0) {
					m = mac->macro_value;
					while(*m){
						ms_buff[ms_cnt++] = *m++;
						if (ms_cnt >= ms_bsize)
							msext();
					}
					break;
				}
				mac = mac->macro_next;
			} while (mac != (struct macro *)NULL);
			if (mac != (struct macro *)NULL)
				continue;
		}
		/*
		 * Check for module sub opt token.
		 * Replace %(tok) with module substitution macros.
		 */
		mac = Pmodp->pmod_common.cmod_subo;
		if ( (mac != (struct macro *)NULL) &&
		     (&Pmodp->pmod_common != Lcmodp) ) {
			if (DEBUG_LEVEL(12))
				printf("Subs_macros on %%(%s)\n", tok);

			/*
			 * search module list.
			 */
			do {
				if (strcmp(tok, mac->macro_name) == 0) {
					m = mac->macro_value;
					while(*m) {
						ms_buff[ms_cnt++] = *m++;
						if (ms_cnt >= ms_bsize)
							msext();
					}
					break;
				}
			     	mac = mac->macro_next;
			} while (mac != (struct macro *)NULL);
			if (mac != (struct macro *)NULL)
				continue;
		}
		/*
		 * Check for global sub opt token.
		 * Replace %(tok) with global substitution macros.
		 */
		mac = Subo;
		if (mac != (struct macro *)NULL) {
			if (DEBUG_LEVEL(12))
				printf("Subs_macros on %%(%s)\n", tok);

			/*
			 * search global list.
			 */
			do {
				if (strcmp(tok, mac->macro_name) == 0) {
					m = mac->macro_value;
					while(*m) {
						ms_buff[ms_cnt++] = *m++;
						if (ms_cnt >= ms_bsize)
							msext();
					}
					break;
				}
				mac = mac->macro_next;
			} while (mac != (struct macro *)NULL);
			if (mac != (struct macro *)NULL)
				continue;
		}
	}
	ms_buff[ms_cnt++] = '\0';
	if (DEBUG_LEVEL(16))
		printf(" ms expanded(%s)\n", ms_buff);
	return (ms_buff);
}


/*
 * extend the string for ms() using realloc().
 */
void
msext()
{
	ms_bsize += BUFSIZ;
	if ((ms_buff=realloc(ms_buff, ms_bsize)) == (char *)NULL){
		fatal("msext: unable to allocate space\n");
		exit(1);
	}
}


/*
 *  Return TRUE if the given configuration is being configured.
 *  Otherwise, FALSE is returned.
 */
boolean
ifconfig(configuration_query)
register char *configuration_query;
{
	if (strcmp(configuration_query, configuration_name) == 0)
		return(TRUE);
	return(FALSE);
}


/*
 *  Return TRUE if the given configuration is NOT the one being configured.
 *  Otherwise, FALSE is returned.
 */
boolean
ifnconfig(configuration_query)
register char *configuration_query;
{
	if (ifconfig(configuration_query))
		return(FALSE);
	return(TRUE);
}


/*
 *  Verify the existence of the arch_type directory
 */
void
check_arch_type(architecture)
char *architecture;
{
	char	buff[MAXPATH];

	if (DEBUG_LEVEL(0))
		printf("Check_arch_type [%s].\n", architecture);
	(void)sprintf(buff, "%s/kernel/%s", Anchor_dir, architecture);
	if (!is_dir(buff)) {
		fatal("Architecture type [%s] is not supported.\n",
		      architecture);
		exit(1);
	}
}


/*
 *  Check for version support
 */
void
check_version(version, sub_version, flags)
int version;
int sub_version;
int flags;
{
	static char *fatal_msg =
"Version (%d.%d) config does not support\n\tversion (%d.%d) specified in %s\n";
	register int new_effect_version;

	if (DEBUG_LEVEL(12)) {
		printf ("Check_version(%d, %d, %d)\n",
			version, sub_version, flags);
	}
	/*
	 *  Test the various version levels.
	 */
	if (sub_version > 99) {
		fatal("Illegal value of sub_version in %s.\n", Ffile);
		exit(1);
	}
	new_effect_version = version * 100 + sub_version;
	if (Vers.version == 0) {	/* First version directive seen */
		if (version != SUPPORT_V) {
			fatal(fatal_msg, SUPPORT_V, SUPPORT_SVH,
					 version, sub_version, Ffile);
			exit(1);
		}
		if (sub_version < SUPPORT_SVL || sub_version > SUPPORT_SVH) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
				"WARNING: config version is (%d.%d) while\n",
				SUPPORT_V, SUPPORT_SVH);
			fprintf(stderr, "\t %s specifies (%d, %d)\n",
				Ffile, version, sub_version);
			Warning = TRUE;
		}
		Vers.version = new_effect_version;
		Vers.flags = flags;
	} else if (Vers.version != new_effect_version) {
		if (version != SUPPORT_V) {
			fatal(fatal_msg, SUPPORT_V, SUPPORT_SVH,
					 version, sub_version, Ffile);
			exit(1);
		}
		if ((Vers.version >= 107 && new_effect_version <  107) ||
		    (Vers.version <  107 && new_effect_version >= 107)) {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
		"ERROR: Version (%d.%d) defined in: [%s] is incompatible.\n",
				version, sub_version, Ffile);
			fprintf(stderr,
				"\twith previous version (%d.%d).\n",
				Vers.version / 100, Vers.version % 100);
			fatal("Cannot continue.\n");
			exit(1);
		} else {
			fflush(stdout);		/* In case stdout > stderr */
			fprintf(stderr,
				"WARNING: [%s] specifies a different\n", Ffile);
			fprintf(stderr, "\tversion than previously defined.\n");
			Warning = TRUE;
		}
	}
}


/*
 *  Check to see that a description directive was contained in the system
 *  configuration file.  If not, complain about it.
 */
void
check_description()
{
  	extern boolean desc_flag;

	if (desc_flag) {
	  	desc_flag = FALSE;
		return;
	}
	fflush(stdout);		/* In case stdout > stderr */
	fprintf(stderr,
		"WARNING: no DESCRIPTION directive present in file \"%s\"\n",
		Ffile);
	Warning = TRUE;
}
