ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/caldefn.c
Revision: 2.25
Committed: Sun Jul 29 22:10:45 2012 UTC (11 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.24: +43 -39 lines
Log Message:
Made varset() behavior identical to loading definition

File Contents

# User Rev Content
1 greg 1.1 #ifndef lint
2 greg 2.25 static const char RCSid[] = "$Id: caldefn.c,v 2.24 2010/03/05 17:28:46 greg Exp $";
3 greg 1.1 #endif
4     /*
5     * Store variable definitions.
6     *
7     * 7/1/85 Greg Ward
8     *
9     * 11/11/85 Added conditional compiles (OUTCHAN) for control output.
10     *
11     * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
12     *
13     * 1/15/88 Added clock for caching of variable values.
14     *
15     * 11/16/88 Added VARDEF structure for hard linking.
16 greg 1.2 *
17     * 5/31/90 Added conditional compile (REDEFW) for redefinition warning.
18 greg 1.8 *
19 greg 1.10 * 4/23/91 Added ':' assignment for constant expressions
20 greg 1.13 *
21     * 8/7/91 Added optional context path to append to variable names
22 greg 2.12 *
23     * 5/17/2001 Fixed clock counter wrapping behavior
24     *
25     * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
26     */
27    
28 greg 2.13 #include "copyright.h"
29 greg 1.1
30     #include <stdio.h>
31 greg 2.12 #include <string.h>
32 greg 1.1 #include <ctype.h>
33    
34 schorsch 2.18 #include "rterror.h"
35 schorsch 2.22 #include "rtio.h"
36 schorsch 2.21 #include "rtmisc.h"
37 greg 1.1 #include "calcomp.h"
38    
39 greg 2.4 #ifndef NHASH
40     #define NHASH 521 /* hash size (a prime!) */
41 greg 1.1 #endif
42    
43 greg 2.5 #define hash(s) (shash(s)%NHASH)
44    
45 greg 2.4 #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
46 greg 1.1
47 schorsch 2.18 static double dvalue(char *name, EPNODE *d);
48 greg 1.1
49 greg 2.12 #define MAXCLOCK (1L<<31) /* clock wrap value */
50 greg 1.1
51 greg 2.8 unsigned long eclock = 0; /* value storage timer */
52 greg 1.1
53 greg 2.11 #define MAXCNTX 1023 /* maximum context length */
54 greg 1.13
55 greg 2.11 static char context[MAXCNTX+1]; /* current context path */
56    
57 greg 1.1 static VARDEF *hashtbl[NHASH]; /* definition list */
58     static int htndx; /* index for */
59     static VARDEF *htpos; /* ...dfirst() and */
60     static EPNODE *ochpos; /* ...dnext */
61     static EPNODE *outchan;
62    
63 greg 2.10 EPNODE *curfunc = NULL;
64 greg 2.4 #define dname(ep) ((ep)->v.kid->type == SYM ? \
65 greg 1.1 (ep)->v.kid->v.name : \
66     (ep)->v.kid->v.kid->v.name)
67    
68    
69 greg 2.12 void
70 schorsch 2.16 fcompile( /* get definitions from a file */
71     char *fname
72     )
73 greg 1.1 {
74     FILE *fp;
75    
76     if (fname == NULL)
77     fp = stdin;
78     else if ((fp = fopen(fname, "r")) == NULL) {
79     eputs(fname);
80     eputs(": cannot open\n");
81     quit(1);
82     }
83 greg 1.4 initfile(fp, fname, 0);
84 greg 1.1 while (nextc != EOF)
85 greg 1.13 getstatement();
86 greg 1.1 if (fname != NULL)
87     fclose(fp);
88     }
89    
90    
91 greg 2.12 void
92 schorsch 2.16 scompile( /* get definitions from a string */
93     char *str,
94     char *fn,
95     int ln
96     )
97 greg 1.1 {
98 greg 1.4 initstr(str, fn, ln);
99 greg 1.1 while (nextc != EOF)
100 greg 1.13 getstatement();
101 greg 1.1 }
102    
103    
104     double
105 schorsch 2.16 varvalue( /* return a variable's value */
106     char *vname
107     )
108 greg 1.1 {
109     return(dvalue(vname, dlookup(vname)));
110     }
111    
112    
113     double
114 schorsch 2.16 evariable( /* evaluate a variable */
115     EPNODE *ep
116     )
117 greg 1.1 {
118 greg 2.25 VARDEF *dp = ep->v.ln;
119 greg 1.1
120     return(dvalue(dp->name, dp->def));
121     }
122    
123    
124 greg 2.12 void
125 schorsch 2.16 varset( /* set a variable's value */
126     char *vname,
127     int assign,
128     double val
129     )
130 greg 1.1 {
131 greg 1.13 char *qname;
132 greg 2.25 EPNODE *ep1, *ep2;
133 greg 1.13 /* get qualified name */
134     qname = qualname(vname, 0);
135 greg 1.1 /* check for quick set */
136 greg 2.25 if ((ep1 = dlookup(qname)) != NULL && ep1->v.kid->type == SYM &&
137     (ep1->type == ':') <= (assign == ':')) {
138 greg 1.1 ep2 = ep1->v.kid->sibling;
139     if (ep2->type == NUM) {
140     ep2->v.num = val;
141 greg 1.8 ep1->type = assign;
142 greg 1.1 return;
143     }
144     }
145     /* hand build definition */
146     ep1 = newnode();
147 greg 1.8 ep1->type = assign;
148 greg 1.1 ep2 = newnode();
149     ep2->type = SYM;
150     ep2->v.name = savestr(vname);
151     addekid(ep1, ep2);
152     ep2 = newnode();
153     ep2->type = NUM;
154     ep2->v.num = val;
155     addekid(ep1, ep2);
156 greg 2.25 if (assign == ':')
157     dremove(qname);
158     else
159     dclear(qname);
160 greg 1.13 dpush(qname, ep1);
161 greg 1.1 }
162    
163    
164 greg 2.12 void
165 schorsch 2.16 dclear( /* delete variable definitions of name */
166     char *name
167     )
168 greg 1.1 {
169 greg 2.25 EPNODE *ep;
170 greg 1.1
171 greg 1.8 while ((ep = dpop(name)) != NULL) {
172     if (ep->type == ':') {
173 greg 1.13 dpush(name, ep); /* don't clear constants */
174 greg 1.8 return;
175     }
176     epfree(ep);
177     }
178     }
179    
180    
181 greg 2.12 void
182 schorsch 2.16 dremove( /* delete all definitions of name */
183     char *name
184     )
185 greg 1.8 {
186 greg 2.25 EPNODE *ep;
187 greg 1.8
188 greg 1.1 while ((ep = dpop(name)) != NULL)
189     epfree(ep);
190     }
191    
192    
193 greg 2.12 int
194 schorsch 2.16 vardefined( /* return non-zero if variable defined */
195     char *name
196     )
197 greg 1.1 {
198 greg 2.25 EPNODE *dp;
199 greg 1.1
200     return((dp = dlookup(name)) != NULL && dp->v.kid->type == SYM);
201     }
202    
203    
204 greg 1.13 char *
205 schorsch 2.16 setcontext( /* set a new context path */
206 greg 2.25 char *ctx
207 schorsch 2.16 )
208 greg 1.13 {
209 greg 2.25 char *cpp;
210 greg 1.13
211     if (ctx == NULL)
212     return(context); /* just asking */
213 greg 2.6 while (*ctx == CNTXMARK)
214     ctx++; /* skip past marks */
215 greg 1.13 if (!*ctx) {
216 greg 2.6 context[0] = '\0'; /* empty means clear context */
217 greg 1.13 return(context);
218     }
219 greg 2.6 cpp = context; /* start context with mark */
220     *cpp++ = CNTXMARK;
221     do { /* carefully copy new context */
222 greg 2.11 if (cpp >= context+MAXCNTX)
223 greg 1.18 break; /* just copy what we can */
224 greg 1.13 if (isid(*ctx))
225     *cpp++ = *ctx++;
226     else {
227     *cpp++ = '_'; ctx++;
228     }
229     } while (*ctx);
230 greg 2.6 while (cpp[-1] == CNTXMARK) /* cannot end in context mark */
231     cpp--;
232 greg 1.13 *cpp = '\0';
233 greg 2.6 return(context);
234     }
235    
236    
237     char *
238 schorsch 2.16 pushcontext( /* push on another context */
239     char *ctx
240     )
241 greg 2.6 {
242 greg 2.11 char oldcontext[MAXCNTX+1];
243 greg 2.25 int n;
244 greg 2.6
245     strcpy(oldcontext, context); /* save old context */
246     setcontext(ctx); /* set new context */
247     n = strlen(context); /* tack on old */
248 greg 2.11 if (n+strlen(oldcontext) > MAXCNTX) {
249     strncpy(context+n, oldcontext, MAXCNTX-n);
250     context[MAXCNTX] = '\0';
251 greg 2.6 } else
252     strcpy(context+n, oldcontext);
253     return(context);
254     }
255    
256    
257     char *
258 schorsch 2.16 popcontext(void) /* pop off top context */
259 greg 2.6 {
260 greg 2.25 char *cp1, *cp2;
261 greg 2.6
262     if (!context[0]) /* nothing left to pop */
263     return(context);
264     cp2 = context; /* find mark */
265     while (*++cp2 && *cp2 != CNTXMARK)
266     ;
267     cp1 = context; /* copy tail to front */
268 schorsch 2.20 while ( (*cp1++ = *cp2++) )
269 greg 2.6 ;
270 greg 1.13 return(context);
271     }
272    
273    
274     char *
275 schorsch 2.16 qualname( /* get qualified name */
276 greg 2.25 char *nam,
277 schorsch 2.16 int lvl
278     )
279 greg 1.13 {
280 schorsch 2.17 static char nambuf[RMAXWORD+1];
281 greg 2.25 char *cp = nambuf, *cpp;
282 greg 1.17 /* check for explicit local */
283 greg 1.15 if (*nam == CNTXMARK)
284 greg 1.17 if (lvl > 0) /* only action is to refuse search */
285     return(NULL);
286     else
287     nam++;
288     else if (nam == nambuf) /* check for repeat call */
289     return(lvl > 0 ? NULL : nam);
290 greg 1.13 /* copy name to static buffer */
291     while (*nam) {
292 schorsch 2.17 if (cp >= nambuf+RMAXWORD)
293 greg 1.13 goto toolong;
294 greg 1.17 *cp++ = *nam++;
295 greg 1.13 }
296 greg 1.17 /* check for explicit global */
297     if (cp > nambuf && cp[-1] == CNTXMARK) {
298 greg 1.13 if (lvl > 0)
299 greg 1.17 return(NULL);
300     *--cp = '\0';
301     return(nambuf); /* already qualified */
302     }
303     cpp = context; /* else skip the requested levels */
304     while (lvl-- > 0) {
305     if (!*cpp)
306     return(NULL); /* return NULL if past global level */
307     while (*++cpp && *cpp != CNTXMARK)
308     ;
309     }
310 greg 1.13 while (*cpp) { /* copy context to static buffer */
311 schorsch 2.17 if (cp >= nambuf+RMAXWORD)
312 greg 1.13 goto toolong;
313     *cp++ = *cpp++;
314     }
315 greg 1.18 toolong:
316 greg 1.13 *cp = '\0';
317     return(nambuf); /* return qualified name */
318     }
319    
320    
321 greg 2.12 int
322 schorsch 2.16 incontext( /* is qualified name in current context? */
323 greg 2.25 char *qn
324 schorsch 2.16 )
325 greg 1.14 {
326 greg 2.9 if (!context[0]) /* global context accepts all */
327     return(1);
328 greg 1.14 while (*qn && *qn != CNTXMARK) /* find context mark */
329 greg 1.19 qn++;
330 greg 1.14 return(!strcmp(qn, context));
331     }
332    
333    
334 greg 2.12 void
335 schorsch 2.16 chanout( /* set output channels */
336     void (*cs)(int n, double v)
337     )
338 greg 1.1 {
339 greg 2.25 EPNODE *ep;
340 greg 1.1
341     for (ep = outchan; ep != NULL; ep = ep->sibling)
342 greg 1.6 (*cs)(ep->v.kid->v.chan, evalue(ep->v.kid->sibling));
343 greg 1.1
344     }
345    
346    
347 greg 2.12 void
348 schorsch 2.16 dcleanup( /* clear definitions (0->vars,1->output,2->consts) */
349     int lvl
350     )
351 greg 1.1 {
352 greg 2.25 int i;
353     VARDEF *vp;
354     EPNODE *ep;
355 greg 1.14 /* if context is global, clear all */
356 greg 1.1 for (i = 0; i < NHASH; i++)
357     for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
358 schorsch 2.19 if (incontext(vp->name)) {
359 greg 1.14 if (lvl >= 2)
360     dremove(vp->name);
361     else
362     dclear(vp->name);
363 schorsch 2.19 }
364 greg 1.12 if (lvl >= 1) {
365 greg 1.9 for (ep = outchan; ep != NULL; ep = ep->sibling)
366     epfree(ep);
367     outchan = NULL;
368     }
369 greg 1.1 }
370    
371    
372     EPNODE *
373 schorsch 2.16 dlookup( /* look up a definition */
374     char *name
375     )
376 greg 1.1 {
377 greg 2.25 VARDEF *vp;
378 greg 1.1
379     if ((vp = varlookup(name)) == NULL)
380 greg 2.4 return(NULL);
381 greg 1.1 return(vp->def);
382     }
383    
384    
385     VARDEF *
386 schorsch 2.16 varlookup( /* look up a variable */
387     char *name
388     )
389 greg 1.1 {
390 greg 2.4 int lvl = 0;
391 greg 2.25 char *qname;
392     VARDEF *vp;
393 greg 2.4 /* find most qualified match */
394 greg 1.13 while ((qname = qualname(name, lvl++)) != NULL)
395     for (vp = hashtbl[hash(qname)]; vp != NULL; vp = vp->next)
396     if (!strcmp(vp->name, qname))
397     return(vp);
398 greg 1.1 return(NULL);
399     }
400    
401    
402     VARDEF *
403 schorsch 2.16 varinsert( /* get a link to a variable */
404     char *name
405     )
406 greg 1.1 {
407 greg 2.25 VARDEF *vp;
408 greg 2.4 int hv;
409 greg 1.1
410 greg 1.13 if ((vp = varlookup(name)) != NULL) {
411     vp->nlinks++;
412     return(vp);
413     }
414 greg 2.3 vp = (VARDEF *)emalloc(sizeof(VARDEF));
415     vp->lib = liblookup(name);
416     if (vp->lib == NULL) /* if name not in library */
417 greg 1.16 name = qualname(name, 0); /* use fully qualified version */
418 greg 1.1 hv = hash(name);
419     vp->name = savestr(name);
420     vp->nlinks = 1;
421     vp->def = NULL;
422     vp->next = hashtbl[hv];
423     hashtbl[hv] = vp;
424     return(vp);
425     }
426 greg 2.2
427    
428 greg 2.12 void
429 schorsch 2.16 libupdate( /* update library links */
430     char *fn
431     )
432 greg 2.2 {
433 greg 2.25 int i;
434     VARDEF *vp;
435 greg 2.2 /* if fn is NULL then relink all */
436     for (i = 0; i < NHASH; i++)
437     for (vp = hashtbl[i]; vp != NULL; vp = vp->next)
438     if (vp->lib != NULL || fn == NULL || !strcmp(fn, vp->name))
439     vp->lib = liblookup(vp->name);
440     }
441 greg 1.1
442    
443 greg 2.12 void
444 schorsch 2.16 varfree( /* release link to variable */
445 greg 2.25 VARDEF *ln
446 schorsch 2.16 )
447 greg 1.1 {
448 greg 2.25 VARDEF *vp;
449 greg 2.4 int hv;
450 greg 1.1
451     if (--ln->nlinks > 0)
452 greg 2.4 return; /* still active */
453 greg 1.1
454     hv = hash(ln->name);
455     vp = hashtbl[hv];
456     if (vp == ln)
457 greg 2.4 hashtbl[hv] = vp->next;
458 greg 1.1 else {
459 greg 2.4 while (vp->next != ln) /* must be in list */
460     vp = vp->next;
461     vp->next = ln->next;
462 greg 1.1 }
463     freestr(ln->name);
464     efree((char *)ln);
465     }
466    
467    
468     EPNODE *
469 schorsch 2.16 dfirst(void) /* return pointer to first definition */
470 greg 1.1 {
471     htndx = 0;
472     htpos = NULL;
473     ochpos = outchan;
474     return(dnext());
475     }
476    
477    
478     EPNODE *
479 schorsch 2.16 dnext(void) /* return pointer to next definition */
480 greg 1.1 {
481 greg 2.25 EPNODE *ep;
482     char *nm;
483 greg 1.1
484     while (htndx < NHASH) {
485 greg 2.4 if (htpos == NULL)
486     htpos = hashtbl[htndx++];
487     while (htpos != NULL) {
488     ep = htpos->def;
489 greg 1.19 nm = htpos->name;
490 greg 2.4 htpos = htpos->next;
491     if (ep != NULL && incontext(nm))
492     return(ep);
493     }
494 greg 1.1 }
495     if ((ep = ochpos) != NULL)
496 greg 2.4 ochpos = ep->sibling;
497 greg 1.1 return(ep);
498     }
499    
500    
501     EPNODE *
502 schorsch 2.16 dpop( /* pop a definition */
503     char *name
504     )
505 greg 1.1 {
506 greg 2.25 VARDEF *vp;
507     EPNODE *dp;
508 greg 1.1
509     if ((vp = varlookup(name)) == NULL || vp->def == NULL)
510 greg 2.4 return(NULL);
511 greg 1.1 dp = vp->def;
512     vp->def = dp->sibling;
513     varfree(vp);
514     return(dp);
515     }
516    
517    
518 greg 2.12 void
519 schorsch 2.16 dpush( /* push on a definition */
520     char *nm,
521 greg 2.25 EPNODE *ep
522 schorsch 2.16 )
523 greg 1.1 {
524 greg 2.25 VARDEF *vp;
525 greg 1.1
526 greg 1.13 vp = varinsert(nm);
527 greg 1.1 ep->sibling = vp->def;
528     vp->def = ep;
529     }
530    
531    
532 greg 2.12 void
533 schorsch 2.16 addchan( /* add an output channel assignment */
534     EPNODE *sp
535     )
536 greg 1.1 {
537 greg 2.4 int ch = sp->v.kid->v.chan;
538 greg 2.25 EPNODE *ep, *epl;
539 greg 1.1
540     for (epl = NULL, ep = outchan; ep != NULL; epl = ep, ep = ep->sibling)
541     if (ep->v.kid->v.chan >= ch) {
542     if (epl != NULL)
543     epl->sibling = sp;
544     else
545     outchan = sp;
546     if (ep->v.kid->v.chan > ch)
547     sp->sibling = ep;
548     else {
549     sp->sibling = ep->sibling;
550     epfree(ep);
551     }
552     return;
553     }
554     if (epl != NULL)
555     epl->sibling = sp;
556     else
557     outchan = sp;
558     sp->sibling = NULL;
559    
560     }
561    
562    
563 greg 2.12 void
564 schorsch 2.16 getstatement(void) /* get next statement */
565 greg 1.1 {
566 greg 2.25 EPNODE *ep;
567 greg 1.13 char *qname;
568 greg 2.25 VARDEF *vdef;
569 greg 1.1
570     if (nextc == ';') { /* empty statement */
571     scan();
572     return;
573     }
574 greg 2.12 if (esupport&E_OUTCHAN &&
575     nextc == '$') { /* channel assignment */
576 greg 1.1 ep = getchan();
577     addchan(ep);
578 greg 2.12 } else { /* ordinary definition */
579 greg 1.1 ep = getdefn();
580 greg 1.13 qname = qualname(dname(ep), 0);
581 schorsch 2.19 if (esupport&E_REDEFW && (vdef = varlookup(qname)) != NULL) {
582 greg 2.7 if (vdef->def != NULL && epcmp(ep, vdef->def)) {
583 greg 1.16 wputs(qname);
584     if (vdef->def->type == ':')
585     wputs(": redefined constant expression\n");
586     else
587     wputs(": redefined\n");
588 greg 2.12 } else if (ep->v.kid->type == FUNC && vdef->lib != NULL) {
589 greg 1.16 wputs(qname);
590     wputs(": definition hides library function\n");
591     }
592 schorsch 2.19 }
593 greg 1.10 if (ep->type == ':')
594 greg 1.13 dremove(qname);
595 greg 1.10 else
596 greg 1.13 dclear(qname);
597     dpush(qname, ep);
598 greg 1.1 }
599     if (nextc != EOF) {
600     if (nextc != ';')
601     syntax("';' expected");
602     scan();
603     }
604     }
605    
606    
607     EPNODE *
608 schorsch 2.16 getdefn(void)
609     /* A -> SYM = E1 */
610     /* SYM : E1 */
611     /* FUNC(SYM,..) = E1 */
612     /* FUNC(SYM,..) : E1 */
613 greg 1.1 {
614 greg 2.25 EPNODE *ep1, *ep2;
615 greg 1.1
616 greg 1.18 if (!isalpha(nextc) && nextc != CNTXMARK)
617 greg 1.1 syntax("illegal variable name");
618    
619     ep1 = newnode();
620     ep1->type = SYM;
621     ep1->v.name = savestr(getname());
622    
623 greg 2.12 if (esupport&E_FUNCTION && nextc == '(') {
624 greg 1.1 ep2 = newnode();
625     ep2->type = FUNC;
626     addekid(ep2, ep1);
627     ep1 = ep2;
628     do {
629     scan();
630     if (!isalpha(nextc))
631 greg 2.24 syntax("illegal parameter name");
632 greg 1.1 ep2 = newnode();
633     ep2->type = SYM;
634     ep2->v.name = savestr(getname());
635     addekid(ep1, ep2);
636     } while (nextc == ',');
637     if (nextc != ')')
638     syntax("')' expected");
639     scan();
640     curfunc = ep1;
641 greg 2.10 }
642 greg 1.1
643 greg 1.8 if (nextc != '=' && nextc != ':')
644     syntax("'=' or ':' expected");
645 greg 1.1
646     ep2 = newnode();
647 greg 1.8 ep2->type = nextc;
648     scan();
649 greg 1.1 addekid(ep2, ep1);
650     addekid(ep2, getE1());
651    
652 greg 2.12 if (ep1->type == SYM && ep1->sibling->type != NUM) {
653 greg 1.1 ep1 = newnode();
654 greg 2.23 ep1->type = CLKT;
655 greg 2.8 ep1->v.tick = 0;
656 greg 1.1 addekid(ep2, ep1);
657     ep1 = newnode();
658     ep1->type = NUM;
659     addekid(ep2, ep1);
660     }
661 greg 2.10 curfunc = NULL;
662 greg 1.1
663     return(ep2);
664     }
665    
666    
667     EPNODE *
668 schorsch 2.16 getchan(void) /* A -> $N = E1 */
669 greg 1.1 {
670 greg 2.25 EPNODE *ep1, *ep2;
671 greg 1.1
672     if (nextc != '$')
673     syntax("missing '$'");
674     scan();
675    
676     ep1 = newnode();
677     ep1->type = CHAN;
678     ep1->v.chan = getinum();
679    
680     if (nextc != '=')
681     syntax("'=' expected");
682     scan();
683    
684     ep2 = newnode();
685     ep2->type = '=';
686     addekid(ep2, ep1);
687     addekid(ep2, getE1());
688    
689     return(ep2);
690     }
691    
692    
693    
694     /*
695     * The following routines are for internal use only:
696     */
697    
698    
699 schorsch 2.18 static double /* evaluate a variable */
700     dvalue(char *name, EPNODE *d)
701 greg 1.1 {
702 greg 2.25 EPNODE *ep1, *ep2;
703 greg 1.1
704     if (d == NULL || d->v.kid->type != SYM) {
705     eputs(name);
706     eputs(": undefined variable\n");
707     quit(1);
708     }
709     ep1 = d->v.kid->sibling; /* get expression */
710 greg 1.5 if (ep1->type == NUM)
711     return(ep1->v.num); /* return if number */
712 greg 1.1 ep2 = ep1->sibling; /* check time */
713 greg 2.12 if (eclock >= MAXCLOCK)
714     eclock = 1; /* wrap clock counter */
715     if (ep2->v.tick < MAXCLOCK &&
716 schorsch 2.20 (ep2->v.tick == 0) | (ep2->v.tick != eclock)) {
717 greg 2.12 ep2->v.tick = d->type == ':' ? MAXCLOCK : eclock;
718 greg 1.1 ep2 = ep2->sibling;
719 greg 1.5 ep2->v.num = evalue(ep1); /* needs new value */
720 greg 1.1 } else
721 greg 1.5 ep2 = ep2->sibling; /* else reuse old value */
722 greg 1.1
723     return(ep2->v.num);
724     }