xref: /csrg-svn/usr.sbin/config.new/sem.c (revision 61818)
157495Storek /*
2*61818Sbostic  * Copyright (c) 1992, 1993
3*61818Sbostic  *	The Regents of the University of California.  All rights reserved.
457495Storek  *
557495Storek  * This software was developed by the Computer Systems Engineering group
657495Storek  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
757495Storek  * contributed to Berkeley.
857495Storek  *
957495Storek  * All advertising materials mentioning features or use of this software
1057495Storek  * must display the following acknowledgement:
1157495Storek  *	This product includes software developed by the University of
1257495Storek  *	California, Lawrence Berkeley Laboratories.
1357495Storek  *
1457495Storek  * %sccs.include.redist.c%
1557495Storek  *
16*61818Sbostic  *	@(#)sem.c	8.1 (Berkeley) 06/06/93
1757495Storek  */
1857495Storek 
1957495Storek #include <sys/param.h>
2057495Storek #include <ctype.h>
2157495Storek #include <stdio.h>
2257495Storek #include <stdlib.h>
2357495Storek #include <string.h>
2457495Storek #include "config.h"
2557495Storek #include "sem.h"
2657495Storek 
2757495Storek /*
2857495Storek  * config semantics.
2957495Storek  */
3057495Storek 
3157495Storek #define	NAMESIZE	100	/* local name buffers */
3257495Storek 
3357495Storek static const char *s_generic;
3457495Storek static const char *s_qmark;
3557495Storek 
3657495Storek static struct hashtab *attrtab;		/* for attribute lookup */
3757495Storek static struct hashtab *cfhashtab;	/* for config lookup */
3857495Storek static struct hashtab *devitab;		/* etc */
3957495Storek 
4057495Storek static struct attr errattr;
4157495Storek static struct devbase errdev;
4257495Storek static struct devbase **nextbase;
4357495Storek static struct config **nextcf;
4457495Storek static struct devi **nextdevi;
4557495Storek static struct devi **nextpseudo;
4657495Storek 
4757495Storek static int has_errobj __P((struct nvlist *, void *));
4857495Storek static struct nvlist *addtoattr __P((struct nvlist *, struct devbase *));
4957495Storek static int exclude __P((struct nvlist *, const char *, const char *));
5057495Storek static int resolve __P((struct nvlist **, const char *, const char *,
5157495Storek 			struct nvlist *, int));
5257495Storek static int lresolve __P((struct nvlist **, const char *, const char *,
5357495Storek 			struct nvlist *, int));
5457495Storek static struct devi *newdevi __P((const char *, int, struct devbase *d));
5557495Storek static struct devi *getdevi __P((const char *));
5657495Storek static const char *concat __P((const char *, int));
5757495Storek static int split __P((const char *, size_t, char *, size_t, int *));
5857495Storek static void selectbase __P((struct devbase *));
5957495Storek static int onlist __P((struct nvlist *, void *));
6057495Storek static const char **fixloc __P((const char *, struct attr *, struct nvlist *));
6157495Storek 
6257495Storek void
initsem()6357495Storek initsem()
6457495Storek {
6557495Storek 
6657495Storek 	attrtab = ht_new();
6757495Storek 	errattr.a_name = "<internal>";
6857495Storek 
6957495Storek 	allbases = NULL;
7057495Storek 	nextbase = &allbases;
7157495Storek 
7257495Storek 	cfhashtab = ht_new();
7357495Storek 	allcf = NULL;
7457495Storek 	nextcf = &allcf;
7557495Storek 
7657495Storek 	devitab = ht_new();
7757495Storek 	alldevi = NULL;
7857495Storek 	nextdevi = &alldevi;
7957495Storek 	errdev.d_name = "<internal>";
8057495Storek 
8157495Storek 	allpseudo = NULL;
8257495Storek 	nextpseudo = &allpseudo;
8357495Storek 
8457495Storek 	s_generic = intern("generic");
8557495Storek 	s_qmark = intern("?");
8657495Storek }
8757495Storek 
8857495Storek void
enddefs(fname)8957495Storek enddefs(fname)
9057495Storek 	const char *fname;
9157495Storek {
9257495Storek 	register struct devbase *dev;
9357495Storek 
9457495Storek 	for (dev = allbases; dev != NULL; dev = dev->d_next) {
9557495Storek 		if (!dev->d_isdef) {
9657495Storek 			(void)fprintf(stderr,
9757495Storek 			    "%s: device `%s' used but not defined\n",
9857495Storek 			    fname, dev->d_name);
9957495Storek 			errors++;
10057495Storek 			continue;
10157495Storek 		}
10257495Storek 	}
10357495Storek 	if (errors) {
10457495Storek 		(void)fprintf(stderr, "*** Stop.\n");
10557495Storek 		exit(1);
10657495Storek 	}
10757495Storek }
10857495Storek 
10957495Storek void
setdefmaxusers(min,def,max)11057495Storek setdefmaxusers(min, def, max)
11157495Storek 	int min, def, max;
11257495Storek {
11357495Storek 
11457495Storek 	if (min < 1 || min > def || def > max)
11557495Storek 		error("maxusers must have 1 <= min <= default <= max");
11657495Storek 	else {
11757495Storek 		minmaxusers = min;
11857495Storek 		defmaxusers = def;
11957495Storek 		maxmaxusers = max;
12057495Storek 	}
12157495Storek }
12257495Storek 
12357495Storek void
setmaxusers(n)12457495Storek setmaxusers(n)
12557495Storek 	int n;
12657495Storek {
12757495Storek 
12857495Storek 	if (maxusers != 0) {
12957495Storek 		error("duplicate maxusers parameter");
13057495Storek 		return;
13157495Storek 	}
13257495Storek 	maxusers = n;
13357495Storek 	if (n < minmaxusers) {
13457495Storek 		error("warning: minimum of %d maxusers assumed\n", minmaxusers);
13557495Storek 		errors--;	/* take it away */
13657495Storek 		maxusers = minmaxusers;
13757495Storek 	} else if (n > maxmaxusers) {
13857495Storek 		error("warning: maxusers (%d) > %d", n, maxmaxusers);
13957495Storek 		errors--;
14057495Storek 	}
14157495Storek }
14257495Storek 
14357495Storek /*
14457495Storek  * Define an attribute, optionally with an interface (a locator list).
14557495Storek  * Since an empty locator list is logically different from "no interface",
14657495Storek  * all locator lists include a dummy head node, which we discard here.
14757495Storek  */
14857495Storek int
defattr(name,locs)14957495Storek defattr(name, locs)
15057495Storek 	const char *name;
15157495Storek 	struct nvlist *locs;
15257495Storek {
15357495Storek 	register struct attr *a;
15457495Storek 	register struct nvlist *nv;
15557495Storek 	register int len;
15657495Storek 
15757495Storek 	a = emalloc(sizeof *a);
15857495Storek 	if (ht_insert(attrtab, name, a)) {
15957495Storek 		free(a);
16057495Storek 		error("attribute `%s' already defined", name);
16157495Storek 		nvfreel(locs);
16257495Storek 		return (1);
16357495Storek 	}
16457495Storek 	a->a_name = name;
16557495Storek 	if (locs != NULL) {
16657495Storek 		a->a_iattr = 1;
16757495Storek 		a->a_locs = locs->nv_next;
16857495Storek 		nvfree(locs);
16957495Storek 	} else {
17057495Storek 		a->a_iattr = 0;
17157495Storek 		a->a_locs = NULL;
17257495Storek 	}
17357495Storek 	len = 0;
17457495Storek 	for (nv = a->a_locs; nv != NULL; nv = nv->nv_next)
17557495Storek 		len++;
17657495Storek 	a->a_loclen = len;
17757495Storek 	a->a_devs = NULL;
17857495Storek 	a->a_refs = NULL;
17957495Storek 	return (0);
18057495Storek }
18157495Storek 
18257495Storek /*
18357495Storek  * Return true if the given `error object' is embedded in the given
18457495Storek  * pointer list.
18557495Storek  */
18657495Storek static int
has_errobj(nv,obj)18757495Storek has_errobj(nv, obj)
18857495Storek 	register struct nvlist *nv;
18957495Storek 	register void *obj;
19057495Storek {
19157495Storek 
19257495Storek 	for (; nv != NULL; nv = nv->nv_next)
19357495Storek 		if (nv->nv_ptr == obj)
19457495Storek 			return (1);
19557495Storek 	return (0);
19657495Storek }
19757495Storek 
19857495Storek /*
19957495Storek  * Add a device base to a list in an attribute (actually, to any list).
20057495Storek  * Note that this does not check for duplicates, and does reverse the
20157495Storek  * list order, but no one cares anyway.
20257495Storek  */
20357495Storek static struct nvlist *
addtoattr(l,dev)20457495Storek addtoattr(l, dev)
20557495Storek 	register struct nvlist *l;
20657495Storek 	register struct devbase *dev;
20757495Storek {
20857495Storek 	register struct nvlist *n;
20957495Storek 
21057495Storek 	n = newnv(NULL, NULL, dev, 0);
21157495Storek 	n->nv_next = l;
21257495Storek 	return (n);
21357495Storek }
21457495Storek 
21557495Storek /*
21657495Storek  * Device a device, giving its allowable parent attachments, if any.
21757495Storek  * This may (or may not) also define an interface attribute and/or refer
21857495Storek  * to existing attributes.  There may be a list of vectors.
21957495Storek  */
22057495Storek void
defdev(dev,ispseudo,atlist,vectors,loclist,attrs)22157495Storek defdev(dev, ispseudo, atlist, vectors, loclist, attrs)
22257495Storek 	register struct devbase *dev;
22357495Storek 	int ispseudo;
22457495Storek 	struct nvlist *atlist, *vectors, *loclist, *attrs;
22557495Storek {
22657495Storek 	register struct nvlist *nv;
22757495Storek 	register struct attr *a;
22857495Storek 
22957495Storek 	if (dev == &errdev)
23057495Storek 		goto bad;
23157495Storek 	if (dev->d_isdef) {
23257495Storek 		error("redefinition of `%s'", dev->d_name);
23357495Storek 		goto bad;
23457495Storek 	}
23557495Storek 	dev->d_isdef = 1;
23657495Storek 	if (has_errobj(attrs, &errattr))
23757495Storek 		goto bad;
23857495Storek 
23957495Storek 	/*
24057495Storek 	 * Handle implicit attribute definition from locator list.  Do
24157495Storek 	 * this before scanning the `at' list so that we can have, e.g.:
24257495Storek 	 *	device foo at other, foo { slot = -1 }
24357495Storek 	 * (where you can plug in a foo-bus extender to a foo-bus).
24457495Storek 	 */
24557495Storek 	if (loclist != NULL) {
24657495Storek 		nv = loclist;
24757495Storek 		loclist = NULL;	/* defattr disposes of them for us */
24857495Storek 		if (defattr(dev->d_name, nv))
24957495Storek 			goto bad;
25057495Storek 		nv = newnv(dev->d_name, NULL, getattr(dev->d_name), 0);
25157495Storek 		nv->nv_next = attrs;
25257495Storek 		attrs = nv;
25357495Storek 	}
25457495Storek 
25557495Storek 	/* Committed!  Set up fields. */
25657495Storek 	dev->d_ispseudo = ispseudo;
25757495Storek 	dev->d_atlist = atlist;
25857495Storek 	dev->d_vectors = vectors;
25957495Storek 	dev->d_attrs = attrs;
26057495Storek 
26157495Storek 	/*
26257495Storek 	 * Turn the `at' list into interface attributes (map each
26357495Storek 	 * nv_name to an attribute, or to NULL for root), and add
26457495Storek 	 * this device to those attributes, so that children can
26557495Storek 	 * be listed at this particular device if they are supported
26657495Storek 	 * by that attribute.
26757495Storek 	 */
26857495Storek 	for (nv = atlist; nv != NULL; nv = nv->nv_next) {
26957495Storek 		if (nv->nv_name == NULL) {
27057495Storek 			nv->nv_ptr = NULL;	/* at root */
27157495Storek 			continue;
27257495Storek 		}
27357495Storek 		nv->nv_ptr = a = getattr(nv->nv_name);
27457495Storek 		if (a == &errattr)
27557495Storek 			continue;		/* already complained */
27657495Storek 		if (!a->a_iattr)
27757495Storek 			error("%s cannot be at plain attribute `%s'",
27857495Storek 			    dev->d_name, a->a_name);
27957495Storek 		else
28057495Storek 			a->a_devs = addtoattr(a->a_devs, dev);
28157495Storek 	}
28257495Storek 
28357495Storek 	/*
28457495Storek 	 * For each interface attribute this device refers to, add this
28557495Storek 	 * device to its reference list.  This makes, e.g., finding all
28657495Storek 	 * "scsi"s easier.
28757495Storek 	 */
28857495Storek 	for (nv = attrs; nv != NULL; nv = nv->nv_next) {
28957495Storek 		a = nv->nv_ptr;
29057495Storek 		if (a->a_iattr)
29157495Storek 			a->a_refs = addtoattr(a->a_refs, dev);
29257495Storek 	}
29357495Storek 	return;
29457495Storek bad:
29557495Storek 	nvfreel(atlist);
29657495Storek 	nvfreel(vectors);
29757495Storek 	nvfreel(loclist);
29857495Storek 	nvfreel(attrs);
29957495Storek }
30057495Storek 
30157495Storek /*
30257495Storek  * Look up a devbase.  Also makes sure it is a reasonable name,
30357495Storek  * i.e., does not end in a digit or contain special characters.
30457495Storek  */
30557495Storek struct devbase *
getdevbase(name)30657495Storek getdevbase(name)
30757495Storek 	const char *name;
30857495Storek {
30957495Storek 	register u_char *p;
31057495Storek 	register struct devbase *dev;
31157495Storek 
31257495Storek 	p = (u_char *)name;
31357495Storek 	if (!isalpha(*p))
31457495Storek 		goto badname;
31557495Storek 	while (*++p) {
31657495Storek 		if (!isalnum(*p) && *p != '_')
31757495Storek 			goto badname;
31857495Storek 	}
31957495Storek 	if (isdigit(*--p)) {
32057495Storek badname:
32157495Storek 		error("bad device base name `%s'", name);
32257495Storek 		return (&errdev);
32357495Storek 	}
32457495Storek 	dev = ht_lookup(devbasetab, name);
32557495Storek 	if (dev == NULL) {
32657495Storek 		dev = emalloc(sizeof *dev);
32757495Storek 		dev->d_name = name;
32857495Storek 		dev->d_next = NULL;
32957495Storek 		dev->d_isdef = 0;
33057495Storek 		dev->d_major = NODEV;
33157495Storek 		dev->d_atlist = NULL;
33257495Storek 		dev->d_vectors = NULL;
33357495Storek 		dev->d_attrs = NULL;
33457495Storek 		dev->d_ihead = NULL;
33557495Storek 		dev->d_ipp = &dev->d_ihead;
33657495Storek 		dev->d_umax = 0;
33757495Storek 		*nextbase = dev;
33857495Storek 		nextbase = &dev->d_next;
33957495Storek 		if (ht_insert(devbasetab, name, dev))
34057495Storek 			panic("getdevbase(%s)", name);
34157495Storek 	}
34257495Storek 	return (dev);
34357495Storek }
34457495Storek 
34557495Storek /*
34657495Storek  * Look up an attribute.
34757495Storek  */
34857495Storek struct attr *
getattr(name)34957495Storek getattr(name)
35057495Storek 	const char *name;
35157495Storek {
35257495Storek 	struct attr *a;
35357495Storek 
35457495Storek 	if ((a = ht_lookup(attrtab, name)) == NULL) {
35557495Storek 		error("undefined attribute `%s'", name);
35657495Storek 		a = &errattr;
35757495Storek 	}
35857495Storek 	return (a);
35957495Storek }
36057495Storek 
36157495Storek /*
36257495Storek  * Set the major device number for a device, so that it can be used
36357495Storek  * as a root/swap/dumps "on" device in a configuration.
36457495Storek  */
36557495Storek void
setmajor(d,n)36657495Storek setmajor(d, n)
36757495Storek 	struct devbase *d;
36857495Storek 	int n;
36957495Storek {
37057495Storek 
37157495Storek 	if (d != &errdev && d->d_major != NODEV)
37257495Storek 		error("device `%s' is already major %d",
37357495Storek 		    d->d_name, d->d_major);
37457495Storek 	else
37557495Storek 		d->d_major = n;
37657495Storek }
37757495Storek 
37857495Storek #define ABS(x) ((x) < 0 ? -(x) : (x))
37957495Storek 
38057495Storek static int
exclude(nv,name,what)38157495Storek exclude(nv, name, what)
38257495Storek 	struct nvlist *nv;
38357495Storek 	const char *name, *what;
38457495Storek {
38557495Storek 
38657495Storek 	if (nv != NULL) {
38757495Storek 		error("%s: swap generic must not specify %s", name, what);
38857495Storek 		return (1);
38957495Storek 	}
39057495Storek 	return (0);
39157495Storek }
39257495Storek 
39357495Storek /*
39457495Storek  * Map things like "ra0b" => makedev(major("ra"), 0*8 + 'b'-'a').
39557495Storek  * Handle the case where the device number is given but there is no
39657495Storek  * corresponding name, and map NULL to the default.
39757495Storek  */
39857495Storek static int
resolve(nvp,name,what,dflt,part)39957495Storek resolve(nvp, name, what, dflt, part)
40057495Storek 	register struct nvlist **nvp;
40157495Storek 	const char *name, *what;
40257495Storek 	struct nvlist *dflt;
40357495Storek 	register int part;
40457495Storek {
40557495Storek 	register struct nvlist *nv;
40657495Storek 	register struct devbase *dev;
40757495Storek 	register const char *cp;
40857495Storek 	register int maj, min, l;
40957495Storek 	int unit;
41057495Storek 	char buf[NAMESIZE];
41157495Storek 
41257495Storek 	if ((u_int)(part -= 'a') >= 7)
41357495Storek 		panic("resolve");
41457495Storek 	if ((nv = *nvp) == NULL) {
41557495Storek 		/*
41657495Storek 		 * Apply default.  Easiest to do this by number.
41757495Storek 		 */
41857495Storek 		maj = major(dflt->nv_int);
41957495Storek 		min = (minor(dflt->nv_int) & ~7) | part;
42057495Storek 		*nvp = nv = newnv(NULL, NULL, NULL, makedev(maj, min));
42157495Storek 	}
42257495Storek 	if (nv->nv_int != NODEV) {
42357495Storek 		/*
42457495Storek 		 * By the numbers.  Find the appropriate major number
42557495Storek 		 * to make a name.
42657495Storek 		 */
42757495Storek 		maj = major(nv->nv_int);
42857495Storek 		min = minor(nv->nv_int);
42957495Storek 		for (dev = allbases; dev != NULL; dev = dev->d_next)
43057495Storek 			if (dev->d_major == maj)
43157495Storek 				break;
43257495Storek 		if (dev == NULL)
43357495Storek 			(void)sprintf(buf, "<%d/%d>", maj, min);
43457495Storek 		else
43557495Storek 			(void)sprintf(buf, "%s%d%c", dev->d_name,
43657495Storek 			    min >> 3, (min & 7) + 'a');
43757495Storek 		nv->nv_str = intern(buf);
43857495Storek 		return (0);
43957495Storek 	}
44057495Storek 
44157495Storek 	/*
44257495Storek 	 * The normal case: things like "ra2b".  Check for partition
44357495Storek 	 * suffix, remove it if there, and split into name ("ra") and
44457495Storek 	 * unit (2).
44557495Storek 	 */
44657495Storek 	l = strlen(nv->nv_str);
44757495Storek 	cp = &nv->nv_str[l];
44857495Storek 	if (l > 1 && *--cp >= 'a' && *cp <= 'h' && isdigit(cp[-1])) {
44957495Storek 		l--;
45057495Storek 		part = *cp - 'a';
45157495Storek 	}
45257495Storek 	cp = nv->nv_str;
45357495Storek 	if (split(cp, l, buf, sizeof buf, &unit)) {
45457495Storek 		error("%s: invalid %s device name `%s'", name, what, cp);
45557495Storek 		return (1);
45657495Storek 	}
45757495Storek 	dev = ht_lookup(devbasetab, intern(buf));
45857495Storek 	if (dev == NULL || dev->d_major == NODEV) {
45957495Storek 		error("%s: can't make %s device from `%s'",
46057495Storek 		    name, what, nv->nv_str);
46157495Storek 		return (1);
46257495Storek 	}
46357495Storek 	nv->nv_name = dev->d_name;
46457495Storek 	nv->nv_int = makedev(dev->d_major, unit * 8 + part);
46557495Storek 	return (0);
46657495Storek }
46757495Storek 
46857495Storek static int
lresolve(nvp,name,what,dflt,part)46957495Storek lresolve(nvp, name, what, dflt, part)
47057495Storek 	register struct nvlist **nvp;
47157495Storek 	const char *name, *what;
47257495Storek 	struct nvlist *dflt;
47357495Storek 	int part;
47457495Storek {
47557495Storek 	int err;
47657495Storek 
47757495Storek 	while ((err = resolve(nvp, name, what, dflt, part)) == 0 &&
47857495Storek 	    (*nvp)->nv_next != NULL)
47957495Storek 		nvp = &(*nvp)->nv_next;
48057495Storek 	return (err);
48157495Storek }
48257495Storek 
48357495Storek /*
48457495Storek  * Add a completed configuration to the list.
48557495Storek  */
48657495Storek void
addconf(cf0)48757495Storek addconf(cf0)
48857495Storek 	register struct config *cf0;
48957495Storek {
49057495Storek 	register struct config *cf;
49157495Storek 	register struct nvlist *nv;
49257495Storek 	const char *name;
49357495Storek 
49457495Storek 	name = cf0->cf_name;
49557495Storek 	cf = emalloc(sizeof *cf);
49657495Storek 	if (ht_insert(cfhashtab, name, cf)) {
49757495Storek 		error("configuration `%s' already defined", name);
49857495Storek 		free(cf);
49957495Storek 		goto bad;
50057495Storek 	}
50157495Storek 	*cf = *cf0;
50257495Storek 
50357495Storek 	/*
50457495Storek 	 * Look for "swap generic".
50557495Storek 	 */
50657495Storek 	for (nv = cf->cf_swap; nv != NULL; nv = nv->nv_next)
50757495Storek 		if (nv->nv_str == s_generic)
50857495Storek 			break;
50957495Storek 	if (nv != NULL) {
51057495Storek 		/*
51157495Storek 		 * Make sure no root or dump device specified, and no
51257495Storek 		 * other swap devices.  Note single | here (check all).
51357495Storek 		 */
51457495Storek 		nv = cf->cf_swap;
51557495Storek 		if (exclude(cf->cf_root, name, "root device") |
51657495Storek 		    exclude(nv->nv_next, name, "additional swap devices") |
51757495Storek 		    exclude(cf->cf_dump, name, "dump device"))
51857495Storek 			goto bad;
51957495Storek 	} else {
52057495Storek 		nv = cf->cf_root;
52157495Storek 		if (nv == NULL) {
52257495Storek 			error("%s: no root device specified", name);
52357495Storek 			goto bad;
52457495Storek 		}
52557495Storek 		if (resolve(&cf->cf_root, name, "root", nv, 'a') |
52657495Storek 		    lresolve(&cf->cf_swap, name, "swap", nv, 'b') |
52757495Storek 		    resolve(&cf->cf_dump, name, "dumps", nv, 'b'))
52857495Storek 			goto bad;
52957495Storek 	}
53057495Storek 	*nextcf = cf;
53157495Storek 	nextcf = &cf->cf_next;
53257495Storek 	return;
53357495Storek bad:
53457495Storek 	nvfreel(cf0->cf_root);
53557495Storek 	nvfreel(cf0->cf_swap);
53657495Storek 	nvfreel(cf0->cf_dump);
53757495Storek }
53857495Storek 
53957495Storek void
setconf(npp,what,v)54057495Storek setconf(npp, what, v)
54157495Storek 	register struct nvlist **npp;
54257495Storek 	const char *what;
54357495Storek 	struct nvlist *v;
54457495Storek {
54557495Storek 
54657495Storek 	if (*npp != NULL) {
54757495Storek 		error("duplicate %s specification", what);
54857495Storek 		nvfreel(v);
54957495Storek 	} else
55057495Storek 		*npp = v;
55157495Storek }
55257495Storek 
55357495Storek static struct devi *
newdevi(name,unit,d)55457495Storek newdevi(name, unit, d)
55557495Storek 	const char *name;
55657495Storek 	int unit;
55757495Storek 	struct devbase *d;
55857495Storek {
55957495Storek 	register struct devi *i;
56057495Storek 
56157495Storek 	i = emalloc(sizeof *i);
56257495Storek 	i->i_name = name;
56357495Storek 	i->i_unit = unit;
56457495Storek 	i->i_base = d;
56557495Storek 	i->i_next = NULL;
56657495Storek 	i->i_bsame = NULL;
56757495Storek 	i->i_alias = NULL;
56857495Storek 	i->i_at = NULL;
56957495Storek 	i->i_atattr = NULL;
57057495Storek 	i->i_atdev = NULL;
57157495Storek 	i->i_locs = NULL;
57257495Storek 	i->i_cfflags = 0;
57357495Storek 	i->i_lineno = currentline();
57457495Storek 	if (unit >= d->d_umax)
57557495Storek 		d->d_umax = unit + 1;
57657495Storek 	return (i);
57757495Storek }
57857495Storek 
57957495Storek /*
58057495Storek  * Add the named device as attaching to the named attribute (or perhaps
58157495Storek  * another device instead) plus unit number.
58257495Storek  */
58357495Storek void
adddev(name,at,loclist,flags)58457495Storek adddev(name, at, loclist, flags)
58557495Storek 	const char *name, *at;
58657495Storek 	struct nvlist *loclist;
58757495Storek 	int flags;
58857495Storek {
58957495Storek 	register struct devi *i;	/* the new instance */
59057495Storek 	register struct attr *attr;	/* attribute that allows attach */
59157495Storek 	register struct devbase *ib;	/* i->i_base */
59257495Storek 	register struct devbase *ab;	/* not NULL => at another dev */
59357495Storek 	register struct nvlist *nv;
59457495Storek 	const char *cp;
59557495Storek 	int atunit;
59657495Storek 	char atbuf[NAMESIZE];
59757495Storek 
59857495Storek 	ab = NULL;
59957495Storek 	if (at == NULL) {
60057495Storek 		/* "at root" */
60157495Storek 		if ((i = getdevi(name)) == NULL)
60257495Storek 			goto bad;
60357495Storek 		/*
60457495Storek 		 * Must warn about i_unit > 0 later, after taking care of
60557495Storek 		 * the STAR cases (we could do non-star's here but why
60657495Storek 		 * bother?).  Make sure this device can be at root.
60757495Storek 		 */
60857495Storek 		ib = i->i_base;
60957495Storek 		if (!onlist(ib->d_atlist, NULL)) {
61057495Storek 			error("%s's cannot attach to the root", ib->d_name);
61157495Storek 			goto bad;
61257495Storek 		}
61357495Storek 		attr = &errattr;	/* a convenient "empty" attr */
61457495Storek 	} else {
61557495Storek 		if (split(at, strlen(at), atbuf, sizeof atbuf, &atunit)) {
61657495Storek 			error("invalid attachment name `%s'", at);
61757495Storek 			/* (void)getdevi(name); -- ??? */
61857495Storek 			goto bad;
61957495Storek 		}
62057495Storek 		if ((i = getdevi(name)) == NULL)
62157495Storek 			goto bad;
62257495Storek 		ib = i->i_base;
62357495Storek 		cp = intern(atbuf);
62457495Storek 		if ((attr = ht_lookup(attrtab, cp)) == NULL) {
62557495Storek 			/*
62657495Storek 			 * Have to work a bit harder to see whether we have
62757495Storek 			 * something like "tg0 at esp0" (where esp is merely
62857495Storek 			 * not an attribute) or "tg0 at nonesuch0" (where
62957495Storek 			 * nonesuch is not even a device).
63057495Storek 			 */
63157495Storek 			if ((ab = ht_lookup(devbasetab, cp)) == NULL) {
63257495Storek 				error("%s at %s: `%s' unknown",
63357495Storek 				    name, at, atbuf);
63457495Storek 				goto bad;
63557495Storek 			}
63657495Storek 			/*
63757495Storek 			 * See if the named parent carries an attribute
63857495Storek 			 * that allows it to supervise device ib.
63957495Storek 			 */
64057495Storek 			for (nv = ab->d_attrs; nv != NULL; nv = nv->nv_next) {
64157495Storek 				attr = nv->nv_ptr;
64257495Storek 				if (onlist(attr->a_devs, ib))
64357495Storek 					goto ok;
64457495Storek 			}
64557495Storek 			attr = &errattr;/* now onlist below will fail */
64657495Storek 		}
64757495Storek 		if (!onlist(attr->a_devs, ib)) {
64857495Storek 			error("%s's cannot attach to %s's", ib->d_name, atbuf);
64957495Storek 			goto bad;
65057495Storek 		}
65157495Storek 	}
65257495Storek ok:
65357495Storek 	if ((i->i_locs = fixloc(name, attr, loclist)) == NULL)
65457495Storek 		goto bad;
65557495Storek 	if (i->i_unit == STAR && ib->d_vectors != NULL) {
65657495Storek 		error("%s's cannot be *'d as they have preset vectors",
65757495Storek 		    ib->d_name);
65857495Storek 		goto bad;
65957495Storek 	}
66057495Storek 	i->i_at = at;
66157495Storek 	i->i_atattr = attr;
66257495Storek 	i->i_atdev = ab;
66357495Storek 	i->i_atunit = atunit;
66457495Storek 	i->i_cfflags = flags;
66557495Storek 	selectbase(ib);
66657495Storek 	/* all done, fall into ... */
66757495Storek bad:
66857495Storek 	nvfreel(loclist);
66957495Storek 	return;
67057495Storek }
67157495Storek 
67257495Storek void
addpseudo(name,number)67357495Storek addpseudo(name, number)
67457495Storek 	const char *name;
67557495Storek 	int number;
67657495Storek {
67757495Storek 	register struct devbase *d;
67857495Storek 	register struct devi *i;
67957495Storek 
68057495Storek 	d = ht_lookup(devbasetab, name);
68157495Storek 	if (d == NULL) {
68257495Storek 		error("undefined pseudo-device %s", name);
68357495Storek 		return;
68457495Storek 	}
68557495Storek 	if (!d->d_ispseudo) {
68657495Storek 		error("%s is a real device, not a pseudo-device", name);
68757495Storek 		return;
68857495Storek 	}
68957495Storek 	if (ht_lookup(devitab, name) != NULL) {
69057495Storek 		error("`%s' already defined", name);
69157495Storek 		return;
69257495Storek 	}
69357495Storek 	i = newdevi(name, number - 1, d);	/* foo 16 => "foo0..foo15" */
69457495Storek 	if (ht_insert(devitab, name, i))
69557495Storek 		panic("addpseudo(%s)", name);
69657495Storek 	selectbase(d);
69757495Storek 	*nextpseudo = i;
69857495Storek 	nextpseudo = &i->i_next;
69957495Storek 	npseudo++;
70057495Storek }
70157495Storek 
70257495Storek /*
70357495Storek  * Define a new instance of a specific device.
70457495Storek  */
70557495Storek static struct devi *
getdevi(name)70657495Storek getdevi(name)
70757495Storek 	const char *name;
70857495Storek {
70957495Storek 	register struct devi *i, *firsti;
71057495Storek 	register struct devbase *d;
71157495Storek 	int unit;
71257495Storek 	char base[NAMESIZE];
71357495Storek 
71457495Storek 	if (split(name, strlen(name), base, sizeof base, &unit)) {
71557495Storek 		error("invalid device name `%s'", name);
71657495Storek 		return (NULL);
71757495Storek 	}
71857495Storek 	d = ht_lookup(devbasetab, intern(base));
71957495Storek 	if (d == NULL) {
72057495Storek 		error("%s: unknown device `%s'", name, base);
72157495Storek 		return (NULL);
72257495Storek 	}
72357495Storek 	if (d->d_ispseudo) {
72457495Storek 		error("%s: %s is a pseudo-device", name, base);
72557495Storek 		return (NULL);
72657495Storek 	}
72757495Storek 	firsti = ht_lookup(devitab, name);
72857495Storek 	i = newdevi(name, unit, d);
72957495Storek 	if (firsti == NULL) {
73057495Storek 		if (ht_insert(devitab, name, i))
73157495Storek 			panic("getdevi(%s)", name);
73257495Storek 		*d->d_ipp = i;
73357495Storek 		d->d_ipp = &i->i_bsame;
73457495Storek 	} else {
73557495Storek 		while (firsti->i_alias)
73657495Storek 			firsti = firsti->i_alias;
73757495Storek 		firsti->i_alias = i;
73857495Storek 	}
73957495Storek 	*nextdevi = i;
74057495Storek 	nextdevi = &i->i_next;
74157495Storek 	ndevi++;
74257495Storek 	return (i);
74357495Storek }
74457495Storek 
74557495Storek static const char *
concat(name,c)74657495Storek concat(name, c)
74757495Storek 	const char *name;
74857495Storek 	int c;
74957495Storek {
75057495Storek 	register int len;
75157495Storek 	char buf[NAMESIZE];
75257495Storek 
75357495Storek 	len = strlen(name);
75457495Storek 	if (len + 2 > sizeof(buf)) {
75557495Storek 		error("device name `%s%c' too long", name, c);
75657495Storek 		len = sizeof(buf) - 2;
75757495Storek 	}
75857495Storek 	bcopy(name, buf, len);
75957495Storek 	buf[len] = c;
76057495Storek 	buf[len + 1] = 0;
76157495Storek 	return (intern(buf));
76257495Storek }
76357495Storek 
76457495Storek const char *
starref(name)76557495Storek starref(name)
76657495Storek 	const char *name;
76757495Storek {
76857495Storek 
76957495Storek 	return (concat(name, '*'));
77057495Storek }
77157495Storek 
77257495Storek const char *
wildref(name)77357495Storek wildref(name)
77457495Storek 	const char *name;
77557495Storek {
77657495Storek 
77757495Storek 	return (concat(name, '?'));
77857495Storek }
77957495Storek 
78057495Storek /*
78157495Storek  * Split a name like "foo0" into base name (foo) and unit number (0).
78257495Storek  * Return 0 on success.  To make this useful for names like "foo0a",
78357495Storek  * the length of the "foo0" part is one of the arguments.
78457495Storek  */
78557495Storek static int
split(name,nlen,base,bsize,aunit)78657495Storek split(name, nlen, base, bsize, aunit)
78757495Storek 	register const char *name;
78857495Storek 	size_t nlen;
78957495Storek 	char *base;
79057495Storek 	size_t bsize;
79157495Storek 	int *aunit;
79257495Storek {
79357495Storek 	register const char *cp;
79457495Storek 	register int c, l;
79557495Storek 
79657495Storek 	l = nlen;
79757495Storek 	if (l < 2 || l >= bsize || isdigit(*name))
79857495Storek 		return (1);
79957495Storek 	c = (u_char)name[--l];
80057495Storek 	if (!isdigit(c)) {
80157495Storek 		if (c == '*')
80257495Storek 			*aunit = STAR;
80357495Storek 		else if (c == '?')
80457495Storek 			*aunit = WILD;
80557495Storek 		else
80657495Storek 			return (1);
80757495Storek 	} else {
80857495Storek 		cp = &name[l];
80957495Storek 		while (isdigit(cp[-1]))
81057495Storek 			l--, cp--;
81157495Storek 		*aunit = atoi(cp);
81257495Storek 	}
81357495Storek 	bcopy(name, base, l);
81457495Storek 	base[l] = 0;
81557495Storek 	return (0);
81657495Storek }
81757495Storek 
81857495Storek /*
81957495Storek  * We have an instance of the base foo, so select it and all its
82057495Storek  * attributes for "optional foo".
82157495Storek  */
82257495Storek static void
selectbase(d)82357495Storek selectbase(d)
82457495Storek 	register struct devbase *d;
82557495Storek {
82657495Storek 	register struct attr *a;
82757495Storek 	register struct nvlist *nv;
82857495Storek 
82957495Storek 	(void)ht_insert(selecttab, d->d_name, (char *)d->d_name);
83057495Storek 	for (nv = d->d_attrs; nv != NULL; nv = nv->nv_next) {
83157495Storek 		a = nv->nv_ptr;
83257495Storek 		(void)ht_insert(selecttab, a->a_name, (char *)a->a_name);
83357495Storek 	}
83457495Storek }
83557495Storek 
83657495Storek /*
83757495Storek  * Is the given pointer on the given list of pointers?
83857495Storek  */
83957495Storek static int
onlist(nv,ptr)84057495Storek onlist(nv, ptr)
84157495Storek 	register struct nvlist *nv;
84257495Storek 	register void *ptr;
84357495Storek {
84457495Storek 	for (; nv != NULL; nv = nv->nv_next)
84557495Storek 		if (nv->nv_ptr == ptr)
84657495Storek 			return (1);
84757495Storek 	return (0);
84857495Storek }
84957495Storek 
85057495Storek static char *
extend(p,name)85157495Storek extend(p, name)
85257495Storek 	register char *p;
85357495Storek 	const char *name;
85457495Storek {
85557495Storek 	register int l;
85657495Storek 
85757495Storek 	l = strlen(name);
85857495Storek 	bcopy(name, p, l);
85957495Storek 	p += l;
86057495Storek 	*p++ = ',';
86157495Storek 	*p++ = ' ';
86257495Storek 	return (p);
86357495Storek }
86457495Storek 
86557495Storek /*
86657495Storek  * Check that we got all required locators, and default any that are
86757495Storek  * given as "?" and have defaults.  Return 0 on success.
86857495Storek  */
86957495Storek static const char **
fixloc(name,attr,got)87057495Storek fixloc(name, attr, got)
87157495Storek 	const char *name;
87257495Storek 	register struct attr *attr;
87357495Storek 	register struct nvlist *got;
87457495Storek {
87557495Storek 	register struct nvlist *m, *n;
87657495Storek 	register int ord;
87757495Storek 	register const char **lp;
87857495Storek 	int nmissing, nextra, nnodefault;
87957495Storek 	char *mp, *ep, *ndp;
88057495Storek 	char missing[1000], extra[1000], nodefault[1000];
88157495Storek 	static const char *nullvec[1];
88257495Storek 
88357495Storek 	/*
88457495Storek 	 * Look for all required locators, and number the given ones
88557495Storek 	 * according to the required order.  While we are numbering,
88657495Storek 	 * set default values for defaulted locators.
88757495Storek 	 */
88857495Storek 	if (attr->a_loclen == 0)	/* e.g., "at root" */
88957495Storek 		lp = nullvec;
89057495Storek 	else
89157495Storek 		lp = emalloc((attr->a_loclen + 1) * sizeof(const char *));
89257495Storek 	for (n = got; n != NULL; n = n->nv_next)
89357495Storek 		n->nv_int = -1;
89457495Storek 	nmissing = 0;
89557495Storek 	mp = missing;
89657495Storek 	/* yes, this is O(mn), but m and n should be small */
89757495Storek 	for (ord = 0, m = attr->a_locs; m != NULL; m = m->nv_next, ord++) {
89857495Storek 		for (n = got; n != NULL; n = n->nv_next) {
89957495Storek 			if (n->nv_name == m->nv_name) {
90057495Storek 				n->nv_int = ord;
90157495Storek 				break;
90257495Storek 			}
90357495Storek 		}
90457495Storek 		if (n == NULL && m->nv_int == 0) {
90557495Storek 			nmissing++;
90657495Storek 			mp = extend(mp, m->nv_name);
90757495Storek 		}
90857495Storek 		lp[ord] = m->nv_str;
90957495Storek 	}
91057495Storek 	if (ord != attr->a_loclen)
91157495Storek 		panic("fixloc");
91257495Storek 	lp[ord] = NULL;
91357495Storek 	nextra = 0;
91457495Storek 	ep = extra;
91557495Storek 	nnodefault = 0;
91657495Storek 	ndp = nodefault;
91757495Storek 	for (n = got; n != NULL; n = n->nv_next) {
91857495Storek 		if (n->nv_int >= 0) {
91957495Storek 			if (n->nv_str != NULL)
92057495Storek 				lp[n->nv_int] = n->nv_str;
92157495Storek 			else if (lp[n->nv_int] == NULL) {
92257495Storek 				nnodefault++;
92357495Storek 				ndp = extend(ndp, n->nv_name);
92457495Storek 			}
92557495Storek 		} else {
92657495Storek 			nextra++;
92757495Storek 			ep = extend(ep, n->nv_name);
92857495Storek 		}
92957495Storek 	}
93057495Storek 	if (nextra) {
93157495Storek 		ep[-2] = 0;	/* kill ", " */
93257495Storek 		error("%s: extraneous locator%s: %s",
93357495Storek 		    name, nextra > 1 ? "s" : "", extra);
93457495Storek 	}
93557495Storek 	if (nmissing) {
93657495Storek 		mp[-2] = 0;
93757495Storek 		error("%s: must specify %s", name, missing);
93857495Storek 	}
93957495Storek 	if (nnodefault) {
94057495Storek 		ndp[-2] = 0;
94157495Storek 		error("%s: cannot wildcard %s", name, nodefault);
94257495Storek 	}
94357495Storek 	if (nmissing || nnodefault) {
94457495Storek 		free(lp);
94557495Storek 		lp = NULL;
94657495Storek 	}
94757495Storek 	return (lp);
94857495Storek }
949