1    	/***********************************************************************
2    	 * show.c - display expressions and their values
3    	 ***********************************************************************
4    	 *
5    	 * Copyright (c) 1995-2003 Silicon Graphics, Inc.  All Rights Reserved.
6    	 * 
7    	 * This program is free software; you can redistribute it and/or modify it
8    	 * under the terms of the GNU General Public License as published by the
9    	 * Free Software Foundation; either version 2 of the License, or (at your
10   	 * option) any later version.
11   	 * 
12   	 * This program is distributed in the hope that it will be useful, but
13   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15   	 * for more details.
16   	 * 
17   	 * You should have received a copy of the GNU General Public License along
18   	 * with this program; if not, write to the Free Software Foundation, Inc.,
19   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20   	 */
21   	
22   	#include <math.h>
23   	#include <ctype.h>
24   	#include "show.h"
25   	#include "impl.h"
26   	#include "dstruct.h"
27   	#include "lexicon.h"
28   	#include "pragmatics.h"
29   	#if defined(HAVE_IEEEFP_H)
30   	#include <ieeefp.h>
31   	#endif
32   	
33   	/***********************************************************************
34   	 * local declarations
35   	 ***********************************************************************/
36   	
37   	static struct {
38   	    int		op;
39   	    char	*str;
40   	} opstr[] = {
41   		{ RULE,	"->" },
42   		{ CND_FETCH,	"<fetch node>" },
43   		{ CND_DELAY,	"<delay node>" },
44   		{ CND_RATE,	"rate" },
45   		{ CND_NEG,	"-" },
46   		{ CND_ADD,	"+" },
47   		{ CND_SUB,	"-" },
48   		{ CND_MUL,	"*" },
49   		{ CND_DIV,	"/" },
50   	/* aggregation */
51   		{ CND_SUM_HOST,	"sum_host" },
52   		{ CND_SUM_INST,	"sum_inst" },
53   		{ CND_SUM_TIME,	"sum_sample" },
54   		{ CND_AVG_HOST,	"avg_host" },
55   		{ CND_AVG_INST,	"avg_inst" },
56   		{ CND_AVG_TIME,	"avg_sample" },
57   		{ CND_MAX_HOST,	"max_host" },
58   		{ CND_MAX_INST,	"max_inst" },
59   		{ CND_MAX_TIME,	"max_sample" },
60   		{ CND_MIN_HOST,	"min_host" },
61   		{ CND_MIN_INST,	"min_inst" },
62   		{ CND_MIN_TIME,	"min_sample" },
63   	/* relational */
64   		{ CND_EQ,	"==" },
65   		{ CND_NEQ,	"!=" },
66   		{ CND_LT,	"<" },
67   		{ CND_LTE,	"<=" },
68   		{ CND_GT,	">" },
69   		{ CND_GTE,	">=" },
70   	/* boolean */
71   		{ CND_NOT,	"!" },
72   		{ CND_RISE,	"rising" },
73   		{ CND_FALL,	"falling" },
74   		{ CND_AND,	"&&" },
75   		{ CND_OR,	"||" },
76   		{ CND_MATCH,	"match_inst" },
77   		{ CND_NOMATCH,	"nomatch_inst" },
78   	/* quantification */
79   		{ CND_ALL_HOST,	"all_host" },
80   		{ CND_ALL_INST,	"all_inst" },
81   		{ CND_ALL_TIME,	"all_sample" },
82   		{ CND_SOME_HOST,	"some_host" },
83   		{ CND_SOME_INST,	"some_inst" },
84   		{ CND_SOME_TIME,	"some_sample" },
85   		{ CND_PCNT_HOST,	"pcnt_host" },
86   		{ CND_PCNT_INST,	"pcnt_inst" },
87   		{ CND_PCNT_TIME,	"pcnt_sample" },
88   		{ CND_COUNT_HOST,	"count_host" },
89   		{ CND_COUNT_INST,	"count_inst" },
90   		{ CND_COUNT_TIME,	"count_sample" },
91   		{ ACT_SEQ,	"&" },
92   		{ ACT_ALT,	"|" },
93   		{ ACT_SHELL,	"shell" },
94   		{ ACT_ALARM,	"alarm" },
95   		{ ACT_SYSLOG,	"syslog" },
96   		{ ACT_PRINT,	"print" },
97   		{ ACT_STOMP,	"stomp" },
98   		{ ACT_ARG,	"<action arg node>" },
99   		{ NOP,		"<nop node>" },
100  		{ OP_VAR,	"<op_var node>" },
101  	};
102  	
103  	static int numopstr = sizeof(opstr) / sizeof(opstr[0]);
104  	
105  	/***********************************************************************
106  	 * local utility functions
107  	 ***********************************************************************/
108  	
109  	/* Concatenate string1 to existing string2 whose original length is given. */
110  	static size_t	 /* new length of *string2 */
111  	concat(char *string1, size_t pos, char **string2)
112  	{
113  	    size_t	slen;
114  	    size_t	tlen;
115  	    char	*cat;
116  	    char	*dog;
117  	
118  	    if ((slen = strlen(string1)) == 0)
119  		return pos;
120  	    tlen = pos + slen;
121  	    cat = (char *) ralloc(*string2, tlen + 1);
122  	    dog = cat + pos;
123  	    strcpy(dog, string1);
124  	    dog += slen;
125  	    *dog = '\0';
126  	
127  	    *string2 = cat;
128  	    return tlen;
129  	}
130  	
131  	
132  	/***********************************************************************
133  	 * host and instance names
134  	 ***********************************************************************/
135  	
136  	/* Return host and instance name for nth value in expression *x */
137  	static int
138  	lookupHostInst(Expr *x, int nth, char **host, char **inst)
139  	{
140  	    Metric	*m;
141  	    int		mi;
142  	    int		sts = 0;
143  	#if PCP_DEBUG
144  	    static Expr	*lastx = NULL;
145  	    int		dbg_dump = 0;
146  	#endif
147  	
148  	#if PCP_DEBUG
149  	    if (pmDebug & DBG_TRACE_APPL2) {
150  		if (x != lastx) {
151  		    fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...)\n", x, nth);
152  		    lastx = x;
153  		    dbg_dump = 1;
154  		}
155  	    }
156  	#endif
157  	
158  	    /* check for no host and instance available e.g. constant expression */
159  	    if ((x->e_idom <= 0 && x->hdom <= 0) || ! x->metrics) {
160  		*host = NULL;
161  		*inst = NULL;
162  	#if PCP_DEBUG
163  		if (pmDebug & DBG_TRACE_APPL2) {
164  		    fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> %%h and %%i undefined\n", x, nth);
165  		}
166  	#endif
167  		return sts;
168  	    }
169  	
170  	    /* find Metric containing the nth instance */
171  	    mi = 0;
172  	    for (;;) {
173  		m = &x->metrics[mi];
174  	#if PCP_DEBUG
175  		if ((pmDebug & DBG_TRACE_APPL2) && dbg_dump) {
176  		    fprintf(stderr, "lookupHostInst: metrics[%d]\n", mi);
177  		    dumpMetric(m);
178  		}
179  	#endif
180  		if (nth < m->m_idom)
181  		    break;
182  		if (m->m_idom > 0)
183  		    nth -= m->m_idom;
184  		mi++;
185  	    }
186  	
187  	    /* host and instance names */
188  	    *host = symName(m->hname);
189  	    sts++;
190  	    if (x->e_idom > 0 && m->inames) {
191  		*inst = m->inames[nth];
192  		sts++;
193  	    }
194  	    else
195  		*inst = NULL;
196  	
197  	#if PCP_DEBUG
198  	    if (pmDebug & DBG_TRACE_APPL2) {
199  		fprintf(stderr, "lookupHostInst(x=" PRINTF_P_PFX "%p, nth=%d, ...) -> sts=%d %%h=%s %%i=%s\n",
200  		    x, nth, sts, *host, *inst == NULL ? "undefined" : *inst);
201  	    }
202  	#endif
203  	
204  	    return sts;
205  	}
206  	
207  	
208  	/***********************************************************************
209  	 * expression value
210  	 ***********************************************************************/
211  	
212  	#define	TRUTH_SPACE 7
213  	
214  	static size_t
215  	showTruth(Expr *x, int nth, size_t length, char **string)
216  	{
217  	    int		smpl;
218  	    size_t	tlen;
219  	    char	*cat;
220  	    char	*dog;
221  	    int		val;
222  	
223  	    tlen = length + (x->nsmpls * TRUTH_SPACE);
224  	    cat = (char *)ralloc(*string, tlen + 1);
225  	    dog = cat + length;
226  	    for (smpl = 0; smpl < x->nsmpls; smpl++) {
227  		if (smpl > 0) {
228  		    strcpy(dog, " ");
229  		    dog += 1;
230  		}
231  	
232  		if (x->valid == 0) {
233  		    strncpy(dog, "?", TRUTH_SPACE);
234  		    dog++;
235  		    continue;
236  		}
237  	
238  		val = *((char *)x->smpls[smpl].ptr + nth);
239  		if (val == FALSE) {
240  		    strncpy(dog, "false", TRUTH_SPACE);
241  		    dog += 5;
242  		}
243  		else if (val == TRUE) {
244  		    strncpy(dog, "true", TRUTH_SPACE);
245  		    dog += 4;
246  		}
247  		else if (val == DUNNO) {
248  		    strncpy(dog, "?", TRUTH_SPACE);
249  		    dog++;
250  		}
251  		else {
252  		    sprintf(dog, "0x%02x?", val & 0xff);
253  		    dog += 5;
254  		}
255  	    }
256  	    *dog = '\0';
257  	
258  	    *string = cat;
259  	    return dog - cat;
260  	}
261  	
262  	
263  	static size_t
264  	showString(Expr *x, size_t length, char **string)
265  	{
266  	    size_t	tlen;
267  	    char	*cat;
268  	    char	*dog;
269  	
270  	    tlen = length + (x->nvals - 1) + 2;
271  	    cat = (char *)ralloc(*string, tlen + 1);
272  	    dog = cat + length;
273  	    *dog++ = '"';
274  	    strcpy(dog, (char *)x->smpls[0].ptr);
275  	    dog += x->nvals - 1;
276  	    *dog++ = '"';
277  	    *dog = '\0';
278  	
279  	    *string = cat;
280  	    return tlen;
281  	}
282  	
283  	#define	DBL_SPACE 24
284  	
285  	static size_t
286  	showNum(Expr *x, int nth, size_t length, char **string)
287  	{
288  	    int		smpl;
289  	    size_t	tlen;
290  	    char	*cat;
291  	    char	*dog;
292  	    char	*fmt;
293  	    double	v;
294  	    double	abs_v;
295  	    int		sts;
296  	
297  	    tlen = length + (x->nsmpls * DBL_SPACE);
298  	    cat = (char *)ralloc(*string, tlen + 1);
299  	    dog = cat + length;
300  	    for (smpl = 0; smpl < x->nsmpls; smpl++) {
301  		if (smpl > 0) {
302  		    strcpy(dog, " ");
303  		    dog += 1;
304  		}
305  		if (x->valid <= smpl || isnand(*((double *)x->smpls[smpl].ptr + nth))) {
306  		    strcpy(dog, "?");
307  		    dog += 1;
308  		}
309  		else {
310  		    v = *((double *)x->smpls[smpl].ptr+nth);
311  		    if (v == (int)v)
312  			sts = sprintf(dog, "%d", (int)v);
313  		    else {
314  			abs_v = v < 0 ? -v : v;
315  			if (abs_v < 0.5)
316  			    fmt = "%g";
317  			else if (abs_v < 5)
318  			    fmt = "%.2f";
319  			else if (abs_v < 50)
320  			    fmt = "%.1f";
321  			else
322  			    fmt = "%.0f";
323  			sts = sprintf(dog, fmt, v);
324  		    }
325  		    if (sts > 0)
326  			dog += sts;
327  		    else {
328  			strcpy(dog, "!");
329  			dog += 1;
330  		    }
331  		}
332  	    }
333  	    *dog = '\0';
334  	
335  	    *string = cat;
336  	    return dog - cat;
337  	}
338  	
339  	static char *
340  	showConst(Expr *x)
341  	{
342  	    char	*string = NULL;
343  	    size_t	length = 0;
344  	    int		i;
345  	    int		first = 1;
346  	
347  	    /* construct string representation */
348  	    if (x->nvals > 0) {
349  		for (i = 0; i < x->tspan; i++) {
350  		    if (first) 
351  			first = 0;
352  		    else
353  			length = concat(" ", length, &string);
354  		    if (x->sem == SEM_TRUTH)
355  			length = showTruth(x, i, length, &string);
356  		    else if (x->sem == SEM_CHAR)
357  			length = showString(x, length, &string);
358  		    else
359  			length = showNum(x, i, length, &string);
360  		}
361  	    }
362  	    return string;
363  	}
364  	
365  	
366  	
367  	/***********************************************************************
368  	 * expression syntax
369  	 ***********************************************************************/
370  	
371  	static void
372  	showSyn(FILE *f, Expr *x)
373  	{
374  	    char	*s;
375  	    char	*c;
376  	    Metric	*m;
377  	    char	**n;
378  	    int		i;
379  	
380  	    /* constant */
381  	    if (x->op >= NOP) {
382  		s = showConst(x);
383  		if (s) {
384  		    c = s;
385  		    while(isspace((int)*c))
386  			c++;
387  		    fputs(c, f);
388  		    free(s);
389  		}
390  	    }
391  	
392  	    /* fetch expression (perhaps with delay) */
393  	    else if ((x->op == CND_FETCH) || (x->op == CND_DELAY)) {
394  		m = x->metrics;
395  		fprintf(f, "%s", symName(m->mname));
396  		for (i = 0; i < x->hdom; i++) {
397  		    fprintf(f, " :%s", symName(m->hname));
398  		    m++;
399  		}
400  		m = x->metrics;
401  		if (m->inames) {
402  		    n = m->inames;
403  		    for (i = 0; i < m->m_idom; i++) {
404  			fprintf(f, " #%s", *n);
405  			n++;
406  		    }
407  		}
408  		if (x->op == CND_FETCH) {
409  		    if (x->tdom == 1) fprintf(f, " @0");
410  		    else fprintf(f, " @0..%d", x->tdom - 1);
411  		}
412  		else {
413  		    if (x->tdom == x->arg1->tdom - 1) fprintf(f, " @%d", x->tdom);
414  		    else fprintf(f, " @%d..%d", x->tdom, x->tdom + x->arg1->tdom - 1);
415  		}
416  	    }
417  	
418  	    /* binary operator */
Event var_compare_op: Comparing "x->arg1" to null implies that "x->arg1" might be null.
Also see events: [var_deref_op]
At conditional (1): "x->arg1": Taking false branch.
419  	    else if (x->arg1 && x->arg2) {
420  		if (x->op >= ACT_SHELL && x->op < ACT_ARG) {
421  		    fputs(opStrings(x->op), f);
422  		    fputc(' ', f);
423  		    showSyn(f, x->arg2);
424  		    fputc(' ', f);
425  		    showSyn(f, x->arg1);
426  		}
427  		else if (x->op >= CND_PCNT_HOST && x->op <= CND_PCNT_TIME) {
428  		    showSyn(f, x->arg2);
429  		    fputc(' ', f);
430  		    fputs(opStrings(x->op), f);
431  		    fputc(' ', f);
432  		    if (x->arg1->op >= NOP || x->arg1->op <= CND_DELAY)
433  			showSyn(f, x->arg1);
434  		    else {
435  			fputc('(', f);
436  			showSyn(f, x->arg1);
437  			fputc(')', f);
438  		    }
439  		}
440  		else {
441  		    if (x->arg1->op >= NOP || x->arg1->op <= CND_DELAY)
442  			showSyn(f, x->arg1);
443  		    else {
444  			fputc('(', f);
445  			showSyn(f, x->arg1);
446  			fputc(')', f);
447  		    }
448  		    fputc(' ', f);
449  		    fputs(opStrings(x->op), f);
450  		    fputc(' ', f);
451  		    if (x->arg2->op >= NOP || x->arg2->op <= CND_DELAY)
452  			showSyn(f, x->arg2);
453  		    else {
454  			fputc('(', f);
455  			showSyn(f, x->arg2);
456  			fputc(')', f);
457  		    }
458  		}
459  	    }
460  	
461  	    /* unary operator */
462  	    else {
463  		fputs(opStrings(x->op), f);
464  		fputc(' ', f);
Event var_deref_op: Dereferencing null variable "x->arg1".
Also see events: [var_compare_op]
465  		if (x->arg1->op >= ACT_ARG || x->arg1->op <= CND_DELAY)
466  		    showSyn(f, x->arg1);
467  		else {
468  		    fputc('(', f);
469  		    showSyn(f, x->arg1);
470  		    fputc(')', f);
471  		}
472  	    }
473  	}
474  	
475  	/*
476  	 * recursive descent to find a conjunct from the root of
477  	 * the expression that has associated metrics (not constants)
478  	 */
479  	static Expr *
480  	findMetrics(Expr *y)
481  	{
482  	    Expr	*z;
483  	
484  	    if (y == NULL) return NULL;
485  	    if (y->metrics) return y;		/* success */
486  	
487  	    /* give up if not a conjunct */
488  	    if (y->op != CND_AND) return NULL;
489  	
490  	    /* recurse left and then right */
491  	    z = findMetrics(y->arg1);
492  	    if (z != NULL) return z;
493  	    return findMetrics(y->arg2);
494  	}
495  	
496  	/***********************************************************************
497  	 * satisfying bindings and values
498  	 ***********************************************************************/
499  	
500  	/* Find sub-expression that reveals host and instance bindings
501  	   that satisfy the given expression *x. */
502  	static Expr *
503  	findBindings(Expr *x)
504  	{
505  	#if PCP_DEBUG
506  	    if (pmDebug & DBG_TRACE_APPL2) {
507  		fprintf(stderr, "call findBindings(x=" PRINTF_P_PFX "%p)\n", x);
508  	    }
509  	#endif
510  	
511  	    if (x->metrics == NULL) {
512  		/*
513  		 * this Expr node has no metrics (involves only constants)
514  		 * ... try and find a conjunct at the top level that has
515  		 * associated metrics
516  		 */
517  		Expr	*y = findMetrics(x->arg1);
518  		if (y != NULL) x = y;
519  	    }
520  	    while (x->metrics && (x->e_idom <= 0 || x->hdom <= 0)) {
521  		if (x->op == CND_SUM_HOST || x->op == CND_SUM_INST || x->op == CND_SUM_TIME ||
522  		    x->op == CND_AVG_HOST || x->op == CND_AVG_INST || x->op == CND_AVG_TIME ||
523  		    x->op == CND_MAX_HOST || x->op == CND_MAX_INST || x->op == CND_MAX_TIME ||
524  		    x->op == CND_MIN_HOST || x->op == CND_MIN_INST || x->op == CND_MIN_TIME)
525  		    /*
526  		     * don't descend below an aggregation operator with a singular
527  		     * value, ... value you seek is right here
528  		     */
529  		    break;
530  		if (x->arg1 && x->metrics == x->arg1->metrics)
531  		    x = x->arg1;
532  		else if (x->arg2)
533  		    x = x->arg2;
534  		else
535  		    break;
536  	#if PCP_DEBUG
537  		if (pmDebug & DBG_TRACE_APPL2) {
538  		    fprintf(stderr, "findBindings: try x=" PRINTF_P_PFX "%p\n", x);
539  		}
540  	#endif
541  	    }
542  	    return x;
543  	}
544  	
545  	
546  	/* Find sub-expression that reveals the values that satisfy the
547  	   given expression *x. */
548  	static Expr *
549  	findValues(Expr *x)
550  	{
551  	#if PCP_DEBUG
552  	    if (pmDebug & DBG_TRACE_APPL2) {
553  		fprintf(stderr, "call findValues(x=" PRINTF_P_PFX "%p)\n", x);
554  	    }
555  	#endif
556  	    while (x->sem == SEM_TRUTH && x->metrics) {
557  		if (x->metrics == x->arg1->metrics)
558  		    x = x->arg1;
559  		else
560  		    x = x->arg2;
561  	#if PCP_DEBUG
562  		if (pmDebug & DBG_TRACE_APPL2) {
563  		    fprintf(stderr, "findValues: try x=" PRINTF_P_PFX "%p\n", x);
564  		}
565  	#endif
566  	    }
567  	    return x;
568  	}
569  	
570  	
571  	/***********************************************************************
572  	 * format string
573  	 ***********************************************************************/
574  	
575  	/* Locate next %h, %i or %v in format string. */
576  	static int	/* 0 -> not found, 1 -> host, 2 -> inst, 3 -> value */
577  	findFormat(char *format, char **pos)
578  	{
579  	    for (;;) {
580  		if (*format == '\0')
581  		    return 0;
582  		if (*format == '%') {
583  		    switch (*(format + 1)) {
584  		    case 'h':
585  			*pos = format;
586  			return 1;
587  		    case 'i':
588  			*pos = format;
589  			return 2;
590  		    case 'v':
591  			*pos = format;
592  			return 3;
593  		    }
594  		}
595  		format++;
596  	    }
597  	
598  	}
599  	
600  	
601  	/***********************************************************************
602  	 * exported functions
603  	 ***********************************************************************/
604  	
605  	void
606  	showSyntax(FILE *f, Symbol s)
607  	{
608  	    char *name = symName(s);
609  	    Expr *x = symValue(s);
610  	
611  	    fprintf(f, "%s =\n", name);
612  	    showSyn(f, x);
613  	    fprintf(f, ";\n\n");
614  	}
615  	
616  	
617  	void
618  	showSubsyntax(FILE *f, Symbol s)
619  	{
620  	    char *name = symName(s);
621  	    Expr *x = symValue(s);
622  	    Expr *x1;
623  	    Expr *x2;
624  	
625  	    fprintf(f, "%s (subexpression for %s, %s and %s bindings) =\n",
626  		   name, "%h", "%i", "%v");
627  	    x1 = findBindings(x);
628  	    x2 = findValues(x1);
629  	    showSyn(f, x2);
630  	    fprintf(f, "\n\n");
631  	}
632  	
633  	
634  	/* Print value of expression */
635  	void
636  	showValue(FILE *f, Expr *x)
637  	{
638  	    char    *string = NULL;
639  	
640  	    string = showConst(x);
641  	    if (string) {
642  		fputs(string, f);
643  		free(string);
644  	    }
645  	    else
646  		fputs("?", f);
647  	}
648  	
649  	
650  	/* Print value of expression together with any host and instance bindings */
651  	void
652  	showAnnotatedValue(FILE *f, Expr *x)
653  	{
654  	    char    *string = NULL;
655  	    size_t  length = 0;
656  	    char    *host;
657  	    char    *inst;
658  	    int	    i;
659  	
660  	    /* no annotation possible */
661  	    if ((x->e_idom <= 0 && x->hdom <= 0) ||
662  		 x->sem == SEM_CHAR ||
663  		 x->metrics == NULL ||
664  		 x->valid == 0) {
665  		showValue(f, x);
666  		return;
667  	    }
668  	
669  	    /* construct string representation */
670  	    for (i = 0; i < x->tspan; i++) {
671  		length = concat("\n    ", length, &string);
672  		lookupHostInst(x, i, &host, &inst);
673  		length = concat(host, length,  &string);
674  		if (inst) {
675  		    length = concat(": [", length,  &string);
676  		    length = concat(inst, length,  &string);
677  		    length = concat("] ", length,  &string);
678  		}
679  		else
680  		    length = concat(": ", length,  &string);
681  		if (x->sem == SEM_TRUTH)
682  		    length = showTruth(x, i, length, &string);
683  		else	/* numeric value */
684  		    length = showNum(x, i, length, &string);
685  	    }
686  	
687  	    /* print string representation */
688  	    if (string) {
689  		fputs(string, f);
690  		free(string);
691  	    }
692  	}
693  	
694  	
695  	void
696  	showTime(FILE *f, RealTime rt)
697  	{
698  	    time_t t = (time_t)rt;
699  	    char   bfr[26];
700  	
701  	    pmCtime(&t, bfr);
702  	    bfr[24] = '\0';
703  	    fprintf(f, "%s", bfr);
704  	}
705  	
706  	
707  	void
708  	showFullTime(FILE *f, RealTime rt)
709  	{
710  	    time_t t = (time_t)rt;
711  	    char   bfr[26];
712  	
713  	    pmCtime(&t, bfr);
714  	    bfr[24] = '\0';
715  	    fprintf(f, "%s.%06d", bfr, (int)((rt-t)*1000000));
716  	}
717  	
718  	
719  	void
720  	showSatisfyingValue(FILE *f, Expr *x)
721  	{
722  	    char    *string = NULL;
723  	    size_t  length = 0;
724  	    char    *host;
725  	    char    *inst;
726  	    int	    i;
727  	    Expr    *x1;
728  	    Expr    *x2;
729  	
730  	    /* no satisfying values possible */
731  	    if (x->metrics == NULL || x->valid == 0) {
732  		showValue(f, x);
733  		return;
734  	    }
735  	
736  	    if (x->sem != SEM_TRUTH) {
737  		showAnnotatedValue(f, x);
738  		return;
739  	    }
740  	
741  	    x1 = findBindings(x);
742  	    x2 = findValues(x1);
743  	
744  	    /* construct string representation */
745  	    for (i = 0; i < x1->tspan; i++) {
746  		if (!x1->valid) continue;
747  		if ((x1->sem == SEM_TRUTH && *((char *)x1->smpls[0].ptr + i) == TRUE)
748  		    || (x1->sem != SEM_TRUTH && x1->sem != SEM_UNKNOWN)) {
749  		    length = concat("\n    ", length, &string);
750  		    lookupHostInst(x1, i, &host, &inst);
751  		    length = concat(host, length,  &string);
752  		    if (inst) {
753  			length = concat(": [", length,  &string);
754  			length = concat(inst, length,  &string);
755  			length = concat("] ", length,  &string);
756  		    }
757  		    else
758  			length = concat(": ", length,  &string);
759  		    if (x2->sem == SEM_TRUTH)
760  			length = showTruth(x2, i, length, &string);
761  		    else	/* numeric value */
762  			length = showNum(x2, i, length, &string);
763  		}
764  	    }
765  	
766  	    /* print string representation */
767  	    if (string) {
768  		fputs(string, f);
769  		free(string);
770  	    }
771  	}
772  	
773  	
774  	/* Instantiate format string for each satisfying binding and value
775  	   of the current rule.
776  	   WARNING: This is not thread safe, it dinks with the format string. */
777  	size_t	/* new length of string */
778  	formatSatisfyingValue(char *format, size_t length, char **string)
779  	{
780  	    char    *host;
781  	    char    *inst;
782  	    char    *first;
783  	    char    *prev;
784  	    char    *next;
785  	    int     i;
786  	    Expr    *x1;
787  	    Expr    *x2;
788  	    int	    sts1;
789  	    int	    sts2;
790  	
791  	    /* no formatting present? */
792  	    if ((sts1 = findFormat(format, &first)) == 0)
793  		return concat(format, length, string);
794  	
795  	    x1 = findBindings(curr);
796  	    x2 = findValues(x1);
797  	
798  	    for (i = 0; i < x1->tspan; i++) {
799  		if (!x1->valid) continue;
800  		if ((x1->sem == SEM_TRUTH && *((char *)x1->smpls[0].ptr + i) == TRUE)
801  		    || (x1->sem != SEM_TRUTH && x1->sem != SEM_UNKNOWN)) {
802  		    prev = format;
803  		    next = first;
804  		    sts2 = sts1;
805  		    lookupHostInst(x1, i, &host, &inst);
806  		    do {
807  			*next = '\0';
808  			length = concat(prev, length, string);
809  			*next = '%';
810  	
811  			switch (sts2) {
812  			case 1:
813  			    if (host)
814  				length = concat(host, length, string);
815  			    else
816  				length = concat("??? unknown %h", length, string);
817  			    break;
818  			case 2:
819  			    if (inst)
820  				length = concat(inst, length, string);
821  			    else
822  				length = concat("??? unknown %i", length, string);
823  			    break;
824  			case 3:
825  			    if (x2->sem == SEM_TRUTH)
826  				length = showTruth(x2, i, length, string);
827  			    else	/* numeric value */
828  				length = showNum(x2, i, length, string);
829  			    break;
830  			}
831  			prev = next + 2;
832  		    } while ((sts2 = findFormat(prev, &next)));
833  		    length = concat(prev, length, string);
834  		}
835  	    }
836  	
837  	    return length;
838  	}
839  	
840  	char *
841  	opStrings(int op)
842  	{
843  	    int		i;
844  	    /*
845  	     * sizing of "eh" is a bit tricky ...
846  	     * XXXXXXXXX is the number of digits in the largest possible value
847  	     * for "op", to handle the default "<unknown op %d>" case, but also
848  	     * "eh" must be long enough to accommodate the longest string from
849  	     * opstr[i].str ... currently "<action arg node>"
850  	     */
851  	    static char	*eh = "<unknown op XXXXXXXXX>";
852  	
853  	    for (i = 0; i < numopstr; i++) {
854  		if (opstr[i].op == op)
855  		    break;
856  	    }
857  	
858  	    if (i < numopstr)
859  		return opstr[i].str;
860  	    else {
861  		sprintf(eh, "<unknown op %d>", op);
862  		return eh;
863  	    }
864  	}