xref: /onnv-gate/usr/src/cmd/fm/modules/common/eversholt/itree.c (revision 186:2094fda9fa81)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*186Sdb35262  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  *
260Sstevel@tonic-gate  * itree.c -- instance tree creation and manipulation
270Sstevel@tonic-gate  *
280Sstevel@tonic-gate  * this module provides the instance tree
290Sstevel@tonic-gate  */
300Sstevel@tonic-gate 
310Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <stdio.h>
340Sstevel@tonic-gate #include <ctype.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate #include <strings.h>
370Sstevel@tonic-gate #include "alloc.h"
380Sstevel@tonic-gate #include "out.h"
390Sstevel@tonic-gate #include "stable.h"
400Sstevel@tonic-gate #include "literals.h"
410Sstevel@tonic-gate #include "lut.h"
420Sstevel@tonic-gate #include "tree.h"
430Sstevel@tonic-gate #include "ptree.h"
440Sstevel@tonic-gate #include "itree.h"
450Sstevel@tonic-gate #include "ipath.h"
460Sstevel@tonic-gate #include "eval.h"
470Sstevel@tonic-gate #include "config.h"
480Sstevel@tonic-gate 
490Sstevel@tonic-gate /*
500Sstevel@tonic-gate  * struct info contains the state we keep when expanding a prop statement
510Sstevel@tonic-gate  * as part of constructing the instance tree.  state kept in struct info
520Sstevel@tonic-gate  * is the non-recursive stuff -- the stuff that doesn't need to be on
530Sstevel@tonic-gate  * the stack.  the rest of the state that is passed between all the
540Sstevel@tonic-gate  * mutually recursive functions, is required to be on the stack since
550Sstevel@tonic-gate  * we need to backtrack and recurse as we do the instance tree construction.
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate struct info {
580Sstevel@tonic-gate 	struct lut *lut;
590Sstevel@tonic-gate 	struct node *anp;	/* arrow np */
600Sstevel@tonic-gate 	struct lut *ex;		/* dictionary of explicit iterators */
610Sstevel@tonic-gate 	struct config *croot;
620Sstevel@tonic-gate } Ninfo;
630Sstevel@tonic-gate 
640Sstevel@tonic-gate /*
650Sstevel@tonic-gate  * struct wildcardinfo is used to track wildcarded portions of paths.
660Sstevel@tonic-gate  *
670Sstevel@tonic-gate  * for example, if the epname of an event is "c/d" and the path "a/b/c/d"
680Sstevel@tonic-gate  * exists, the wildcard path ewname is filled in with the path "a/b".  when
690Sstevel@tonic-gate  * matching is done, epname is temporarily replaced with the concatenation
700Sstevel@tonic-gate  * of ewname and epname.  cpstart is set to the (struct config *)
710Sstevel@tonic-gate  * corresponding to component "c".
720Sstevel@tonic-gate  *
730Sstevel@tonic-gate  * a linked list of these structs is used to track the expansion of each
740Sstevel@tonic-gate  * event node as it is processed in vmatch() --> vmatch_event() calls.
750Sstevel@tonic-gate  */
760Sstevel@tonic-gate struct wildcardinfo {
770Sstevel@tonic-gate 	struct node *nptop;		/* event node fed to vmatch */
780Sstevel@tonic-gate 	struct node *oldepname;		/* epname without the wildcard part */
790Sstevel@tonic-gate 	enum status {
800Sstevel@tonic-gate 		WC_UNDEFINED,		/* struct is not yet initialized */
810Sstevel@tonic-gate 		WC_UNDERCONSTRUCTION,	/* wildcard path not yet done */
82*186Sdb35262 		WC_COMPLETE		/* wildcard path done and is in use */
830Sstevel@tonic-gate 	} s;
840Sstevel@tonic-gate 	struct wildcardpath {
850Sstevel@tonic-gate 		struct node *ewname;	/* wildcard path */
860Sstevel@tonic-gate 		struct config *cpstart;	/* starting cp node for oldepname */
870Sstevel@tonic-gate 		int refcount;		/* number of event nodes using this */
880Sstevel@tonic-gate 	} *p;
89*186Sdb35262 	struct wildcardpath *matchwc;	/* ptr to wc path to be matched */
900Sstevel@tonic-gate 	struct wildcardinfo *next;
910Sstevel@tonic-gate };
920Sstevel@tonic-gate 
930Sstevel@tonic-gate static void vmatch(struct info *infop, struct node *np,
940Sstevel@tonic-gate     struct node *lnp, struct node *anp, struct wildcardinfo **wcproot);
950Sstevel@tonic-gate static void hmatch(struct info *infop, struct node *np, struct node *nextnp);
960Sstevel@tonic-gate static void itree_pbubble(int flags, struct bubble *bp);
970Sstevel@tonic-gate static void itree_destructor(void *left, void *right, void *arg);
980Sstevel@tonic-gate static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev,
990Sstevel@tonic-gate     struct node *toev, struct lut *ex);
1000Sstevel@tonic-gate static void itree_free_arrowlists(struct bubble *bubp, int arrows_too);
1010Sstevel@tonic-gate static void arrow_add_within(struct arrow *ap, struct node *xpr);
1020Sstevel@tonic-gate static struct arrow *itree_add_arrow(struct bubble *frombubblep,
1030Sstevel@tonic-gate     struct bubble *tobubblep, struct node *apnode, struct node *fromevent,
1040Sstevel@tonic-gate     struct node *toevent, struct lut *ex);
1050Sstevel@tonic-gate static struct constraintlist *itree_add_constraint(struct arrow *arrowp,
1060Sstevel@tonic-gate     struct node *c);
1070Sstevel@tonic-gate static struct bubble *itree_add_bubble(struct event *eventp,
1080Sstevel@tonic-gate     enum bubbletype btype, int nork, int gen);
1090Sstevel@tonic-gate static void itree_free_bubble(struct bubble *freeme);
1100Sstevel@tonic-gate static void itree_free_constraints(struct arrow *ap);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate /*
1130Sstevel@tonic-gate  * the following struct contains the state we build up during
1140Sstevel@tonic-gate  * vertical and horizontal expansion so that generate()
1150Sstevel@tonic-gate  * has everything it needs to construct the appropriate arrows.
1160Sstevel@tonic-gate  * after setting up the state by calling:
1170Sstevel@tonic-gate  *	generate_arrownp()
1180Sstevel@tonic-gate  *	generate_nork()
1190Sstevel@tonic-gate  *	generate_new()
1200Sstevel@tonic-gate  *	generate_from()
1210Sstevel@tonic-gate  *	generate_to()
1220Sstevel@tonic-gate  * the actual arrow generation is done by calling:
1230Sstevel@tonic-gate  *	generate()
1240Sstevel@tonic-gate  */
1250Sstevel@tonic-gate static struct {
1260Sstevel@tonic-gate 	int generation;		/* generation number of arrow set */
1270Sstevel@tonic-gate 	struct node *arrownp;	/* top-level parse tree for arrow */
1280Sstevel@tonic-gate 	int n;			/* n value associated with arrow */
1290Sstevel@tonic-gate 	int k;			/* k value associated with arrow */
1300Sstevel@tonic-gate 	struct node *fromnp;	/* left-hand-side event in parse tree */
1310Sstevel@tonic-gate 	struct node *tonp;	/* right-hand-side event in parse tree */
1320Sstevel@tonic-gate 	struct event *frome;	/* left-hand-side event in instance tree */
1330Sstevel@tonic-gate 	struct event *toe;	/* right-hand-side event in instance tree */
1340Sstevel@tonic-gate 	struct bubble *frombp;	/* bubble arrow comes from */
1350Sstevel@tonic-gate 	struct bubble *tobp;	/* bubble arrow goes to */
1360Sstevel@tonic-gate } G;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate static void
1390Sstevel@tonic-gate generate_arrownp(struct node *arrownp)
1400Sstevel@tonic-gate {
1410Sstevel@tonic-gate 	G.arrownp = arrownp;
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static void
1450Sstevel@tonic-gate generate_nork(int n, int k)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate 	G.n = n;
1480Sstevel@tonic-gate 	G.k = k;
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate static void
1520Sstevel@tonic-gate generate_new(void)
1530Sstevel@tonic-gate {
1540Sstevel@tonic-gate 	G.generation++;
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate static void
1580Sstevel@tonic-gate generate_from(struct node *fromeventnp, struct event *fromevent)
1590Sstevel@tonic-gate {
1600Sstevel@tonic-gate 	G.fromnp = fromeventnp;
1610Sstevel@tonic-gate 	G.frome = fromevent;
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3|O_NONL, "from bubble on ");
1640Sstevel@tonic-gate 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp);
1650Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3, NULL);
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	G.frombp = itree_add_bubble(G.frome, B_FROM, G.n, 0);
1680Sstevel@tonic-gate }
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate static void
1710Sstevel@tonic-gate generate_to(struct node *toeventnp, struct event *toevent)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate 	G.tonp = toeventnp;
1740Sstevel@tonic-gate 	G.toe = toevent;
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3|O_NONL, "to bubble (gen %d) on ", G.generation);
1770Sstevel@tonic-gate 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp);
1780Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3, NULL);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	G.tobp = itree_add_bubble(G.toe, B_TO, G.k, G.generation);
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate static void
1840Sstevel@tonic-gate generate(struct lut *ex)
1850Sstevel@tonic-gate {
1860Sstevel@tonic-gate 	ASSERT(G.arrownp != NULL);
1870Sstevel@tonic-gate 	ASSERT(G.fromnp != NULL);
1880Sstevel@tonic-gate 	ASSERT(G.frome != NULL);
1890Sstevel@tonic-gate 	ASSERT(G.frombp != NULL);
1900Sstevel@tonic-gate 	ASSERT(G.tonp != NULL);
1910Sstevel@tonic-gate 	ASSERT(G.toe != NULL);
1920Sstevel@tonic-gate 	ASSERT(G.tobp != NULL);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3|O_NONL, "        Arrow \"");
1950Sstevel@tonic-gate 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp);
1960Sstevel@tonic-gate 	out(O_ALTFP|O_VERB3|O_NONL, "\" -> \"");
1970Sstevel@tonic-gate 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if (itree_add_arrow(G.frombp, G.tobp, G.arrownp,
2000Sstevel@tonic-gate 	    G.fromnp, G.tonp, ex) == NULL) {
2010Sstevel@tonic-gate 		out(O_ALTFP|O_VERB3, "\" (prevented by constraints)");
2020Sstevel@tonic-gate 	} else {
2030Sstevel@tonic-gate 		out(O_ALTFP|O_VERB3, "\"");
2040Sstevel@tonic-gate 	}
2050Sstevel@tonic-gate }
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate enum childnode_action {
2080Sstevel@tonic-gate 	CN_NONE,
2090Sstevel@tonic-gate 	CN_INSTANTIZE,
2100Sstevel@tonic-gate 	CN_DUP
2110Sstevel@tonic-gate };
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate static struct node *
2140Sstevel@tonic-gate tname_dup(struct node *namep, enum childnode_action act)
2150Sstevel@tonic-gate {
2160Sstevel@tonic-gate 	struct node *retp = NULL;
2170Sstevel@tonic-gate 	const char *file;
2180Sstevel@tonic-gate 	int line;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	if (namep == NULL)
2210Sstevel@tonic-gate 		return (NULL);
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	file = namep->file;
2240Sstevel@tonic-gate 	line = namep->line;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	for (; namep != NULL; namep = namep->u.name.next) {
2270Sstevel@tonic-gate 		struct node *newnp = newnode(T_NAME, file, line);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		newnp->u.name.t = namep->u.name.t;
2300Sstevel@tonic-gate 		newnp->u.name.s = namep->u.name.s;
2310Sstevel@tonic-gate 		newnp->u.name.last = newnp;
2320Sstevel@tonic-gate 		newnp->u.name.it = namep->u.name.it;
2330Sstevel@tonic-gate 		newnp->u.name.cp = namep->u.name.cp;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 		if (act == CN_DUP) {
2360Sstevel@tonic-gate 			struct node *npc;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 			npc = namep->u.name.child;
2390Sstevel@tonic-gate 			if (npc != NULL) {
2400Sstevel@tonic-gate 				switch (npc->t) {
2410Sstevel@tonic-gate 				case T_NUM:
2420Sstevel@tonic-gate 					newnp->u.name.child =
2430Sstevel@tonic-gate 						newnode(T_NUM, file, line);
2440Sstevel@tonic-gate 					newnp->u.name.child->u.ull =
2450Sstevel@tonic-gate 						npc->u.ull;
2460Sstevel@tonic-gate 					break;
2470Sstevel@tonic-gate 				case T_NAME:
2480Sstevel@tonic-gate 					newnp->u.name.child =
2490Sstevel@tonic-gate 						tree_name(npc->u.name.s,
2500Sstevel@tonic-gate 							npc->u.name.it,
2510Sstevel@tonic-gate 							file, line);
2520Sstevel@tonic-gate 					break;
2530Sstevel@tonic-gate 				default:
2540Sstevel@tonic-gate 					out(O_DIE, "tname_dup: "
2550Sstevel@tonic-gate 					    "invalid child type %s",
2560Sstevel@tonic-gate 					    ptree_nodetype2str(npc->t));
2570Sstevel@tonic-gate 				}
2580Sstevel@tonic-gate 			}
2590Sstevel@tonic-gate 		} else if (act == CN_INSTANTIZE) {
2600Sstevel@tonic-gate 			newnp->u.name.child = newnode(T_NUM, file, line);
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 			if (namep->u.name.child == NULL ||
2630Sstevel@tonic-gate 			    namep->u.name.child->t != T_NUM) {
2640Sstevel@tonic-gate 				int inum;
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 				ASSERT(newnp->u.name.cp != NULL);
2670Sstevel@tonic-gate 				config_getcompname(newnp->u.name.cp,
2680Sstevel@tonic-gate 						    NULL, &inum);
2690Sstevel@tonic-gate 				newnp->u.name.child->u.ull =
2700Sstevel@tonic-gate 					(unsigned long long)inum;
2710Sstevel@tonic-gate 			} else {
2720Sstevel@tonic-gate 				newnp->u.name.child->u.ull =
2730Sstevel@tonic-gate 					namep->u.name.child->u.ull;
2740Sstevel@tonic-gate 			}
2750Sstevel@tonic-gate 		}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 		if (retp == NULL) {
2780Sstevel@tonic-gate 			retp = newnp;
2790Sstevel@tonic-gate 		} else {
2800Sstevel@tonic-gate 			retp->u.name.last->u.name.next = newnp;
2810Sstevel@tonic-gate 			retp->u.name.last = newnp;
2820Sstevel@tonic-gate 		}
2830Sstevel@tonic-gate 	}
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	return (retp);
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate struct prop_wlk_data {
2890Sstevel@tonic-gate 	struct lut *props;
2900Sstevel@tonic-gate 	struct node *epname;
2910Sstevel@tonic-gate };
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate static struct lut *props2instance(struct node *, struct node *);
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate  * let oldepname be a subset of epname.  return the subsection of epname
2970Sstevel@tonic-gate  * that ends with oldepname.  make each component in the path explicitly
2980Sstevel@tonic-gate  * instanced (i.e., with a T_NUM child).
2990Sstevel@tonic-gate  */
3000Sstevel@tonic-gate static struct node *
3010Sstevel@tonic-gate tname_dup_to_epname(struct node *oldepname, struct node *epname)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	struct node *npref, *npend, *np1, *np2;
3040Sstevel@tonic-gate 	struct node *ret = NULL;
3050Sstevel@tonic-gate 	int foundmatch = 0;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	if (epname == NULL)
3080Sstevel@tonic-gate 		return (NULL);
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	/*
3110Sstevel@tonic-gate 	 * search for the longest path in epname which contains
3120Sstevel@tonic-gate 	 * oldnode->u.event.epname.  set npend to point to just past the
3130Sstevel@tonic-gate 	 * end of this path.
3140Sstevel@tonic-gate 	 */
3150Sstevel@tonic-gate 	npend = NULL;
3160Sstevel@tonic-gate 	for (npref = epname; npref; npref = npref->u.name.next) {
3170Sstevel@tonic-gate 		if (npref->u.name.s == oldepname->u.name.s) {
3180Sstevel@tonic-gate 			for (np1 = npref, np2 = oldepname;
3190Sstevel@tonic-gate 			    np1 != NULL && np2 != NULL;
3200Sstevel@tonic-gate 			    np1 = np1->u.name.next, np2 = np2->u.name.next) {
3210Sstevel@tonic-gate 				if (np1->u.name.s != np2->u.name.s)
3220Sstevel@tonic-gate 					break;
3230Sstevel@tonic-gate 			}
3240Sstevel@tonic-gate 			if (np2 == NULL) {
3250Sstevel@tonic-gate 				foundmatch = 1;
3260Sstevel@tonic-gate 				npend = np1;
3270Sstevel@tonic-gate 				if (np1 == NULL) {
3280Sstevel@tonic-gate 					/* oldepname matched npref up to end */
3290Sstevel@tonic-gate 					break;
3300Sstevel@tonic-gate 				}
3310Sstevel@tonic-gate 			}
3320Sstevel@tonic-gate 		}
3330Sstevel@tonic-gate 	}
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	if (foundmatch == 0) {
3360Sstevel@tonic-gate 		/*
3370Sstevel@tonic-gate 		 * if oldepname could not be found in epname, return a
3380Sstevel@tonic-gate 		 * duplicate of the former.  do not try to instantize
3390Sstevel@tonic-gate 		 * oldepname since it might not be a path.
3400Sstevel@tonic-gate 		 */
3410Sstevel@tonic-gate 		return (tname_dup(oldepname, CN_DUP));
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	/*
3450Sstevel@tonic-gate 	 * dup (epname -- npend).  all children should be T_NUMs.
3460Sstevel@tonic-gate 	 */
3470Sstevel@tonic-gate 	for (npref = epname;
3480Sstevel@tonic-gate 	    ! (npref == NULL || npref == npend);
3490Sstevel@tonic-gate 	    npref = npref->u.name.next) {
3500Sstevel@tonic-gate 		struct node *newnp = newnode(T_NAME, oldepname->file,
3510Sstevel@tonic-gate 					    oldepname->line);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		newnp->u.name.t = npref->u.name.t;
3540Sstevel@tonic-gate 		newnp->u.name.s = npref->u.name.s;
3550Sstevel@tonic-gate 		newnp->u.name.last = newnp;
3560Sstevel@tonic-gate 		newnp->u.name.it = npref->u.name.it;
3570Sstevel@tonic-gate 		newnp->u.name.cp = npref->u.name.cp;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 		newnp->u.name.child = newnode(T_NUM, oldepname->file,
3600Sstevel@tonic-gate 					    oldepname->line);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 		if (npref->u.name.child == NULL ||
3630Sstevel@tonic-gate 		    npref->u.name.child->t != T_NUM) {
3640Sstevel@tonic-gate 			int childnum;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 			ASSERT(npref->u.name.cp != NULL);
3670Sstevel@tonic-gate 			config_getcompname(npref->u.name.cp, NULL, &childnum);
3680Sstevel@tonic-gate 			newnp->u.name.child->u.ull = childnum;
3690Sstevel@tonic-gate 		} else {
3700Sstevel@tonic-gate 			newnp->u.name.child->u.ull =
3710Sstevel@tonic-gate 				npref->u.name.child->u.ull;
3720Sstevel@tonic-gate 		}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		if (ret == NULL) {
3750Sstevel@tonic-gate 			ret = newnp;
3760Sstevel@tonic-gate 		} else {
3770Sstevel@tonic-gate 			ret->u.name.last->u.name.next = newnp;
3780Sstevel@tonic-gate 			ret->u.name.last = newnp;
3790Sstevel@tonic-gate 		}
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	return (ret);
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate /*
3860Sstevel@tonic-gate  * restriction: oldnode->u.event.epname has to be equivalent to or a subset
3870Sstevel@tonic-gate  * of epname
3880Sstevel@tonic-gate  */
3890Sstevel@tonic-gate static struct node *
3900Sstevel@tonic-gate tevent_dup_to_epname(struct node *oldnode, struct node *epname)
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate 	struct node *ret;
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	ret = newnode(T_EVENT, oldnode->file, oldnode->line);
3950Sstevel@tonic-gate 	ret->u.event.ename = tname_dup(oldnode->u.event.ename, CN_NONE);
3960Sstevel@tonic-gate 	ret->u.event.epname = tname_dup_to_epname(oldnode->u.event.epname,
3970Sstevel@tonic-gate 						    epname);
3980Sstevel@tonic-gate 	return (ret);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate static void
4020Sstevel@tonic-gate nv_instantiate(void *name, void *val, void *arg)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	struct prop_wlk_data *pd = (struct prop_wlk_data *)arg;
4050Sstevel@tonic-gate 	struct node *orhs = (struct node *)val;
4060Sstevel@tonic-gate 	struct node *nrhs;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	/* handle engines by instantizing the entire engine */
4090Sstevel@tonic-gate 	if (name == L_engine) {
4100Sstevel@tonic-gate 		ASSERT(orhs->t == T_EVENT);
4110Sstevel@tonic-gate 		ASSERT(orhs->u.event.ename->u.name.t == N_SERD);
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 		/* there are only SERD engines for now */
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 		nrhs = newnode(T_SERD, orhs->file, orhs->line);
4160Sstevel@tonic-gate 		nrhs->u.stmt.np = tevent_dup_to_epname(orhs, pd->epname);
4170Sstevel@tonic-gate 		nrhs->u.stmt.lutp = props2instance(orhs, pd->epname);
4180Sstevel@tonic-gate 		pd->props = lut_add(pd->props, name, nrhs, NULL);
4190Sstevel@tonic-gate 		return;
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	switch (orhs->t) {
4230Sstevel@tonic-gate 	case T_NUM:
4240Sstevel@tonic-gate 		nrhs = newnode(T_NUM, orhs->file, orhs->line);
4250Sstevel@tonic-gate 		nrhs->u.ull = orhs->u.ull;
4260Sstevel@tonic-gate 		pd->props = lut_add(pd->props, name, nrhs, NULL);
4270Sstevel@tonic-gate 		break;
4280Sstevel@tonic-gate 	case T_TIMEVAL:
4290Sstevel@tonic-gate 		nrhs = newnode(T_TIMEVAL, orhs->file, orhs->line);
4300Sstevel@tonic-gate 		nrhs->u.ull = orhs->u.ull;
4310Sstevel@tonic-gate 		pd->props = lut_add(pd->props, name, nrhs, NULL);
4320Sstevel@tonic-gate 		break;
4330Sstevel@tonic-gate 	case T_NAME:
4340Sstevel@tonic-gate 		nrhs = tname_dup_to_epname(orhs, pd->epname);
4350Sstevel@tonic-gate 		pd->props = lut_add(pd->props, name, nrhs, NULL);
4360Sstevel@tonic-gate 		break;
4370Sstevel@tonic-gate 	case T_EVENT:
4380Sstevel@tonic-gate 		nrhs = tevent_dup_to_epname(orhs, pd->epname);
4390Sstevel@tonic-gate 		pd->props = lut_add(pd->props, name, nrhs, NULL);
4400Sstevel@tonic-gate 		break;
4410Sstevel@tonic-gate 	default:
4420Sstevel@tonic-gate 		out(O_DEBUG, "unexpected nvpair value type %s",
4430Sstevel@tonic-gate 		    ptree_nodetype2str(((struct node *)val)->t));
4440Sstevel@tonic-gate 	}
4450Sstevel@tonic-gate }
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate static struct lut *
4480Sstevel@tonic-gate props2instance(struct node *eventnp, struct node *epname)
4490Sstevel@tonic-gate {
4500Sstevel@tonic-gate 	struct prop_wlk_data pd;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	pd.props = NULL;
4530Sstevel@tonic-gate 	pd.epname = epname;
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	ASSERT(eventnp->u.event.declp != NULL);
4560Sstevel@tonic-gate 	lut_walk(eventnp->u.event.declp->u.stmt.lutp, nv_instantiate, &pd);
4570Sstevel@tonic-gate 	return (pd.props);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate /*ARGSUSED*/
4610Sstevel@tonic-gate static void
4620Sstevel@tonic-gate instances_destructor(void *left, void *right, void *arg)
4630Sstevel@tonic-gate {
4640Sstevel@tonic-gate 	struct node *dn = (struct node *)right;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	if (dn->t == T_SERD) {
4670Sstevel@tonic-gate 		/* we allocated the lut during itree_create(), so free it */
4680Sstevel@tonic-gate 		lut_free(dn->u.stmt.lutp, instances_destructor, NULL);
4690Sstevel@tonic-gate 		dn->u.stmt.lutp = NULL;
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 	tree_free(dn);
4720Sstevel@tonic-gate }
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate /*
4750Sstevel@tonic-gate  * event_cmp -- used via lut_lookup/lut_add on instance tree lut
4760Sstevel@tonic-gate  */
4770Sstevel@tonic-gate static int
4780Sstevel@tonic-gate event_cmp(struct event *ep1, struct event *ep2)
4790Sstevel@tonic-gate {
4800Sstevel@tonic-gate 	int diff;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	if ((diff = ep2->enode->u.event.ename->u.name.s -
4830Sstevel@tonic-gate 	    ep1->enode->u.event.ename->u.name.s) != 0)
4840Sstevel@tonic-gate 		return (diff);
4850Sstevel@tonic-gate 	if ((diff = (char *)ep2->ipp - (char *)ep1->ipp) != 0)
4860Sstevel@tonic-gate 		return (diff);
4870Sstevel@tonic-gate 	return (0);
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate struct event *
4920Sstevel@tonic-gate itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp)
4930Sstevel@tonic-gate {
4940Sstevel@tonic-gate 	struct event searchevent;	/* just used for searching */
4950Sstevel@tonic-gate 	struct node searcheventnode;
4960Sstevel@tonic-gate 	struct node searchenamenode;
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	searchevent.enode = &searcheventnode;
4990Sstevel@tonic-gate 	searcheventnode.t = T_EVENT;
5000Sstevel@tonic-gate 	searcheventnode.u.event.ename = &searchenamenode;
5010Sstevel@tonic-gate 	searchenamenode.t = T_NAME;
5020Sstevel@tonic-gate 	searchenamenode.u.name.s = ename;
5030Sstevel@tonic-gate 	searchevent.ipp = ipp;
5040Sstevel@tonic-gate 	return (lut_lookup(itp, (void *)&searchevent, (lut_cmp)event_cmp));
5050Sstevel@tonic-gate }
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate static struct event *
5080Sstevel@tonic-gate find_or_add_event(struct info *infop, struct node *np)
5090Sstevel@tonic-gate {
5100Sstevel@tonic-gate 	struct event *ret;
5110Sstevel@tonic-gate 	struct event searchevent;	/* just used for searching */
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	ASSERTeq(np->t, T_EVENT, ptree_nodetype2str);
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	searchevent.enode = np;
5160Sstevel@tonic-gate 	searchevent.ipp = ipath(np->u.event.epname);
5170Sstevel@tonic-gate 	if ((ret = lut_lookup(infop->lut, (void *)&searchevent,
5180Sstevel@tonic-gate 	    (lut_cmp)event_cmp)) != NULL)
5190Sstevel@tonic-gate 		return (ret);
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	/* wasn't already in tree, allocate it */
5220Sstevel@tonic-gate 	ret = MALLOC(sizeof (*ret));
5230Sstevel@tonic-gate 	bzero(ret, sizeof (*ret));
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	ret->t = np->u.event.ename->u.name.t;
5260Sstevel@tonic-gate 	ret->enode = np;
5270Sstevel@tonic-gate 	ret->ipp = searchevent.ipp;
5280Sstevel@tonic-gate 	ret->props = props2instance(np, np->u.event.epname);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	infop->lut = lut_add(infop->lut, (void *)ret, (void *)ret,
5310Sstevel@tonic-gate 	    (lut_cmp)event_cmp);
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	return (ret);
5340Sstevel@tonic-gate }
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate /*
5370Sstevel@tonic-gate  * hmatch_event -- perform any appropriate horizontal expansion on an event
5380Sstevel@tonic-gate  *
5390Sstevel@tonic-gate  * this routine is used to perform horizontal expansion on both the
5400Sstevel@tonic-gate  * left-hand-side events in a prop, and the right-hand-side events.
5410Sstevel@tonic-gate  * when called to handle a left-side event, nextnp point to the right
5420Sstevel@tonic-gate  * side of the prop that should be passed to hmatch() for each match
5430Sstevel@tonic-gate  * found by horizontal expansion.   when no horizontal expansion exists,
5440Sstevel@tonic-gate  * we will still "match" one event for every event found in the list on
5450Sstevel@tonic-gate  * the left-hand-side of the prop because vmatch() already found that
5460Sstevel@tonic-gate  * there's at least one match during vertical expansion.
5470Sstevel@tonic-gate  */
5480Sstevel@tonic-gate static void
5490Sstevel@tonic-gate hmatch_event(struct info *infop, struct node *eventnp, struct node *epname,
5500Sstevel@tonic-gate     struct config *ncp, struct node *nextnp, int rematch)
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	if (epname == NULL) {
5530Sstevel@tonic-gate 		/*
5540Sstevel@tonic-gate 		 * end of pathname recursion, either we just located
5550Sstevel@tonic-gate 		 * a left-hand-side event and we're ready to move on
5560Sstevel@tonic-gate 		 * to the expanding the right-hand-side events, or
5570Sstevel@tonic-gate 		 * we're further down the recursion and we just located
5580Sstevel@tonic-gate 		 * a right-hand-side event.  the passed-in parameter
5590Sstevel@tonic-gate 		 * "nextnp" tells us whether we're working on the left
5600Sstevel@tonic-gate 		 * side and need to move on to nextnp, or if nextnp is
5610Sstevel@tonic-gate 		 * NULL, we're working on the right side.
5620Sstevel@tonic-gate 		 */
5630Sstevel@tonic-gate 		if (nextnp) {
5640Sstevel@tonic-gate 			/*
5650Sstevel@tonic-gate 			 * finished a left side expansion, move on to right.
5660Sstevel@tonic-gate 			 * tell generate() what event we just matched so
5670Sstevel@tonic-gate 			 * it can be used at the source of any arrows
5680Sstevel@tonic-gate 			 * we generate as we match events on the right side.
5690Sstevel@tonic-gate 			 */
5700Sstevel@tonic-gate 			generate_from(eventnp,
5710Sstevel@tonic-gate 			    find_or_add_event(infop, eventnp));
5720Sstevel@tonic-gate 			hmatch(infop, nextnp, NULL);
5730Sstevel@tonic-gate 		} else {
5740Sstevel@tonic-gate 			/*
5750Sstevel@tonic-gate 			 * finished a right side expansion.  tell generate
5760Sstevel@tonic-gate 			 * the information about the destination and let
5770Sstevel@tonic-gate 			 * it construct the arrows as appropriate.
5780Sstevel@tonic-gate 			 */
5790Sstevel@tonic-gate 			generate_to(eventnp,
5800Sstevel@tonic-gate 			    find_or_add_event(infop, eventnp));
5810Sstevel@tonic-gate 			generate(infop->ex);
5820Sstevel@tonic-gate 		}
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 		return;
5850Sstevel@tonic-gate 	}
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	ASSERTeq(epname->t, T_NAME, ptree_nodetype2str);
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	/*
5900Sstevel@tonic-gate 	 * we only get here when eventnp already has a completely
5910Sstevel@tonic-gate 	 * instanced epname in it already.  so we first recurse
5920Sstevel@tonic-gate 	 * down to the end of the name and as the recursion pops
5930Sstevel@tonic-gate 	 * up, we look for opportunities to advance horizontal
5940Sstevel@tonic-gate 	 * expansions on to the next match.  when we do advance
5950Sstevel@tonic-gate 	 * horizontal expansions, we potentially render all cp
5960Sstevel@tonic-gate 	 * pointers on all components to the right as invalid,
5970Sstevel@tonic-gate 	 * so we pass in an "ncp" config handle so matching against
5980Sstevel@tonic-gate 	 * the config can happen.
5990Sstevel@tonic-gate 	 */
6000Sstevel@tonic-gate 	if (rematch) {
6010Sstevel@tonic-gate 		struct config *ocp = epname->u.name.cp;
6020Sstevel@tonic-gate 		char *ncp_s;
6030Sstevel@tonic-gate 		int ncp_num, num;
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 		for (; ncp; ncp = config_next(ncp)) {
6060Sstevel@tonic-gate 			config_getcompname(ncp, &ncp_s, &ncp_num);
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 			if (ncp_s == epname->u.name.s) {
6090Sstevel@tonic-gate 				/* found a matching component name */
6100Sstevel@tonic-gate 				config_getcompname(epname->u.name.cp,
6110Sstevel@tonic-gate 				    NULL, &num);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 				if (epname->u.name.it != IT_HORIZONTAL &&
6140Sstevel@tonic-gate 				    ncp_num != num)
6150Sstevel@tonic-gate 					continue;
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 				epname->u.name.cp = ncp;
6180Sstevel@tonic-gate 				hmatch_event(infop, eventnp,
6190Sstevel@tonic-gate 				    epname->u.name.next, config_child(ncp),
6200Sstevel@tonic-gate 				    nextnp, 1);
6210Sstevel@tonic-gate 			}
6220Sstevel@tonic-gate 		}
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 		epname->u.name.cp = ocp;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		return;		/* no more config to match against */
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	} else {
6290Sstevel@tonic-gate 		hmatch_event(infop, eventnp, epname->u.name.next, ncp,
6300Sstevel@tonic-gate 		    nextnp, 0);
6310Sstevel@tonic-gate 	}
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 	if (epname->u.name.it == IT_HORIZONTAL) {
6340Sstevel@tonic-gate 		struct config *cp;
6350Sstevel@tonic-gate 		struct config *ocp = epname->u.name.cp;
6360Sstevel@tonic-gate 		char *cp_s;
6370Sstevel@tonic-gate 		int cp_num;
6380Sstevel@tonic-gate 		int ocp_num;
6390Sstevel@tonic-gate 		struct iterinfo *iterinfop = NULL;
6400Sstevel@tonic-gate 		const char *iters;
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 		config_getcompname(ocp, NULL, &ocp_num);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		for (cp = config_next(ocp); cp; cp = config_next(cp)) {
6450Sstevel@tonic-gate 			config_getcompname(cp, &cp_s, &cp_num);
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 			if (cp_s == epname->u.name.s) {
6480Sstevel@tonic-gate 				ASSERT(epname->u.name.child != NULL);
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 				iters = epname->u.name.child->u.name.s;
6510Sstevel@tonic-gate 				if ((iterinfop = lut_lookup(infop->ex,
6520Sstevel@tonic-gate 				    (void *)iters, NULL)) == NULL) {
6530Sstevel@tonic-gate 					out(O_DIE,
6540Sstevel@tonic-gate 					    "hmatch_event: internal error: "
6550Sstevel@tonic-gate 					    "iterator \"%s\" undefined", iters);
6560Sstevel@tonic-gate 				} else {
6570Sstevel@tonic-gate 					/* advance dict entry to next match */
6580Sstevel@tonic-gate 					iterinfop->num = cp_num;
6590Sstevel@tonic-gate 				}
6600Sstevel@tonic-gate 				epname->u.name.cp = cp;
6610Sstevel@tonic-gate 				hmatch_event(infop, eventnp,
6620Sstevel@tonic-gate 				    epname->u.name.next, config_child(cp),
6630Sstevel@tonic-gate 				    nextnp, 1);
6640Sstevel@tonic-gate 			}
6650Sstevel@tonic-gate 		}
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 		if (iterinfop != NULL) {
6680Sstevel@tonic-gate 			/* restore dict entry */
6690Sstevel@tonic-gate 			iterinfop->num = ocp_num;
6700Sstevel@tonic-gate 		}
6710Sstevel@tonic-gate 		epname->u.name.cp = ocp;
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate /*
6760Sstevel@tonic-gate  * hmatch -- check for horizontal expansion matches
6770Sstevel@tonic-gate  *
6780Sstevel@tonic-gate  * np points to the things we're matching (like a T_LIST or a T_EVENT)
6790Sstevel@tonic-gate  * and if we're working on a left-side of a prop, nextnp points to
6800Sstevel@tonic-gate  * the other side of the prop that we'll tackle next when this recursion
6810Sstevel@tonic-gate  * bottoms out.  when all the events in the entire prop arrow have been
6820Sstevel@tonic-gate  * horizontally expanded, generate() will be called to generate the
6830Sstevel@tonic-gate  * actualy arrow.
6840Sstevel@tonic-gate  */
6850Sstevel@tonic-gate static void
6860Sstevel@tonic-gate hmatch(struct info *infop, struct node *np, struct node *nextnp)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate 	if (np == NULL)
6890Sstevel@tonic-gate 		return;		/* all done */
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	/*
6920Sstevel@tonic-gate 	 * for each item in the list of events (which could just
6930Sstevel@tonic-gate 	 * be a single event, or it could get larger in the loop
6940Sstevel@tonic-gate 	 * below due to horizontal expansion), call hmatch on
6950Sstevel@tonic-gate 	 * the right side and create arrows to each element.
6960Sstevel@tonic-gate 	 */
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	switch (np->t) {
6990Sstevel@tonic-gate 	case T_LIST:
7000Sstevel@tonic-gate 		/* loop through the list */
7010Sstevel@tonic-gate 		if (np->u.expr.left)
7020Sstevel@tonic-gate 			hmatch(infop, np->u.expr.left, nextnp);
7030Sstevel@tonic-gate 		if (np->u.expr.right)
7040Sstevel@tonic-gate 			hmatch(infop, np->u.expr.right, nextnp);
7050Sstevel@tonic-gate 		break;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	case T_EVENT:
7080Sstevel@tonic-gate 		hmatch_event(infop, np, np->u.event.epname,
7090Sstevel@tonic-gate 		    NULL, nextnp, 0);
7100Sstevel@tonic-gate 		break;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 	default:
7130Sstevel@tonic-gate 		outfl(O_DIE, np->file, np->line,
7140Sstevel@tonic-gate 		    "hmatch: unexpected type: %s",
7150Sstevel@tonic-gate 		    ptree_nodetype2str(np->t));
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate }
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate static int
7200Sstevel@tonic-gate itree_np2nork(struct node *norknp)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	if (norknp == NULL)
7230Sstevel@tonic-gate 		return (1);
7240Sstevel@tonic-gate 	else if (norknp->t == T_NAME && norknp->u.name.s == L_A)
7250Sstevel@tonic-gate 		return (-1);	/* our internal version of "all" */
7260Sstevel@tonic-gate 	else if (norknp->t == T_NUM)
7270Sstevel@tonic-gate 		return ((int)norknp->u.ull);
7280Sstevel@tonic-gate 	else
7290Sstevel@tonic-gate 		out(O_DIE, norknp->file, norknp->line,
7300Sstevel@tonic-gate 		    "itree_np2nork: internal error type %s",
7310Sstevel@tonic-gate 		    ptree_nodetype2str(norknp->t));
7320Sstevel@tonic-gate 	/*NOTREACHED*/
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate static struct iterinfo *
7360Sstevel@tonic-gate newiterinfo(int num, struct node *np)
7370Sstevel@tonic-gate {
7380Sstevel@tonic-gate 	struct iterinfo *ret = MALLOC(sizeof (*ret));
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	ret->num = num;
7410Sstevel@tonic-gate 	ret->np = np;
7420Sstevel@tonic-gate 
7430Sstevel@tonic-gate 	return (ret);
7440Sstevel@tonic-gate }
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate /*ARGSUSED*/
7470Sstevel@tonic-gate static void
7480Sstevel@tonic-gate iterinfo_destructor(void *left, void *right, void *arg)
7490Sstevel@tonic-gate {
7500Sstevel@tonic-gate 	struct iterinfo *iterinfop = (struct iterinfo *)right;
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	bzero(iterinfop, sizeof (*iterinfop));
7530Sstevel@tonic-gate 	FREE(iterinfop);
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate /*
757*186Sdb35262  * return 1 if wildcard path for wcp matches another wildcard path;
758*186Sdb35262  * return 0 if otherwise.
759*186Sdb35262  */
760*186Sdb35262 static int
761*186Sdb35262 wc_paths_match(struct wildcardinfo *wcp)
762*186Sdb35262 {
763*186Sdb35262 	struct node *np1, *np2;
764*186Sdb35262 
765*186Sdb35262 	ASSERT(wcp->matchwc != NULL);
766*186Sdb35262 
767*186Sdb35262 	for (np1 = wcp->p->ewname, np2 = wcp->matchwc->ewname;
768*186Sdb35262 	    np1 != NULL && np2 != NULL;
769*186Sdb35262 	    np1 = np1->u.name.next, np2 = np2->u.name.next) {
770*186Sdb35262 		/*
771*186Sdb35262 		 * names must match
772*186Sdb35262 		 */
773*186Sdb35262 		if (np1->u.name.s != np2->u.name.s)
774*186Sdb35262 			return (0);
775*186Sdb35262 
776*186Sdb35262 		/*
777*186Sdb35262 		 * children must exist and have the same numerical value
778*186Sdb35262 		 */
779*186Sdb35262 		if (np1->u.name.child == NULL || np2->u.name.child == NULL)
780*186Sdb35262 			return (0);
781*186Sdb35262 
782*186Sdb35262 		if (np1->u.name.child->t != T_NUM ||
783*186Sdb35262 		    np2->u.name.child->t != T_NUM)
784*186Sdb35262 			return (0);
785*186Sdb35262 
786*186Sdb35262 		if (np1->u.name.child->u.ull != np2->u.name.child->u.ull)
787*186Sdb35262 			return (0);
788*186Sdb35262 	}
789*186Sdb35262 
790*186Sdb35262 	/*
791*186Sdb35262 	 * return true only if we have matches for all entries of n1 and
792*186Sdb35262 	 * n2.  note that NULL wildcard paths (i.e., both wcp->p->ewname
793*186Sdb35262 	 * and wcp->matchwc->ewname are NULL) will be considered as
794*186Sdb35262 	 * matching paths.
795*186Sdb35262 	 */
796*186Sdb35262 	if (np1 == NULL && np2 == NULL)
797*186Sdb35262 		return (1);
798*186Sdb35262 
799*186Sdb35262 	return (0);
800*186Sdb35262 }
801*186Sdb35262 
802*186Sdb35262 /*
8030Sstevel@tonic-gate  * update epname to include the wildcarded portion
8040Sstevel@tonic-gate  */
8050Sstevel@tonic-gate static void
8060Sstevel@tonic-gate create_wildcardedpath(struct wildcardinfo **wcproot)
8070Sstevel@tonic-gate {
8080Sstevel@tonic-gate 	struct wildcardinfo *wcp;
8090Sstevel@tonic-gate 	struct node *nptop;
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	wcp = *wcproot;
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	if (wcp->s == WC_UNDERCONSTRUCTION) {
8140Sstevel@tonic-gate 		ASSERT(wcp->p->refcount == 1);
8150Sstevel@tonic-gate 		wcp->s = WC_COMPLETE;
8160Sstevel@tonic-gate 	}
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	/* path has no wildcard */
8190Sstevel@tonic-gate 	if (wcp->p->ewname == NULL)
8200Sstevel@tonic-gate 		return;
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 	/*
8230Sstevel@tonic-gate 	 * get to this point if a wildcard portion of the path exists.
8240Sstevel@tonic-gate 	 *
8250Sstevel@tonic-gate 	 * first set oldepname to the start of the existing epname for use
8260Sstevel@tonic-gate 	 * in future comparisons, then update epname to include the
8270Sstevel@tonic-gate 	 * wildcard portion.
8280Sstevel@tonic-gate 	 */
8290Sstevel@tonic-gate 	nptop = wcp->nptop;
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 	ASSERT(wcp->oldepname == nptop->u.event.epname);
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 	nptop->u.event.epname =	tname_dup(wcp->p->ewname, CN_DUP);
8340Sstevel@tonic-gate 	nptop->u.event.epname = tree_name_append(nptop->u.event.epname,
8350Sstevel@tonic-gate 					tname_dup(wcp->oldepname, CN_DUP));
8360Sstevel@tonic-gate }
8370Sstevel@tonic-gate 
8380Sstevel@tonic-gate /*
8390Sstevel@tonic-gate  * restore epname to its former (nonwildcarded) state
8400Sstevel@tonic-gate  */
8410Sstevel@tonic-gate static void
8420Sstevel@tonic-gate undo_wildcardedpath(struct wildcardinfo **wcproot)
8430Sstevel@tonic-gate {
8440Sstevel@tonic-gate 	struct wildcardinfo *wcp;
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	wcp = *wcproot;
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	if (wcp->s == WC_COMPLETE) {
8490Sstevel@tonic-gate 		ASSERT(wcp->p->refcount == 1);
8500Sstevel@tonic-gate 		wcp->s = WC_UNDERCONSTRUCTION;
8510Sstevel@tonic-gate 	}
8520Sstevel@tonic-gate 
8530Sstevel@tonic-gate 	/* path has no wildcard */
8540Sstevel@tonic-gate 	if (wcp->p->ewname == NULL)
8550Sstevel@tonic-gate 		return;
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	ASSERT(wcp->oldepname != NULL);
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	tree_free(wcp->nptop->u.event.epname);
8600Sstevel@tonic-gate 	wcp->nptop->u.event.epname = wcp->oldepname;
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate 
863*186Sdb35262 enum wildcard_action {
864*186Sdb35262 	WA_NONE,	/* do not do any wildcards */
865*186Sdb35262 	WA_SINGLE,	/* do wildcard only for current cp node */
866*186Sdb35262 	WA_ALL		/* do wildcards for all cp nodes */
867*186Sdb35262 };
868*186Sdb35262 
8690Sstevel@tonic-gate static void
8700Sstevel@tonic-gate vmatch_event(struct info *infop, struct config *cp, struct node *np,
8710Sstevel@tonic-gate 	    struct node *lnp, struct node *anp,
872*186Sdb35262 	    struct wildcardinfo **wcproot, enum wildcard_action dowildcard)
8730Sstevel@tonic-gate {
8740Sstevel@tonic-gate 	struct wildcardinfo *wcp;
8750Sstevel@tonic-gate 	char *cp_s;
8760Sstevel@tonic-gate 	int cp_num;
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	wcp = *wcproot;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	if ((np == NULL && wcp->oldepname != NULL) ||
8810Sstevel@tonic-gate 	    (cp == NULL && wcp->oldepname == NULL)) {
8820Sstevel@tonic-gate 		/*
883*186Sdb35262 		 * get to this point if the pathname matched the config
884*186Sdb35262 		 * (but not necessarily a match at the end).  first check
885*186Sdb35262 		 * for any matching wildcard paths.
8860Sstevel@tonic-gate 		 */
887*186Sdb35262 		if (wcp->matchwc != NULL && wc_paths_match(wcp) == 0)
888*186Sdb35262 			return;
889*186Sdb35262 
8900Sstevel@tonic-gate 		create_wildcardedpath(wcproot);
8910Sstevel@tonic-gate 		vmatch(infop, np, lnp, anp, wcproot);
8920Sstevel@tonic-gate 		undo_wildcardedpath(wcproot);
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 		return;
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 	if (cp == NULL)
8980Sstevel@tonic-gate 		return;	/* no more config to match against */
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	for (; cp; cp = config_next(cp)) {
9010Sstevel@tonic-gate 		config_getcompname(cp, &cp_s, &cp_num);
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 		if (cp_s == np->u.name.s &&
9040Sstevel@tonic-gate 		    ! (wcp->s == WC_UNDERCONSTRUCTION &&
905*186Sdb35262 		    dowildcard == WA_SINGLE)) {
9060Sstevel@tonic-gate 			/* found a matching component name */
9070Sstevel@tonic-gate 			if (np->u.name.child &&
9080Sstevel@tonic-gate 			    np->u.name.child->t == T_NUM) {
9090Sstevel@tonic-gate 				/*
9100Sstevel@tonic-gate 				 * an explicit instance number was given
9110Sstevel@tonic-gate 				 * in the source.  so only consider this
9120Sstevel@tonic-gate 				 * a configuration match if the number
9130Sstevel@tonic-gate 				 * also matches.
9140Sstevel@tonic-gate 				 */
9150Sstevel@tonic-gate 				if (cp_num != np->u.name.child->u.ull)
9160Sstevel@tonic-gate 					continue;
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 				np->u.name.cp = cp;
9190Sstevel@tonic-gate 			} else {
9200Sstevel@tonic-gate 				struct iterinfo *iterinfop;
9210Sstevel@tonic-gate 				const char *iters;
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 				/*
9240Sstevel@tonic-gate 				 * vertical iterator.  look it up in
9250Sstevel@tonic-gate 				 * the appropriate lut and if we get
9260Sstevel@tonic-gate 				 * back a value it is either one that we
9270Sstevel@tonic-gate 				 * set earlier, in which case we record
9280Sstevel@tonic-gate 				 * the new value for this iteration and
9290Sstevel@tonic-gate 				 * keep matching, or it is one that was
9300Sstevel@tonic-gate 				 * set by an earlier reference to the
9310Sstevel@tonic-gate 				 * iterator, in which case we only consider
9320Sstevel@tonic-gate 				 * this a configuration match if the number
9330Sstevel@tonic-gate 				 * matches cp_num.
9340Sstevel@tonic-gate 				 */
9350Sstevel@tonic-gate 
9360Sstevel@tonic-gate 				ASSERT(np->u.name.child != NULL);
9370Sstevel@tonic-gate 				ASSERT(np->u.name.child->t == T_NAME);
9380Sstevel@tonic-gate 				iters = np->u.name.child->u.name.s;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 				if ((iterinfop = lut_lookup(infop->ex,
9410Sstevel@tonic-gate 				    (void *)iters, NULL)) == NULL) {
9420Sstevel@tonic-gate 					/* we're the first use, record our np */
9430Sstevel@tonic-gate 					infop->ex = lut_add(infop->ex,
9440Sstevel@tonic-gate 					    (void *)iters,
9450Sstevel@tonic-gate 					    newiterinfo(cp_num, np), NULL);
9460Sstevel@tonic-gate 				} else if (np == iterinfop->np) {
9470Sstevel@tonic-gate 					/*
9480Sstevel@tonic-gate 					 * we're the first use, back again
9490Sstevel@tonic-gate 					 * for another iteration.  so update
9500Sstevel@tonic-gate 					 * the num bound to this iterator in
9510Sstevel@tonic-gate 					 * the lut.
9520Sstevel@tonic-gate 					 */
9530Sstevel@tonic-gate 					iterinfop->num = cp_num;
9540Sstevel@tonic-gate 				} else if (cp_num != iterinfop->num) {
9550Sstevel@tonic-gate 					/*
9560Sstevel@tonic-gate 					 * an earlier reference to this
9570Sstevel@tonic-gate 					 * iterator bound it to a different
9580Sstevel@tonic-gate 					 * instance number, so there's no
9590Sstevel@tonic-gate 					 * match here after all.
960*186Sdb35262 					 *
961*186Sdb35262 					 * however, it's possible that this
962*186Sdb35262 					 * component should really be part of
963*186Sdb35262 					 * the wildcard.  we explore this by
964*186Sdb35262 					 * forcing this component into the
965*186Sdb35262 					 * wildcarded section.
966*186Sdb35262 					 *
967*186Sdb35262 					 * for an more details of what's
968*186Sdb35262 					 * going to happen now, see
969*186Sdb35262 					 * comments block below entitled
970*186Sdb35262 					 * "forcing components into
971*186Sdb35262 					 * wildcard path".
9720Sstevel@tonic-gate 					 */
973*186Sdb35262 					if (dowildcard == WA_ALL &&
974*186Sdb35262 					    wcp->s == WC_UNDERCONSTRUCTION) {
975*186Sdb35262 						vmatch_event(infop, cp, np,
976*186Sdb35262 							    lnp, anp, wcproot,
977*186Sdb35262 							    WA_SINGLE);
978*186Sdb35262 					}
9790Sstevel@tonic-gate 					continue;
9800Sstevel@tonic-gate 				}
9810Sstevel@tonic-gate 				np->u.name.cp = cp;
9820Sstevel@tonic-gate 			}
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 			/*
9850Sstevel@tonic-gate 			 * if wildcarding was done in a call earlier in the
9860Sstevel@tonic-gate 			 * stack, record the current cp as the first
9870Sstevel@tonic-gate 			 * matching and nonwildcarded cp.
9880Sstevel@tonic-gate 			 */
989*186Sdb35262 			if (dowildcard == WA_ALL &&
990*186Sdb35262 			    wcp->s == WC_UNDERCONSTRUCTION)
9910Sstevel@tonic-gate 				wcp->p->cpstart = cp;
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 			/*
9940Sstevel@tonic-gate 			 * if this was an IT_HORIZONTAL name,
9950Sstevel@tonic-gate 			 * hmatch() will use the cp to expand
9960Sstevel@tonic-gate 			 * all matches horizontally into a list.
9970Sstevel@tonic-gate 			 * we know the list will contain at least
9980Sstevel@tonic-gate 			 * one element (the one we just matched),
9990Sstevel@tonic-gate 			 * so we just store cp and let hmatch_event()
10000Sstevel@tonic-gate 			 * do the rest.
10010Sstevel@tonic-gate 			 *
10020Sstevel@tonic-gate 			 * recurse on to next component.  note that
10030Sstevel@tonic-gate 			 * wildcarding is now turned off.
10040Sstevel@tonic-gate 			 */
10050Sstevel@tonic-gate 			vmatch_event(infop, config_child(cp), np->u.name.next,
1006*186Sdb35262 				    lnp, anp, wcproot, WA_NONE);
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 			/*
1009*186Sdb35262 			 * forcing components into wildcard path:
1010*186Sdb35262 			 *
1011*186Sdb35262 			 * if this component is the first match, force it
1012*186Sdb35262 			 * to be part of the wildcarded path and see if we
1013*186Sdb35262 			 * can get additional matches.  repeat call to
1014*186Sdb35262 			 * vmatch_event() with the same np, making sure
1015*186Sdb35262 			 * wildcarding is forced for this component alone
1016*186Sdb35262 			 * and not its peers by specifying vmatch_event(
1017*186Sdb35262 			 * ..., WA_SINGLE).  in other words, in the call to
1018*186Sdb35262 			 * vmatch_event() below, there should be no loop
1019*186Sdb35262 			 * over cp's peers since that is being done in the
1020*186Sdb35262 			 * current loop [i.e., the loop we're in now].
1021*186Sdb35262 			 *
1022*186Sdb35262 			 * here's an example.  suppose we have the
1023*186Sdb35262 			 * definition
1024*186Sdb35262 			 *	event foo@x/y
1025*186Sdb35262 			 * and configuration
1026*186Sdb35262 			 *	a0/x0/y0/a1/x1/y1
1027*186Sdb35262 			 *
1028*186Sdb35262 			 * the code up to this point will treat "a0" as the
1029*186Sdb35262 			 * wildcarded part of the path and "x0/y0" as the
1030*186Sdb35262 			 * nonwildcarded part, resulting in the instanced
1031*186Sdb35262 			 * event
1032*186Sdb35262 			 *	foo@a0/x0/y0
1033*186Sdb35262 			 *
1034*186Sdb35262 			 * in order to discover the next match (.../x1/y1)
1035*186Sdb35262 			 * in the configuration we have to force "x0" into
1036*186Sdb35262 			 * the wildcarded part of the path.  the following
1037*186Sdb35262 			 * call to vmatch_event(..., WA_SINGLE) does this.
1038*186Sdb35262 			 * by doing so, we discover the wildcarded part
1039*186Sdb35262 			 * "a0/x0/y0/a1" and the nonwildcarded part "x1/y1"
1040*186Sdb35262 			 *
1041*186Sdb35262 			 * the following call to vmatch_event() is also
1042*186Sdb35262 			 * needed to properly handle the configuration
1043*186Sdb35262 			 *	b0/x0/b1/x1/y1
1044*186Sdb35262 			 *
1045*186Sdb35262 			 * the recursions into vmatch_event() will start
1046*186Sdb35262 			 * off uncovering "b0" as the wildcarded part and
1047*186Sdb35262 			 * "x0" as the start of the nonwildcarded path.
1048*186Sdb35262 			 * however, the next recursion will not result in a
1049*186Sdb35262 			 * match since there is no "y" following "x0".  the
1050*186Sdb35262 			 * subsequent match of (wildcard = "b0/x0/b1" and
1051*186Sdb35262 			 * nonwildcard = "x1/y1") will be discovered only
1052*186Sdb35262 			 * if "x0" is forced to be a part of the wildcarded
1053*186Sdb35262 			 * path.
10540Sstevel@tonic-gate 			 */
1055*186Sdb35262 			if (dowildcard == WA_ALL &&
1056*186Sdb35262 			    wcp->s == WC_UNDERCONSTRUCTION) {
10570Sstevel@tonic-gate 				vmatch_event(infop, cp, np, lnp, anp,
1058*186Sdb35262 					    wcproot, WA_SINGLE);
10590Sstevel@tonic-gate 			}
10600Sstevel@tonic-gate 
10610Sstevel@tonic-gate 			if (np->u.name.it == IT_HORIZONTAL) {
10620Sstevel@tonic-gate 				/*
10630Sstevel@tonic-gate 				 * hmatch() finished iterating through
10640Sstevel@tonic-gate 				 * the configuration as described above, so
10650Sstevel@tonic-gate 				 * don't continue iterating here.
10660Sstevel@tonic-gate 				 */
10670Sstevel@tonic-gate 				return;
10680Sstevel@tonic-gate 			}
10690Sstevel@tonic-gate 
1070*186Sdb35262 		} else if ((dowildcard == WA_SINGLE || dowildcard == WA_ALL) &&
1071*186Sdb35262 			    wcp->s == WC_UNDERCONSTRUCTION) {
10720Sstevel@tonic-gate 			/*
10730Sstevel@tonic-gate 			 * no matching cp, and we are constructing our own
10740Sstevel@tonic-gate 			 * wildcard path.  (in other words, we are not
10750Sstevel@tonic-gate 			 * referencing a wildcard path created for an
10760Sstevel@tonic-gate 			 * earlier event.)
10770Sstevel@tonic-gate 			 *
10780Sstevel@tonic-gate 			 * add wildcard entry, then recurse on to config
10790Sstevel@tonic-gate 			 * child
10800Sstevel@tonic-gate 			 */
10810Sstevel@tonic-gate 			struct node *cpnode, *prevlast;
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 			cpnode = tree_name(cp_s, IT_NONE, NULL, 0);
10840Sstevel@tonic-gate 			cpnode->u.name.child = newnode(T_NUM, NULL, 0);
10850Sstevel@tonic-gate 			cpnode->u.name.child->u.ull = cp_num;
10860Sstevel@tonic-gate 			cpnode->u.name.cp = cp;
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate 			if (wcp->p->ewname == NULL) {
10890Sstevel@tonic-gate 				prevlast = NULL;
10900Sstevel@tonic-gate 				wcp->p->ewname = cpnode;
10910Sstevel@tonic-gate 			} else {
10920Sstevel@tonic-gate 				prevlast = wcp->p->ewname->u.name.last;
10930Sstevel@tonic-gate 				wcp->p->ewname =
10940Sstevel@tonic-gate 					tree_name_append(wcp->p->ewname,
10950Sstevel@tonic-gate 							    cpnode);
10960Sstevel@tonic-gate 			}
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 			vmatch_event(infop, config_child(cp), np, lnp, anp,
1099*186Sdb35262 				    wcproot, WA_ALL);
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 			/*
11020Sstevel@tonic-gate 			 * back out last addition to ewname and continue
11030Sstevel@tonic-gate 			 * with loop
11040Sstevel@tonic-gate 			 */
11050Sstevel@tonic-gate 			tree_free(cpnode);
11060Sstevel@tonic-gate 			if (prevlast == NULL) {
11070Sstevel@tonic-gate 				wcp->p->ewname = NULL;
11080Sstevel@tonic-gate 			} else {
11090Sstevel@tonic-gate 				prevlast->u.name.next = NULL;
11100Sstevel@tonic-gate 				wcp->p->ewname->u.name.last = prevlast;
11110Sstevel@tonic-gate 			}
1112*186Sdb35262 
1113*186Sdb35262 			/*
1114*186Sdb35262 			 * return if wildcarding is done only for this cp
1115*186Sdb35262 			 */
1116*186Sdb35262 			if (dowildcard == WA_SINGLE)
1117*186Sdb35262 				return;
11180Sstevel@tonic-gate 		}
11190Sstevel@tonic-gate 	}
11200Sstevel@tonic-gate }
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate /*
11230Sstevel@tonic-gate  * for the event node np, which will be subjected to pathname
11240Sstevel@tonic-gate  * expansion/matching, create a (struct wildcardinfo) to hold wildcard
11250Sstevel@tonic-gate  * information.  this struct will be inserted into the first location in
11260Sstevel@tonic-gate  * the list that starts with *wcproot.
11270Sstevel@tonic-gate  *
11280Sstevel@tonic-gate  * cp is the starting node of the configuration; cpstart, which is output,
11290Sstevel@tonic-gate  * is the starting node of the nonwildcarded portion of the path.
11300Sstevel@tonic-gate  */
11310Sstevel@tonic-gate static void
11320Sstevel@tonic-gate add_wildcardentry(struct wildcardinfo **wcproot, struct config *cp,
1133*186Sdb35262 		struct node *np)
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	struct wildcardinfo *wcpnew, *wcp;
11360Sstevel@tonic-gate 	struct node *np1, *np2;
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	/*
11390Sstevel@tonic-gate 	 * create entry for np
11400Sstevel@tonic-gate 	 */
11410Sstevel@tonic-gate 	wcpnew = MALLOC(sizeof (struct wildcardinfo));
11420Sstevel@tonic-gate 	bzero(wcpnew, sizeof (struct wildcardinfo));
11430Sstevel@tonic-gate 	wcpnew->nptop = np;
11440Sstevel@tonic-gate 	wcpnew->oldepname = np->u.event.epname;
1145*186Sdb35262 	wcpnew->s = WC_UNDERCONSTRUCTION;
1146*186Sdb35262 
1147*186Sdb35262 	wcpnew->p = MALLOC(sizeof (struct wildcardpath));
1148*186Sdb35262 	bzero(wcpnew->p, sizeof (struct wildcardpath));
1149*186Sdb35262 	wcpnew->p->cpstart = cp;
1150*186Sdb35262 	wcpnew->p->refcount = 1;
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	/*
11530Sstevel@tonic-gate 	 * search all completed entries for an epname whose first entry
11540Sstevel@tonic-gate 	 * matches.  note that NULL epnames are considered valid and can be
11550Sstevel@tonic-gate 	 * matched.
11560Sstevel@tonic-gate 	 */
1157*186Sdb35262 	np2 = wcpnew->oldepname;
11580Sstevel@tonic-gate 	for (wcp = *wcproot; wcp; wcp = wcp->next) {
1159*186Sdb35262 		ASSERT(wcp->s == WC_COMPLETE);
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 		np1 = wcp->oldepname;
11620Sstevel@tonic-gate 		if ((np1 && np2 && np1->u.name.s == np2->u.name.s) ||
11630Sstevel@tonic-gate 		    (np1 == NULL && np2 == NULL)) {
1164*186Sdb35262 			/*
1165*186Sdb35262 			 * if we find a match in a completed entry, set
1166*186Sdb35262 			 * matchwc to indicate that we would like to match
1167*186Sdb35262 			 * it.  it is necessary to do this since wildcards
1168*186Sdb35262 			 * for each event are constructed independently.
1169*186Sdb35262 			 */
1170*186Sdb35262 			wcpnew->matchwc = wcp->p;
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 			wcp->p->refcount++;
11730Sstevel@tonic-gate 			break;
11740Sstevel@tonic-gate 		}
11750Sstevel@tonic-gate 	}
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	wcpnew->next = *wcproot;
11780Sstevel@tonic-gate 	*wcproot = wcpnew;
11790Sstevel@tonic-gate }
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate static void
11820Sstevel@tonic-gate delete_wildcardentry(struct wildcardinfo **wcproot)
11830Sstevel@tonic-gate {
11840Sstevel@tonic-gate 	struct wildcardinfo *wcp;
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 	wcp = *wcproot;
11870Sstevel@tonic-gate 	*wcproot = wcp->next;
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate 	switch (wcp->s) {
11900Sstevel@tonic-gate 	case WC_UNDERCONSTRUCTION:
11910Sstevel@tonic-gate 	case WC_COMPLETE:
1192*186Sdb35262 		if (wcp->matchwc != NULL)
1193*186Sdb35262 			wcp->matchwc->refcount--;
1194*186Sdb35262 
11950Sstevel@tonic-gate 		ASSERT(wcp->p->refcount == 1);
11960Sstevel@tonic-gate 		tree_free(wcp->p->ewname);
11970Sstevel@tonic-gate 		FREE(wcp->p);
11980Sstevel@tonic-gate 		break;
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	default:
12010Sstevel@tonic-gate 		out(O_DIE, "deletewc: invalid status");
12020Sstevel@tonic-gate 		break;
12030Sstevel@tonic-gate 	}
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	FREE(wcp);
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate 
12080Sstevel@tonic-gate /*
12090Sstevel@tonic-gate  * vmatch -- find the next vertical expansion match in the config database
12100Sstevel@tonic-gate  *
12110Sstevel@tonic-gate  * this routine is called with three node pointers:
12120Sstevel@tonic-gate  *	 np -- the parse we're matching
12130Sstevel@tonic-gate  *	lnp -- the rest of the list we're currently working on
12140Sstevel@tonic-gate  *	anp -- the rest of the arrow we're currently working on
12150Sstevel@tonic-gate  *
12160Sstevel@tonic-gate  * the expansion matching happens via three types of recursion:
12170Sstevel@tonic-gate  *
12180Sstevel@tonic-gate  *	- when given an arrow, handle the left-side and then recursively
12190Sstevel@tonic-gate  *	  handle the right side (which might be another cascaded arrow).
12200Sstevel@tonic-gate  *
12210Sstevel@tonic-gate  *	- when handling one side of an arrow, recurse through the T_LIST
12220Sstevel@tonic-gate  *	  to get to each event (or just move on to the event if there
12230Sstevel@tonic-gate  *	  is a single event instead of a list)  since the arrow parse
12240Sstevel@tonic-gate  *	  trees recurse left, we actually start with the right-most
12250Sstevel@tonic-gate  *	  event list in the prop statement and work our way towards
12260Sstevel@tonic-gate  *	  the left-most event list.
12270Sstevel@tonic-gate  *
12280Sstevel@tonic-gate  *	- when handling an event, recurse down each component of the
12290Sstevel@tonic-gate  *	  pathname, matching in the config database and recording the
12300Sstevel@tonic-gate  *	  matches in the explicit iterator dictionary as we go.
12310Sstevel@tonic-gate  *
12320Sstevel@tonic-gate  * when the bottom of this matching recursion is met, meaning we have
12330Sstevel@tonic-gate  * set the "cp" pointers on all the names in the entire statement,
12340Sstevel@tonic-gate  * we call hmatch() which does it's own recursion to handle horizontal
12350Sstevel@tonic-gate  * expandsion and then call generate() to generate nodes, bubbles, and
12360Sstevel@tonic-gate  * arrows in the instance tree.  generate() looks at the cp pointers to
12370Sstevel@tonic-gate  * see what instance numbers were matched in the configuration database.
12380Sstevel@tonic-gate  *
12390Sstevel@tonic-gate  * when horizontal expansion appears, vmatch() finds only the first match
12400Sstevel@tonic-gate  * and hmatch() then takes the horizontal expansion through all the other
12410Sstevel@tonic-gate  * matches when generating the arrows in the instance tree.
12420Sstevel@tonic-gate  *
12430Sstevel@tonic-gate  * the "infop" passed down through the recursion contains a dictionary
12440Sstevel@tonic-gate  * of the explicit iterators (all the implicit iterators have been converted
12450Sstevel@tonic-gate  * to explicit iterators when the parse tree was created by tree.c), which
12460Sstevel@tonic-gate  * allows things like this to work correctly:
12470Sstevel@tonic-gate  *
12480Sstevel@tonic-gate  *	prop error.a@x[n]/y/z -> error.b@x/y[n]/z -> error.c@x/y/z[n];
12490Sstevel@tonic-gate  *
12500Sstevel@tonic-gate  * during the top level call, the explicit iterator "n" will match an
12510Sstevel@tonic-gate  * instance number in the config database, and the result will be recorded
12520Sstevel@tonic-gate  * in the explicit iterator dictionary and passed down via "infop".  so
12530Sstevel@tonic-gate  * when the recursive call tries to match y[n] in the config database, it
12540Sstevel@tonic-gate  * will only match the same instance number as x[n] did since the dictionary
12550Sstevel@tonic-gate  * is consulted to see if "n" took on a value already.
12560Sstevel@tonic-gate  *
12570Sstevel@tonic-gate  * at any point during the recursion, match*() can return to indicate
12580Sstevel@tonic-gate  * a match was not found in the config database and that the caller should
12590Sstevel@tonic-gate  * move on to the next potential match, if any.
12600Sstevel@tonic-gate  *
12610Sstevel@tonic-gate  * constraints are completely ignored by match(), so the statement:
12620Sstevel@tonic-gate  *
12630Sstevel@tonic-gate  *	prop error.a@x[n] -> error.b@x[n] {n != 0};
12640Sstevel@tonic-gate  *
12650Sstevel@tonic-gate  * might very well match x[0] if it appears in the config database.  it
12660Sstevel@tonic-gate  * is the generate() routine that takes that match and then decides what
12670Sstevel@tonic-gate  * arrow, if any, should be generated in the instance tree.  generate()
12680Sstevel@tonic-gate  * looks at the explicit iterator dictionary to get values like "n" in
12690Sstevel@tonic-gate  * the above example so that it can evaluate constraints.
12700Sstevel@tonic-gate  *
12710Sstevel@tonic-gate  */
12720Sstevel@tonic-gate static void
12730Sstevel@tonic-gate vmatch(struct info *infop, struct node *np, struct node *lnp,
12740Sstevel@tonic-gate     struct node *anp, struct wildcardinfo **wcproot)
12750Sstevel@tonic-gate {
12760Sstevel@tonic-gate 	if (np == NULL) {
12770Sstevel@tonic-gate 		if (lnp)
12780Sstevel@tonic-gate 			vmatch(infop, lnp, NULL, anp, wcproot);
12790Sstevel@tonic-gate 		else if (anp)
12800Sstevel@tonic-gate 			vmatch(infop, anp, NULL, NULL, wcproot);
12810Sstevel@tonic-gate 		else {
12820Sstevel@tonic-gate 			struct node *src;
12830Sstevel@tonic-gate 			struct node *dst;
12840Sstevel@tonic-gate 
12850Sstevel@tonic-gate 			/* end of vertical match recursion */
12860Sstevel@tonic-gate 			outfl(O_ALTFP|O_VERB3|O_NONL,
12870Sstevel@tonic-gate 			    infop->anp->file, infop->anp->line, "vmatch: ");
12880Sstevel@tonic-gate 			ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, infop->anp);
12890Sstevel@tonic-gate 			out(O_ALTFP|O_VERB3, NULL);
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 			generate_nork(
12920Sstevel@tonic-gate 			    itree_np2nork(infop->anp->u.arrow.nnp),
12930Sstevel@tonic-gate 			    itree_np2nork(infop->anp->u.arrow.knp));
12940Sstevel@tonic-gate 			dst = infop->anp->u.arrow.rhs;
12950Sstevel@tonic-gate 			src = infop->anp->u.arrow.lhs;
12960Sstevel@tonic-gate 			for (;;) {
12970Sstevel@tonic-gate 				generate_new();	/* new set of arrows */
12980Sstevel@tonic-gate 				if (src->t == T_ARROW) {
12990Sstevel@tonic-gate 					hmatch(infop, src->u.arrow.rhs, dst);
13000Sstevel@tonic-gate 					generate_nork(
13010Sstevel@tonic-gate 					    itree_np2nork(src->u.arrow.nnp),
13020Sstevel@tonic-gate 					    itree_np2nork(src->u.arrow.knp));
13030Sstevel@tonic-gate 					dst = src->u.arrow.rhs;
13040Sstevel@tonic-gate 					src = src->u.arrow.lhs;
13050Sstevel@tonic-gate 				} else {
13060Sstevel@tonic-gate 					hmatch(infop, src, dst);
13070Sstevel@tonic-gate 					break;
13080Sstevel@tonic-gate 				}
13090Sstevel@tonic-gate 			}
13100Sstevel@tonic-gate 		}
13110Sstevel@tonic-gate 		return;
13120Sstevel@tonic-gate 	}
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 	switch (np->t) {
13150Sstevel@tonic-gate 	case T_EVENT: {
1316*186Sdb35262 		add_wildcardentry(wcproot, config_child(infop->croot), np);
1317*186Sdb35262 		vmatch_event(infop, config_child(infop->croot),
1318*186Sdb35262 			    np->u.event.epname, lnp, anp, wcproot, WA_ALL);
13190Sstevel@tonic-gate 		delete_wildcardentry(wcproot);
13200Sstevel@tonic-gate 		break;
13210Sstevel@tonic-gate 	}
13220Sstevel@tonic-gate 	case T_LIST:
13230Sstevel@tonic-gate 		ASSERT(lnp == NULL);
13240Sstevel@tonic-gate 		vmatch(infop, np->u.expr.right, np->u.expr.left, anp, wcproot);
13250Sstevel@tonic-gate 		break;
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 	case T_ARROW:
13280Sstevel@tonic-gate 		ASSERT(lnp == NULL && anp == NULL);
13290Sstevel@tonic-gate 		vmatch(infop, np->u.arrow.rhs, NULL, np->u.arrow.lhs, wcproot);
13300Sstevel@tonic-gate 		break;
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate 	default:
13330Sstevel@tonic-gate 		outfl(O_DIE, np->file, np->line,
13340Sstevel@tonic-gate 		    "vmatch: unexpected type: %s",
13350Sstevel@tonic-gate 		    ptree_nodetype2str(np->t));
13360Sstevel@tonic-gate 	}
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate static void
13400Sstevel@tonic-gate cp_reset(struct node *np)
13410Sstevel@tonic-gate {
13420Sstevel@tonic-gate 	if (np == NULL)
13430Sstevel@tonic-gate 		return;
13440Sstevel@tonic-gate 	switch (np->t) {
13450Sstevel@tonic-gate 	case T_NAME:
13460Sstevel@tonic-gate 		np->u.name.cp = NULL;
13470Sstevel@tonic-gate 		cp_reset(np->u.name.next);
13480Sstevel@tonic-gate 		break;
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 	case T_LIST:
13510Sstevel@tonic-gate 		cp_reset(np->u.expr.left);
13520Sstevel@tonic-gate 		cp_reset(np->u.expr.right);
13530Sstevel@tonic-gate 		break;
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 	case T_ARROW:
13560Sstevel@tonic-gate 		cp_reset(np->u.arrow.lhs);
13570Sstevel@tonic-gate 		cp_reset(np->u.arrow.rhs);
13580Sstevel@tonic-gate 		break;
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 	case T_EVENT:
13610Sstevel@tonic-gate 		cp_reset(np->u.event.epname);
13620Sstevel@tonic-gate 		break;
13630Sstevel@tonic-gate 	}
13640Sstevel@tonic-gate }
13650Sstevel@tonic-gate 
13660Sstevel@tonic-gate /*
13670Sstevel@tonic-gate  * itree_create -- apply the current config to the current parse tree
13680Sstevel@tonic-gate  *
13690Sstevel@tonic-gate  * returns a lut mapping fully-instance-qualified names to struct events.
13700Sstevel@tonic-gate  *
13710Sstevel@tonic-gate  */
13720Sstevel@tonic-gate struct lut *
13730Sstevel@tonic-gate itree_create(struct config *croot)
13740Sstevel@tonic-gate {
13750Sstevel@tonic-gate 	struct lut *retval;
13760Sstevel@tonic-gate 	struct node *propnp;
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	Ninfo.lut = NULL;
13790Sstevel@tonic-gate 	Ninfo.croot = croot;
13800Sstevel@tonic-gate 	for (propnp = Props; propnp; propnp = propnp->u.stmt.next) {
13810Sstevel@tonic-gate 		struct node *anp = propnp->u.stmt.np;
13820Sstevel@tonic-gate 		struct wildcardinfo *wcproot = NULL;
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 		ASSERTeq(anp->t, T_ARROW, ptree_nodetype2str);
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 		Ninfo.anp = anp;
13870Sstevel@tonic-gate 		Ninfo.ex = NULL;
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 		generate_arrownp(anp);
13900Sstevel@tonic-gate 		vmatch(&Ninfo, anp, NULL, NULL, &wcproot);
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate 		if (Ninfo.ex) {
13930Sstevel@tonic-gate 			lut_free(Ninfo.ex, iterinfo_destructor, NULL);
13940Sstevel@tonic-gate 			Ninfo.ex = NULL;
13950Sstevel@tonic-gate 		}
13960Sstevel@tonic-gate 		ASSERT(wcproot == NULL);
13970Sstevel@tonic-gate 		cp_reset(anp);
13980Sstevel@tonic-gate 	}
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 	retval = Ninfo.lut;
14010Sstevel@tonic-gate 	Ninfo.lut = NULL;
14020Sstevel@tonic-gate 	return (retval);
14030Sstevel@tonic-gate }
14040Sstevel@tonic-gate 
14050Sstevel@tonic-gate void
14060Sstevel@tonic-gate itree_free(struct lut *lutp)
14070Sstevel@tonic-gate {
14080Sstevel@tonic-gate 	lut_free(lutp, itree_destructor, NULL);
14090Sstevel@tonic-gate }
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate int
14120Sstevel@tonic-gate itree_nameinstancecmp(struct node *np1, struct node *np2)
14130Sstevel@tonic-gate {
14140Sstevel@tonic-gate 	int np1type = (int)np1->u.name.t;
14150Sstevel@tonic-gate 	int np2type = (int)np2->u.name.t;
14160Sstevel@tonic-gate 	int num1;
14170Sstevel@tonic-gate 	int num2;
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 	while (np1 && np2 && np1->u.name.s == np2->u.name.s) {
14200Sstevel@tonic-gate 		if (np1->u.name.next != NULL && np2->u.name.next != NULL) {
14210Sstevel@tonic-gate 			if (np1->u.name.cp != NULL) {
14220Sstevel@tonic-gate 				config_getcompname(np1->u.name.cp, NULL, &num1);
14230Sstevel@tonic-gate 			} else {
14240Sstevel@tonic-gate 				ASSERT(np1->u.name.child != NULL);
14250Sstevel@tonic-gate 				ASSERT(np1->u.name.child->t == T_NUM);
14260Sstevel@tonic-gate 				num1 = (int)np1->u.name.child->u.ull;
14270Sstevel@tonic-gate 			}
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 			if (np2->u.name.cp != NULL) {
14300Sstevel@tonic-gate 				config_getcompname(np2->u.name.cp, NULL, &num2);
14310Sstevel@tonic-gate 			} else {
14320Sstevel@tonic-gate 				ASSERT(np2->u.name.child != NULL);
14330Sstevel@tonic-gate 				ASSERT(np2->u.name.child->t == T_NUM);
14340Sstevel@tonic-gate 				num2 = (int)np2->u.name.child->u.ull;
14350Sstevel@tonic-gate 			}
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 			if (num1 != num2)
14380Sstevel@tonic-gate 				return (num1 - num2);
14390Sstevel@tonic-gate 		}
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 		np1 = np1->u.name.next;
14420Sstevel@tonic-gate 		np2 = np2->u.name.next;
14430Sstevel@tonic-gate 	}
14440Sstevel@tonic-gate 	if (np1 == NULL)
14450Sstevel@tonic-gate 		if (np2 == NULL)
14460Sstevel@tonic-gate 			return (np1type - np2type);
14470Sstevel@tonic-gate 		else
14480Sstevel@tonic-gate 			return (-1);
14490Sstevel@tonic-gate 	else if (np2 == NULL)
14500Sstevel@tonic-gate 		return (1);
14510Sstevel@tonic-gate 	else
14520Sstevel@tonic-gate 		return (strcmp(np1->u.name.s, np2->u.name.s));
14530Sstevel@tonic-gate }
14540Sstevel@tonic-gate 
14550Sstevel@tonic-gate void
14560Sstevel@tonic-gate itree_pevent_brief(int flags, struct event *ep)
14570Sstevel@tonic-gate {
14580Sstevel@tonic-gate 	ASSERT(ep != NULL);
14590Sstevel@tonic-gate 	ASSERT(ep->enode != NULL);
14600Sstevel@tonic-gate 	ASSERT(ep->ipp != NULL);
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	ipath_print(flags, ep->enode->u.event.ename->u.name.s, ep->ipp);
14630Sstevel@tonic-gate }
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate /*ARGSUSED*/
14660Sstevel@tonic-gate static void
14670Sstevel@tonic-gate itree_pevent(struct event *lhs, struct event *ep, void *arg)
14680Sstevel@tonic-gate {
14690Sstevel@tonic-gate 	struct plut_wlk_data propd;
14700Sstevel@tonic-gate 	struct bubble *bp;
14710Sstevel@tonic-gate 	int flags = (int)arg;
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	itree_pevent_brief(flags, ep);
14740Sstevel@tonic-gate 	if (ep->t == N_EREPORT)
14750Sstevel@tonic-gate 		out(flags, " (count %d)", ep->count);
14760Sstevel@tonic-gate 	else
14770Sstevel@tonic-gate 		out(flags, NULL);
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	if (ep->props) {
14800Sstevel@tonic-gate 		propd.flags = flags;
14810Sstevel@tonic-gate 		propd.first = 1;
14820Sstevel@tonic-gate 		out(flags, "Properties:");
14830Sstevel@tonic-gate 		lut_walk(ep->props, ptree_plut, (void *)&propd);
14840Sstevel@tonic-gate 	}
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	for (bp = itree_next_bubble(ep, NULL); bp;
14870Sstevel@tonic-gate 	    bp = itree_next_bubble(ep, bp)) {
14880Sstevel@tonic-gate 		/* Print only TO bubbles in this loop */
14890Sstevel@tonic-gate 		if (bp->t != B_TO)
14900Sstevel@tonic-gate 			continue;
14910Sstevel@tonic-gate 		itree_pbubble(flags, bp);
14920Sstevel@tonic-gate 	}
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 	for (bp = itree_next_bubble(ep, NULL); bp;
14950Sstevel@tonic-gate 	    bp = itree_next_bubble(ep, bp)) {
14960Sstevel@tonic-gate 		/* Print only INHIBIT bubbles in this loop */
14970Sstevel@tonic-gate 		if (bp->t != B_INHIBIT)
14980Sstevel@tonic-gate 			continue;
14990Sstevel@tonic-gate 		itree_pbubble(flags, bp);
15000Sstevel@tonic-gate 	}
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate 	for (bp = itree_next_bubble(ep, NULL); bp;
15030Sstevel@tonic-gate 	    bp = itree_next_bubble(ep, bp)) {
15040Sstevel@tonic-gate 		/* Print only FROM bubbles in this loop */
15050Sstevel@tonic-gate 		if (bp->t != B_FROM)
15060Sstevel@tonic-gate 			continue;
15070Sstevel@tonic-gate 		itree_pbubble(flags, bp);
15080Sstevel@tonic-gate 	}
15090Sstevel@tonic-gate }
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate static void
15120Sstevel@tonic-gate itree_pbubble(int flags, struct bubble *bp)
15130Sstevel@tonic-gate {
15140Sstevel@tonic-gate 	struct constraintlist *cp;
15150Sstevel@tonic-gate 	struct arrowlist *ap;
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	ASSERT(bp != NULL);
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	out(flags|O_NONL, "   ");
15200Sstevel@tonic-gate 	if (bp->mark)
15210Sstevel@tonic-gate 		out(flags|O_NONL, "*");
15220Sstevel@tonic-gate 	else
15230Sstevel@tonic-gate 		out(flags|O_NONL, " ");
15240Sstevel@tonic-gate 	if (bp->t == B_FROM)
15250Sstevel@tonic-gate 		out(flags|O_NONL, "N=%d to:", bp->nork);
15260Sstevel@tonic-gate 	else if (bp->t == B_TO)
15270Sstevel@tonic-gate 		out(flags|O_NONL, "K=%d from:", bp->nork);
15280Sstevel@tonic-gate 	else
15290Sstevel@tonic-gate 		out(flags|O_NONL, "K=%d masked from:", bp->nork);
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 	if (bp->t == B_TO || bp->t == B_INHIBIT) {
15320Sstevel@tonic-gate 		for (ap = itree_next_arrow(bp, NULL); ap;
15330Sstevel@tonic-gate 		    ap = itree_next_arrow(bp, ap)) {
15340Sstevel@tonic-gate 			ASSERT(ap->arrowp->head == bp);
15350Sstevel@tonic-gate 			ASSERT(ap->arrowp->tail != NULL);
15360Sstevel@tonic-gate 			ASSERT(ap->arrowp->tail->myevent != NULL);
15370Sstevel@tonic-gate 			out(flags|O_NONL, " ");
15380Sstevel@tonic-gate 			itree_pevent_brief(flags, ap->arrowp->tail->myevent);
15390Sstevel@tonic-gate 		}
15400Sstevel@tonic-gate 		out(flags, NULL);
15410Sstevel@tonic-gate 		return;
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate 	for (ap = itree_next_arrow(bp, NULL); ap;
15450Sstevel@tonic-gate 	    ap = itree_next_arrow(bp, ap)) {
15460Sstevel@tonic-gate 		ASSERT(ap->arrowp->tail == bp);
15470Sstevel@tonic-gate 		ASSERT(ap->arrowp->head != NULL);
15480Sstevel@tonic-gate 		ASSERT(ap->arrowp->head->myevent != NULL);
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate 		out(flags|O_NONL, " ");
15510Sstevel@tonic-gate 		itree_pevent_brief(flags, ap->arrowp->head->myevent);
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 		out(flags|O_NONL, " ");
15540Sstevel@tonic-gate 		ptree_timeval(flags, &ap->arrowp->mindelay);
15550Sstevel@tonic-gate 		out(flags|O_NONL, ",");
15560Sstevel@tonic-gate 		ptree_timeval(flags, &ap->arrowp->maxdelay);
15570Sstevel@tonic-gate 
15580Sstevel@tonic-gate 		/* Display anything from the propogation node? */
15590Sstevel@tonic-gate 		out(O_VERB3|O_NONL, " <%s:%d>",
15600Sstevel@tonic-gate 		    ap->arrowp->pnode->file, ap->arrowp->pnode->line);
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 		if (itree_next_constraint(ap->arrowp, NULL))
15630Sstevel@tonic-gate 			out(flags|O_NONL, " {");
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate 		for (cp = itree_next_constraint(ap->arrowp, NULL); cp;
15660Sstevel@tonic-gate 		    cp = itree_next_constraint(ap->arrowp, cp)) {
15670Sstevel@tonic-gate 			ptree(flags, cp->cnode, 1, 0);
15680Sstevel@tonic-gate 			if (itree_next_constraint(ap->arrowp, cp))
15690Sstevel@tonic-gate 				out(flags|O_NONL, ", ");
15700Sstevel@tonic-gate 		}
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 		if (itree_next_constraint(ap->arrowp, NULL))
15730Sstevel@tonic-gate 			out(flags|O_NONL, "}");
15740Sstevel@tonic-gate 	}
15750Sstevel@tonic-gate 	out(flags, NULL);
15760Sstevel@tonic-gate }
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate void
15790Sstevel@tonic-gate itree_ptree(int flags, struct lut *itp)
15800Sstevel@tonic-gate {
15810Sstevel@tonic-gate 	lut_walk(itp, (lut_cb)itree_pevent, (void *)flags);
15820Sstevel@tonic-gate }
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate /*ARGSUSED*/
15850Sstevel@tonic-gate static void
15860Sstevel@tonic-gate itree_destructor(void *left, void *right, void *arg)
15870Sstevel@tonic-gate {
15880Sstevel@tonic-gate 	struct event *ep = (struct event *)right;
15890Sstevel@tonic-gate 	struct bubble *nextbub, *bub;
15900Sstevel@tonic-gate 
15910Sstevel@tonic-gate 	/* Free the properties */
15920Sstevel@tonic-gate 	lut_free(ep->props, instances_destructor, NULL);
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 	/* Free my bubbles */
15950Sstevel@tonic-gate 	for (bub = ep->bubbles; bub != NULL; ) {
15960Sstevel@tonic-gate 		nextbub = bub->next;
15970Sstevel@tonic-gate 		/*
15980Sstevel@tonic-gate 		 * Free arrows if they are FROM me.  Free arrowlists on
15990Sstevel@tonic-gate 		 * other types of bubbles (but not the attached arrows,
16000Sstevel@tonic-gate 		 * which will be freed when we free the originating
16010Sstevel@tonic-gate 		 * bubble.
16020Sstevel@tonic-gate 		 */
16030Sstevel@tonic-gate 		if (bub->t == B_FROM)
16040Sstevel@tonic-gate 			itree_free_arrowlists(bub, 1);
16050Sstevel@tonic-gate 		else
16060Sstevel@tonic-gate 			itree_free_arrowlists(bub, 0);
16070Sstevel@tonic-gate 		itree_free_bubble(bub);
16080Sstevel@tonic-gate 		bub = nextbub;
16090Sstevel@tonic-gate 	}
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	if (ep->nvp != NULL)
16120Sstevel@tonic-gate 		nvlist_free(ep->nvp);
16130Sstevel@tonic-gate 	bzero(ep, sizeof (*ep));
16140Sstevel@tonic-gate 	FREE(ep);
16150Sstevel@tonic-gate }
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate static void
16180Sstevel@tonic-gate itree_free_bubble(struct bubble *freeme)
16190Sstevel@tonic-gate {
16200Sstevel@tonic-gate 	bzero(freeme, sizeof (*freeme));
16210Sstevel@tonic-gate 	FREE(freeme);
16220Sstevel@tonic-gate }
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate static struct bubble *
16250Sstevel@tonic-gate itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen)
16260Sstevel@tonic-gate {
16270Sstevel@tonic-gate 	struct bubble *prev = NULL;
16280Sstevel@tonic-gate 	struct bubble *curr;
16290Sstevel@tonic-gate 	struct bubble *newb;
16300Sstevel@tonic-gate 
16310Sstevel@tonic-gate 	/* Use existing bubbles as appropriate when possible */
16320Sstevel@tonic-gate 	for (curr = eventp->bubbles;
16330Sstevel@tonic-gate 	    curr != NULL;
16340Sstevel@tonic-gate 	    prev = curr, curr = curr->next) {
16350Sstevel@tonic-gate 		if (btype == B_TO && curr->t == B_TO) {
16360Sstevel@tonic-gate 			/* see if an existing "to" bubble works for us */
16370Sstevel@tonic-gate 			if (gen == curr->gen)
16380Sstevel@tonic-gate 				return (curr);	/* matched gen number */
16390Sstevel@tonic-gate 			else if (nork == 1 && curr->nork == 1) {
16400Sstevel@tonic-gate 				curr->gen = gen;
16410Sstevel@tonic-gate 				return (curr);	/* coalesce K==1 bubbles */
16420Sstevel@tonic-gate 			}
16430Sstevel@tonic-gate 		} else if (btype == B_FROM && curr->t == B_FROM) {
16440Sstevel@tonic-gate 			/* see if an existing "from" bubble works for us */
16450Sstevel@tonic-gate 			if ((nork == N_IS_ALL && curr->nork == N_IS_ALL) ||
16460Sstevel@tonic-gate 			    (nork == 0 && curr->nork == 0))
16470Sstevel@tonic-gate 				return (curr);
16480Sstevel@tonic-gate 		}
16490Sstevel@tonic-gate 	}
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 	newb = MALLOC(sizeof (struct bubble));
16520Sstevel@tonic-gate 	newb->next = NULL;
16530Sstevel@tonic-gate 	newb->t = btype;
16540Sstevel@tonic-gate 	newb->myevent = eventp;
16550Sstevel@tonic-gate 	newb->nork = nork;
16560Sstevel@tonic-gate 	newb->mark = 0;
16570Sstevel@tonic-gate 	newb->gen = gen;
16580Sstevel@tonic-gate 	newb->arrows = NULL;
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 	if (prev == NULL)
16610Sstevel@tonic-gate 		eventp->bubbles = newb;
16620Sstevel@tonic-gate 	else
16630Sstevel@tonic-gate 		prev->next = newb;
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 	return (newb);
16660Sstevel@tonic-gate }
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate struct bubble *
16690Sstevel@tonic-gate itree_next_bubble(struct event *eventp, struct bubble *last)
16700Sstevel@tonic-gate {
16710Sstevel@tonic-gate 	struct bubble *next;
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 	for (;;) {
16740Sstevel@tonic-gate 		if (last != NULL)
16750Sstevel@tonic-gate 			next = last->next;
16760Sstevel@tonic-gate 		else
16770Sstevel@tonic-gate 			next = eventp->bubbles;
16780Sstevel@tonic-gate 
16790Sstevel@tonic-gate 		if (next == NULL || next->arrows != NULL)
16800Sstevel@tonic-gate 			return (next);
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 		/* bubble was empty, skip it */
16830Sstevel@tonic-gate 		last = next;
16840Sstevel@tonic-gate 	}
16850Sstevel@tonic-gate }
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate static void
16880Sstevel@tonic-gate add_arrow(struct bubble *bp, struct arrow *ap)
16890Sstevel@tonic-gate {
16900Sstevel@tonic-gate 	struct arrowlist *prev = NULL;
16910Sstevel@tonic-gate 	struct arrowlist *curr;
16920Sstevel@tonic-gate 	struct arrowlist *newal;
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	newal = MALLOC(sizeof (struct arrowlist));
16950Sstevel@tonic-gate 	bzero(newal, sizeof (struct arrowlist));
16960Sstevel@tonic-gate 	newal->arrowp = ap;
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 	curr = itree_next_arrow(bp, NULL);
16990Sstevel@tonic-gate 	while (curr != NULL) {
17000Sstevel@tonic-gate 		prev = curr;
17010Sstevel@tonic-gate 		curr = itree_next_arrow(bp, curr);
17020Sstevel@tonic-gate 	}
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 	if (prev == NULL)
17050Sstevel@tonic-gate 		bp->arrows = newal;
17060Sstevel@tonic-gate 	else
17070Sstevel@tonic-gate 		prev->next = newal;
17080Sstevel@tonic-gate }
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate static struct arrow *
17110Sstevel@tonic-gate itree_add_arrow(struct bubble *frombubblep, struct bubble *tobubblep,
17120Sstevel@tonic-gate     struct node *apnode, struct node *fromevent, struct node *toevent,
17130Sstevel@tonic-gate     struct lut *ex)
17140Sstevel@tonic-gate {
17150Sstevel@tonic-gate 	struct arrow *newa;
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	ASSERTeq(frombubblep->t, B_FROM, itree_bubbletype2str);
17180Sstevel@tonic-gate 	ASSERTinfo(tobubblep->t == B_TO || tobubblep->t == B_INHIBIT,
17190Sstevel@tonic-gate 	    itree_bubbletype2str(tobubblep->t));
17200Sstevel@tonic-gate 	newa = MALLOC(sizeof (struct arrow));
17210Sstevel@tonic-gate 	newa->tail = frombubblep;
17220Sstevel@tonic-gate 	newa->head = tobubblep;
17230Sstevel@tonic-gate 	newa->pnode = apnode;
17240Sstevel@tonic-gate 	newa->constraints = NULL;
17250Sstevel@tonic-gate 
17260Sstevel@tonic-gate 	/*
17270Sstevel@tonic-gate 	 *  Set default delays, then try to re-set them from
17280Sstevel@tonic-gate 	 *  any within() constraints.
17290Sstevel@tonic-gate 	 */
17300Sstevel@tonic-gate 	newa->mindelay = newa->maxdelay = 0ULL;
17310Sstevel@tonic-gate 	if (itree_set_arrow_traits(newa, fromevent, toevent, ex) == 0) {
17320Sstevel@tonic-gate 		FREE(newa);
17330Sstevel@tonic-gate 		return (NULL);
17340Sstevel@tonic-gate 	}
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 	add_arrow(frombubblep, newa);
17370Sstevel@tonic-gate 	add_arrow(tobubblep, newa);
17380Sstevel@tonic-gate 	return (newa);
17390Sstevel@tonic-gate }
17400Sstevel@tonic-gate 
17410Sstevel@tonic-gate /* returns false if traits show that arrow should not be added after all */
17420Sstevel@tonic-gate static int
17430Sstevel@tonic-gate itree_set_arrow_traits(struct arrow *ap, struct node *fromev,
17440Sstevel@tonic-gate     struct node *toev, struct lut *ex)
17450Sstevel@tonic-gate {
17460Sstevel@tonic-gate 	struct node *epnames[] = { NULL, NULL, NULL };
17470Sstevel@tonic-gate 	struct node *newc = NULL;
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 	ASSERTeq(fromev->t, T_EVENT, ptree_nodetype2str);
17500Sstevel@tonic-gate 	ASSERTeq(toev->t, T_EVENT, ptree_nodetype2str);
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate 	/*
17530Sstevel@tonic-gate 	 * search for the within values first on the declaration of
17540Sstevel@tonic-gate 	 * the destination event, and then on the prop.  this allows
17550Sstevel@tonic-gate 	 * one to specify a "default" within by putting it on the
17560Sstevel@tonic-gate 	 * declaration,  but then allow overriding on the prop statement.
17570Sstevel@tonic-gate 	 */
17580Sstevel@tonic-gate 	arrow_add_within(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist);
17590Sstevel@tonic-gate 	arrow_add_within(ap, toev->u.event.eexprlist);
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate 	/*
17620Sstevel@tonic-gate 	 * handle any global constraints inherited from the
17630Sstevel@tonic-gate 	 * "fromev" event's declaration
17640Sstevel@tonic-gate 	 */
17650Sstevel@tonic-gate 	ASSERT(fromev->u.event.declp != NULL);
17660Sstevel@tonic-gate 	ASSERT(fromev->u.event.declp->u.stmt.np != NULL);
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate #ifdef	notdef
17690Sstevel@tonic-gate 	/* XXX not quite ready to evaluate constraints from decls yet */
17700Sstevel@tonic-gate 	if (fromev->u.event.declp->u.stmt.np->u.event.eexprlist)
17710Sstevel@tonic-gate 		(void) itree_add_constraint(ap,
17720Sstevel@tonic-gate 		    fromev->u.event.declp->u.stmt.np->u.event.eexprlist);
17730Sstevel@tonic-gate #endif	/* notdef */
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	/* handle constraints on the from event in the prop statement */
17760Sstevel@tonic-gate 	epnames[0] = fromev->u.event.epname;
17770Sstevel@tonic-gate 	epnames[1] = toev->u.event.epname;
17780Sstevel@tonic-gate 	if (eval_potential(fromev->u.event.eexprlist, ex, epnames, &newc) == 0)
17790Sstevel@tonic-gate 		return (0);		/* constraint disallows arrow */
17800Sstevel@tonic-gate 
17810Sstevel@tonic-gate 	/*
17820Sstevel@tonic-gate 	 * handle any global constraints inherited from the
17830Sstevel@tonic-gate 	 * "toev" event's declaration
17840Sstevel@tonic-gate 	 */
17850Sstevel@tonic-gate 	ASSERT(toev->u.event.declp != NULL);
17860Sstevel@tonic-gate 	ASSERT(toev->u.event.declp->u.stmt.np != NULL);
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate #ifdef	notdef
17890Sstevel@tonic-gate 	/* XXX not quite ready to evaluate constraints from decls yet */
17900Sstevel@tonic-gate 	if (toev->u.event.declp->u.stmt.np->u.event.eexprlist)
17910Sstevel@tonic-gate 		(void) itree_add_constraint(ap,
17920Sstevel@tonic-gate 		    toev->u.event.declp->u.stmt.np->u.event.eexprlist);
17930Sstevel@tonic-gate #endif	/* notdef */
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 	/* handle constraints on the to event in the prop statement */
17960Sstevel@tonic-gate 	epnames[0] = toev->u.event.epname;
17970Sstevel@tonic-gate 	epnames[1] = fromev->u.event.epname;
17980Sstevel@tonic-gate 	if (eval_potential(toev->u.event.eexprlist, ex, epnames, &newc) == 0)
17990Sstevel@tonic-gate 		return (0);		/* constraint disallows arrow */
18000Sstevel@tonic-gate 
18010Sstevel@tonic-gate 	/* if we came up with any deferred constraints, add them to arrow */
18020Sstevel@tonic-gate 	if (newc != NULL)
18030Sstevel@tonic-gate 		(void) itree_add_constraint(ap, newc);
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 	return (1);	/* constraints allow arrow */
18060Sstevel@tonic-gate }
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate /*
18090Sstevel@tonic-gate  * Set within() constraint.  If the constraint were set multiple times,
18100Sstevel@tonic-gate  * the last one would "win".
18110Sstevel@tonic-gate  */
18120Sstevel@tonic-gate static void
18130Sstevel@tonic-gate arrow_add_within(struct arrow *ap, struct node *xpr)
18140Sstevel@tonic-gate {
18150Sstevel@tonic-gate 	struct node *arglist;
18160Sstevel@tonic-gate 
18170Sstevel@tonic-gate 	/* end of expressions list */
18180Sstevel@tonic-gate 	if (xpr == NULL)
18190Sstevel@tonic-gate 		return;
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 	switch (xpr->t) {
18220Sstevel@tonic-gate 	case T_LIST:
18230Sstevel@tonic-gate 		arrow_add_within(ap, xpr->u.expr.left);
18240Sstevel@tonic-gate 		arrow_add_within(ap, xpr->u.expr.right);
18250Sstevel@tonic-gate 		return;
18260Sstevel@tonic-gate 	case T_FUNC:
18270Sstevel@tonic-gate 		if (xpr->u.func.s != L_within)
18280Sstevel@tonic-gate 			return;
18290Sstevel@tonic-gate 		arglist = xpr->u.func.arglist;
18300Sstevel@tonic-gate 		switch (arglist->t) {
18310Sstevel@tonic-gate 		case T_TIMEVAL:
18320Sstevel@tonic-gate 			ap->mindelay = 0;
18330Sstevel@tonic-gate 			ap->maxdelay = arglist->u.ull;
18340Sstevel@tonic-gate 			break;
18350Sstevel@tonic-gate 		case T_NAME:
18360Sstevel@tonic-gate 			ASSERT(arglist->u.name.s == L_infinity);
18370Sstevel@tonic-gate 			ap->mindelay = 0;
18380Sstevel@tonic-gate 			ap->maxdelay = TIMEVAL_EVENTUALLY;
18390Sstevel@tonic-gate 			break;
18400Sstevel@tonic-gate 		case T_LIST:
18410Sstevel@tonic-gate 			ASSERT(arglist->u.expr.left->t == T_TIMEVAL);
18420Sstevel@tonic-gate 			ap->mindelay = arglist->u.expr.left->u.ull;
18430Sstevel@tonic-gate 			switch (arglist->u.expr.right->t) {
18440Sstevel@tonic-gate 			case T_TIMEVAL:
18450Sstevel@tonic-gate 				ap->maxdelay = arglist->u.ull;
18460Sstevel@tonic-gate 				break;
18470Sstevel@tonic-gate 			case T_NAME:
18480Sstevel@tonic-gate 				ASSERT(arglist->u.expr.right->u.name.s ==
18490Sstevel@tonic-gate 				    L_infinity);
18500Sstevel@tonic-gate 				ap->maxdelay = TIMEVAL_EVENTUALLY;
18510Sstevel@tonic-gate 				break;
18520Sstevel@tonic-gate 			default:
18530Sstevel@tonic-gate 				out(O_DIE, "within: unexpected 2nd arg type");
18540Sstevel@tonic-gate 			}
18550Sstevel@tonic-gate 			break;
18560Sstevel@tonic-gate 		default:
18570Sstevel@tonic-gate 			out(O_DIE, "within: unexpected 1st arg type");
18580Sstevel@tonic-gate 		}
18590Sstevel@tonic-gate 		break;
18600Sstevel@tonic-gate 	default:
18610Sstevel@tonic-gate 		return;
18620Sstevel@tonic-gate 	}
18630Sstevel@tonic-gate }
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate static void
18660Sstevel@tonic-gate itree_free_arrowlists(struct bubble *bubp, int arrows_too)
18670Sstevel@tonic-gate {
18680Sstevel@tonic-gate 	struct arrowlist *al, *nal;
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 	al = bubp->arrows;
18710Sstevel@tonic-gate 	while (al != NULL) {
18720Sstevel@tonic-gate 		nal = al->next;
18730Sstevel@tonic-gate 		if (arrows_too) {
18740Sstevel@tonic-gate 			itree_free_constraints(al->arrowp);
18750Sstevel@tonic-gate 			bzero(al->arrowp, sizeof (struct arrow));
18760Sstevel@tonic-gate 			FREE(al->arrowp);
18770Sstevel@tonic-gate 		}
18780Sstevel@tonic-gate 		bzero(al, sizeof (*al));
18790Sstevel@tonic-gate 		FREE(al);
18800Sstevel@tonic-gate 		al = nal;
18810Sstevel@tonic-gate 	}
18820Sstevel@tonic-gate }
18830Sstevel@tonic-gate 
18840Sstevel@tonic-gate struct arrowlist *
18850Sstevel@tonic-gate itree_next_arrow(struct bubble *bubble, struct arrowlist *last)
18860Sstevel@tonic-gate {
18870Sstevel@tonic-gate 	struct arrowlist *next;
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate 	if (last != NULL)
18900Sstevel@tonic-gate 		next = last->next;
18910Sstevel@tonic-gate 	else
18920Sstevel@tonic-gate 		next = bubble->arrows;
18930Sstevel@tonic-gate 	return (next);
18940Sstevel@tonic-gate }
18950Sstevel@tonic-gate 
18960Sstevel@tonic-gate static struct constraintlist *
18970Sstevel@tonic-gate itree_add_constraint(struct arrow *arrowp, struct node *c)
18980Sstevel@tonic-gate {
18990Sstevel@tonic-gate 	struct constraintlist *prev = NULL;
19000Sstevel@tonic-gate 	struct constraintlist *curr;
19010Sstevel@tonic-gate 	struct constraintlist *newc;
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 	for (curr = arrowp->constraints;
19040Sstevel@tonic-gate 	    curr != NULL;
19050Sstevel@tonic-gate 	    prev = curr, curr = curr->next);
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	newc = MALLOC(sizeof (struct constraintlist));
19080Sstevel@tonic-gate 	newc->next = NULL;
19090Sstevel@tonic-gate 	newc->cnode = c;
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	if (prev == NULL)
19120Sstevel@tonic-gate 		arrowp->constraints = newc;
19130Sstevel@tonic-gate 	else
19140Sstevel@tonic-gate 		prev->next = newc;
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate 	return (newc);
19170Sstevel@tonic-gate }
19180Sstevel@tonic-gate 
19190Sstevel@tonic-gate struct constraintlist *
19200Sstevel@tonic-gate itree_next_constraint(struct arrow *arrowp, struct constraintlist *last)
19210Sstevel@tonic-gate {
19220Sstevel@tonic-gate 	struct constraintlist *next;
19230Sstevel@tonic-gate 
19240Sstevel@tonic-gate 	if (last != NULL)
19250Sstevel@tonic-gate 		next = last->next;
19260Sstevel@tonic-gate 	else
19270Sstevel@tonic-gate 		next = arrowp->constraints;
19280Sstevel@tonic-gate 	return (next);
19290Sstevel@tonic-gate }
19300Sstevel@tonic-gate 
19310Sstevel@tonic-gate static void
19320Sstevel@tonic-gate itree_free_constraints(struct arrow *ap)
19330Sstevel@tonic-gate {
19340Sstevel@tonic-gate 	struct constraintlist *cl, *ncl;
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	cl = ap->constraints;
19370Sstevel@tonic-gate 	while (cl != NULL) {
19380Sstevel@tonic-gate 		ncl = cl->next;
19390Sstevel@tonic-gate 		ASSERT(cl->cnode != NULL);
19400Sstevel@tonic-gate 		tree_free(cl->cnode);
19410Sstevel@tonic-gate 		bzero(cl, sizeof (*cl));
19420Sstevel@tonic-gate 		FREE(cl);
19430Sstevel@tonic-gate 		cl = ncl;
19440Sstevel@tonic-gate 	}
19450Sstevel@tonic-gate }
19460Sstevel@tonic-gate 
19470Sstevel@tonic-gate const char *
19480Sstevel@tonic-gate itree_bubbletype2str(enum bubbletype t)
19490Sstevel@tonic-gate {
19500Sstevel@tonic-gate 	static char buf[100];
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 	switch (t) {
19530Sstevel@tonic-gate 	case B_FROM:	return L_from;
19540Sstevel@tonic-gate 	case B_TO:	return L_to;
19550Sstevel@tonic-gate 	case B_INHIBIT:	return L_inhibit;
19560Sstevel@tonic-gate 	default:
19570Sstevel@tonic-gate 		(void) sprintf(buf, "[unexpected bubbletype: %d]", t);
19580Sstevel@tonic-gate 		return (buf);
19590Sstevel@tonic-gate 	}
19600Sstevel@tonic-gate }
19610Sstevel@tonic-gate 
19620Sstevel@tonic-gate /*
19630Sstevel@tonic-gate  * itree_fini -- clean up any half-built itrees
19640Sstevel@tonic-gate  */
19650Sstevel@tonic-gate void
19660Sstevel@tonic-gate itree_fini(void)
19670Sstevel@tonic-gate {
19680Sstevel@tonic-gate 	if (Ninfo.lut != NULL) {
19690Sstevel@tonic-gate 		itree_free(Ninfo.lut);
19700Sstevel@tonic-gate 		Ninfo.lut = NULL;
19710Sstevel@tonic-gate 	}
19720Sstevel@tonic-gate 	if (Ninfo.ex) {
19730Sstevel@tonic-gate 		lut_free(Ninfo.ex, iterinfo_destructor, NULL);
19740Sstevel@tonic-gate 		Ninfo.ex = NULL;
19750Sstevel@tonic-gate 	}
19760Sstevel@tonic-gate }
1977