/* $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: read_constab.c 1.3 88/11/27 $"

#include <string.h>
#include <stdio.h>
#include <constab.h>

#define MAXFIELDS 100

char line[BUFSIZ];
char *fields[MAXFIELDS];
char *Strcpy();

extern char *calloc();

/*
 * scan the CONSTAB file and load up the structure table ctab with
 * the results.  The parameter last_entry will point at the last entry in
 * the table ctab.  Format of the constab file is:
 *
 * <devtype>	<parent>	[ <child> [ <child> .. ] ] <other stuff>
 */

read_constab(ctab, last_entry)
	struct constab **ctab, **last_entry;
{
	int i, nkids, nhints;
	int nlines = 0;
	char **cp, **flp;
	FILE *f;
	struct constab *ct;
	int nargs;

	if ((f = fopen(CONSTAB, "r")) == NULL) {
		(void)fprintf(stderr, "devcons: unable to open %s\n", CONSTAB);
		return(-1);
	}

	/*
	 * we need to learn how big to make the table - means reading
	 * through the file once to decide how many lines are there.
	 */
	
	while (getline(f, line) != 0)
		nlines++;
	if (nlines == 0)
		return(-1);
	rewind(f);
	*ctab = (struct constab *)calloc((unsigned)nlines,
					sizeof(struct constab));
	if (*ctab == NULL) {
		(void)fprintf(stderr, "devcons: out of memory\n");
		return(-1);
	}
	ct = *ctab;

	/*
	 * loop through the lines in the file, absorbing comment chars
	 * and handling escaped newlines.
	 */

	while (getline(f, line) > 0) {
		nargs = getargs(line, fields, MAXFIELDS);
		if (nargs == -1) {
			/*
			 * Just ignore this line & hope it goes away
			 */
			(void)fprintf(stderr, "devcons: too many fields: <%s>\n",
				line);
			continue;

		}
		if (nargs < 2) {
			/*
			 * Need at least two args per line
			 */
			(void)fprintf(stderr, "devcons: not enough fields: <%s>\n",
				line);
			continue;
		}

		ct->ct_devtype = Strcpy(fields[CT_DEVTYPE]);
		if (ct->ct_devtype == NULL) {
			(void)fprintf(stderr, "devcons: out of memory\n");
			return(-1);
		}
			
		ct->ct_parent = Strcpy(fields[CT_PARENT]);
		if (ct->ct_parent == NULL) {
			(void)fprintf(stderr, "devcons: out of memory\n");
			return(-1);
		}

		/*
		 * skip the first two args and get to the rest
		 */
		flp = &fields[2];
		nargs -= 2;

		/*
		 * short circuit
		 */
		if (nargs == 0) {
			*last_entry = ct;
			ct++;
			continue;
		}

		/*
		 * scan through the kids, discovering how many of them are
		 * present.  Then allocate enough space for the whole lot
		 * of them, plus an extra for a NULL entry.
		 * NOTE: We assume all kids are listed before
		 * any of the hints.
		 */
		nkids = 0;
		for (i = 0; i < nargs; i++) {
			if (*flp[i] == '/') {
				nkids++;
			} else {
				break;
			}
		}

		ct->ct_kids = (char **)calloc((unsigned)nkids + 1,
						sizeof(char *));
		if (nkids && ct->ct_kids == NULL) {
			(void)fprintf(stderr, "devcons: out of memory\n");
			return(-1);
		}

		/*
		 * all the rest of the arguments will be hints.
		 * NOTE: we assume all hints are listed after the kids.
		 */
		nhints = nargs - i;

		ct->ct_hints = (char **)calloc((unsigned)nhints + 1,
						sizeof(char *));
		if (nhints && ct->ct_hints == NULL) {
			(void)fprintf(stderr, "devcons: out of memory\n");
			return(-1);
		}

		/*
		 * Now process the line, setting up the table
		 */
		cp = &(ct->ct_kids[0]);
		while (nargs && *flp[0] == '/') {
			*cp = Strcpy(*flp);
			if (*cp == NULL) {
				(void)fprintf(stderr, "devcons: out of memory\n");
				return(-1);
			}
			cp++;
			flp++;
			nargs--;
		}

		cp = &(ct->ct_hints[0]);
		while (nargs) {
			*cp = Strcpy(*flp);
			if (*cp == NULL) {
				(void)fprintf(stderr, "devcons: out of memory\n");
				return(-1);
			}
			cp++;
			flp++;
			nargs--;
		}
		*last_entry = ct;
		ct++;
	}
			
	return(0);
}

/*
 *  given a file pointer and buffer, construct logical line in buffer
 *  (i.e., concatenate lines ending in '\').  return length of line
 *  treat as comments all chars after '#'
 *  ASSUMES that buffer is BUFSIZ long!
 */

static int
getline(f, linep)
	FILE *f;
	char *linep;
{	
	char *lptr, *lend;

	lptr = linep;
loop:
	while (fgets(lptr, (linep + BUFSIZ) - lptr, f) != NULL) {
		lend = lptr + strlen(lptr);
		if (lend == lptr || lend[-1] != '\n')	
			/* empty buf or line too long! */
			break;
		*--lend = '\0'; /* lop off ending '\n' */
		if ( lend == linep ) /* empty line - ignore */
			continue;
		lptr = lend;
		if (lend[-1] != '\\')
			break;
		/* continuation */
		lend[-1] = ' ';
	}
	if ((lend = strchr(linep, '#')) != NULL) {
		*lend = '\0'; /* lop off ending '\n' */
		lptr = lend;
		if (lend == linep)
			goto loop;
	}
	return(lptr - linep);
}

/*
 *	getargs  -  this routine will generate a vector of
 *	pointers (arps) to the substrings in string "s".
 *	Each substring is separated by blanks and/or tabs.
 *
 *	return - the number of subfields, or -1 if >= maxargs.
 */

static int
getargs(s, arps, maxargs)
register char *s;
char *arps[];
int maxargs;
{
	register int i;

	i = 0;
	while (i < maxargs) {
		while (*s == ' ' || *s == '\t')
			*s++ = '\0';
		if (*s == '\n')
			*s = '\0';
		if (*s == '\0')
			break;
		arps[i++] = s++;
		while (*s != '\0' && *s != ' '
			&& *s != '\t' && *s != '\n')
				s++;
	}
	if (i >= maxargs)
		return -1;
	arps[i] = NULL;
	return i;
}

/*
 * malloc some space for a string and copy it in, returning a pointer to
 * that space.
 */

static char *
Strcpy(s)
	char *s;
{
	char *r = calloc((unsigned)strlen(s) + 1, 1);

	if (!r)
		return(NULL);
	return(strcpy(r, s));
}
