1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate * 26*0Sstevel@tonic-gate * itree.c -- instance tree creation and manipulation 27*0Sstevel@tonic-gate * 28*0Sstevel@tonic-gate * this module provides the instance tree 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <stdio.h> 34*0Sstevel@tonic-gate #include <ctype.h> 35*0Sstevel@tonic-gate #include <string.h> 36*0Sstevel@tonic-gate #include <strings.h> 37*0Sstevel@tonic-gate #include "alloc.h" 38*0Sstevel@tonic-gate #include "out.h" 39*0Sstevel@tonic-gate #include "stable.h" 40*0Sstevel@tonic-gate #include "literals.h" 41*0Sstevel@tonic-gate #include "lut.h" 42*0Sstevel@tonic-gate #include "tree.h" 43*0Sstevel@tonic-gate #include "ptree.h" 44*0Sstevel@tonic-gate #include "itree.h" 45*0Sstevel@tonic-gate #include "ipath.h" 46*0Sstevel@tonic-gate #include "eval.h" 47*0Sstevel@tonic-gate #include "config.h" 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate /* 50*0Sstevel@tonic-gate * struct info contains the state we keep when expanding a prop statement 51*0Sstevel@tonic-gate * as part of constructing the instance tree. state kept in struct info 52*0Sstevel@tonic-gate * is the non-recursive stuff -- the stuff that doesn't need to be on 53*0Sstevel@tonic-gate * the stack. the rest of the state that is passed between all the 54*0Sstevel@tonic-gate * mutually recursive functions, is required to be on the stack since 55*0Sstevel@tonic-gate * we need to backtrack and recurse as we do the instance tree construction. 56*0Sstevel@tonic-gate */ 57*0Sstevel@tonic-gate struct info { 58*0Sstevel@tonic-gate struct lut *lut; 59*0Sstevel@tonic-gate struct node *anp; /* arrow np */ 60*0Sstevel@tonic-gate struct lut *ex; /* dictionary of explicit iterators */ 61*0Sstevel@tonic-gate struct config *croot; 62*0Sstevel@tonic-gate } Ninfo; 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate /* 65*0Sstevel@tonic-gate * struct wildcardinfo is used to track wildcarded portions of paths. 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate * for example, if the epname of an event is "c/d" and the path "a/b/c/d" 68*0Sstevel@tonic-gate * exists, the wildcard path ewname is filled in with the path "a/b". when 69*0Sstevel@tonic-gate * matching is done, epname is temporarily replaced with the concatenation 70*0Sstevel@tonic-gate * of ewname and epname. cpstart is set to the (struct config *) 71*0Sstevel@tonic-gate * corresponding to component "c". 72*0Sstevel@tonic-gate * 73*0Sstevel@tonic-gate * a linked list of these structs is used to track the expansion of each 74*0Sstevel@tonic-gate * event node as it is processed in vmatch() --> vmatch_event() calls. 75*0Sstevel@tonic-gate */ 76*0Sstevel@tonic-gate struct wildcardinfo { 77*0Sstevel@tonic-gate struct node *nptop; /* event node fed to vmatch */ 78*0Sstevel@tonic-gate struct node *oldepname; /* epname without the wildcard part */ 79*0Sstevel@tonic-gate enum status { 80*0Sstevel@tonic-gate WC_UNDEFINED, /* struct is not yet initialized */ 81*0Sstevel@tonic-gate WC_UNDERCONSTRUCTION, /* wildcard path not yet done */ 82*0Sstevel@tonic-gate WC_COMPLETE, /* wildcard path done and is in use */ 83*0Sstevel@tonic-gate WC_REFERENCING /* use another node's wildcard path */ 84*0Sstevel@tonic-gate } s; 85*0Sstevel@tonic-gate struct wildcardpath { 86*0Sstevel@tonic-gate struct node *ewname; /* wildcard path */ 87*0Sstevel@tonic-gate struct config *cpstart; /* starting cp node for oldepname */ 88*0Sstevel@tonic-gate struct config *cpforcedwc; /* forced wildcard for this */ 89*0Sstevel@tonic-gate int refcount; /* number of event nodes using this */ 90*0Sstevel@tonic-gate } *p; 91*0Sstevel@tonic-gate struct wildcardinfo *next; 92*0Sstevel@tonic-gate }; 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate static void vmatch(struct info *infop, struct node *np, 95*0Sstevel@tonic-gate struct node *lnp, struct node *anp, struct wildcardinfo **wcproot); 96*0Sstevel@tonic-gate static void hmatch(struct info *infop, struct node *np, struct node *nextnp); 97*0Sstevel@tonic-gate static void itree_pbubble(int flags, struct bubble *bp); 98*0Sstevel@tonic-gate static void itree_destructor(void *left, void *right, void *arg); 99*0Sstevel@tonic-gate static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev, 100*0Sstevel@tonic-gate struct node *toev, struct lut *ex); 101*0Sstevel@tonic-gate static void itree_free_arrowlists(struct bubble *bubp, int arrows_too); 102*0Sstevel@tonic-gate static void arrow_add_within(struct arrow *ap, struct node *xpr); 103*0Sstevel@tonic-gate static struct arrow *itree_add_arrow(struct bubble *frombubblep, 104*0Sstevel@tonic-gate struct bubble *tobubblep, struct node *apnode, struct node *fromevent, 105*0Sstevel@tonic-gate struct node *toevent, struct lut *ex); 106*0Sstevel@tonic-gate static struct constraintlist *itree_add_constraint(struct arrow *arrowp, 107*0Sstevel@tonic-gate struct node *c); 108*0Sstevel@tonic-gate static struct bubble *itree_add_bubble(struct event *eventp, 109*0Sstevel@tonic-gate enum bubbletype btype, int nork, int gen); 110*0Sstevel@tonic-gate static void itree_free_bubble(struct bubble *freeme); 111*0Sstevel@tonic-gate static void itree_free_constraints(struct arrow *ap); 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gate /* 114*0Sstevel@tonic-gate * the following struct contains the state we build up during 115*0Sstevel@tonic-gate * vertical and horizontal expansion so that generate() 116*0Sstevel@tonic-gate * has everything it needs to construct the appropriate arrows. 117*0Sstevel@tonic-gate * after setting up the state by calling: 118*0Sstevel@tonic-gate * generate_arrownp() 119*0Sstevel@tonic-gate * generate_nork() 120*0Sstevel@tonic-gate * generate_new() 121*0Sstevel@tonic-gate * generate_from() 122*0Sstevel@tonic-gate * generate_to() 123*0Sstevel@tonic-gate * the actual arrow generation is done by calling: 124*0Sstevel@tonic-gate * generate() 125*0Sstevel@tonic-gate */ 126*0Sstevel@tonic-gate static struct { 127*0Sstevel@tonic-gate int generation; /* generation number of arrow set */ 128*0Sstevel@tonic-gate struct node *arrownp; /* top-level parse tree for arrow */ 129*0Sstevel@tonic-gate int n; /* n value associated with arrow */ 130*0Sstevel@tonic-gate int k; /* k value associated with arrow */ 131*0Sstevel@tonic-gate struct node *fromnp; /* left-hand-side event in parse tree */ 132*0Sstevel@tonic-gate struct node *tonp; /* right-hand-side event in parse tree */ 133*0Sstevel@tonic-gate struct event *frome; /* left-hand-side event in instance tree */ 134*0Sstevel@tonic-gate struct event *toe; /* right-hand-side event in instance tree */ 135*0Sstevel@tonic-gate struct bubble *frombp; /* bubble arrow comes from */ 136*0Sstevel@tonic-gate struct bubble *tobp; /* bubble arrow goes to */ 137*0Sstevel@tonic-gate } G; 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate static void 140*0Sstevel@tonic-gate generate_arrownp(struct node *arrownp) 141*0Sstevel@tonic-gate { 142*0Sstevel@tonic-gate G.arrownp = arrownp; 143*0Sstevel@tonic-gate } 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate static void 146*0Sstevel@tonic-gate generate_nork(int n, int k) 147*0Sstevel@tonic-gate { 148*0Sstevel@tonic-gate G.n = n; 149*0Sstevel@tonic-gate G.k = k; 150*0Sstevel@tonic-gate } 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate static void 153*0Sstevel@tonic-gate generate_new(void) 154*0Sstevel@tonic-gate { 155*0Sstevel@tonic-gate G.generation++; 156*0Sstevel@tonic-gate } 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate static void 159*0Sstevel@tonic-gate generate_from(struct node *fromeventnp, struct event *fromevent) 160*0Sstevel@tonic-gate { 161*0Sstevel@tonic-gate G.fromnp = fromeventnp; 162*0Sstevel@tonic-gate G.frome = fromevent; 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, "from bubble on "); 165*0Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp); 166*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3, NULL); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate G.frombp = itree_add_bubble(G.frome, B_FROM, G.n, 0); 169*0Sstevel@tonic-gate } 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate static void 172*0Sstevel@tonic-gate generate_to(struct node *toeventnp, struct event *toevent) 173*0Sstevel@tonic-gate { 174*0Sstevel@tonic-gate G.tonp = toeventnp; 175*0Sstevel@tonic-gate G.toe = toevent; 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, "to bubble (gen %d) on ", G.generation); 178*0Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp); 179*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3, NULL); 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate G.tobp = itree_add_bubble(G.toe, B_TO, G.k, G.generation); 182*0Sstevel@tonic-gate } 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate static void 185*0Sstevel@tonic-gate generate(struct lut *ex) 186*0Sstevel@tonic-gate { 187*0Sstevel@tonic-gate ASSERT(G.arrownp != NULL); 188*0Sstevel@tonic-gate ASSERT(G.fromnp != NULL); 189*0Sstevel@tonic-gate ASSERT(G.frome != NULL); 190*0Sstevel@tonic-gate ASSERT(G.frombp != NULL); 191*0Sstevel@tonic-gate ASSERT(G.tonp != NULL); 192*0Sstevel@tonic-gate ASSERT(G.toe != NULL); 193*0Sstevel@tonic-gate ASSERT(G.tobp != NULL); 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, " Arrow \""); 196*0Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp); 197*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3|O_NONL, "\" -> \""); 198*0Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate if (itree_add_arrow(G.frombp, G.tobp, G.arrownp, 201*0Sstevel@tonic-gate G.fromnp, G.tonp, ex) == NULL) { 202*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "\" (prevented by constraints)"); 203*0Sstevel@tonic-gate } else { 204*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3, "\""); 205*0Sstevel@tonic-gate } 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate enum childnode_action { 209*0Sstevel@tonic-gate CN_NONE, 210*0Sstevel@tonic-gate CN_INSTANTIZE, 211*0Sstevel@tonic-gate CN_DUP 212*0Sstevel@tonic-gate }; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate static struct node * 215*0Sstevel@tonic-gate tname_dup(struct node *namep, enum childnode_action act) 216*0Sstevel@tonic-gate { 217*0Sstevel@tonic-gate struct node *retp = NULL; 218*0Sstevel@tonic-gate const char *file; 219*0Sstevel@tonic-gate int line; 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate if (namep == NULL) 222*0Sstevel@tonic-gate return (NULL); 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate file = namep->file; 225*0Sstevel@tonic-gate line = namep->line; 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate for (; namep != NULL; namep = namep->u.name.next) { 228*0Sstevel@tonic-gate struct node *newnp = newnode(T_NAME, file, line); 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate newnp->u.name.t = namep->u.name.t; 231*0Sstevel@tonic-gate newnp->u.name.s = namep->u.name.s; 232*0Sstevel@tonic-gate newnp->u.name.last = newnp; 233*0Sstevel@tonic-gate newnp->u.name.it = namep->u.name.it; 234*0Sstevel@tonic-gate newnp->u.name.cp = namep->u.name.cp; 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate if (act == CN_DUP) { 237*0Sstevel@tonic-gate struct node *npc; 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate npc = namep->u.name.child; 240*0Sstevel@tonic-gate if (npc != NULL) { 241*0Sstevel@tonic-gate switch (npc->t) { 242*0Sstevel@tonic-gate case T_NUM: 243*0Sstevel@tonic-gate newnp->u.name.child = 244*0Sstevel@tonic-gate newnode(T_NUM, file, line); 245*0Sstevel@tonic-gate newnp->u.name.child->u.ull = 246*0Sstevel@tonic-gate npc->u.ull; 247*0Sstevel@tonic-gate break; 248*0Sstevel@tonic-gate case T_NAME: 249*0Sstevel@tonic-gate newnp->u.name.child = 250*0Sstevel@tonic-gate tree_name(npc->u.name.s, 251*0Sstevel@tonic-gate npc->u.name.it, 252*0Sstevel@tonic-gate file, line); 253*0Sstevel@tonic-gate break; 254*0Sstevel@tonic-gate default: 255*0Sstevel@tonic-gate out(O_DIE, "tname_dup: " 256*0Sstevel@tonic-gate "invalid child type %s", 257*0Sstevel@tonic-gate ptree_nodetype2str(npc->t)); 258*0Sstevel@tonic-gate } 259*0Sstevel@tonic-gate } 260*0Sstevel@tonic-gate } else if (act == CN_INSTANTIZE) { 261*0Sstevel@tonic-gate newnp->u.name.child = newnode(T_NUM, file, line); 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate if (namep->u.name.child == NULL || 264*0Sstevel@tonic-gate namep->u.name.child->t != T_NUM) { 265*0Sstevel@tonic-gate int inum; 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate ASSERT(newnp->u.name.cp != NULL); 268*0Sstevel@tonic-gate config_getcompname(newnp->u.name.cp, 269*0Sstevel@tonic-gate NULL, &inum); 270*0Sstevel@tonic-gate newnp->u.name.child->u.ull = 271*0Sstevel@tonic-gate (unsigned long long)inum; 272*0Sstevel@tonic-gate } else { 273*0Sstevel@tonic-gate newnp->u.name.child->u.ull = 274*0Sstevel@tonic-gate namep->u.name.child->u.ull; 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate if (retp == NULL) { 279*0Sstevel@tonic-gate retp = newnp; 280*0Sstevel@tonic-gate } else { 281*0Sstevel@tonic-gate retp->u.name.last->u.name.next = newnp; 282*0Sstevel@tonic-gate retp->u.name.last = newnp; 283*0Sstevel@tonic-gate } 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate return (retp); 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate struct prop_wlk_data { 290*0Sstevel@tonic-gate struct lut *props; 291*0Sstevel@tonic-gate struct node *epname; 292*0Sstevel@tonic-gate }; 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate static struct lut *props2instance(struct node *, struct node *); 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate /* 297*0Sstevel@tonic-gate * let oldepname be a subset of epname. return the subsection of epname 298*0Sstevel@tonic-gate * that ends with oldepname. make each component in the path explicitly 299*0Sstevel@tonic-gate * instanced (i.e., with a T_NUM child). 300*0Sstevel@tonic-gate */ 301*0Sstevel@tonic-gate static struct node * 302*0Sstevel@tonic-gate tname_dup_to_epname(struct node *oldepname, struct node *epname) 303*0Sstevel@tonic-gate { 304*0Sstevel@tonic-gate struct node *npref, *npend, *np1, *np2; 305*0Sstevel@tonic-gate struct node *ret = NULL; 306*0Sstevel@tonic-gate int foundmatch = 0; 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate if (epname == NULL) 309*0Sstevel@tonic-gate return (NULL); 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate /* 312*0Sstevel@tonic-gate * search for the longest path in epname which contains 313*0Sstevel@tonic-gate * oldnode->u.event.epname. set npend to point to just past the 314*0Sstevel@tonic-gate * end of this path. 315*0Sstevel@tonic-gate */ 316*0Sstevel@tonic-gate npend = NULL; 317*0Sstevel@tonic-gate for (npref = epname; npref; npref = npref->u.name.next) { 318*0Sstevel@tonic-gate if (npref->u.name.s == oldepname->u.name.s) { 319*0Sstevel@tonic-gate for (np1 = npref, np2 = oldepname; 320*0Sstevel@tonic-gate np1 != NULL && np2 != NULL; 321*0Sstevel@tonic-gate np1 = np1->u.name.next, np2 = np2->u.name.next) { 322*0Sstevel@tonic-gate if (np1->u.name.s != np2->u.name.s) 323*0Sstevel@tonic-gate break; 324*0Sstevel@tonic-gate } 325*0Sstevel@tonic-gate if (np2 == NULL) { 326*0Sstevel@tonic-gate foundmatch = 1; 327*0Sstevel@tonic-gate npend = np1; 328*0Sstevel@tonic-gate if (np1 == NULL) { 329*0Sstevel@tonic-gate /* oldepname matched npref up to end */ 330*0Sstevel@tonic-gate break; 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate } 333*0Sstevel@tonic-gate } 334*0Sstevel@tonic-gate } 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate if (foundmatch == 0) { 337*0Sstevel@tonic-gate /* 338*0Sstevel@tonic-gate * if oldepname could not be found in epname, return a 339*0Sstevel@tonic-gate * duplicate of the former. do not try to instantize 340*0Sstevel@tonic-gate * oldepname since it might not be a path. 341*0Sstevel@tonic-gate */ 342*0Sstevel@tonic-gate return (tname_dup(oldepname, CN_DUP)); 343*0Sstevel@tonic-gate } 344*0Sstevel@tonic-gate 345*0Sstevel@tonic-gate /* 346*0Sstevel@tonic-gate * dup (epname -- npend). all children should be T_NUMs. 347*0Sstevel@tonic-gate */ 348*0Sstevel@tonic-gate for (npref = epname; 349*0Sstevel@tonic-gate ! (npref == NULL || npref == npend); 350*0Sstevel@tonic-gate npref = npref->u.name.next) { 351*0Sstevel@tonic-gate struct node *newnp = newnode(T_NAME, oldepname->file, 352*0Sstevel@tonic-gate oldepname->line); 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate newnp->u.name.t = npref->u.name.t; 355*0Sstevel@tonic-gate newnp->u.name.s = npref->u.name.s; 356*0Sstevel@tonic-gate newnp->u.name.last = newnp; 357*0Sstevel@tonic-gate newnp->u.name.it = npref->u.name.it; 358*0Sstevel@tonic-gate newnp->u.name.cp = npref->u.name.cp; 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate newnp->u.name.child = newnode(T_NUM, oldepname->file, 361*0Sstevel@tonic-gate oldepname->line); 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate if (npref->u.name.child == NULL || 364*0Sstevel@tonic-gate npref->u.name.child->t != T_NUM) { 365*0Sstevel@tonic-gate int childnum; 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate ASSERT(npref->u.name.cp != NULL); 368*0Sstevel@tonic-gate config_getcompname(npref->u.name.cp, NULL, &childnum); 369*0Sstevel@tonic-gate newnp->u.name.child->u.ull = childnum; 370*0Sstevel@tonic-gate } else { 371*0Sstevel@tonic-gate newnp->u.name.child->u.ull = 372*0Sstevel@tonic-gate npref->u.name.child->u.ull; 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate if (ret == NULL) { 376*0Sstevel@tonic-gate ret = newnp; 377*0Sstevel@tonic-gate } else { 378*0Sstevel@tonic-gate ret->u.name.last->u.name.next = newnp; 379*0Sstevel@tonic-gate ret->u.name.last = newnp; 380*0Sstevel@tonic-gate } 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate return (ret); 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate /* 387*0Sstevel@tonic-gate * restriction: oldnode->u.event.epname has to be equivalent to or a subset 388*0Sstevel@tonic-gate * of epname 389*0Sstevel@tonic-gate */ 390*0Sstevel@tonic-gate static struct node * 391*0Sstevel@tonic-gate tevent_dup_to_epname(struct node *oldnode, struct node *epname) 392*0Sstevel@tonic-gate { 393*0Sstevel@tonic-gate struct node *ret; 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate ret = newnode(T_EVENT, oldnode->file, oldnode->line); 396*0Sstevel@tonic-gate ret->u.event.ename = tname_dup(oldnode->u.event.ename, CN_NONE); 397*0Sstevel@tonic-gate ret->u.event.epname = tname_dup_to_epname(oldnode->u.event.epname, 398*0Sstevel@tonic-gate epname); 399*0Sstevel@tonic-gate return (ret); 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate static void 403*0Sstevel@tonic-gate nv_instantiate(void *name, void *val, void *arg) 404*0Sstevel@tonic-gate { 405*0Sstevel@tonic-gate struct prop_wlk_data *pd = (struct prop_wlk_data *)arg; 406*0Sstevel@tonic-gate struct node *orhs = (struct node *)val; 407*0Sstevel@tonic-gate struct node *nrhs; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate /* handle engines by instantizing the entire engine */ 410*0Sstevel@tonic-gate if (name == L_engine) { 411*0Sstevel@tonic-gate ASSERT(orhs->t == T_EVENT); 412*0Sstevel@tonic-gate ASSERT(orhs->u.event.ename->u.name.t == N_SERD); 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gate /* there are only SERD engines for now */ 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate nrhs = newnode(T_SERD, orhs->file, orhs->line); 417*0Sstevel@tonic-gate nrhs->u.stmt.np = tevent_dup_to_epname(orhs, pd->epname); 418*0Sstevel@tonic-gate nrhs->u.stmt.lutp = props2instance(orhs, pd->epname); 419*0Sstevel@tonic-gate pd->props = lut_add(pd->props, name, nrhs, NULL); 420*0Sstevel@tonic-gate return; 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate switch (orhs->t) { 424*0Sstevel@tonic-gate case T_NUM: 425*0Sstevel@tonic-gate nrhs = newnode(T_NUM, orhs->file, orhs->line); 426*0Sstevel@tonic-gate nrhs->u.ull = orhs->u.ull; 427*0Sstevel@tonic-gate pd->props = lut_add(pd->props, name, nrhs, NULL); 428*0Sstevel@tonic-gate break; 429*0Sstevel@tonic-gate case T_TIMEVAL: 430*0Sstevel@tonic-gate nrhs = newnode(T_TIMEVAL, orhs->file, orhs->line); 431*0Sstevel@tonic-gate nrhs->u.ull = orhs->u.ull; 432*0Sstevel@tonic-gate pd->props = lut_add(pd->props, name, nrhs, NULL); 433*0Sstevel@tonic-gate break; 434*0Sstevel@tonic-gate case T_NAME: 435*0Sstevel@tonic-gate nrhs = tname_dup_to_epname(orhs, pd->epname); 436*0Sstevel@tonic-gate pd->props = lut_add(pd->props, name, nrhs, NULL); 437*0Sstevel@tonic-gate break; 438*0Sstevel@tonic-gate case T_EVENT: 439*0Sstevel@tonic-gate nrhs = tevent_dup_to_epname(orhs, pd->epname); 440*0Sstevel@tonic-gate pd->props = lut_add(pd->props, name, nrhs, NULL); 441*0Sstevel@tonic-gate break; 442*0Sstevel@tonic-gate default: 443*0Sstevel@tonic-gate out(O_DEBUG, "unexpected nvpair value type %s", 444*0Sstevel@tonic-gate ptree_nodetype2str(((struct node *)val)->t)); 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate 448*0Sstevel@tonic-gate static struct lut * 449*0Sstevel@tonic-gate props2instance(struct node *eventnp, struct node *epname) 450*0Sstevel@tonic-gate { 451*0Sstevel@tonic-gate struct prop_wlk_data pd; 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate pd.props = NULL; 454*0Sstevel@tonic-gate pd.epname = epname; 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate ASSERT(eventnp->u.event.declp != NULL); 457*0Sstevel@tonic-gate lut_walk(eventnp->u.event.declp->u.stmt.lutp, nv_instantiate, &pd); 458*0Sstevel@tonic-gate return (pd.props); 459*0Sstevel@tonic-gate } 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate /*ARGSUSED*/ 462*0Sstevel@tonic-gate static void 463*0Sstevel@tonic-gate instances_destructor(void *left, void *right, void *arg) 464*0Sstevel@tonic-gate { 465*0Sstevel@tonic-gate struct node *dn = (struct node *)right; 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate if (dn->t == T_SERD) { 468*0Sstevel@tonic-gate /* we allocated the lut during itree_create(), so free it */ 469*0Sstevel@tonic-gate lut_free(dn->u.stmt.lutp, instances_destructor, NULL); 470*0Sstevel@tonic-gate dn->u.stmt.lutp = NULL; 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate tree_free(dn); 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate /* 476*0Sstevel@tonic-gate * event_cmp -- used via lut_lookup/lut_add on instance tree lut 477*0Sstevel@tonic-gate */ 478*0Sstevel@tonic-gate static int 479*0Sstevel@tonic-gate event_cmp(struct event *ep1, struct event *ep2) 480*0Sstevel@tonic-gate { 481*0Sstevel@tonic-gate int diff; 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate if ((diff = ep2->enode->u.event.ename->u.name.s - 484*0Sstevel@tonic-gate ep1->enode->u.event.ename->u.name.s) != 0) 485*0Sstevel@tonic-gate return (diff); 486*0Sstevel@tonic-gate if ((diff = (char *)ep2->ipp - (char *)ep1->ipp) != 0) 487*0Sstevel@tonic-gate return (diff); 488*0Sstevel@tonic-gate return (0); 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate 492*0Sstevel@tonic-gate struct event * 493*0Sstevel@tonic-gate itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp) 494*0Sstevel@tonic-gate { 495*0Sstevel@tonic-gate struct event searchevent; /* just used for searching */ 496*0Sstevel@tonic-gate struct node searcheventnode; 497*0Sstevel@tonic-gate struct node searchenamenode; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate searchevent.enode = &searcheventnode; 500*0Sstevel@tonic-gate searcheventnode.t = T_EVENT; 501*0Sstevel@tonic-gate searcheventnode.u.event.ename = &searchenamenode; 502*0Sstevel@tonic-gate searchenamenode.t = T_NAME; 503*0Sstevel@tonic-gate searchenamenode.u.name.s = ename; 504*0Sstevel@tonic-gate searchevent.ipp = ipp; 505*0Sstevel@tonic-gate return (lut_lookup(itp, (void *)&searchevent, (lut_cmp)event_cmp)); 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate static struct event * 509*0Sstevel@tonic-gate find_or_add_event(struct info *infop, struct node *np) 510*0Sstevel@tonic-gate { 511*0Sstevel@tonic-gate struct event *ret; 512*0Sstevel@tonic-gate struct event searchevent; /* just used for searching */ 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate ASSERTeq(np->t, T_EVENT, ptree_nodetype2str); 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate searchevent.enode = np; 517*0Sstevel@tonic-gate searchevent.ipp = ipath(np->u.event.epname); 518*0Sstevel@tonic-gate if ((ret = lut_lookup(infop->lut, (void *)&searchevent, 519*0Sstevel@tonic-gate (lut_cmp)event_cmp)) != NULL) 520*0Sstevel@tonic-gate return (ret); 521*0Sstevel@tonic-gate 522*0Sstevel@tonic-gate /* wasn't already in tree, allocate it */ 523*0Sstevel@tonic-gate ret = MALLOC(sizeof (*ret)); 524*0Sstevel@tonic-gate bzero(ret, sizeof (*ret)); 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate ret->t = np->u.event.ename->u.name.t; 527*0Sstevel@tonic-gate ret->enode = np; 528*0Sstevel@tonic-gate ret->ipp = searchevent.ipp; 529*0Sstevel@tonic-gate ret->props = props2instance(np, np->u.event.epname); 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate infop->lut = lut_add(infop->lut, (void *)ret, (void *)ret, 532*0Sstevel@tonic-gate (lut_cmp)event_cmp); 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate return (ret); 535*0Sstevel@tonic-gate } 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate /* 538*0Sstevel@tonic-gate * hmatch_event -- perform any appropriate horizontal expansion on an event 539*0Sstevel@tonic-gate * 540*0Sstevel@tonic-gate * this routine is used to perform horizontal expansion on both the 541*0Sstevel@tonic-gate * left-hand-side events in a prop, and the right-hand-side events. 542*0Sstevel@tonic-gate * when called to handle a left-side event, nextnp point to the right 543*0Sstevel@tonic-gate * side of the prop that should be passed to hmatch() for each match 544*0Sstevel@tonic-gate * found by horizontal expansion. when no horizontal expansion exists, 545*0Sstevel@tonic-gate * we will still "match" one event for every event found in the list on 546*0Sstevel@tonic-gate * the left-hand-side of the prop because vmatch() already found that 547*0Sstevel@tonic-gate * there's at least one match during vertical expansion. 548*0Sstevel@tonic-gate */ 549*0Sstevel@tonic-gate static void 550*0Sstevel@tonic-gate hmatch_event(struct info *infop, struct node *eventnp, struct node *epname, 551*0Sstevel@tonic-gate struct config *ncp, struct node *nextnp, int rematch) 552*0Sstevel@tonic-gate { 553*0Sstevel@tonic-gate if (epname == NULL) { 554*0Sstevel@tonic-gate /* 555*0Sstevel@tonic-gate * end of pathname recursion, either we just located 556*0Sstevel@tonic-gate * a left-hand-side event and we're ready to move on 557*0Sstevel@tonic-gate * to the expanding the right-hand-side events, or 558*0Sstevel@tonic-gate * we're further down the recursion and we just located 559*0Sstevel@tonic-gate * a right-hand-side event. the passed-in parameter 560*0Sstevel@tonic-gate * "nextnp" tells us whether we're working on the left 561*0Sstevel@tonic-gate * side and need to move on to nextnp, or if nextnp is 562*0Sstevel@tonic-gate * NULL, we're working on the right side. 563*0Sstevel@tonic-gate */ 564*0Sstevel@tonic-gate if (nextnp) { 565*0Sstevel@tonic-gate /* 566*0Sstevel@tonic-gate * finished a left side expansion, move on to right. 567*0Sstevel@tonic-gate * tell generate() what event we just matched so 568*0Sstevel@tonic-gate * it can be used at the source of any arrows 569*0Sstevel@tonic-gate * we generate as we match events on the right side. 570*0Sstevel@tonic-gate */ 571*0Sstevel@tonic-gate generate_from(eventnp, 572*0Sstevel@tonic-gate find_or_add_event(infop, eventnp)); 573*0Sstevel@tonic-gate hmatch(infop, nextnp, NULL); 574*0Sstevel@tonic-gate } else { 575*0Sstevel@tonic-gate /* 576*0Sstevel@tonic-gate * finished a right side expansion. tell generate 577*0Sstevel@tonic-gate * the information about the destination and let 578*0Sstevel@tonic-gate * it construct the arrows as appropriate. 579*0Sstevel@tonic-gate */ 580*0Sstevel@tonic-gate generate_to(eventnp, 581*0Sstevel@tonic-gate find_or_add_event(infop, eventnp)); 582*0Sstevel@tonic-gate generate(infop->ex); 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate return; 586*0Sstevel@tonic-gate } 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate ASSERTeq(epname->t, T_NAME, ptree_nodetype2str); 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate /* 591*0Sstevel@tonic-gate * we only get here when eventnp already has a completely 592*0Sstevel@tonic-gate * instanced epname in it already. so we first recurse 593*0Sstevel@tonic-gate * down to the end of the name and as the recursion pops 594*0Sstevel@tonic-gate * up, we look for opportunities to advance horizontal 595*0Sstevel@tonic-gate * expansions on to the next match. when we do advance 596*0Sstevel@tonic-gate * horizontal expansions, we potentially render all cp 597*0Sstevel@tonic-gate * pointers on all components to the right as invalid, 598*0Sstevel@tonic-gate * so we pass in an "ncp" config handle so matching against 599*0Sstevel@tonic-gate * the config can happen. 600*0Sstevel@tonic-gate */ 601*0Sstevel@tonic-gate if (rematch) { 602*0Sstevel@tonic-gate struct config *ocp = epname->u.name.cp; 603*0Sstevel@tonic-gate char *ncp_s; 604*0Sstevel@tonic-gate int ncp_num, num; 605*0Sstevel@tonic-gate 606*0Sstevel@tonic-gate for (; ncp; ncp = config_next(ncp)) { 607*0Sstevel@tonic-gate config_getcompname(ncp, &ncp_s, &ncp_num); 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate if (ncp_s == epname->u.name.s) { 610*0Sstevel@tonic-gate /* found a matching component name */ 611*0Sstevel@tonic-gate config_getcompname(epname->u.name.cp, 612*0Sstevel@tonic-gate NULL, &num); 613*0Sstevel@tonic-gate 614*0Sstevel@tonic-gate if (epname->u.name.it != IT_HORIZONTAL && 615*0Sstevel@tonic-gate ncp_num != num) 616*0Sstevel@tonic-gate continue; 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate epname->u.name.cp = ncp; 619*0Sstevel@tonic-gate hmatch_event(infop, eventnp, 620*0Sstevel@tonic-gate epname->u.name.next, config_child(ncp), 621*0Sstevel@tonic-gate nextnp, 1); 622*0Sstevel@tonic-gate } 623*0Sstevel@tonic-gate } 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate epname->u.name.cp = ocp; 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate return; /* no more config to match against */ 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate } else { 630*0Sstevel@tonic-gate hmatch_event(infop, eventnp, epname->u.name.next, ncp, 631*0Sstevel@tonic-gate nextnp, 0); 632*0Sstevel@tonic-gate } 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gate if (epname->u.name.it == IT_HORIZONTAL) { 635*0Sstevel@tonic-gate struct config *cp; 636*0Sstevel@tonic-gate struct config *ocp = epname->u.name.cp; 637*0Sstevel@tonic-gate char *cp_s; 638*0Sstevel@tonic-gate int cp_num; 639*0Sstevel@tonic-gate int ocp_num; 640*0Sstevel@tonic-gate struct iterinfo *iterinfop = NULL; 641*0Sstevel@tonic-gate const char *iters; 642*0Sstevel@tonic-gate 643*0Sstevel@tonic-gate config_getcompname(ocp, NULL, &ocp_num); 644*0Sstevel@tonic-gate 645*0Sstevel@tonic-gate for (cp = config_next(ocp); cp; cp = config_next(cp)) { 646*0Sstevel@tonic-gate config_getcompname(cp, &cp_s, &cp_num); 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate if (cp_s == epname->u.name.s) { 649*0Sstevel@tonic-gate ASSERT(epname->u.name.child != NULL); 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate iters = epname->u.name.child->u.name.s; 652*0Sstevel@tonic-gate if ((iterinfop = lut_lookup(infop->ex, 653*0Sstevel@tonic-gate (void *)iters, NULL)) == NULL) { 654*0Sstevel@tonic-gate out(O_DIE, 655*0Sstevel@tonic-gate "hmatch_event: internal error: " 656*0Sstevel@tonic-gate "iterator \"%s\" undefined", iters); 657*0Sstevel@tonic-gate } else { 658*0Sstevel@tonic-gate /* advance dict entry to next match */ 659*0Sstevel@tonic-gate iterinfop->num = cp_num; 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate epname->u.name.cp = cp; 662*0Sstevel@tonic-gate hmatch_event(infop, eventnp, 663*0Sstevel@tonic-gate epname->u.name.next, config_child(cp), 664*0Sstevel@tonic-gate nextnp, 1); 665*0Sstevel@tonic-gate } 666*0Sstevel@tonic-gate } 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate if (iterinfop != NULL) { 669*0Sstevel@tonic-gate /* restore dict entry */ 670*0Sstevel@tonic-gate iterinfop->num = ocp_num; 671*0Sstevel@tonic-gate } 672*0Sstevel@tonic-gate epname->u.name.cp = ocp; 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate } 675*0Sstevel@tonic-gate 676*0Sstevel@tonic-gate /* 677*0Sstevel@tonic-gate * hmatch -- check for horizontal expansion matches 678*0Sstevel@tonic-gate * 679*0Sstevel@tonic-gate * np points to the things we're matching (like a T_LIST or a T_EVENT) 680*0Sstevel@tonic-gate * and if we're working on a left-side of a prop, nextnp points to 681*0Sstevel@tonic-gate * the other side of the prop that we'll tackle next when this recursion 682*0Sstevel@tonic-gate * bottoms out. when all the events in the entire prop arrow have been 683*0Sstevel@tonic-gate * horizontally expanded, generate() will be called to generate the 684*0Sstevel@tonic-gate * actualy arrow. 685*0Sstevel@tonic-gate */ 686*0Sstevel@tonic-gate static void 687*0Sstevel@tonic-gate hmatch(struct info *infop, struct node *np, struct node *nextnp) 688*0Sstevel@tonic-gate { 689*0Sstevel@tonic-gate if (np == NULL) 690*0Sstevel@tonic-gate return; /* all done */ 691*0Sstevel@tonic-gate 692*0Sstevel@tonic-gate /* 693*0Sstevel@tonic-gate * for each item in the list of events (which could just 694*0Sstevel@tonic-gate * be a single event, or it could get larger in the loop 695*0Sstevel@tonic-gate * below due to horizontal expansion), call hmatch on 696*0Sstevel@tonic-gate * the right side and create arrows to each element. 697*0Sstevel@tonic-gate */ 698*0Sstevel@tonic-gate 699*0Sstevel@tonic-gate switch (np->t) { 700*0Sstevel@tonic-gate case T_LIST: 701*0Sstevel@tonic-gate /* loop through the list */ 702*0Sstevel@tonic-gate if (np->u.expr.left) 703*0Sstevel@tonic-gate hmatch(infop, np->u.expr.left, nextnp); 704*0Sstevel@tonic-gate if (np->u.expr.right) 705*0Sstevel@tonic-gate hmatch(infop, np->u.expr.right, nextnp); 706*0Sstevel@tonic-gate break; 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate case T_EVENT: 709*0Sstevel@tonic-gate hmatch_event(infop, np, np->u.event.epname, 710*0Sstevel@tonic-gate NULL, nextnp, 0); 711*0Sstevel@tonic-gate break; 712*0Sstevel@tonic-gate 713*0Sstevel@tonic-gate default: 714*0Sstevel@tonic-gate outfl(O_DIE, np->file, np->line, 715*0Sstevel@tonic-gate "hmatch: unexpected type: %s", 716*0Sstevel@tonic-gate ptree_nodetype2str(np->t)); 717*0Sstevel@tonic-gate } 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate static int 721*0Sstevel@tonic-gate itree_np2nork(struct node *norknp) 722*0Sstevel@tonic-gate { 723*0Sstevel@tonic-gate if (norknp == NULL) 724*0Sstevel@tonic-gate return (1); 725*0Sstevel@tonic-gate else if (norknp->t == T_NAME && norknp->u.name.s == L_A) 726*0Sstevel@tonic-gate return (-1); /* our internal version of "all" */ 727*0Sstevel@tonic-gate else if (norknp->t == T_NUM) 728*0Sstevel@tonic-gate return ((int)norknp->u.ull); 729*0Sstevel@tonic-gate else 730*0Sstevel@tonic-gate out(O_DIE, norknp->file, norknp->line, 731*0Sstevel@tonic-gate "itree_np2nork: internal error type %s", 732*0Sstevel@tonic-gate ptree_nodetype2str(norknp->t)); 733*0Sstevel@tonic-gate /*NOTREACHED*/ 734*0Sstevel@tonic-gate } 735*0Sstevel@tonic-gate 736*0Sstevel@tonic-gate static struct iterinfo * 737*0Sstevel@tonic-gate newiterinfo(int num, struct node *np) 738*0Sstevel@tonic-gate { 739*0Sstevel@tonic-gate struct iterinfo *ret = MALLOC(sizeof (*ret)); 740*0Sstevel@tonic-gate 741*0Sstevel@tonic-gate ret->num = num; 742*0Sstevel@tonic-gate ret->np = np; 743*0Sstevel@tonic-gate 744*0Sstevel@tonic-gate return (ret); 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate /*ARGSUSED*/ 748*0Sstevel@tonic-gate static void 749*0Sstevel@tonic-gate iterinfo_destructor(void *left, void *right, void *arg) 750*0Sstevel@tonic-gate { 751*0Sstevel@tonic-gate struct iterinfo *iterinfop = (struct iterinfo *)right; 752*0Sstevel@tonic-gate 753*0Sstevel@tonic-gate bzero(iterinfop, sizeof (*iterinfop)); 754*0Sstevel@tonic-gate FREE(iterinfop); 755*0Sstevel@tonic-gate } 756*0Sstevel@tonic-gate 757*0Sstevel@tonic-gate /* 758*0Sstevel@tonic-gate * update epname to include the wildcarded portion 759*0Sstevel@tonic-gate */ 760*0Sstevel@tonic-gate static void 761*0Sstevel@tonic-gate create_wildcardedpath(struct wildcardinfo **wcproot) 762*0Sstevel@tonic-gate { 763*0Sstevel@tonic-gate struct wildcardinfo *wcp; 764*0Sstevel@tonic-gate struct node *nptop; 765*0Sstevel@tonic-gate 766*0Sstevel@tonic-gate wcp = *wcproot; 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate if (wcp->s == WC_UNDERCONSTRUCTION) { 769*0Sstevel@tonic-gate ASSERT(wcp->p->refcount == 1); 770*0Sstevel@tonic-gate wcp->s = WC_COMPLETE; 771*0Sstevel@tonic-gate } 772*0Sstevel@tonic-gate 773*0Sstevel@tonic-gate /* path has no wildcard */ 774*0Sstevel@tonic-gate if (wcp->p->ewname == NULL) 775*0Sstevel@tonic-gate return; 776*0Sstevel@tonic-gate 777*0Sstevel@tonic-gate /* 778*0Sstevel@tonic-gate * get to this point if a wildcard portion of the path exists. 779*0Sstevel@tonic-gate * 780*0Sstevel@tonic-gate * first set oldepname to the start of the existing epname for use 781*0Sstevel@tonic-gate * in future comparisons, then update epname to include the 782*0Sstevel@tonic-gate * wildcard portion. 783*0Sstevel@tonic-gate */ 784*0Sstevel@tonic-gate nptop = wcp->nptop; 785*0Sstevel@tonic-gate 786*0Sstevel@tonic-gate ASSERT(wcp->oldepname == nptop->u.event.epname); 787*0Sstevel@tonic-gate 788*0Sstevel@tonic-gate nptop->u.event.epname = tname_dup(wcp->p->ewname, CN_DUP); 789*0Sstevel@tonic-gate nptop->u.event.epname = tree_name_append(nptop->u.event.epname, 790*0Sstevel@tonic-gate tname_dup(wcp->oldepname, CN_DUP)); 791*0Sstevel@tonic-gate } 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate /* 794*0Sstevel@tonic-gate * restore epname to its former (nonwildcarded) state 795*0Sstevel@tonic-gate */ 796*0Sstevel@tonic-gate static void 797*0Sstevel@tonic-gate undo_wildcardedpath(struct wildcardinfo **wcproot) 798*0Sstevel@tonic-gate { 799*0Sstevel@tonic-gate struct wildcardinfo *wcp; 800*0Sstevel@tonic-gate 801*0Sstevel@tonic-gate wcp = *wcproot; 802*0Sstevel@tonic-gate 803*0Sstevel@tonic-gate if (wcp->s == WC_COMPLETE) { 804*0Sstevel@tonic-gate ASSERT(wcp->p->refcount == 1); 805*0Sstevel@tonic-gate wcp->s = WC_UNDERCONSTRUCTION; 806*0Sstevel@tonic-gate } 807*0Sstevel@tonic-gate 808*0Sstevel@tonic-gate /* path has no wildcard */ 809*0Sstevel@tonic-gate if (wcp->p->ewname == NULL) 810*0Sstevel@tonic-gate return; 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate ASSERT(wcp->oldepname != NULL); 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate tree_free(wcp->nptop->u.event.epname); 815*0Sstevel@tonic-gate wcp->nptop->u.event.epname = wcp->oldepname; 816*0Sstevel@tonic-gate } 817*0Sstevel@tonic-gate 818*0Sstevel@tonic-gate static void 819*0Sstevel@tonic-gate vmatch_event(struct info *infop, struct config *cp, struct node *np, 820*0Sstevel@tonic-gate struct node *lnp, struct node *anp, 821*0Sstevel@tonic-gate struct wildcardinfo **wcproot, int dowildcard) 822*0Sstevel@tonic-gate { 823*0Sstevel@tonic-gate struct wildcardinfo *wcp; 824*0Sstevel@tonic-gate char *cp_s; 825*0Sstevel@tonic-gate int cp_num; 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate wcp = *wcproot; 828*0Sstevel@tonic-gate 829*0Sstevel@tonic-gate if ((np == NULL && wcp->oldepname != NULL) || 830*0Sstevel@tonic-gate (cp == NULL && wcp->oldepname == NULL)) { 831*0Sstevel@tonic-gate /* 832*0Sstevel@tonic-gate * the pathname matched the config (but not necessarily a 833*0Sstevel@tonic-gate * match at the end) 834*0Sstevel@tonic-gate */ 835*0Sstevel@tonic-gate create_wildcardedpath(wcproot); 836*0Sstevel@tonic-gate vmatch(infop, np, lnp, anp, wcproot); 837*0Sstevel@tonic-gate undo_wildcardedpath(wcproot); 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate if (cp != NULL && wcp->s == WC_UNDERCONSTRUCTION) { 840*0Sstevel@tonic-gate /* 841*0Sstevel@tonic-gate * pathname match did not occur at the end of the 842*0Sstevel@tonic-gate * configuration path. more matches may be found 843*0Sstevel@tonic-gate * later in the path, so we set cpforcedwc to force 844*0Sstevel@tonic-gate * wildcarding for startcp. 845*0Sstevel@tonic-gate * 846*0Sstevel@tonic-gate * note that cpforcedwc may already have been set 847*0Sstevel@tonic-gate * in an earlier match due to vertical expansion on 848*0Sstevel@tonic-gate * the nonwildcarded epname. 849*0Sstevel@tonic-gate */ 850*0Sstevel@tonic-gate if (wcp->p->cpforcedwc != wcp->p->cpstart) { 851*0Sstevel@tonic-gate ASSERT(wcp->p->cpforcedwc == NULL); 852*0Sstevel@tonic-gate wcp->p->cpforcedwc = wcp->p->cpstart; 853*0Sstevel@tonic-gate } 854*0Sstevel@tonic-gate } 855*0Sstevel@tonic-gate 856*0Sstevel@tonic-gate return; 857*0Sstevel@tonic-gate } 858*0Sstevel@tonic-gate 859*0Sstevel@tonic-gate if (cp == NULL) 860*0Sstevel@tonic-gate return; /* no more config to match against */ 861*0Sstevel@tonic-gate 862*0Sstevel@tonic-gate for (; cp; cp = config_next(cp)) { 863*0Sstevel@tonic-gate config_getcompname(cp, &cp_s, &cp_num); 864*0Sstevel@tonic-gate 865*0Sstevel@tonic-gate if (cp_s == np->u.name.s && 866*0Sstevel@tonic-gate ! (wcp->s == WC_UNDERCONSTRUCTION && 867*0Sstevel@tonic-gate cp == wcp->p->cpstart && 868*0Sstevel@tonic-gate wcp->p->cpstart == wcp->p->cpforcedwc)) { 869*0Sstevel@tonic-gate /* found a matching component name */ 870*0Sstevel@tonic-gate if (np->u.name.child && 871*0Sstevel@tonic-gate np->u.name.child->t == T_NUM) { 872*0Sstevel@tonic-gate /* 873*0Sstevel@tonic-gate * an explicit instance number was given 874*0Sstevel@tonic-gate * in the source. so only consider this 875*0Sstevel@tonic-gate * a configuration match if the number 876*0Sstevel@tonic-gate * also matches. 877*0Sstevel@tonic-gate */ 878*0Sstevel@tonic-gate if (cp_num != np->u.name.child->u.ull) 879*0Sstevel@tonic-gate continue; 880*0Sstevel@tonic-gate 881*0Sstevel@tonic-gate np->u.name.cp = cp; 882*0Sstevel@tonic-gate } else { 883*0Sstevel@tonic-gate struct iterinfo *iterinfop; 884*0Sstevel@tonic-gate const char *iters; 885*0Sstevel@tonic-gate 886*0Sstevel@tonic-gate /* 887*0Sstevel@tonic-gate * vertical iterator. look it up in 888*0Sstevel@tonic-gate * the appropriate lut and if we get 889*0Sstevel@tonic-gate * back a value it is either one that we 890*0Sstevel@tonic-gate * set earlier, in which case we record 891*0Sstevel@tonic-gate * the new value for this iteration and 892*0Sstevel@tonic-gate * keep matching, or it is one that was 893*0Sstevel@tonic-gate * set by an earlier reference to the 894*0Sstevel@tonic-gate * iterator, in which case we only consider 895*0Sstevel@tonic-gate * this a configuration match if the number 896*0Sstevel@tonic-gate * matches cp_num. 897*0Sstevel@tonic-gate */ 898*0Sstevel@tonic-gate 899*0Sstevel@tonic-gate ASSERT(np->u.name.child != NULL); 900*0Sstevel@tonic-gate ASSERT(np->u.name.child->t == T_NAME); 901*0Sstevel@tonic-gate iters = np->u.name.child->u.name.s; 902*0Sstevel@tonic-gate 903*0Sstevel@tonic-gate if ((iterinfop = lut_lookup(infop->ex, 904*0Sstevel@tonic-gate (void *)iters, NULL)) == NULL) { 905*0Sstevel@tonic-gate /* we're the first use, record our np */ 906*0Sstevel@tonic-gate infop->ex = lut_add(infop->ex, 907*0Sstevel@tonic-gate (void *)iters, 908*0Sstevel@tonic-gate newiterinfo(cp_num, np), NULL); 909*0Sstevel@tonic-gate } else if (np == iterinfop->np) { 910*0Sstevel@tonic-gate /* 911*0Sstevel@tonic-gate * we're the first use, back again 912*0Sstevel@tonic-gate * for another iteration. so update 913*0Sstevel@tonic-gate * the num bound to this iterator in 914*0Sstevel@tonic-gate * the lut. 915*0Sstevel@tonic-gate */ 916*0Sstevel@tonic-gate iterinfop->num = cp_num; 917*0Sstevel@tonic-gate } else if (cp_num != iterinfop->num) { 918*0Sstevel@tonic-gate /* 919*0Sstevel@tonic-gate * an earlier reference to this 920*0Sstevel@tonic-gate * iterator bound it to a different 921*0Sstevel@tonic-gate * instance number, so there's no 922*0Sstevel@tonic-gate * match here after all. 923*0Sstevel@tonic-gate */ 924*0Sstevel@tonic-gate continue; 925*0Sstevel@tonic-gate } 926*0Sstevel@tonic-gate np->u.name.cp = cp; 927*0Sstevel@tonic-gate } 928*0Sstevel@tonic-gate 929*0Sstevel@tonic-gate /* 930*0Sstevel@tonic-gate * if wildcarding was done in a call earlier in the 931*0Sstevel@tonic-gate * stack, record the current cp as the first 932*0Sstevel@tonic-gate * matching and nonwildcarded cp. 933*0Sstevel@tonic-gate */ 934*0Sstevel@tonic-gate if (dowildcard && wcp->s == WC_UNDERCONSTRUCTION) 935*0Sstevel@tonic-gate wcp->p->cpstart = cp; 936*0Sstevel@tonic-gate 937*0Sstevel@tonic-gate /* 938*0Sstevel@tonic-gate * if this was an IT_HORIZONTAL name, 939*0Sstevel@tonic-gate * hmatch() will use the cp to expand 940*0Sstevel@tonic-gate * all matches horizontally into a list. 941*0Sstevel@tonic-gate * we know the list will contain at least 942*0Sstevel@tonic-gate * one element (the one we just matched), 943*0Sstevel@tonic-gate * so we just store cp and let hmatch_event() 944*0Sstevel@tonic-gate * do the rest. 945*0Sstevel@tonic-gate * 946*0Sstevel@tonic-gate * recurse on to next component. note that 947*0Sstevel@tonic-gate * wildcarding is now turned off. 948*0Sstevel@tonic-gate */ 949*0Sstevel@tonic-gate vmatch_event(infop, config_child(cp), np->u.name.next, 950*0Sstevel@tonic-gate lnp, anp, wcproot, 0); 951*0Sstevel@tonic-gate 952*0Sstevel@tonic-gate /* 953*0Sstevel@tonic-gate * if wildcarding is being forced for this 954*0Sstevel@tonic-gate * component, repeat call to vmatch_event() with 955*0Sstevel@tonic-gate * the same np 956*0Sstevel@tonic-gate */ 957*0Sstevel@tonic-gate if (dowildcard && 958*0Sstevel@tonic-gate wcp->s == WC_UNDERCONSTRUCTION && 959*0Sstevel@tonic-gate cp == wcp->p->cpforcedwc) { 960*0Sstevel@tonic-gate vmatch_event(infop, cp, np, lnp, anp, 961*0Sstevel@tonic-gate wcproot, 1); 962*0Sstevel@tonic-gate } 963*0Sstevel@tonic-gate 964*0Sstevel@tonic-gate if (np->u.name.it == IT_HORIZONTAL) { 965*0Sstevel@tonic-gate /* 966*0Sstevel@tonic-gate * hmatch() finished iterating through 967*0Sstevel@tonic-gate * the configuration as described above, so 968*0Sstevel@tonic-gate * don't continue iterating here. 969*0Sstevel@tonic-gate */ 970*0Sstevel@tonic-gate return; 971*0Sstevel@tonic-gate } 972*0Sstevel@tonic-gate 973*0Sstevel@tonic-gate } else if (dowildcard && wcp->s == WC_UNDERCONSTRUCTION) { 974*0Sstevel@tonic-gate /* 975*0Sstevel@tonic-gate * no matching cp, and we are constructing our own 976*0Sstevel@tonic-gate * wildcard path. (in other words, we are not 977*0Sstevel@tonic-gate * referencing a wildcard path created for an 978*0Sstevel@tonic-gate * earlier event.) 979*0Sstevel@tonic-gate * 980*0Sstevel@tonic-gate * add wildcard entry, then recurse on to config 981*0Sstevel@tonic-gate * child 982*0Sstevel@tonic-gate */ 983*0Sstevel@tonic-gate struct node *cpnode, *prevlast; 984*0Sstevel@tonic-gate 985*0Sstevel@tonic-gate if (wcp->p->cpstart == wcp->p->cpforcedwc) 986*0Sstevel@tonic-gate wcp->p->cpforcedwc = NULL; 987*0Sstevel@tonic-gate 988*0Sstevel@tonic-gate cpnode = tree_name(cp_s, IT_NONE, NULL, 0); 989*0Sstevel@tonic-gate cpnode->u.name.child = newnode(T_NUM, NULL, 0); 990*0Sstevel@tonic-gate cpnode->u.name.child->u.ull = cp_num; 991*0Sstevel@tonic-gate cpnode->u.name.cp = cp; 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate if (wcp->p->ewname == NULL) { 994*0Sstevel@tonic-gate prevlast = NULL; 995*0Sstevel@tonic-gate wcp->p->ewname = cpnode; 996*0Sstevel@tonic-gate } else { 997*0Sstevel@tonic-gate prevlast = wcp->p->ewname->u.name.last; 998*0Sstevel@tonic-gate wcp->p->ewname = 999*0Sstevel@tonic-gate tree_name_append(wcp->p->ewname, 1000*0Sstevel@tonic-gate cpnode); 1001*0Sstevel@tonic-gate } 1002*0Sstevel@tonic-gate 1003*0Sstevel@tonic-gate vmatch_event(infop, config_child(cp), np, lnp, anp, 1004*0Sstevel@tonic-gate wcproot, 1); 1005*0Sstevel@tonic-gate 1006*0Sstevel@tonic-gate /* 1007*0Sstevel@tonic-gate * back out last addition to ewname and continue 1008*0Sstevel@tonic-gate * with loop 1009*0Sstevel@tonic-gate */ 1010*0Sstevel@tonic-gate tree_free(cpnode); 1011*0Sstevel@tonic-gate if (prevlast == NULL) { 1012*0Sstevel@tonic-gate wcp->p->ewname = NULL; 1013*0Sstevel@tonic-gate } else { 1014*0Sstevel@tonic-gate prevlast->u.name.next = NULL; 1015*0Sstevel@tonic-gate wcp->p->ewname->u.name.last = prevlast; 1016*0Sstevel@tonic-gate } 1017*0Sstevel@tonic-gate } 1018*0Sstevel@tonic-gate } 1019*0Sstevel@tonic-gate } 1020*0Sstevel@tonic-gate 1021*0Sstevel@tonic-gate /* 1022*0Sstevel@tonic-gate * for the event node np, which will be subjected to pathname 1023*0Sstevel@tonic-gate * expansion/matching, create a (struct wildcardinfo) to hold wildcard 1024*0Sstevel@tonic-gate * information. this struct will be inserted into the first location in 1025*0Sstevel@tonic-gate * the list that starts with *wcproot. 1026*0Sstevel@tonic-gate * 1027*0Sstevel@tonic-gate * cp is the starting node of the configuration; cpstart, which is output, 1028*0Sstevel@tonic-gate * is the starting node of the nonwildcarded portion of the path. 1029*0Sstevel@tonic-gate */ 1030*0Sstevel@tonic-gate static void 1031*0Sstevel@tonic-gate add_wildcardentry(struct wildcardinfo **wcproot, struct config *cp, 1032*0Sstevel@tonic-gate struct node *np, struct config **cpstart) 1033*0Sstevel@tonic-gate { 1034*0Sstevel@tonic-gate struct wildcardinfo *wcpnew, *wcp; 1035*0Sstevel@tonic-gate struct node *np1, *np2; 1036*0Sstevel@tonic-gate 1037*0Sstevel@tonic-gate /* 1038*0Sstevel@tonic-gate * create entry for np 1039*0Sstevel@tonic-gate */ 1040*0Sstevel@tonic-gate wcpnew = MALLOC(sizeof (struct wildcardinfo)); 1041*0Sstevel@tonic-gate bzero(wcpnew, sizeof (struct wildcardinfo)); 1042*0Sstevel@tonic-gate wcpnew->nptop = np; 1043*0Sstevel@tonic-gate wcpnew->oldepname = np->u.event.epname; 1044*0Sstevel@tonic-gate np2 = wcpnew->oldepname; 1045*0Sstevel@tonic-gate 1046*0Sstevel@tonic-gate /* 1047*0Sstevel@tonic-gate * search all completed entries for an epname whose first entry 1048*0Sstevel@tonic-gate * matches. note that NULL epnames are considered valid and can be 1049*0Sstevel@tonic-gate * matched. 1050*0Sstevel@tonic-gate */ 1051*0Sstevel@tonic-gate for (wcp = *wcproot; wcp; wcp = wcp->next) { 1052*0Sstevel@tonic-gate ASSERT(wcp->s == WC_COMPLETE || wcp->s == WC_REFERENCING); 1053*0Sstevel@tonic-gate if (wcp->s != WC_COMPLETE) 1054*0Sstevel@tonic-gate continue; 1055*0Sstevel@tonic-gate 1056*0Sstevel@tonic-gate np1 = wcp->oldepname; 1057*0Sstevel@tonic-gate if ((np1 && np2 && np1->u.name.s == np2->u.name.s) || 1058*0Sstevel@tonic-gate (np1 == NULL && np2 == NULL)) { 1059*0Sstevel@tonic-gate wcpnew->p = wcp->p; 1060*0Sstevel@tonic-gate wcpnew->s = WC_REFERENCING; 1061*0Sstevel@tonic-gate 1062*0Sstevel@tonic-gate wcp->p->refcount++; 1063*0Sstevel@tonic-gate break; 1064*0Sstevel@tonic-gate } 1065*0Sstevel@tonic-gate } 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate if (wcpnew->p == NULL) { 1068*0Sstevel@tonic-gate wcpnew->s = WC_UNDERCONSTRUCTION; 1069*0Sstevel@tonic-gate 1070*0Sstevel@tonic-gate wcpnew->p = MALLOC(sizeof (struct wildcardpath)); 1071*0Sstevel@tonic-gate bzero(wcpnew->p, sizeof (struct wildcardpath)); 1072*0Sstevel@tonic-gate wcpnew->p->cpstart = cp; 1073*0Sstevel@tonic-gate wcpnew->p->refcount = 1; 1074*0Sstevel@tonic-gate } 1075*0Sstevel@tonic-gate 1076*0Sstevel@tonic-gate wcpnew->next = *wcproot; 1077*0Sstevel@tonic-gate *wcproot = wcpnew; 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate *cpstart = wcpnew->p->cpstart; 1080*0Sstevel@tonic-gate } 1081*0Sstevel@tonic-gate 1082*0Sstevel@tonic-gate static void 1083*0Sstevel@tonic-gate delete_wildcardentry(struct wildcardinfo **wcproot) 1084*0Sstevel@tonic-gate { 1085*0Sstevel@tonic-gate struct wildcardinfo *wcp; 1086*0Sstevel@tonic-gate 1087*0Sstevel@tonic-gate wcp = *wcproot; 1088*0Sstevel@tonic-gate *wcproot = wcp->next; 1089*0Sstevel@tonic-gate 1090*0Sstevel@tonic-gate switch (wcp->s) { 1091*0Sstevel@tonic-gate case WC_UNDERCONSTRUCTION: 1092*0Sstevel@tonic-gate case WC_COMPLETE: 1093*0Sstevel@tonic-gate ASSERT(wcp->p->refcount == 1); 1094*0Sstevel@tonic-gate tree_free(wcp->p->ewname); 1095*0Sstevel@tonic-gate FREE(wcp->p); 1096*0Sstevel@tonic-gate break; 1097*0Sstevel@tonic-gate 1098*0Sstevel@tonic-gate case WC_REFERENCING: 1099*0Sstevel@tonic-gate ASSERT(wcp->p->refcount > 1); 1100*0Sstevel@tonic-gate wcp->p->refcount--; 1101*0Sstevel@tonic-gate break; 1102*0Sstevel@tonic-gate 1103*0Sstevel@tonic-gate default: 1104*0Sstevel@tonic-gate out(O_DIE, "deletewc: invalid status"); 1105*0Sstevel@tonic-gate break; 1106*0Sstevel@tonic-gate } 1107*0Sstevel@tonic-gate 1108*0Sstevel@tonic-gate FREE(wcp); 1109*0Sstevel@tonic-gate } 1110*0Sstevel@tonic-gate 1111*0Sstevel@tonic-gate /* 1112*0Sstevel@tonic-gate * vmatch -- find the next vertical expansion match in the config database 1113*0Sstevel@tonic-gate * 1114*0Sstevel@tonic-gate * this routine is called with three node pointers: 1115*0Sstevel@tonic-gate * np -- the parse we're matching 1116*0Sstevel@tonic-gate * lnp -- the rest of the list we're currently working on 1117*0Sstevel@tonic-gate * anp -- the rest of the arrow we're currently working on 1118*0Sstevel@tonic-gate * 1119*0Sstevel@tonic-gate * the expansion matching happens via three types of recursion: 1120*0Sstevel@tonic-gate * 1121*0Sstevel@tonic-gate * - when given an arrow, handle the left-side and then recursively 1122*0Sstevel@tonic-gate * handle the right side (which might be another cascaded arrow). 1123*0Sstevel@tonic-gate * 1124*0Sstevel@tonic-gate * - when handling one side of an arrow, recurse through the T_LIST 1125*0Sstevel@tonic-gate * to get to each event (or just move on to the event if there 1126*0Sstevel@tonic-gate * is a single event instead of a list) since the arrow parse 1127*0Sstevel@tonic-gate * trees recurse left, we actually start with the right-most 1128*0Sstevel@tonic-gate * event list in the prop statement and work our way towards 1129*0Sstevel@tonic-gate * the left-most event list. 1130*0Sstevel@tonic-gate * 1131*0Sstevel@tonic-gate * - when handling an event, recurse down each component of the 1132*0Sstevel@tonic-gate * pathname, matching in the config database and recording the 1133*0Sstevel@tonic-gate * matches in the explicit iterator dictionary as we go. 1134*0Sstevel@tonic-gate * 1135*0Sstevel@tonic-gate * when the bottom of this matching recursion is met, meaning we have 1136*0Sstevel@tonic-gate * set the "cp" pointers on all the names in the entire statement, 1137*0Sstevel@tonic-gate * we call hmatch() which does it's own recursion to handle horizontal 1138*0Sstevel@tonic-gate * expandsion and then call generate() to generate nodes, bubbles, and 1139*0Sstevel@tonic-gate * arrows in the instance tree. generate() looks at the cp pointers to 1140*0Sstevel@tonic-gate * see what instance numbers were matched in the configuration database. 1141*0Sstevel@tonic-gate * 1142*0Sstevel@tonic-gate * when horizontal expansion appears, vmatch() finds only the first match 1143*0Sstevel@tonic-gate * and hmatch() then takes the horizontal expansion through all the other 1144*0Sstevel@tonic-gate * matches when generating the arrows in the instance tree. 1145*0Sstevel@tonic-gate * 1146*0Sstevel@tonic-gate * the "infop" passed down through the recursion contains a dictionary 1147*0Sstevel@tonic-gate * of the explicit iterators (all the implicit iterators have been converted 1148*0Sstevel@tonic-gate * to explicit iterators when the parse tree was created by tree.c), which 1149*0Sstevel@tonic-gate * allows things like this to work correctly: 1150*0Sstevel@tonic-gate * 1151*0Sstevel@tonic-gate * prop error.a@x[n]/y/z -> error.b@x/y[n]/z -> error.c@x/y/z[n]; 1152*0Sstevel@tonic-gate * 1153*0Sstevel@tonic-gate * during the top level call, the explicit iterator "n" will match an 1154*0Sstevel@tonic-gate * instance number in the config database, and the result will be recorded 1155*0Sstevel@tonic-gate * in the explicit iterator dictionary and passed down via "infop". so 1156*0Sstevel@tonic-gate * when the recursive call tries to match y[n] in the config database, it 1157*0Sstevel@tonic-gate * will only match the same instance number as x[n] did since the dictionary 1158*0Sstevel@tonic-gate * is consulted to see if "n" took on a value already. 1159*0Sstevel@tonic-gate * 1160*0Sstevel@tonic-gate * at any point during the recursion, match*() can return to indicate 1161*0Sstevel@tonic-gate * a match was not found in the config database and that the caller should 1162*0Sstevel@tonic-gate * move on to the next potential match, if any. 1163*0Sstevel@tonic-gate * 1164*0Sstevel@tonic-gate * constraints are completely ignored by match(), so the statement: 1165*0Sstevel@tonic-gate * 1166*0Sstevel@tonic-gate * prop error.a@x[n] -> error.b@x[n] {n != 0}; 1167*0Sstevel@tonic-gate * 1168*0Sstevel@tonic-gate * might very well match x[0] if it appears in the config database. it 1169*0Sstevel@tonic-gate * is the generate() routine that takes that match and then decides what 1170*0Sstevel@tonic-gate * arrow, if any, should be generated in the instance tree. generate() 1171*0Sstevel@tonic-gate * looks at the explicit iterator dictionary to get values like "n" in 1172*0Sstevel@tonic-gate * the above example so that it can evaluate constraints. 1173*0Sstevel@tonic-gate * 1174*0Sstevel@tonic-gate */ 1175*0Sstevel@tonic-gate static void 1176*0Sstevel@tonic-gate vmatch(struct info *infop, struct node *np, struct node *lnp, 1177*0Sstevel@tonic-gate struct node *anp, struct wildcardinfo **wcproot) 1178*0Sstevel@tonic-gate { 1179*0Sstevel@tonic-gate if (np == NULL) { 1180*0Sstevel@tonic-gate if (lnp) 1181*0Sstevel@tonic-gate vmatch(infop, lnp, NULL, anp, wcproot); 1182*0Sstevel@tonic-gate else if (anp) 1183*0Sstevel@tonic-gate vmatch(infop, anp, NULL, NULL, wcproot); 1184*0Sstevel@tonic-gate else { 1185*0Sstevel@tonic-gate struct node *src; 1186*0Sstevel@tonic-gate struct node *dst; 1187*0Sstevel@tonic-gate 1188*0Sstevel@tonic-gate /* end of vertical match recursion */ 1189*0Sstevel@tonic-gate outfl(O_ALTFP|O_VERB3|O_NONL, 1190*0Sstevel@tonic-gate infop->anp->file, infop->anp->line, "vmatch: "); 1191*0Sstevel@tonic-gate ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, infop->anp); 1192*0Sstevel@tonic-gate out(O_ALTFP|O_VERB3, NULL); 1193*0Sstevel@tonic-gate 1194*0Sstevel@tonic-gate generate_nork( 1195*0Sstevel@tonic-gate itree_np2nork(infop->anp->u.arrow.nnp), 1196*0Sstevel@tonic-gate itree_np2nork(infop->anp->u.arrow.knp)); 1197*0Sstevel@tonic-gate dst = infop->anp->u.arrow.rhs; 1198*0Sstevel@tonic-gate src = infop->anp->u.arrow.lhs; 1199*0Sstevel@tonic-gate for (;;) { 1200*0Sstevel@tonic-gate generate_new(); /* new set of arrows */ 1201*0Sstevel@tonic-gate if (src->t == T_ARROW) { 1202*0Sstevel@tonic-gate hmatch(infop, src->u.arrow.rhs, dst); 1203*0Sstevel@tonic-gate generate_nork( 1204*0Sstevel@tonic-gate itree_np2nork(src->u.arrow.nnp), 1205*0Sstevel@tonic-gate itree_np2nork(src->u.arrow.knp)); 1206*0Sstevel@tonic-gate dst = src->u.arrow.rhs; 1207*0Sstevel@tonic-gate src = src->u.arrow.lhs; 1208*0Sstevel@tonic-gate } else { 1209*0Sstevel@tonic-gate hmatch(infop, src, dst); 1210*0Sstevel@tonic-gate break; 1211*0Sstevel@tonic-gate } 1212*0Sstevel@tonic-gate } 1213*0Sstevel@tonic-gate } 1214*0Sstevel@tonic-gate return; 1215*0Sstevel@tonic-gate } 1216*0Sstevel@tonic-gate 1217*0Sstevel@tonic-gate switch (np->t) { 1218*0Sstevel@tonic-gate case T_EVENT: { 1219*0Sstevel@tonic-gate struct config *cpstart; 1220*0Sstevel@tonic-gate 1221*0Sstevel@tonic-gate add_wildcardentry(wcproot, config_child(infop->croot), 1222*0Sstevel@tonic-gate np, &cpstart); 1223*0Sstevel@tonic-gate vmatch_event(infop, cpstart, np->u.event.epname, lnp, anp, 1224*0Sstevel@tonic-gate wcproot, 1); 1225*0Sstevel@tonic-gate delete_wildcardentry(wcproot); 1226*0Sstevel@tonic-gate break; 1227*0Sstevel@tonic-gate } 1228*0Sstevel@tonic-gate case T_LIST: 1229*0Sstevel@tonic-gate ASSERT(lnp == NULL); 1230*0Sstevel@tonic-gate vmatch(infop, np->u.expr.right, np->u.expr.left, anp, wcproot); 1231*0Sstevel@tonic-gate break; 1232*0Sstevel@tonic-gate 1233*0Sstevel@tonic-gate case T_ARROW: 1234*0Sstevel@tonic-gate ASSERT(lnp == NULL && anp == NULL); 1235*0Sstevel@tonic-gate vmatch(infop, np->u.arrow.rhs, NULL, np->u.arrow.lhs, wcproot); 1236*0Sstevel@tonic-gate break; 1237*0Sstevel@tonic-gate 1238*0Sstevel@tonic-gate default: 1239*0Sstevel@tonic-gate outfl(O_DIE, np->file, np->line, 1240*0Sstevel@tonic-gate "vmatch: unexpected type: %s", 1241*0Sstevel@tonic-gate ptree_nodetype2str(np->t)); 1242*0Sstevel@tonic-gate } 1243*0Sstevel@tonic-gate } 1244*0Sstevel@tonic-gate 1245*0Sstevel@tonic-gate static void 1246*0Sstevel@tonic-gate cp_reset(struct node *np) 1247*0Sstevel@tonic-gate { 1248*0Sstevel@tonic-gate if (np == NULL) 1249*0Sstevel@tonic-gate return; 1250*0Sstevel@tonic-gate switch (np->t) { 1251*0Sstevel@tonic-gate case T_NAME: 1252*0Sstevel@tonic-gate np->u.name.cp = NULL; 1253*0Sstevel@tonic-gate cp_reset(np->u.name.next); 1254*0Sstevel@tonic-gate break; 1255*0Sstevel@tonic-gate 1256*0Sstevel@tonic-gate case T_LIST: 1257*0Sstevel@tonic-gate cp_reset(np->u.expr.left); 1258*0Sstevel@tonic-gate cp_reset(np->u.expr.right); 1259*0Sstevel@tonic-gate break; 1260*0Sstevel@tonic-gate 1261*0Sstevel@tonic-gate case T_ARROW: 1262*0Sstevel@tonic-gate cp_reset(np->u.arrow.lhs); 1263*0Sstevel@tonic-gate cp_reset(np->u.arrow.rhs); 1264*0Sstevel@tonic-gate break; 1265*0Sstevel@tonic-gate 1266*0Sstevel@tonic-gate case T_EVENT: 1267*0Sstevel@tonic-gate cp_reset(np->u.event.epname); 1268*0Sstevel@tonic-gate break; 1269*0Sstevel@tonic-gate } 1270*0Sstevel@tonic-gate } 1271*0Sstevel@tonic-gate 1272*0Sstevel@tonic-gate /* 1273*0Sstevel@tonic-gate * itree_create -- apply the current config to the current parse tree 1274*0Sstevel@tonic-gate * 1275*0Sstevel@tonic-gate * returns a lut mapping fully-instance-qualified names to struct events. 1276*0Sstevel@tonic-gate * 1277*0Sstevel@tonic-gate */ 1278*0Sstevel@tonic-gate struct lut * 1279*0Sstevel@tonic-gate itree_create(struct config *croot) 1280*0Sstevel@tonic-gate { 1281*0Sstevel@tonic-gate struct lut *retval; 1282*0Sstevel@tonic-gate struct node *propnp; 1283*0Sstevel@tonic-gate 1284*0Sstevel@tonic-gate Ninfo.lut = NULL; 1285*0Sstevel@tonic-gate Ninfo.croot = croot; 1286*0Sstevel@tonic-gate for (propnp = Props; propnp; propnp = propnp->u.stmt.next) { 1287*0Sstevel@tonic-gate struct node *anp = propnp->u.stmt.np; 1288*0Sstevel@tonic-gate struct wildcardinfo *wcproot = NULL; 1289*0Sstevel@tonic-gate 1290*0Sstevel@tonic-gate ASSERTeq(anp->t, T_ARROW, ptree_nodetype2str); 1291*0Sstevel@tonic-gate 1292*0Sstevel@tonic-gate Ninfo.anp = anp; 1293*0Sstevel@tonic-gate Ninfo.ex = NULL; 1294*0Sstevel@tonic-gate 1295*0Sstevel@tonic-gate generate_arrownp(anp); 1296*0Sstevel@tonic-gate vmatch(&Ninfo, anp, NULL, NULL, &wcproot); 1297*0Sstevel@tonic-gate 1298*0Sstevel@tonic-gate if (Ninfo.ex) { 1299*0Sstevel@tonic-gate lut_free(Ninfo.ex, iterinfo_destructor, NULL); 1300*0Sstevel@tonic-gate Ninfo.ex = NULL; 1301*0Sstevel@tonic-gate } 1302*0Sstevel@tonic-gate ASSERT(wcproot == NULL); 1303*0Sstevel@tonic-gate cp_reset(anp); 1304*0Sstevel@tonic-gate } 1305*0Sstevel@tonic-gate 1306*0Sstevel@tonic-gate retval = Ninfo.lut; 1307*0Sstevel@tonic-gate Ninfo.lut = NULL; 1308*0Sstevel@tonic-gate return (retval); 1309*0Sstevel@tonic-gate } 1310*0Sstevel@tonic-gate 1311*0Sstevel@tonic-gate void 1312*0Sstevel@tonic-gate itree_free(struct lut *lutp) 1313*0Sstevel@tonic-gate { 1314*0Sstevel@tonic-gate lut_free(lutp, itree_destructor, NULL); 1315*0Sstevel@tonic-gate } 1316*0Sstevel@tonic-gate 1317*0Sstevel@tonic-gate int 1318*0Sstevel@tonic-gate itree_nameinstancecmp(struct node *np1, struct node *np2) 1319*0Sstevel@tonic-gate { 1320*0Sstevel@tonic-gate int np1type = (int)np1->u.name.t; 1321*0Sstevel@tonic-gate int np2type = (int)np2->u.name.t; 1322*0Sstevel@tonic-gate int num1; 1323*0Sstevel@tonic-gate int num2; 1324*0Sstevel@tonic-gate 1325*0Sstevel@tonic-gate while (np1 && np2 && np1->u.name.s == np2->u.name.s) { 1326*0Sstevel@tonic-gate if (np1->u.name.next != NULL && np2->u.name.next != NULL) { 1327*0Sstevel@tonic-gate if (np1->u.name.cp != NULL) { 1328*0Sstevel@tonic-gate config_getcompname(np1->u.name.cp, NULL, &num1); 1329*0Sstevel@tonic-gate } else { 1330*0Sstevel@tonic-gate ASSERT(np1->u.name.child != NULL); 1331*0Sstevel@tonic-gate ASSERT(np1->u.name.child->t == T_NUM); 1332*0Sstevel@tonic-gate num1 = (int)np1->u.name.child->u.ull; 1333*0Sstevel@tonic-gate } 1334*0Sstevel@tonic-gate 1335*0Sstevel@tonic-gate if (np2->u.name.cp != NULL) { 1336*0Sstevel@tonic-gate config_getcompname(np2->u.name.cp, NULL, &num2); 1337*0Sstevel@tonic-gate } else { 1338*0Sstevel@tonic-gate ASSERT(np2->u.name.child != NULL); 1339*0Sstevel@tonic-gate ASSERT(np2->u.name.child->t == T_NUM); 1340*0Sstevel@tonic-gate num2 = (int)np2->u.name.child->u.ull; 1341*0Sstevel@tonic-gate } 1342*0Sstevel@tonic-gate 1343*0Sstevel@tonic-gate if (num1 != num2) 1344*0Sstevel@tonic-gate return (num1 - num2); 1345*0Sstevel@tonic-gate } 1346*0Sstevel@tonic-gate 1347*0Sstevel@tonic-gate np1 = np1->u.name.next; 1348*0Sstevel@tonic-gate np2 = np2->u.name.next; 1349*0Sstevel@tonic-gate } 1350*0Sstevel@tonic-gate if (np1 == NULL) 1351*0Sstevel@tonic-gate if (np2 == NULL) 1352*0Sstevel@tonic-gate return (np1type - np2type); 1353*0Sstevel@tonic-gate else 1354*0Sstevel@tonic-gate return (-1); 1355*0Sstevel@tonic-gate else if (np2 == NULL) 1356*0Sstevel@tonic-gate return (1); 1357*0Sstevel@tonic-gate else 1358*0Sstevel@tonic-gate return (strcmp(np1->u.name.s, np2->u.name.s)); 1359*0Sstevel@tonic-gate } 1360*0Sstevel@tonic-gate 1361*0Sstevel@tonic-gate void 1362*0Sstevel@tonic-gate itree_pevent_brief(int flags, struct event *ep) 1363*0Sstevel@tonic-gate { 1364*0Sstevel@tonic-gate ASSERT(ep != NULL); 1365*0Sstevel@tonic-gate ASSERT(ep->enode != NULL); 1366*0Sstevel@tonic-gate ASSERT(ep->ipp != NULL); 1367*0Sstevel@tonic-gate 1368*0Sstevel@tonic-gate ipath_print(flags, ep->enode->u.event.ename->u.name.s, ep->ipp); 1369*0Sstevel@tonic-gate } 1370*0Sstevel@tonic-gate 1371*0Sstevel@tonic-gate /*ARGSUSED*/ 1372*0Sstevel@tonic-gate static void 1373*0Sstevel@tonic-gate itree_pevent(struct event *lhs, struct event *ep, void *arg) 1374*0Sstevel@tonic-gate { 1375*0Sstevel@tonic-gate struct plut_wlk_data propd; 1376*0Sstevel@tonic-gate struct bubble *bp; 1377*0Sstevel@tonic-gate int flags = (int)arg; 1378*0Sstevel@tonic-gate 1379*0Sstevel@tonic-gate itree_pevent_brief(flags, ep); 1380*0Sstevel@tonic-gate if (ep->t == N_EREPORT) 1381*0Sstevel@tonic-gate out(flags, " (count %d)", ep->count); 1382*0Sstevel@tonic-gate else 1383*0Sstevel@tonic-gate out(flags, NULL); 1384*0Sstevel@tonic-gate 1385*0Sstevel@tonic-gate if (ep->props) { 1386*0Sstevel@tonic-gate propd.flags = flags; 1387*0Sstevel@tonic-gate propd.first = 1; 1388*0Sstevel@tonic-gate out(flags, "Properties:"); 1389*0Sstevel@tonic-gate lut_walk(ep->props, ptree_plut, (void *)&propd); 1390*0Sstevel@tonic-gate } 1391*0Sstevel@tonic-gate 1392*0Sstevel@tonic-gate for (bp = itree_next_bubble(ep, NULL); bp; 1393*0Sstevel@tonic-gate bp = itree_next_bubble(ep, bp)) { 1394*0Sstevel@tonic-gate /* Print only TO bubbles in this loop */ 1395*0Sstevel@tonic-gate if (bp->t != B_TO) 1396*0Sstevel@tonic-gate continue; 1397*0Sstevel@tonic-gate itree_pbubble(flags, bp); 1398*0Sstevel@tonic-gate } 1399*0Sstevel@tonic-gate 1400*0Sstevel@tonic-gate for (bp = itree_next_bubble(ep, NULL); bp; 1401*0Sstevel@tonic-gate bp = itree_next_bubble(ep, bp)) { 1402*0Sstevel@tonic-gate /* Print only INHIBIT bubbles in this loop */ 1403*0Sstevel@tonic-gate if (bp->t != B_INHIBIT) 1404*0Sstevel@tonic-gate continue; 1405*0Sstevel@tonic-gate itree_pbubble(flags, bp); 1406*0Sstevel@tonic-gate } 1407*0Sstevel@tonic-gate 1408*0Sstevel@tonic-gate for (bp = itree_next_bubble(ep, NULL); bp; 1409*0Sstevel@tonic-gate bp = itree_next_bubble(ep, bp)) { 1410*0Sstevel@tonic-gate /* Print only FROM bubbles in this loop */ 1411*0Sstevel@tonic-gate if (bp->t != B_FROM) 1412*0Sstevel@tonic-gate continue; 1413*0Sstevel@tonic-gate itree_pbubble(flags, bp); 1414*0Sstevel@tonic-gate } 1415*0Sstevel@tonic-gate } 1416*0Sstevel@tonic-gate 1417*0Sstevel@tonic-gate static void 1418*0Sstevel@tonic-gate itree_pbubble(int flags, struct bubble *bp) 1419*0Sstevel@tonic-gate { 1420*0Sstevel@tonic-gate struct constraintlist *cp; 1421*0Sstevel@tonic-gate struct arrowlist *ap; 1422*0Sstevel@tonic-gate 1423*0Sstevel@tonic-gate ASSERT(bp != NULL); 1424*0Sstevel@tonic-gate 1425*0Sstevel@tonic-gate out(flags|O_NONL, " "); 1426*0Sstevel@tonic-gate if (bp->mark) 1427*0Sstevel@tonic-gate out(flags|O_NONL, "*"); 1428*0Sstevel@tonic-gate else 1429*0Sstevel@tonic-gate out(flags|O_NONL, " "); 1430*0Sstevel@tonic-gate if (bp->t == B_FROM) 1431*0Sstevel@tonic-gate out(flags|O_NONL, "N=%d to:", bp->nork); 1432*0Sstevel@tonic-gate else if (bp->t == B_TO) 1433*0Sstevel@tonic-gate out(flags|O_NONL, "K=%d from:", bp->nork); 1434*0Sstevel@tonic-gate else 1435*0Sstevel@tonic-gate out(flags|O_NONL, "K=%d masked from:", bp->nork); 1436*0Sstevel@tonic-gate 1437*0Sstevel@tonic-gate if (bp->t == B_TO || bp->t == B_INHIBIT) { 1438*0Sstevel@tonic-gate for (ap = itree_next_arrow(bp, NULL); ap; 1439*0Sstevel@tonic-gate ap = itree_next_arrow(bp, ap)) { 1440*0Sstevel@tonic-gate ASSERT(ap->arrowp->head == bp); 1441*0Sstevel@tonic-gate ASSERT(ap->arrowp->tail != NULL); 1442*0Sstevel@tonic-gate ASSERT(ap->arrowp->tail->myevent != NULL); 1443*0Sstevel@tonic-gate out(flags|O_NONL, " "); 1444*0Sstevel@tonic-gate itree_pevent_brief(flags, ap->arrowp->tail->myevent); 1445*0Sstevel@tonic-gate } 1446*0Sstevel@tonic-gate out(flags, NULL); 1447*0Sstevel@tonic-gate return; 1448*0Sstevel@tonic-gate } 1449*0Sstevel@tonic-gate 1450*0Sstevel@tonic-gate for (ap = itree_next_arrow(bp, NULL); ap; 1451*0Sstevel@tonic-gate ap = itree_next_arrow(bp, ap)) { 1452*0Sstevel@tonic-gate ASSERT(ap->arrowp->tail == bp); 1453*0Sstevel@tonic-gate ASSERT(ap->arrowp->head != NULL); 1454*0Sstevel@tonic-gate ASSERT(ap->arrowp->head->myevent != NULL); 1455*0Sstevel@tonic-gate 1456*0Sstevel@tonic-gate out(flags|O_NONL, " "); 1457*0Sstevel@tonic-gate itree_pevent_brief(flags, ap->arrowp->head->myevent); 1458*0Sstevel@tonic-gate 1459*0Sstevel@tonic-gate out(flags|O_NONL, " "); 1460*0Sstevel@tonic-gate ptree_timeval(flags, &ap->arrowp->mindelay); 1461*0Sstevel@tonic-gate out(flags|O_NONL, ","); 1462*0Sstevel@tonic-gate ptree_timeval(flags, &ap->arrowp->maxdelay); 1463*0Sstevel@tonic-gate 1464*0Sstevel@tonic-gate /* Display anything from the propogation node? */ 1465*0Sstevel@tonic-gate out(O_VERB3|O_NONL, " <%s:%d>", 1466*0Sstevel@tonic-gate ap->arrowp->pnode->file, ap->arrowp->pnode->line); 1467*0Sstevel@tonic-gate 1468*0Sstevel@tonic-gate if (itree_next_constraint(ap->arrowp, NULL)) 1469*0Sstevel@tonic-gate out(flags|O_NONL, " {"); 1470*0Sstevel@tonic-gate 1471*0Sstevel@tonic-gate for (cp = itree_next_constraint(ap->arrowp, NULL); cp; 1472*0Sstevel@tonic-gate cp = itree_next_constraint(ap->arrowp, cp)) { 1473*0Sstevel@tonic-gate ptree(flags, cp->cnode, 1, 0); 1474*0Sstevel@tonic-gate if (itree_next_constraint(ap->arrowp, cp)) 1475*0Sstevel@tonic-gate out(flags|O_NONL, ", "); 1476*0Sstevel@tonic-gate } 1477*0Sstevel@tonic-gate 1478*0Sstevel@tonic-gate if (itree_next_constraint(ap->arrowp, NULL)) 1479*0Sstevel@tonic-gate out(flags|O_NONL, "}"); 1480*0Sstevel@tonic-gate } 1481*0Sstevel@tonic-gate out(flags, NULL); 1482*0Sstevel@tonic-gate } 1483*0Sstevel@tonic-gate 1484*0Sstevel@tonic-gate void 1485*0Sstevel@tonic-gate itree_ptree(int flags, struct lut *itp) 1486*0Sstevel@tonic-gate { 1487*0Sstevel@tonic-gate lut_walk(itp, (lut_cb)itree_pevent, (void *)flags); 1488*0Sstevel@tonic-gate } 1489*0Sstevel@tonic-gate 1490*0Sstevel@tonic-gate /*ARGSUSED*/ 1491*0Sstevel@tonic-gate static void 1492*0Sstevel@tonic-gate itree_destructor(void *left, void *right, void *arg) 1493*0Sstevel@tonic-gate { 1494*0Sstevel@tonic-gate struct event *ep = (struct event *)right; 1495*0Sstevel@tonic-gate struct bubble *nextbub, *bub; 1496*0Sstevel@tonic-gate 1497*0Sstevel@tonic-gate /* Free the properties */ 1498*0Sstevel@tonic-gate lut_free(ep->props, instances_destructor, NULL); 1499*0Sstevel@tonic-gate 1500*0Sstevel@tonic-gate /* Free my bubbles */ 1501*0Sstevel@tonic-gate for (bub = ep->bubbles; bub != NULL; ) { 1502*0Sstevel@tonic-gate nextbub = bub->next; 1503*0Sstevel@tonic-gate /* 1504*0Sstevel@tonic-gate * Free arrows if they are FROM me. Free arrowlists on 1505*0Sstevel@tonic-gate * other types of bubbles (but not the attached arrows, 1506*0Sstevel@tonic-gate * which will be freed when we free the originating 1507*0Sstevel@tonic-gate * bubble. 1508*0Sstevel@tonic-gate */ 1509*0Sstevel@tonic-gate if (bub->t == B_FROM) 1510*0Sstevel@tonic-gate itree_free_arrowlists(bub, 1); 1511*0Sstevel@tonic-gate else 1512*0Sstevel@tonic-gate itree_free_arrowlists(bub, 0); 1513*0Sstevel@tonic-gate itree_free_bubble(bub); 1514*0Sstevel@tonic-gate bub = nextbub; 1515*0Sstevel@tonic-gate } 1516*0Sstevel@tonic-gate 1517*0Sstevel@tonic-gate if (ep->nvp != NULL) 1518*0Sstevel@tonic-gate nvlist_free(ep->nvp); 1519*0Sstevel@tonic-gate bzero(ep, sizeof (*ep)); 1520*0Sstevel@tonic-gate FREE(ep); 1521*0Sstevel@tonic-gate } 1522*0Sstevel@tonic-gate 1523*0Sstevel@tonic-gate static void 1524*0Sstevel@tonic-gate itree_free_bubble(struct bubble *freeme) 1525*0Sstevel@tonic-gate { 1526*0Sstevel@tonic-gate bzero(freeme, sizeof (*freeme)); 1527*0Sstevel@tonic-gate FREE(freeme); 1528*0Sstevel@tonic-gate } 1529*0Sstevel@tonic-gate 1530*0Sstevel@tonic-gate static struct bubble * 1531*0Sstevel@tonic-gate itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen) 1532*0Sstevel@tonic-gate { 1533*0Sstevel@tonic-gate struct bubble *prev = NULL; 1534*0Sstevel@tonic-gate struct bubble *curr; 1535*0Sstevel@tonic-gate struct bubble *newb; 1536*0Sstevel@tonic-gate 1537*0Sstevel@tonic-gate /* Use existing bubbles as appropriate when possible */ 1538*0Sstevel@tonic-gate for (curr = eventp->bubbles; 1539*0Sstevel@tonic-gate curr != NULL; 1540*0Sstevel@tonic-gate prev = curr, curr = curr->next) { 1541*0Sstevel@tonic-gate if (btype == B_TO && curr->t == B_TO) { 1542*0Sstevel@tonic-gate /* see if an existing "to" bubble works for us */ 1543*0Sstevel@tonic-gate if (gen == curr->gen) 1544*0Sstevel@tonic-gate return (curr); /* matched gen number */ 1545*0Sstevel@tonic-gate else if (nork == 1 && curr->nork == 1) { 1546*0Sstevel@tonic-gate curr->gen = gen; 1547*0Sstevel@tonic-gate return (curr); /* coalesce K==1 bubbles */ 1548*0Sstevel@tonic-gate } 1549*0Sstevel@tonic-gate } else if (btype == B_FROM && curr->t == B_FROM) { 1550*0Sstevel@tonic-gate /* see if an existing "from" bubble works for us */ 1551*0Sstevel@tonic-gate if ((nork == N_IS_ALL && curr->nork == N_IS_ALL) || 1552*0Sstevel@tonic-gate (nork == 0 && curr->nork == 0)) 1553*0Sstevel@tonic-gate return (curr); 1554*0Sstevel@tonic-gate } 1555*0Sstevel@tonic-gate } 1556*0Sstevel@tonic-gate 1557*0Sstevel@tonic-gate newb = MALLOC(sizeof (struct bubble)); 1558*0Sstevel@tonic-gate newb->next = NULL; 1559*0Sstevel@tonic-gate newb->t = btype; 1560*0Sstevel@tonic-gate newb->myevent = eventp; 1561*0Sstevel@tonic-gate newb->nork = nork; 1562*0Sstevel@tonic-gate newb->mark = 0; 1563*0Sstevel@tonic-gate newb->gen = gen; 1564*0Sstevel@tonic-gate newb->arrows = NULL; 1565*0Sstevel@tonic-gate 1566*0Sstevel@tonic-gate if (prev == NULL) 1567*0Sstevel@tonic-gate eventp->bubbles = newb; 1568*0Sstevel@tonic-gate else 1569*0Sstevel@tonic-gate prev->next = newb; 1570*0Sstevel@tonic-gate 1571*0Sstevel@tonic-gate return (newb); 1572*0Sstevel@tonic-gate } 1573*0Sstevel@tonic-gate 1574*0Sstevel@tonic-gate struct bubble * 1575*0Sstevel@tonic-gate itree_next_bubble(struct event *eventp, struct bubble *last) 1576*0Sstevel@tonic-gate { 1577*0Sstevel@tonic-gate struct bubble *next; 1578*0Sstevel@tonic-gate 1579*0Sstevel@tonic-gate for (;;) { 1580*0Sstevel@tonic-gate if (last != NULL) 1581*0Sstevel@tonic-gate next = last->next; 1582*0Sstevel@tonic-gate else 1583*0Sstevel@tonic-gate next = eventp->bubbles; 1584*0Sstevel@tonic-gate 1585*0Sstevel@tonic-gate if (next == NULL || next->arrows != NULL) 1586*0Sstevel@tonic-gate return (next); 1587*0Sstevel@tonic-gate 1588*0Sstevel@tonic-gate /* bubble was empty, skip it */ 1589*0Sstevel@tonic-gate last = next; 1590*0Sstevel@tonic-gate } 1591*0Sstevel@tonic-gate } 1592*0Sstevel@tonic-gate 1593*0Sstevel@tonic-gate static void 1594*0Sstevel@tonic-gate add_arrow(struct bubble *bp, struct arrow *ap) 1595*0Sstevel@tonic-gate { 1596*0Sstevel@tonic-gate struct arrowlist *prev = NULL; 1597*0Sstevel@tonic-gate struct arrowlist *curr; 1598*0Sstevel@tonic-gate struct arrowlist *newal; 1599*0Sstevel@tonic-gate 1600*0Sstevel@tonic-gate newal = MALLOC(sizeof (struct arrowlist)); 1601*0Sstevel@tonic-gate bzero(newal, sizeof (struct arrowlist)); 1602*0Sstevel@tonic-gate newal->arrowp = ap; 1603*0Sstevel@tonic-gate 1604*0Sstevel@tonic-gate curr = itree_next_arrow(bp, NULL); 1605*0Sstevel@tonic-gate while (curr != NULL) { 1606*0Sstevel@tonic-gate prev = curr; 1607*0Sstevel@tonic-gate curr = itree_next_arrow(bp, curr); 1608*0Sstevel@tonic-gate } 1609*0Sstevel@tonic-gate 1610*0Sstevel@tonic-gate if (prev == NULL) 1611*0Sstevel@tonic-gate bp->arrows = newal; 1612*0Sstevel@tonic-gate else 1613*0Sstevel@tonic-gate prev->next = newal; 1614*0Sstevel@tonic-gate } 1615*0Sstevel@tonic-gate 1616*0Sstevel@tonic-gate static struct arrow * 1617*0Sstevel@tonic-gate itree_add_arrow(struct bubble *frombubblep, struct bubble *tobubblep, 1618*0Sstevel@tonic-gate struct node *apnode, struct node *fromevent, struct node *toevent, 1619*0Sstevel@tonic-gate struct lut *ex) 1620*0Sstevel@tonic-gate { 1621*0Sstevel@tonic-gate struct arrow *newa; 1622*0Sstevel@tonic-gate 1623*0Sstevel@tonic-gate ASSERTeq(frombubblep->t, B_FROM, itree_bubbletype2str); 1624*0Sstevel@tonic-gate ASSERTinfo(tobubblep->t == B_TO || tobubblep->t == B_INHIBIT, 1625*0Sstevel@tonic-gate itree_bubbletype2str(tobubblep->t)); 1626*0Sstevel@tonic-gate newa = MALLOC(sizeof (struct arrow)); 1627*0Sstevel@tonic-gate newa->tail = frombubblep; 1628*0Sstevel@tonic-gate newa->head = tobubblep; 1629*0Sstevel@tonic-gate newa->pnode = apnode; 1630*0Sstevel@tonic-gate newa->constraints = NULL; 1631*0Sstevel@tonic-gate 1632*0Sstevel@tonic-gate /* 1633*0Sstevel@tonic-gate * Set default delays, then try to re-set them from 1634*0Sstevel@tonic-gate * any within() constraints. 1635*0Sstevel@tonic-gate */ 1636*0Sstevel@tonic-gate newa->mindelay = newa->maxdelay = 0ULL; 1637*0Sstevel@tonic-gate if (itree_set_arrow_traits(newa, fromevent, toevent, ex) == 0) { 1638*0Sstevel@tonic-gate FREE(newa); 1639*0Sstevel@tonic-gate return (NULL); 1640*0Sstevel@tonic-gate } 1641*0Sstevel@tonic-gate 1642*0Sstevel@tonic-gate add_arrow(frombubblep, newa); 1643*0Sstevel@tonic-gate add_arrow(tobubblep, newa); 1644*0Sstevel@tonic-gate return (newa); 1645*0Sstevel@tonic-gate } 1646*0Sstevel@tonic-gate 1647*0Sstevel@tonic-gate /* returns false if traits show that arrow should not be added after all */ 1648*0Sstevel@tonic-gate static int 1649*0Sstevel@tonic-gate itree_set_arrow_traits(struct arrow *ap, struct node *fromev, 1650*0Sstevel@tonic-gate struct node *toev, struct lut *ex) 1651*0Sstevel@tonic-gate { 1652*0Sstevel@tonic-gate struct node *epnames[] = { NULL, NULL, NULL }; 1653*0Sstevel@tonic-gate struct node *newc = NULL; 1654*0Sstevel@tonic-gate 1655*0Sstevel@tonic-gate ASSERTeq(fromev->t, T_EVENT, ptree_nodetype2str); 1656*0Sstevel@tonic-gate ASSERTeq(toev->t, T_EVENT, ptree_nodetype2str); 1657*0Sstevel@tonic-gate 1658*0Sstevel@tonic-gate /* 1659*0Sstevel@tonic-gate * search for the within values first on the declaration of 1660*0Sstevel@tonic-gate * the destination event, and then on the prop. this allows 1661*0Sstevel@tonic-gate * one to specify a "default" within by putting it on the 1662*0Sstevel@tonic-gate * declaration, but then allow overriding on the prop statement. 1663*0Sstevel@tonic-gate */ 1664*0Sstevel@tonic-gate arrow_add_within(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist); 1665*0Sstevel@tonic-gate arrow_add_within(ap, toev->u.event.eexprlist); 1666*0Sstevel@tonic-gate 1667*0Sstevel@tonic-gate /* 1668*0Sstevel@tonic-gate * handle any global constraints inherited from the 1669*0Sstevel@tonic-gate * "fromev" event's declaration 1670*0Sstevel@tonic-gate */ 1671*0Sstevel@tonic-gate ASSERT(fromev->u.event.declp != NULL); 1672*0Sstevel@tonic-gate ASSERT(fromev->u.event.declp->u.stmt.np != NULL); 1673*0Sstevel@tonic-gate 1674*0Sstevel@tonic-gate #ifdef notdef 1675*0Sstevel@tonic-gate /* XXX not quite ready to evaluate constraints from decls yet */ 1676*0Sstevel@tonic-gate if (fromev->u.event.declp->u.stmt.np->u.event.eexprlist) 1677*0Sstevel@tonic-gate (void) itree_add_constraint(ap, 1678*0Sstevel@tonic-gate fromev->u.event.declp->u.stmt.np->u.event.eexprlist); 1679*0Sstevel@tonic-gate #endif /* notdef */ 1680*0Sstevel@tonic-gate 1681*0Sstevel@tonic-gate /* handle constraints on the from event in the prop statement */ 1682*0Sstevel@tonic-gate epnames[0] = fromev->u.event.epname; 1683*0Sstevel@tonic-gate epnames[1] = toev->u.event.epname; 1684*0Sstevel@tonic-gate if (eval_potential(fromev->u.event.eexprlist, ex, epnames, &newc) == 0) 1685*0Sstevel@tonic-gate return (0); /* constraint disallows arrow */ 1686*0Sstevel@tonic-gate 1687*0Sstevel@tonic-gate /* 1688*0Sstevel@tonic-gate * handle any global constraints inherited from the 1689*0Sstevel@tonic-gate * "toev" event's declaration 1690*0Sstevel@tonic-gate */ 1691*0Sstevel@tonic-gate ASSERT(toev->u.event.declp != NULL); 1692*0Sstevel@tonic-gate ASSERT(toev->u.event.declp->u.stmt.np != NULL); 1693*0Sstevel@tonic-gate 1694*0Sstevel@tonic-gate #ifdef notdef 1695*0Sstevel@tonic-gate /* XXX not quite ready to evaluate constraints from decls yet */ 1696*0Sstevel@tonic-gate if (toev->u.event.declp->u.stmt.np->u.event.eexprlist) 1697*0Sstevel@tonic-gate (void) itree_add_constraint(ap, 1698*0Sstevel@tonic-gate toev->u.event.declp->u.stmt.np->u.event.eexprlist); 1699*0Sstevel@tonic-gate #endif /* notdef */ 1700*0Sstevel@tonic-gate 1701*0Sstevel@tonic-gate /* handle constraints on the to event in the prop statement */ 1702*0Sstevel@tonic-gate epnames[0] = toev->u.event.epname; 1703*0Sstevel@tonic-gate epnames[1] = fromev->u.event.epname; 1704*0Sstevel@tonic-gate if (eval_potential(toev->u.event.eexprlist, ex, epnames, &newc) == 0) 1705*0Sstevel@tonic-gate return (0); /* constraint disallows arrow */ 1706*0Sstevel@tonic-gate 1707*0Sstevel@tonic-gate /* if we came up with any deferred constraints, add them to arrow */ 1708*0Sstevel@tonic-gate if (newc != NULL) 1709*0Sstevel@tonic-gate (void) itree_add_constraint(ap, newc); 1710*0Sstevel@tonic-gate 1711*0Sstevel@tonic-gate return (1); /* constraints allow arrow */ 1712*0Sstevel@tonic-gate } 1713*0Sstevel@tonic-gate 1714*0Sstevel@tonic-gate /* 1715*0Sstevel@tonic-gate * Set within() constraint. If the constraint were set multiple times, 1716*0Sstevel@tonic-gate * the last one would "win". 1717*0Sstevel@tonic-gate */ 1718*0Sstevel@tonic-gate static void 1719*0Sstevel@tonic-gate arrow_add_within(struct arrow *ap, struct node *xpr) 1720*0Sstevel@tonic-gate { 1721*0Sstevel@tonic-gate struct node *arglist; 1722*0Sstevel@tonic-gate 1723*0Sstevel@tonic-gate /* end of expressions list */ 1724*0Sstevel@tonic-gate if (xpr == NULL) 1725*0Sstevel@tonic-gate return; 1726*0Sstevel@tonic-gate 1727*0Sstevel@tonic-gate switch (xpr->t) { 1728*0Sstevel@tonic-gate case T_LIST: 1729*0Sstevel@tonic-gate arrow_add_within(ap, xpr->u.expr.left); 1730*0Sstevel@tonic-gate arrow_add_within(ap, xpr->u.expr.right); 1731*0Sstevel@tonic-gate return; 1732*0Sstevel@tonic-gate case T_FUNC: 1733*0Sstevel@tonic-gate if (xpr->u.func.s != L_within) 1734*0Sstevel@tonic-gate return; 1735*0Sstevel@tonic-gate arglist = xpr->u.func.arglist; 1736*0Sstevel@tonic-gate switch (arglist->t) { 1737*0Sstevel@tonic-gate case T_TIMEVAL: 1738*0Sstevel@tonic-gate ap->mindelay = 0; 1739*0Sstevel@tonic-gate ap->maxdelay = arglist->u.ull; 1740*0Sstevel@tonic-gate break; 1741*0Sstevel@tonic-gate case T_NAME: 1742*0Sstevel@tonic-gate ASSERT(arglist->u.name.s == L_infinity); 1743*0Sstevel@tonic-gate ap->mindelay = 0; 1744*0Sstevel@tonic-gate ap->maxdelay = TIMEVAL_EVENTUALLY; 1745*0Sstevel@tonic-gate break; 1746*0Sstevel@tonic-gate case T_LIST: 1747*0Sstevel@tonic-gate ASSERT(arglist->u.expr.left->t == T_TIMEVAL); 1748*0Sstevel@tonic-gate ap->mindelay = arglist->u.expr.left->u.ull; 1749*0Sstevel@tonic-gate switch (arglist->u.expr.right->t) { 1750*0Sstevel@tonic-gate case T_TIMEVAL: 1751*0Sstevel@tonic-gate ap->maxdelay = arglist->u.ull; 1752*0Sstevel@tonic-gate break; 1753*0Sstevel@tonic-gate case T_NAME: 1754*0Sstevel@tonic-gate ASSERT(arglist->u.expr.right->u.name.s == 1755*0Sstevel@tonic-gate L_infinity); 1756*0Sstevel@tonic-gate ap->maxdelay = TIMEVAL_EVENTUALLY; 1757*0Sstevel@tonic-gate break; 1758*0Sstevel@tonic-gate default: 1759*0Sstevel@tonic-gate out(O_DIE, "within: unexpected 2nd arg type"); 1760*0Sstevel@tonic-gate } 1761*0Sstevel@tonic-gate break; 1762*0Sstevel@tonic-gate default: 1763*0Sstevel@tonic-gate out(O_DIE, "within: unexpected 1st arg type"); 1764*0Sstevel@tonic-gate } 1765*0Sstevel@tonic-gate break; 1766*0Sstevel@tonic-gate default: 1767*0Sstevel@tonic-gate return; 1768*0Sstevel@tonic-gate } 1769*0Sstevel@tonic-gate } 1770*0Sstevel@tonic-gate 1771*0Sstevel@tonic-gate static void 1772*0Sstevel@tonic-gate itree_free_arrowlists(struct bubble *bubp, int arrows_too) 1773*0Sstevel@tonic-gate { 1774*0Sstevel@tonic-gate struct arrowlist *al, *nal; 1775*0Sstevel@tonic-gate 1776*0Sstevel@tonic-gate al = bubp->arrows; 1777*0Sstevel@tonic-gate while (al != NULL) { 1778*0Sstevel@tonic-gate nal = al->next; 1779*0Sstevel@tonic-gate if (arrows_too) { 1780*0Sstevel@tonic-gate itree_free_constraints(al->arrowp); 1781*0Sstevel@tonic-gate bzero(al->arrowp, sizeof (struct arrow)); 1782*0Sstevel@tonic-gate FREE(al->arrowp); 1783*0Sstevel@tonic-gate } 1784*0Sstevel@tonic-gate bzero(al, sizeof (*al)); 1785*0Sstevel@tonic-gate FREE(al); 1786*0Sstevel@tonic-gate al = nal; 1787*0Sstevel@tonic-gate } 1788*0Sstevel@tonic-gate } 1789*0Sstevel@tonic-gate 1790*0Sstevel@tonic-gate struct arrowlist * 1791*0Sstevel@tonic-gate itree_next_arrow(struct bubble *bubble, struct arrowlist *last) 1792*0Sstevel@tonic-gate { 1793*0Sstevel@tonic-gate struct arrowlist *next; 1794*0Sstevel@tonic-gate 1795*0Sstevel@tonic-gate if (last != NULL) 1796*0Sstevel@tonic-gate next = last->next; 1797*0Sstevel@tonic-gate else 1798*0Sstevel@tonic-gate next = bubble->arrows; 1799*0Sstevel@tonic-gate return (next); 1800*0Sstevel@tonic-gate } 1801*0Sstevel@tonic-gate 1802*0Sstevel@tonic-gate static struct constraintlist * 1803*0Sstevel@tonic-gate itree_add_constraint(struct arrow *arrowp, struct node *c) 1804*0Sstevel@tonic-gate { 1805*0Sstevel@tonic-gate struct constraintlist *prev = NULL; 1806*0Sstevel@tonic-gate struct constraintlist *curr; 1807*0Sstevel@tonic-gate struct constraintlist *newc; 1808*0Sstevel@tonic-gate 1809*0Sstevel@tonic-gate for (curr = arrowp->constraints; 1810*0Sstevel@tonic-gate curr != NULL; 1811*0Sstevel@tonic-gate prev = curr, curr = curr->next); 1812*0Sstevel@tonic-gate 1813*0Sstevel@tonic-gate newc = MALLOC(sizeof (struct constraintlist)); 1814*0Sstevel@tonic-gate newc->next = NULL; 1815*0Sstevel@tonic-gate newc->cnode = c; 1816*0Sstevel@tonic-gate 1817*0Sstevel@tonic-gate if (prev == NULL) 1818*0Sstevel@tonic-gate arrowp->constraints = newc; 1819*0Sstevel@tonic-gate else 1820*0Sstevel@tonic-gate prev->next = newc; 1821*0Sstevel@tonic-gate 1822*0Sstevel@tonic-gate return (newc); 1823*0Sstevel@tonic-gate } 1824*0Sstevel@tonic-gate 1825*0Sstevel@tonic-gate struct constraintlist * 1826*0Sstevel@tonic-gate itree_next_constraint(struct arrow *arrowp, struct constraintlist *last) 1827*0Sstevel@tonic-gate { 1828*0Sstevel@tonic-gate struct constraintlist *next; 1829*0Sstevel@tonic-gate 1830*0Sstevel@tonic-gate if (last != NULL) 1831*0Sstevel@tonic-gate next = last->next; 1832*0Sstevel@tonic-gate else 1833*0Sstevel@tonic-gate next = arrowp->constraints; 1834*0Sstevel@tonic-gate return (next); 1835*0Sstevel@tonic-gate } 1836*0Sstevel@tonic-gate 1837*0Sstevel@tonic-gate static void 1838*0Sstevel@tonic-gate itree_free_constraints(struct arrow *ap) 1839*0Sstevel@tonic-gate { 1840*0Sstevel@tonic-gate struct constraintlist *cl, *ncl; 1841*0Sstevel@tonic-gate 1842*0Sstevel@tonic-gate cl = ap->constraints; 1843*0Sstevel@tonic-gate while (cl != NULL) { 1844*0Sstevel@tonic-gate ncl = cl->next; 1845*0Sstevel@tonic-gate ASSERT(cl->cnode != NULL); 1846*0Sstevel@tonic-gate tree_free(cl->cnode); 1847*0Sstevel@tonic-gate bzero(cl, sizeof (*cl)); 1848*0Sstevel@tonic-gate FREE(cl); 1849*0Sstevel@tonic-gate cl = ncl; 1850*0Sstevel@tonic-gate } 1851*0Sstevel@tonic-gate } 1852*0Sstevel@tonic-gate 1853*0Sstevel@tonic-gate const char * 1854*0Sstevel@tonic-gate itree_bubbletype2str(enum bubbletype t) 1855*0Sstevel@tonic-gate { 1856*0Sstevel@tonic-gate static char buf[100]; 1857*0Sstevel@tonic-gate 1858*0Sstevel@tonic-gate switch (t) { 1859*0Sstevel@tonic-gate case B_FROM: return L_from; 1860*0Sstevel@tonic-gate case B_TO: return L_to; 1861*0Sstevel@tonic-gate case B_INHIBIT: return L_inhibit; 1862*0Sstevel@tonic-gate default: 1863*0Sstevel@tonic-gate (void) sprintf(buf, "[unexpected bubbletype: %d]", t); 1864*0Sstevel@tonic-gate return (buf); 1865*0Sstevel@tonic-gate } 1866*0Sstevel@tonic-gate } 1867*0Sstevel@tonic-gate 1868*0Sstevel@tonic-gate /* 1869*0Sstevel@tonic-gate * itree_fini -- clean up any half-built itrees 1870*0Sstevel@tonic-gate */ 1871*0Sstevel@tonic-gate void 1872*0Sstevel@tonic-gate itree_fini(void) 1873*0Sstevel@tonic-gate { 1874*0Sstevel@tonic-gate if (Ninfo.lut != NULL) { 1875*0Sstevel@tonic-gate itree_free(Ninfo.lut); 1876*0Sstevel@tonic-gate Ninfo.lut = NULL; 1877*0Sstevel@tonic-gate } 1878*0Sstevel@tonic-gate if (Ninfo.ex) { 1879*0Sstevel@tonic-gate lut_free(Ninfo.ex, iterinfo_destructor, NULL); 1880*0Sstevel@tonic-gate Ninfo.ex = NULL; 1881*0Sstevel@tonic-gate } 1882*0Sstevel@tonic-gate } 1883