xref: /illumos-gate/usr/src/cmd/fm/eversholt/common/tree.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * tree.c -- routines for manipulating the prop tree
26  *
27  * the actions in escparse.y call these routines to construct
28  * the parse tree.  these routines, in turn, call the check_X()
29  * routines for semantic checking.
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <ctype.h>
35 #include <strings.h>
36 #include <alloca.h>
37 #include "alloc.h"
38 #include "out.h"
39 #include "stats.h"
40 #include "stable.h"
41 #include "literals.h"
42 #include "lut.h"
43 #include "esclex.h"
44 #include "tree.h"
45 #include "check.h"
46 #include "ptree.h"
47 
48 struct lut *Faults;
49 struct lut *Upsets;
50 struct lut *Defects;
51 struct lut *Errors;
52 struct lut *Ereports;
53 struct lut *Ereportenames;
54 struct lut *Ereportenames_discard;
55 struct lut *SERDs;
56 struct lut *STATs;
57 struct lut *ASRUs;
58 struct lut *FRUs;
59 struct lut *Configs;
60 struct node *Props;
61 struct node *Lastprops;
62 struct node *Masks;
63 struct node *Lastmasks;
64 struct node *Problems;
65 struct node *Lastproblems;
66 
67 static struct node *Root;
68 static char *Newname;
69 
70 static struct stats *Faultcount;
71 static struct stats *Upsetcount;
72 static struct stats *Defectcount;
73 static struct stats *Errorcount;
74 static struct stats *Ereportcount;
75 static struct stats *SERDcount;
76 static struct stats *STATcount;
77 static struct stats *ASRUcount;
78 static struct stats *FRUcount;
79 static struct stats *Configcount;
80 static struct stats *Propcount;
81 static struct stats *Maskcount;
82 static struct stats *Nodecount;
83 static struct stats *Namecount;
84 static struct stats *Nodesize;
85 
86 struct lut *Usedprops;
87 
88 void
89 tree_init(void)
90 {
91 	Faultcount = stats_new_counter("parser.fault", "fault decls", 1);
92 	Upsetcount = stats_new_counter("parser.upset", "upset decls", 1);
93 	Defectcount = stats_new_counter("parser.defect", "defect decls", 1);
94 	Errorcount = stats_new_counter("parser.error", "error decls", 1);
95 	Ereportcount = stats_new_counter("parser.ereport", "ereport decls", 1);
96 	SERDcount = stats_new_counter("parser.SERD", "SERD engine decls", 1);
97 	STATcount = stats_new_counter("parser.STAT", "STAT engine decls", 1);
98 	ASRUcount = stats_new_counter("parser.ASRU", "ASRU decls", 1);
99 	FRUcount = stats_new_counter("parser.FRU", "FRU decls", 1);
100 	Configcount = stats_new_counter("parser.config", "config stmts", 1);
101 	Propcount = stats_new_counter("parser.prop", "prop stmts", 1);
102 	Maskcount = stats_new_counter("parser.mask", "mask stmts", 1);
103 	Nodecount = stats_new_counter("parser.node", "nodes created", 1);
104 	Namecount = stats_new_counter("parser.name", "names created", 1);
105 	Nodesize =
106 	    stats_new_counter("parser.nodesize", "sizeof(struct node)", 1);
107 	stats_counter_add(Nodesize, sizeof (struct node));
108 }
109 
110 void
111 tree_fini(void)
112 {
113 	stats_delete(Faultcount);
114 	stats_delete(Upsetcount);
115 	stats_delete(Defectcount);
116 	stats_delete(Errorcount);
117 	stats_delete(Ereportcount);
118 	stats_delete(SERDcount);
119 	stats_delete(STATcount);
120 	stats_delete(ASRUcount);
121 	stats_delete(FRUcount);
122 	stats_delete(Configcount);
123 	stats_delete(Propcount);
124 	stats_delete(Maskcount);
125 	stats_delete(Nodecount);
126 	stats_delete(Namecount);
127 	stats_delete(Nodesize);
128 
129 	/* free entire parse tree */
130 	tree_free(Root);
131 
132 	/* free up the luts we keep for decls */
133 	lut_free(Faults, NULL, NULL);
134 	Faults = NULL;
135 	lut_free(Upsets, NULL, NULL);
136 	Upsets = NULL;
137 	lut_free(Defects, NULL, NULL);
138 	Defects = NULL;
139 	lut_free(Errors, NULL, NULL);
140 	Errors = NULL;
141 	lut_free(Ereports, NULL, NULL);
142 	Ereports = NULL;
143 	lut_free(Ereportenames, NULL, NULL);
144 	Ereportenames = NULL;
145 	lut_free(Ereportenames_discard, NULL, NULL);
146 	Ereportenames_discard = NULL;
147 	lut_free(SERDs, NULL, NULL);
148 	SERDs = NULL;
149 	lut_free(STATs, NULL, NULL);
150 	STATs = NULL;
151 	lut_free(ASRUs, NULL, NULL);
152 	ASRUs = NULL;
153 	lut_free(FRUs, NULL, NULL);
154 	FRUs = NULL;
155 	lut_free(Configs, NULL, NULL);
156 	Configs = NULL;
157 	lut_free(Usedprops, NULL, NULL);
158 	Usedprops = NULL;
159 
160 	Props = Lastprops = NULL;
161 	Masks = Lastmasks = NULL;
162 	Problems = Lastproblems = NULL;
163 
164 	if (Newname != NULL) {
165 		FREE(Newname);
166 		Newname = NULL;
167 	}
168 }
169 
170 /*ARGSUSED*/
171 static int
172 nodesize(enum nodetype t, struct node *ret)
173 {
174 	int size = sizeof (struct node);
175 
176 	switch (t) {
177 	case T_NAME:
178 		size += sizeof (ret->u.name) - sizeof (ret->u);
179 		break;
180 
181 	case T_GLOBID:
182 		size += sizeof (ret->u.globid) - sizeof (ret->u);
183 		break;
184 
185 	case T_TIMEVAL:
186 	case T_NUM:
187 		size += sizeof (ret->u.ull) - sizeof (ret->u);
188 		break;
189 
190 	case T_QUOTE:
191 		size += sizeof (ret->u.quote) - sizeof (ret->u);
192 		break;
193 
194 	case T_FUNC:
195 		size += sizeof (ret->u.func) - sizeof (ret->u);
196 		break;
197 
198 	case T_FAULT:
199 	case T_UPSET:
200 	case T_DEFECT:
201 	case T_ERROR:
202 	case T_EREPORT:
203 	case T_ASRU:
204 	case T_FRU:
205 	case T_SERD:
206 	case T_STAT:
207 	case T_CONFIG:
208 	case T_PROP:
209 	case T_MASK:
210 		size += sizeof (ret->u.stmt) - sizeof (ret->u);
211 		break;
212 
213 	case T_EVENT:
214 		size += sizeof (ret->u.event) - sizeof (ret->u);
215 		break;
216 
217 	case T_ARROW:
218 		size += sizeof (ret->u.arrow) - sizeof (ret->u);
219 		break;
220 
221 	default:
222 		size += sizeof (ret->u.expr) - sizeof (ret->u);
223 		break;
224 	}
225 	return (size);
226 }
227 
228 struct node *
229 newnode(enum nodetype t, const char *file, int line)
230 {
231 	struct node *ret = NULL;
232 	int size = nodesize(t, ret);
233 
234 	ret = alloc_xmalloc(size);
235 	stats_counter_bump(Nodecount);
236 	bzero(ret, size);
237 	ret->t = t;
238 	ret->file = (file == NULL) ? "<nofile>" : file;
239 	ret->line = line;
240 
241 	return (ret);
242 }
243 
244 /*ARGSUSED*/
245 void
246 tree_free(struct node *root)
247 {
248 	if (root == NULL)
249 		return;
250 
251 	switch (root->t) {
252 	case T_NAME:
253 		tree_free(root->u.name.child);
254 		tree_free(root->u.name.next);
255 		break;
256 	case T_FUNC:
257 		tree_free(root->u.func.arglist);
258 		break;
259 	case T_AND:
260 	case T_OR:
261 	case T_EQ:
262 	case T_NE:
263 	case T_ADD:
264 	case T_DIV:
265 	case T_MOD:
266 	case T_MUL:
267 	case T_SUB:
268 	case T_LT:
269 	case T_LE:
270 	case T_GT:
271 	case T_GE:
272 	case T_BITAND:
273 	case T_BITOR:
274 	case T_BITXOR:
275 	case T_BITNOT:
276 	case T_LSHIFT:
277 	case T_RSHIFT:
278 	case T_NVPAIR:
279 	case T_ASSIGN:
280 	case T_CONDIF:
281 	case T_CONDELSE:
282 	case T_LIST:
283 		tree_free(root->u.expr.left);
284 		tree_free(root->u.expr.right);
285 		break;
286 	case T_EVENT:
287 		tree_free(root->u.event.ename);
288 		tree_free(root->u.event.epname);
289 		tree_free(root->u.event.eexprlist);
290 		break;
291 	case T_NOT:
292 		tree_free(root->u.expr.left);
293 		break;
294 	case T_ARROW:
295 		tree_free(root->u.arrow.lhs);
296 		tree_free(root->u.arrow.nnp);
297 		tree_free(root->u.arrow.knp);
298 		tree_free(root->u.arrow.rhs);
299 		break;
300 	case T_PROP:
301 	case T_MASK:
302 		tree_free(root->u.stmt.np);
303 		break;
304 	case T_FAULT:
305 	case T_UPSET:
306 	case T_DEFECT:
307 	case T_ERROR:
308 	case T_EREPORT:
309 	case T_ASRU:
310 	case T_FRU:
311 	case T_SERD:
312 	case T_STAT:
313 	case T_CONFIG:
314 		tree_free(root->u.stmt.np);
315 		if (root->u.stmt.nvpairs)
316 			tree_free(root->u.stmt.nvpairs);
317 		if (root->u.stmt.lutp)
318 			lut_free(root->u.stmt.lutp, NULL, NULL);
319 		break;
320 	case T_TIMEVAL:
321 	case T_NUM:
322 	case T_QUOTE:
323 	case T_GLOBID:
324 	case T_NOTHING:
325 		break;
326 	default:
327 		out(O_DIE,
328 		    "internal error: tree_free unexpected nodetype: %d",
329 		    root->t);
330 		/*NOTREACHED*/
331 	}
332 	alloc_xfree((char *)root, nodesize(root->t, root));
333 }
334 
335 static int
336 tree_treecmp(struct node *np1, struct node *np2, enum nodetype t,
337     lut_cmp cmp_func)
338 {
339 	if (np1 == NULL || np2 == NULL)
340 		return (0);
341 
342 	if (np1->t != np2->t)
343 		return (1);
344 
345 	ASSERT(cmp_func != NULL);
346 
347 	if (np1->t == t)
348 		return ((*cmp_func)(np1, np2));
349 
350 	switch (np1->t) {
351 	case T_NAME:
352 		if (tree_treecmp(np1->u.name.child, np2->u.name.child, t,
353 		    cmp_func))
354 			return (1);
355 		return (tree_treecmp(np1->u.name.next, np2->u.name.next, t,
356 		    cmp_func));
357 		/*NOTREACHED*/
358 		break;
359 	case T_FUNC:
360 		return (tree_treecmp(np1->u.func.arglist, np2->u.func.arglist,
361 		    t, cmp_func));
362 		/*NOTREACHED*/
363 		break;
364 	case T_AND:
365 	case T_OR:
366 	case T_EQ:
367 	case T_NE:
368 	case T_ADD:
369 	case T_DIV:
370 	case T_MOD:
371 	case T_MUL:
372 	case T_SUB:
373 	case T_LT:
374 	case T_LE:
375 	case T_GT:
376 	case T_GE:
377 	case T_BITAND:
378 	case T_BITOR:
379 	case T_BITXOR:
380 	case T_BITNOT:
381 	case T_LSHIFT:
382 	case T_RSHIFT:
383 	case T_NVPAIR:
384 	case T_ASSIGN:
385 	case T_CONDIF:
386 	case T_CONDELSE:
387 	case T_LIST:
388 		if (tree_treecmp(np1->u.expr.left, np2->u.expr.left, t,
389 		    cmp_func))
390 			return (1);
391 		return (tree_treecmp(np1->u.expr.right, np2->u.expr.right, t,
392 		    cmp_func));
393 		/*NOTREACHED*/
394 		break;
395 	case T_EVENT:
396 		if (tree_treecmp(np1->u.event.ename, np2->u.event.ename, t,
397 		    cmp_func))
398 			return (1);
399 		if (tree_treecmp(np1->u.event.epname, np2->u.event.epname, t,
400 		    cmp_func))
401 			return (1);
402 		return (tree_treecmp(np1->u.event.eexprlist,
403 		    np2->u.event.eexprlist, t, cmp_func));
404 		/*NOTREACHED*/
405 		break;
406 	case T_NOT:
407 		return (tree_treecmp(np1->u.expr.left, np2->u.expr.left, t,
408 		    cmp_func));
409 		/*NOTREACHED*/
410 		break;
411 	case T_ARROW:
412 		if (tree_treecmp(np1->u.arrow.lhs, np2->u.arrow.lhs, t,
413 		    cmp_func))
414 			return (1);
415 		if (tree_treecmp(np1->u.arrow.nnp, np2->u.arrow.nnp, t,
416 		    cmp_func))
417 			return (1);
418 		if (tree_treecmp(np1->u.arrow.knp, np2->u.arrow.knp, t,
419 		    cmp_func))
420 			return (1);
421 		return (tree_treecmp(np1->u.arrow.rhs, np2->u.arrow.rhs, t,
422 		    cmp_func));
423 		/*NOTREACHED*/
424 		break;
425 	case T_PROP:
426 	case T_MASK:
427 		return (tree_treecmp(np1->u.stmt.np, np2->u.stmt.np, t,
428 		    cmp_func));
429 		/*NOTREACHED*/
430 		break;
431 	case T_FAULT:
432 	case T_UPSET:
433 	case T_DEFECT:
434 	case T_ERROR:
435 	case T_EREPORT:
436 	case T_ASRU:
437 	case T_FRU:
438 	case T_SERD:
439 	case T_STAT:
440 		if (tree_treecmp(np1->u.stmt.np, np2->u.stmt.np, t, cmp_func))
441 			return (1);
442 		return (tree_treecmp(np1->u.stmt.nvpairs, np2->u.stmt.nvpairs,
443 		    t, cmp_func));
444 		/*NOTREACHED*/
445 		break;
446 	case T_TIMEVAL:
447 	case T_NUM:
448 	case T_QUOTE:
449 	case T_GLOBID:
450 	case T_NOTHING:
451 		break;
452 	default:
453 		out(O_DIE,
454 		    "internal error: tree_treecmp unexpected nodetype: %d",
455 		    np1->t);
456 		/*NOTREACHED*/
457 		break;
458 	}
459 
460 	return (0);
461 }
462 
463 struct node *
464 tree_root(struct node *np)
465 {
466 	if (np)
467 		Root = np;
468 	return (Root);
469 }
470 
471 struct node *
472 tree_nothing(void)
473 {
474 	return (newnode(T_NOTHING, L_nofile, 0));
475 }
476 
477 struct node *
478 tree_expr(enum nodetype t, struct node *left, struct node *right)
479 {
480 	struct node *ret;
481 
482 	ASSERTinfo(left != NULL || right != NULL, ptree_nodetype2str(t));
483 
484 	ret = newnode(t,
485 	    (left) ? left->file : right->file,
486 	    (left) ? left->line : right->line);
487 
488 	ret->u.expr.left = left;
489 	ret->u.expr.right = right;
490 
491 	check_expr(ret);
492 
493 	return (ret);
494 }
495 
496 /*
497  * ename_compress -- convert event class name in to more space-efficient form
498  *
499  * this routine is called after the parser has completed an "ename", which
500  * is that part of an event that contains the class name (like ereport.x.y.z).
501  * after this routine gets done with the ename, two things are true:
502  *   1. the ename uses only a single struct node
503  *   2. ename->u.name.s contains the *complete* class name, dots and all,
504  *      entered into the string table.
505  *
506  * so in addition to saving space by using fewer struct nodes, this routine
507  * allows consumers of the fault tree to assume the ename is a single
508  * string, rather than a linked list of strings.
509  */
510 static struct node *
511 ename_compress(struct node *ename)
512 {
513 	char *buf;
514 	char *cp;
515 	int len = 0;
516 	struct node *np;
517 
518 	if (ename == NULL)
519 		return (ename);
520 
521 	ASSERT(ename->t == T_NAME);
522 
523 	if (ename->u.name.next == NULL)
524 		return (ename);	/* no compression to be applied here */
525 
526 	for (np = ename; np != NULL; np = np->u.name.next) {
527 		ASSERT(np->t == T_NAME);
528 		len++;	/* room for '.' and final '\0' */
529 		len += strlen(np->u.name.s);
530 	}
531 	cp = buf = alloca(len);
532 	for (np = ename; np != NULL; np = np->u.name.next) {
533 		ASSERT(np->t == T_NAME);
534 		if (np != ename)
535 			*cp++ = '.';
536 		(void) strcpy(cp, np->u.name.s);
537 		cp += strlen(cp);
538 	}
539 
540 	ename->u.name.s = stable(buf);
541 	tree_free(ename->u.name.next);
542 	ename->u.name.next = NULL;
543 	ename->u.name.last = ename;
544 	return (ename);
545 }
546 
547 struct node *
548 tree_event(struct node *ename, struct node *epname, struct node *eexprlist)
549 {
550 	struct node *ret;
551 
552 	ASSERT(ename != NULL);
553 
554 	ret = newnode(T_EVENT, ename->file, ename->line);
555 
556 	ret->u.event.ename = ename_compress(ename);
557 	ret->u.event.epname = epname;
558 	ret->u.event.eexprlist = eexprlist;
559 
560 	check_event(ret);
561 
562 	return (ret);
563 }
564 
565 struct node *
566 tree_name(const char *s, enum itertype it, const char *file, int line)
567 {
568 	struct node *ret = newnode(T_NAME, file, line);
569 
570 	ASSERT(s != NULL);
571 
572 	stats_counter_bump(Namecount);
573 	ret->u.name.t = N_UNSPEC;
574 	ret->u.name.s = stable(s);
575 	ret->u.name.it = it;
576 	ret->u.name.last = ret;
577 
578 	if (it == IT_ENAME) {
579 		/* PHASE2, possible optimization: convert to table driven */
580 		if (s == L_fault)
581 			ret->u.name.t = N_FAULT;
582 		else if (s == L_upset)
583 			ret->u.name.t = N_UPSET;
584 		else if (s == L_defect)
585 			ret->u.name.t = N_DEFECT;
586 		else if (s == L_error)
587 			ret->u.name.t = N_ERROR;
588 		else if (s == L_ereport)
589 			ret->u.name.t = N_EREPORT;
590 		else if (s == L_serd)
591 			ret->u.name.t = N_SERD;
592 		else if (s == L_stat)
593 			ret->u.name.t = N_STAT;
594 		else
595 			outfl(O_ERR, file, line, "unknown class: %s", s);
596 	}
597 	return (ret);
598 }
599 
600 struct node *
601 tree_iname(const char *s, const char *file, int line)
602 {
603 	struct node *ret;
604 	char *ss;
605 	char *ptr;
606 
607 	ASSERT(s != NULL && *s != '\0');
608 
609 	ss = STRDUP(s);
610 
611 	ptr = &ss[strlen(ss) - 1];
612 	if (!isdigit(*ptr)) {
613 		outfl(O_ERR, file, line,
614 		    "instanced name expected (i.e. \"x0/y1\")");
615 		FREE(ss);
616 		return (tree_name(s, IT_NONE, file, line));
617 	}
618 	while (ptr > ss && isdigit(*(ptr - 1)))
619 		ptr--;
620 
621 	ret = newnode(T_NAME, file, line);
622 	stats_counter_bump(Namecount);
623 	ret->u.name.child = tree_num(ptr, file, line);
624 	*ptr = '\0';
625 	ret->u.name.t = N_UNSPEC;
626 	ret->u.name.s = stable(ss);
627 	ret->u.name.it = IT_NONE;
628 	ret->u.name.last = ret;
629 	FREE(ss);
630 
631 	return (ret);
632 }
633 
634 struct node *
635 tree_globid(const char *s, const char *file, int line)
636 {
637 	struct node *ret = newnode(T_GLOBID, file, line);
638 
639 	ASSERT(s != NULL);
640 
641 	ret->u.globid.s = stable(s);
642 
643 	return (ret);
644 }
645 
646 struct node *
647 tree_name_append(struct node *np1, struct node *np2)
648 {
649 	ASSERT(np1 != NULL && np2 != NULL);
650 
651 	if (np1->t != T_NAME)
652 		outfl(O_DIE, np1->file, np1->line,
653 		    "tree_name_append: internal error (np1 type %d)", np1->t);
654 	if (np2->t != T_NAME)
655 		outfl(O_DIE, np2->file, np2->line,
656 		    "tree_name_append: internal error (np2 type %d)", np2->t);
657 
658 	ASSERT(np1->u.name.last != NULL);
659 
660 	np1->u.name.last->u.name.next = np2;
661 	np1->u.name.last = np2;
662 	return (np1);
663 }
664 
665 /*
666  * tree_name_repairdash -- repair a class name that contained a dash
667  *
668  * this routine is called by the parser when a dash is encountered
669  * in a class name.  the event protocol allows the dashes but our
670  * lexer considers them a separate token (arithmetic minus).  an extra
671  * rule in the parser catches this case and calls this routine to fixup
672  * the last component of the class name (so far) by constructing the
673  * new stable entry for a name including the dash.
674  */
675 struct node *
676 tree_name_repairdash(struct node *np, const char *s)
677 {
678 	int len;
679 	char *buf;
680 
681 	ASSERT(np != NULL && s != NULL);
682 
683 	if (np->t != T_NAME)
684 		outfl(O_DIE, np->file, np->line,
685 		    "tree_name_repairdash: internal error (np type %d)",
686 		    np->t);
687 
688 	ASSERT(np->u.name.last != NULL);
689 
690 	len = strlen(np->u.name.last->u.name.s) + 1 + strlen(s) + 1;
691 	buf = MALLOC(len);
692 	(void) snprintf(buf, len, "%s-%s", np->u.name.last->u.name.s, s);
693 	np->u.name.last->u.name.s = stable(buf);
694 	FREE(buf);
695 	return (np);
696 }
697 
698 struct node *
699 tree_name_repairdash2(const char *s, struct node *np)
700 {
701 	int len;
702 	char *buf;
703 
704 	ASSERT(np != NULL && s != NULL);
705 
706 	if (np->t != T_NAME)
707 		outfl(O_DIE, np->file, np->line,
708 		    "tree_name_repairdash: internal error (np type %d)",
709 		    np->t);
710 
711 	ASSERT(np->u.name.last != NULL);
712 
713 	len = strlen(np->u.name.last->u.name.s) + 1 + strlen(s) + 1;
714 	buf = MALLOC(len);
715 	(void) snprintf(buf, len, "%s-%s", s, np->u.name.last->u.name.s);
716 	np->u.name.last->u.name.s = stable(buf);
717 	FREE(buf);
718 	return (np);
719 }
720 
721 struct node *
722 tree_name_iterator(struct node *np1, struct node *np2)
723 {
724 	ASSERT(np1 != NULL);
725 	ASSERT(np2 != NULL);
726 	ASSERTinfo(np1->t == T_NAME, ptree_nodetype2str(np1->t));
727 
728 	np1->u.name.child = np2;
729 
730 	check_name_iterator(np1);
731 
732 	return (np1);
733 }
734 
735 struct node *
736 tree_timeval(const char *s, const char *suffix, const char *file, int line)
737 {
738 	struct node *ret = newnode(T_TIMEVAL, file, line);
739 	const unsigned long long *ullp;
740 
741 	ASSERT(s != NULL);
742 	ASSERT(suffix != NULL);
743 
744 	if ((ullp = lex_s2ullp_lut_lookup(Timesuffixlut, suffix)) == NULL) {
745 		outfl(O_ERR, file, line,
746 		    "unrecognized number suffix: %s", suffix);
747 		/* still construct a valid timeval node so parsing continues */
748 		ret->u.ull = 1;
749 	} else {
750 		ret->u.ull = (unsigned long long)strtoul(s, NULL, 0) * *ullp;
751 	}
752 
753 	return (ret);
754 }
755 
756 struct node *
757 tree_num(const char *s, const char *file, int line)
758 {
759 	struct node *ret = newnode(T_NUM, file, line);
760 
761 	ret->u.ull = (unsigned long long)strtoul(s, NULL, 0);
762 	return (ret);
763 }
764 
765 struct node *
766 tree_quote(const char *s, const char *file, int line)
767 {
768 	struct node *ret = newnode(T_QUOTE, file, line);
769 
770 	ret->u.quote.s = stable(s);
771 	return (ret);
772 }
773 
774 struct node *
775 tree_func(const char *s, struct node *np, const char *file, int line)
776 {
777 	struct node *ret = newnode(T_FUNC, file, line);
778 	const char *ptr;
779 
780 	ret->u.func.s = s;
781 	ret->u.func.arglist = np;
782 
783 	check_func(ret);
784 
785 	/*
786 	 * keep track of the properties we're interested in so we can ignore the
787 	 * rest
788 	 */
789 	if (strcmp(s, L_confprop) == 0 || strcmp(s, L_confprop_defined) == 0) {
790 		ptr = stable(np->u.expr.right->u.quote.s);
791 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
792 	} else if (strcmp(s, L_is_connected) == 0) {
793 		ptr = stable("connected");
794 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
795 		ptr = stable("CONNECTED");
796 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
797 	} else if (strcmp(s, L_is_type) == 0) {
798 		ptr = stable("type");
799 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
800 		ptr = stable("TYPE");
801 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
802 	} else if (strcmp(s, L_is_on) == 0) {
803 		ptr = stable("on");
804 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
805 		ptr = stable("ON");
806 		Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
807 	}
808 
809 	return (ret);
810 }
811 
812 /*
813  * given a list from a prop or mask statement or a function argument,
814  * convert all iterators to explicit iterators by inventing appropriate
815  * iterator names.
816  */
817 static void
818 make_explicit(struct node *np, int eventonly)
819 {
820 	struct node *pnp;	/* component of pathname */
821 	struct node *pnp2;
822 	int count;
823 	static size_t namesz;
824 
825 	if (Newname == NULL) {
826 		namesz = 200;
827 		Newname = MALLOC(namesz);
828 	}
829 
830 	if (np == NULL)
831 		return;		/* all done */
832 
833 	switch (np->t) {
834 		case T_ASSIGN:
835 		case T_CONDIF:
836 		case T_CONDELSE:
837 		case T_NE:
838 		case T_EQ:
839 		case T_LT:
840 		case T_LE:
841 		case T_GT:
842 		case T_GE:
843 		case T_BITAND:
844 		case T_BITOR:
845 		case T_BITXOR:
846 		case T_BITNOT:
847 		case T_LSHIFT:
848 		case T_RSHIFT:
849 		case T_LIST:
850 		case T_AND:
851 		case T_OR:
852 		case T_NOT:
853 		case T_ADD:
854 		case T_SUB:
855 		case T_MUL:
856 		case T_DIV:
857 		case T_MOD:
858 			make_explicit(np->u.expr.left, eventonly);
859 			make_explicit(np->u.expr.right, eventonly);
860 			break;
861 
862 		case T_EVENT:
863 			make_explicit(np->u.event.epname, 0);
864 			make_explicit(np->u.event.eexprlist, 1);
865 			break;
866 
867 		case T_FUNC:
868 			make_explicit(np->u.func.arglist, eventonly);
869 			break;
870 
871 		case T_NAME:
872 			if (eventonly)
873 				return;
874 			for (pnp = np; pnp != NULL; pnp = pnp->u.name.next)
875 				if (pnp->u.name.child == NULL) {
876 					/*
877 					 * found implicit iterator.  convert
878 					 * it to an explicit iterator by
879 					 * using the name of the component
880 					 * appended with '#' and the number
881 					 * of times we've seen this same
882 					 * component name in this path so far.
883 					 */
884 					count = 0;
885 					for (pnp2 = np; pnp2 != NULL;
886 					    pnp2 = pnp2->u.name.next)
887 						if (pnp2 == pnp)
888 							break;
889 						else if (pnp2->u.name.s ==
890 						    pnp->u.name.s)
891 							count++;
892 
893 					if (namesz < strlen(pnp->u.name.s) +
894 					    100) {
895 						namesz = strlen(pnp->u.name.s) +
896 						    100;
897 						FREE(Newname);
898 						Newname = MALLOC(namesz);
899 					}
900 					/*
901 					 * made up interator name is:
902 					 *	name#ordinal
903 					 * or
904 					 *	name##ordinal
905 					 * the first one is used for vertical
906 					 * expansion, the second for horizontal.
907 					 * either way, the '#' embedded in
908 					 * the name makes it impossible to
909 					 * collide with an actual iterator
910 					 * given to us in the eversholt file.
911 					 */
912 					(void) snprintf(Newname, namesz,
913 					    "%s#%s%d", pnp->u.name.s,
914 					    (pnp->u.name.it == IT_HORIZONTAL) ?
915 					    "#" : "", count);
916 
917 					pnp->u.name.child = tree_name(Newname,
918 					    IT_NONE, pnp->file, pnp->line);
919 					pnp->u.name.childgen = 1;
920 				}
921 			break;
922 	}
923 }
924 
925 struct node *
926 tree_pname(struct node *np)
927 {
928 	make_explicit(np, 0);
929 	return (np);
930 }
931 
932 struct node *
933 tree_arrow(struct node *lhs, struct node *nnp, struct node *knp,
934     struct node *rhs)
935 {
936 	struct node *ret;
937 
938 	ASSERT(lhs != NULL || rhs != NULL);
939 
940 	ret = newnode(T_ARROW,
941 	    (lhs) ? lhs->file : rhs->file,
942 	    (lhs) ? lhs->line : rhs->line);
943 
944 	ret->u.arrow.lhs = lhs;
945 	ret->u.arrow.nnp = nnp;
946 	ret->u.arrow.knp = knp;
947 	ret->u.arrow.rhs = rhs;
948 
949 	make_explicit(lhs, 0);
950 	make_explicit(rhs, 0);
951 
952 	check_arrow(ret);
953 
954 	return (ret);
955 }
956 
957 static struct lut *
958 nvpair2lut(struct node *np, struct lut *lutp, enum nodetype t)
959 {
960 	if (np) {
961 		if (np->t == T_NVPAIR) {
962 			ASSERTeq(np->u.expr.left->t, T_NAME,
963 			    ptree_nodetype2str);
964 			check_stmt_allowed_properties(t, np, lutp);
965 			lutp = tree_s2np_lut_add(lutp,
966 			    np->u.expr.left->u.name.s, np->u.expr.right);
967 		} else if (np->t == T_LIST) {
968 			lutp = nvpair2lut(np->u.expr.left, lutp, t);
969 			lutp = nvpair2lut(np->u.expr.right, lutp, t);
970 		} else
971 			outfl(O_DIE, np->file, np->line,
972 			    "internal error: nvpair2lut type %s",
973 			    ptree_nodetype2str(np->t));
974 	}
975 
976 	return (lutp);
977 }
978 
979 struct lut *
980 tree_s2np_lut_add(struct lut *root, const char *s, struct node *np)
981 {
982 	return (lut_add(root, (void *)s, (void *)np, NULL));
983 }
984 
985 struct node *
986 tree_s2np_lut_lookup(struct lut *root, const char *s)
987 {
988 	return (struct node *)lut_lookup(root, (void *)s, NULL);
989 }
990 
991 struct lut *
992 tree_name2np_lut_add(struct lut *root, struct node *namep, struct node *np)
993 {
994 	return (lut_add(root, (void *)namep, (void *)np,
995 	    (lut_cmp)tree_namecmp));
996 }
997 
998 struct node *
999 tree_name2np_lut_lookup(struct lut *root, struct node *namep)
1000 {
1001 	return (struct node *)
1002 	    lut_lookup(root, (void *)namep, (lut_cmp)tree_namecmp);
1003 }
1004 
1005 struct node *
1006 tree_name2np_lut_lookup_name(struct lut *root, struct node *namep)
1007 {
1008 	return (struct node *)
1009 	    lut_lookup_lhs(root, (void *)namep, (lut_cmp)tree_namecmp);
1010 }
1011 
1012 struct lut *
1013 tree_event2np_lut_add(struct lut *root, struct node *enp, struct node *np)
1014 {
1015 	return (lut_add(root, (void *)enp, (void *)np, (lut_cmp)tree_eventcmp));
1016 }
1017 
1018 struct node *
1019 tree_event2np_lut_lookup(struct lut *root, struct node *enp)
1020 {
1021 	return ((struct node *)
1022 	    lut_lookup(root, (void *)enp, (lut_cmp)tree_eventcmp));
1023 }
1024 
1025 struct node *
1026 tree_event2np_lut_lookup_event(struct lut *root, struct node *enp)
1027 {
1028 	return ((struct node *)
1029 	    lut_lookup_lhs(root, (void *)enp, (lut_cmp)tree_eventcmp));
1030 }
1031 
1032 static struct node *
1033 dodecl(enum nodetype t, const char *file, int line,
1034     struct node *np, struct node *nvpairs, struct lut **lutpp,
1035     struct stats *countp, int justpath)
1036 {
1037 	struct node *ret;
1038 	struct node *decl;
1039 
1040 	/* allocate parse tree node */
1041 	ret = newnode(t, file, line);
1042 	ret->u.stmt.np = np;
1043 	ret->u.stmt.nvpairs = nvpairs;
1044 
1045 	/*
1046 	 * the global lut pointed to by lutpp (Faults, Defects, Upsets,
1047 	 * Errors, Ereports, Serds, FRUs, or ASRUs) keeps the first decl.
1048 	 * if this isn't the first declr, we merge the
1049 	 * nvpairs into the first decl so we have a
1050 	 * merged table to look up properties from.
1051 	 * if this is the first time we've seen this fault,
1052 	 * we add it to the global lut and start lutp
1053 	 * off with any nvpairs from this declaration statement.
1054 	 */
1055 	if (justpath && (decl = tree_name2np_lut_lookup(*lutpp, np)) == NULL) {
1056 		/* this is the first time name is declared */
1057 		stats_counter_bump(countp);
1058 		*lutpp = tree_name2np_lut_add(*lutpp, np, ret);
1059 		ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, t);
1060 	} else if (!justpath &&
1061 	    (decl = tree_event2np_lut_lookup(*lutpp, np)) == NULL) {
1062 		/* this is the first time event is declared */
1063 		stats_counter_bump(countp);
1064 		*lutpp = tree_event2np_lut_add(*lutpp, np, ret);
1065 		ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, t);
1066 	} else {
1067 		/* was declared before, just add new nvpairs to its lutp */
1068 		decl->u.stmt.lutp = nvpair2lut(nvpairs, decl->u.stmt.lutp, t);
1069 	}
1070 
1071 	return (ret);
1072 }
1073 
1074 /*ARGSUSED*/
1075 static void
1076 update_serd_refstmt(void *lhs, void *rhs, void *arg)
1077 {
1078 	struct node *serd;
1079 
1080 	ASSERT(rhs != NULL);
1081 
1082 	serd = tree_s2np_lut_lookup(((struct node *)rhs)->u.stmt.lutp,
1083 	    L_engine);
1084 	if (serd == NULL)
1085 		return;
1086 
1087 	ASSERT(serd->t == T_EVENT);
1088 	if (arg != NULL && tree_eventcmp(serd, (struct node *)arg) != 0)
1089 		return;
1090 
1091 	serd = tree_event2np_lut_lookup(SERDs, serd);
1092 	if (serd != NULL)
1093 		serd->u.stmt.flags |= STMT_REF;
1094 }
1095 
1096 struct node *
1097 tree_decl(enum nodetype t, struct node *np, struct node *nvpairs,
1098     const char *file, int line)
1099 {
1100 	struct node *decl;
1101 	struct node *ret;
1102 
1103 	ASSERT(np != NULL);
1104 
1105 	check_type_iterator(np);
1106 
1107 	switch (t) {
1108 	case T_EVENT:
1109 		/* determine the type of event being declared */
1110 		ASSERT(np->u.event.ename->t == T_NAME);
1111 		switch (np->u.event.ename->u.name.t) {
1112 		case N_FAULT:
1113 			ret = dodecl(T_FAULT, file, line, np, nvpairs,
1114 			    &Faults, Faultcount, 0);
1115 
1116 			/* increment serd statement reference */
1117 			decl = tree_event2np_lut_lookup(Faults, np);
1118 			update_serd_refstmt(NULL, decl, NULL);
1119 			break;
1120 
1121 		case N_UPSET:
1122 			ret = dodecl(T_UPSET, file, line, np, nvpairs,
1123 			    &Upsets, Upsetcount, 0);
1124 
1125 			/* increment serd statement reference */
1126 			decl = tree_event2np_lut_lookup(Upsets, np);
1127 			update_serd_refstmt(NULL, decl, NULL);
1128 			break;
1129 
1130 		case N_DEFECT:
1131 			ret = dodecl(T_DEFECT, file, line, np, nvpairs,
1132 			    &Defects, Defectcount, 0);
1133 
1134 			/* increment serd statement reference */
1135 			decl = tree_event2np_lut_lookup(Defects, np);
1136 			update_serd_refstmt(NULL, decl, NULL);
1137 			break;
1138 
1139 		case N_ERROR:
1140 			ret = dodecl(T_ERROR, file, line, np, nvpairs,
1141 			    &Errors, Errorcount, 0);
1142 			break;
1143 
1144 		case N_EREPORT:
1145 			ret = dodecl(T_EREPORT, file, line, np, nvpairs,
1146 			    &Ereports, Ereportcount, 0);
1147 			/*
1148 			 * Keep a lut of just the enames, so that the DE
1149 			 * can subscribe to a uniqified list of event
1150 			 * classes.
1151 			 */
1152 			Ereportenames =
1153 			    tree_name2np_lut_add(Ereportenames,
1154 			    np->u.event.ename, np);
1155 
1156 			/*
1157 			 * Keep a lut of the enames (event classes) to
1158 			 * silently discard if we can't find a matching
1159 			 * configuration node when an ereport of of a given
1160 			 * class is received.  Such events are declaired
1161 			 * with 'discard_if_config_unknown=1'.
1162 			 */
1163 			if (tree_s2np_lut_lookup(ret->u.stmt.lutp,
1164 			    L_discard_if_config_unknown)) {
1165 				Ereportenames_discard = lut_add(
1166 				    Ereportenames_discard,
1167 				    (void *)np->u.event.ename->u.name.s,
1168 				    (void *)np->u.event.ename->u.name.s, NULL);
1169 			}
1170 			break;
1171 
1172 		default:
1173 			outfl(O_ERR, file, line,
1174 			    "tree_decl: internal error, event name type %s",
1175 			    ptree_nametype2str(np->u.event.ename->u.name.t));
1176 		}
1177 		break;
1178 
1179 	case T_ENGINE:
1180 		/* determine the type of engine being declared */
1181 		ASSERT(np->u.event.ename->t == T_NAME);
1182 		switch (np->u.event.ename->u.name.t) {
1183 		case N_SERD:
1184 			ret = dodecl(T_SERD, file, line, np, nvpairs,
1185 			    &SERDs, SERDcount, 0);
1186 			lut_walk(Upsets, update_serd_refstmt, np);
1187 			break;
1188 
1189 		case N_STAT:
1190 			ret = dodecl(T_STAT, file, line, np, nvpairs,
1191 			    &STATs, STATcount, 0);
1192 			break;
1193 
1194 		default:
1195 			outfl(O_ERR, file, line,
1196 			    "tree_decl: internal error, engine name type %s",
1197 			    ptree_nametype2str(np->u.event.ename->u.name.t));
1198 		}
1199 		break;
1200 	case T_ASRU:
1201 		ret = dodecl(T_ASRU, file, line, np, nvpairs,
1202 		    &ASRUs, ASRUcount, 1);
1203 		break;
1204 
1205 	case T_FRU:
1206 		ret = dodecl(T_FRU, file, line, np, nvpairs,
1207 		    &FRUs, FRUcount, 1);
1208 		break;
1209 
1210 	case T_CONFIG:
1211 		/*
1212 		 * config statements are different from above: they
1213 		 * are not merged at all (until the configuration cache
1214 		 * code does its own style of merging.  and the properties
1215 		 * are a free-for-all -- we don't check for allowed or
1216 		 * required config properties.
1217 		 */
1218 		ret = newnode(T_CONFIG, file, line);
1219 		ret->u.stmt.np = np;
1220 		ret->u.stmt.nvpairs = nvpairs;
1221 		ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, T_CONFIG);
1222 
1223 		if (lut_lookup(Configs, np, (lut_cmp)tree_namecmp) == NULL)
1224 			stats_counter_bump(Configcount);
1225 
1226 		Configs = lut_add(Configs, (void *)np, (void *)ret, NULL);
1227 		break;
1228 
1229 	default:
1230 		out(O_DIE, "tree_decl: internal error, type %s",
1231 		    ptree_nodetype2str(t));
1232 	}
1233 
1234 	return (ret);
1235 }
1236 
1237 /* keep backpointers in arrows to the prop they belong to (used for scoping) */
1238 static void
1239 set_arrow_prop(struct node *prop, struct node *np)
1240 {
1241 	if (np == NULL)
1242 		return;
1243 
1244 	if (np->t == T_ARROW) {
1245 		np->u.arrow.prop = prop;
1246 		set_arrow_prop(prop, np->u.arrow.lhs);
1247 		/*
1248 		 * no need to recurse right or handle T_LIST since
1249 		 * T_ARROWs always cascade left and are at the top
1250 		 * of the parse tree.  (you can see this in the rule
1251 		 * for "propbody" in escparse.y.)
1252 		 */
1253 	}
1254 }
1255 
1256 struct node *
1257 tree_stmt(enum nodetype t, struct node *np, const char *file, int line)
1258 {
1259 	struct node *ret = newnode(t, file, line);
1260 	struct node *pp;
1261 	int inlist = 0;
1262 
1263 	ret->u.stmt.np = np;
1264 
1265 	switch (t) {
1266 	case T_PROP:
1267 		check_proplists(t, np);
1268 		check_propnames(t, np, 0, 0);
1269 		check_propscope(np);
1270 		set_arrow_prop(ret, np);
1271 
1272 		for (pp = Props; pp; pp = pp->u.stmt.next) {
1273 			if (tree_treecmp(pp, ret, T_NAME,
1274 			    (lut_cmp)tree_namecmp) == 0) {
1275 				inlist = 1;
1276 				break;
1277 			}
1278 		}
1279 		if (inlist == 0)
1280 			stats_counter_bump(Propcount);
1281 
1282 		/* "Props" is a linked list of all prop statements */
1283 		if (Lastprops)
1284 			Lastprops->u.stmt.next = ret;
1285 		else
1286 			Props = ret;
1287 		Lastprops = ret;
1288 		break;
1289 
1290 	case T_MASK:
1291 		check_proplists(t, np);
1292 		check_propnames(t, np, 0, 0);
1293 		check_propscope(np);
1294 		set_arrow_prop(ret, np);
1295 
1296 		for (pp = Masks; pp; pp = pp->u.stmt.next) {
1297 			if (tree_treecmp(pp, ret, T_NAME,
1298 			    (lut_cmp)tree_namecmp) == 0) {
1299 				inlist = 1;
1300 				break;
1301 			}
1302 		}
1303 		if (inlist == 0)
1304 			stats_counter_bump(Maskcount);
1305 
1306 		/* "Masks" is a linked list of all mask statements */
1307 		if (Lastmasks)
1308 			Lastmasks->u.stmt.next = ret;
1309 		else
1310 			Masks = ret;
1311 		Lastmasks = ret;
1312 		stats_counter_bump(Maskcount);
1313 		break;
1314 
1315 	default:
1316 		outfl(O_DIE, np->file, np->line,
1317 		    "tree_stmt: internal error (t %d)", t);
1318 	}
1319 
1320 	return (ret);
1321 }
1322 
1323 void
1324 tree_report()
1325 {
1326 	/*
1327 	 * The only declarations with required properties
1328 	 * currently are faults and serds. Make sure the
1329 	 * the declarations have the required properties.
1330 	 */
1331 	lut_walk(Faults, (lut_cb)check_required_props, (void *)T_FAULT);
1332 	lut_walk(Upsets, (lut_cb)check_required_props, (void *)T_UPSET);
1333 	lut_walk(Errors, (lut_cb)check_required_props, (void *)T_ERROR);
1334 	lut_walk(Ereports, (lut_cb)check_required_props, (void *)T_EREPORT);
1335 	lut_walk(SERDs, (lut_cb)check_required_props, (void *)T_SERD);
1336 	lut_walk(STATs, (lut_cb)check_required_props, (void *)T_STAT);
1337 
1338 	/*
1339 	 * we do this now rather than while building the parse
1340 	 * tree because it is inconvenient for the user if we
1341 	 * require SERD engines to be declared before used in
1342 	 * an upset "engine" property.
1343 	 */
1344 	lut_walk(Faults, (lut_cb)check_refcount, (void *)T_FAULT);
1345 	lut_walk(Faults, (lut_cb)check_upset_engine, (void *)T_FAULT);
1346 	lut_walk(Defects, (lut_cb)check_upset_engine, (void *)T_DEFECT);
1347 	lut_walk(Upsets, (lut_cb)check_upset_engine, (void *)T_UPSET);
1348 	lut_walk(Upsets, (lut_cb)check_refcount, (void *)T_UPSET);
1349 	lut_walk(Errors, (lut_cb)check_refcount, (void *)T_ERROR);
1350 	lut_walk(Ereports, (lut_cb)check_refcount, (void *)T_EREPORT);
1351 	lut_walk(SERDs, (lut_cb)check_refcount, (void *)T_SERD);
1352 
1353 	/* check for cycles */
1354 	lut_walk(Errors, (lut_cb)check_cycle, (void *)0);
1355 }
1356 
1357 /* compare two T_NAMES by only looking at components, not iterators */
1358 int
1359 tree_namecmp(struct node *np1, struct node *np2)
1360 {
1361 	ASSERT(np1 != NULL);
1362 	ASSERT(np2 != NULL);
1363 	ASSERTinfo(np1->t == T_NAME, ptree_nodetype2str(np1->t));
1364 	ASSERTinfo(np2->t == T_NAME, ptree_nodetype2str(np1->t));
1365 
1366 	while (np1 && np2 && np1->u.name.s == np2->u.name.s) {
1367 		np1 = np1->u.name.next;
1368 		np2 = np2->u.name.next;
1369 	}
1370 	if (np1 == NULL)
1371 		if (np2 == NULL)
1372 			return (0);
1373 		else
1374 			return (-1);
1375 	else if (np2 == NULL)
1376 		return (1);
1377 	else
1378 		return (np2->u.name.s - np1->u.name.s);
1379 }
1380 
1381 int
1382 tree_eventcmp(struct node *np1, struct node *np2)
1383 {
1384 	int ret;
1385 
1386 	ASSERT(np1 != NULL);
1387 	ASSERT(np2 != NULL);
1388 	ASSERTinfo(np1->t == T_EVENT, ptree_nodetype2str(np1->t));
1389 	ASSERTinfo(np2->t == T_EVENT, ptree_nodetype2str(np2->t));
1390 
1391 	if ((ret = tree_namecmp(np1->u.event.ename,
1392 	    np2->u.event.ename)) == 0) {
1393 			if (np1->u.event.epname == NULL &&
1394 			    np2->u.event.epname == NULL)
1395 				return (0);
1396 			else if (np1->u.event.epname == NULL)
1397 				return (-1);
1398 			else if (np2->u.event.epname == NULL)
1399 				return (1);
1400 			else
1401 				return tree_namecmp(np1->u.event.epname,
1402 				    np2->u.event.epname);
1403 	} else
1404 	return (ret);
1405 }
1406