ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/calexpr.c
Revision: 2.47
Committed: Sun Feb 25 04:11:10 2024 UTC (2 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.46: +11 -8 lines
Log Message:
fix: Potential bug in free allocated EPNODE arrays

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: calexpr.c,v 2.46 2024/02/24 19:26:44 greg Exp $";
3 #endif
4 /*
5 * Compute data values using expression parser
6 *
7 * 7/1/85 Greg Ward
8 *
9 * 11/11/85 Made channel input conditional with (INCHAN) compiles.
10 *
11 * 4/2/86 Added conditional compiles for function definitions (FUNCTION).
12 *
13 * 1/29/87 Made variables conditional (VARIABLE)
14 *
15 * 5/19/88 Added constant subexpression elimination (RCONST)
16 *
17 * 2/19/03 Eliminated conditional compiles in favor of esupport extern.
18 */
19
20 #include "copyright.h"
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <math.h>
25 #include <stdlib.h>
26
27 #include "rtmisc.h"
28 #include "rtio.h"
29 #include "rterror.h"
30 #include "calcomp.h"
31
32 #define MAXLINE 256 /* maximum line length */
33
34 #define newnode() (EPNODE *)ecalloc(1, sizeof(EPNODE))
35
36 #define isdecimal(c) (isdigit(c) | ((c) == '.'))
37
38 #define envalue(ep) ((ep)->type==NUM ? (ep)->v.num : evalue(ep))
39
40 static double euminus(EPNODE *), eargument(EPNODE *), enumber(EPNODE *);
41 static double echannel(EPNODE *);
42 static double eadd(EPNODE *), esubtr(EPNODE *),
43 emult(EPNODE *), edivi(EPNODE *),
44 epow(EPNODE *);
45 static double ebotch(EPNODE *);
46
47 unsigned int esupport = /* what to support */
48 E_VARIABLE | E_FUNCTION ;
49
50 int eofc = 0; /* optional end-of-file character */
51 int nextc; /* lookahead character */
52
53 double (*eoper[])(EPNODE *) = { /* expression operations */
54 ebotch,
55 evariable,
56 enumber,
57 euminus,
58 echannel,
59 efunc,
60 eargument,
61 ebotch,
62 ebotch,
63 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
64 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
65 emult,
66 eadd,
67 0,
68 esubtr,
69 0,
70 edivi,
71 0,0,0,0,0,0,0,0,0,0,
72 ebotch,
73 0,0,
74 ebotch,
75 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
76 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
77 epow,
78 };
79
80 static FILE *infp; /* input file pointer */
81 static char *linbuf; /* line buffer */
82 static char *infile; /* input file name */
83 static int lineno; /* input line number */
84 static int linepos; /* position in buffer */
85
86
87 EPNODE *
88 eparse( /* parse an expression string */
89 char *expr
90 )
91 {
92 EPNODE *ep;
93
94 initstr(expr, NULL, 0);
95 curfunc = NULL;
96 ep = getE1();
97 if (nextc != EOF)
98 syntax("unexpected character");
99 return(ep);
100 }
101
102
103 double
104 eval( /* evaluate an expression string */
105 char *expr
106 )
107 {
108 int prev_support = esupport;
109 EPNODE *ep;
110 double rval;
111
112 esupport &= ~E_RCONST; /* don't bother reducing constant expr */
113 ep = eparse(expr);
114 esupport = prev_support; /* as you were */
115 rval = evalue(ep);
116 epfree(ep,1);
117 return(rval);
118 }
119
120
121 int
122 epcmp( /* compare two expressions for equivalence */
123 EPNODE *ep1,
124 EPNODE *ep2
125 )
126 {
127 double d;
128
129 if (ep1->type != ep2->type)
130 return(1);
131
132 switch (ep1->type) {
133
134 case VAR:
135 return(ep1->v.ln != ep2->v.ln);
136
137 case NUM:
138 if (ep2->v.num == 0)
139 return(ep1->v.num != 0);
140 d = ep1->v.num / ep2->v.num;
141 return((d > 1.000000000001) | (d < 0.999999999999));
142
143 case CHAN:
144 case ARG:
145 return(ep1->v.chan != ep2->v.chan);
146
147 case '=':
148 case ':':
149 return(epcmp(ep1->v.kid->sibling, ep2->v.kid->sibling));
150
151 case CLKT:
152 case SYM: /* should never get this one */
153 return(0);
154
155 default:
156 ep1 = ep1->v.kid;
157 ep2 = ep2->v.kid;
158 while (ep1 != NULL) {
159 if (ep2 == NULL)
160 return(1);
161 if (epcmp(ep1, ep2))
162 return(1);
163 ep1 = ep1->sibling;
164 ep2 = ep2->sibling;
165 }
166 return(ep2 != NULL);
167 }
168 }
169
170
171 void
172 epfree( /* free a parse tree */
173 EPNODE *epar,
174 int frep
175 )
176 {
177 EPNODE *ep;
178
179 switch (epar->type) {
180
181 case VAR:
182 varfree(epar->v.ln);
183 break;
184
185 case SYM:
186 freestr(epar->v.name);
187 break;
188
189 case NUM:
190 case CHAN:
191 case ARG:
192 case CLKT:
193 break;
194
195 default:
196 if (epar->nkids < 0) {
197 ep = epar->v.kid - epar->nkids;
198 while (ep > epar->v.kid)
199 epfree(--ep, 0);
200 efree(ep); /* free array space */
201 } else
202 while ((ep = epar->v.kid) != NULL) {
203 epar->v.kid = ep->sibling;
204 epfree(ep, 1);
205 }
206 break;
207
208 }
209 if (frep)
210 efree(epar);
211 }
212
213
214 static void
215 epflatten( /* flatten hierarchies for '+', '*' */
216 EPNODE *epar
217 )
218 {
219 EPNODE *ep;
220
221 if (epar->nkids < 0) {
222 eputs("Cannot flatten EPNODE array\n");
223 quit(1);
224 }
225 for (ep = epar->v.kid; ep != NULL; ep = ep->sibling)
226 while (ep->type == epar->type) {
227 EPNODE *ep1 = ep->v.kid;
228 while (ep1->sibling != NULL)
229 ep1 = ep1->sibling;
230 ep1->sibling = ep->sibling;
231 epar->nkids += nekids(ep) - 1;
232 ep1 = ep->v.kid;
233 *ep = *ep1;
234 efree(ep1); /* not epfree()! */
235 }
236 }
237
238
239 void
240 epoptimize( /* flatten operations, lists -> arrays */
241 EPNODE *epar
242 )
243 {
244 EPNODE *ep;
245
246 if ((epar->type == '+') | (epar->type == '*'))
247 epflatten(epar); /* flatten associative operations */
248
249 if (epar->nkids) /* do children if any */
250 for (ep = epar->v.kid; ep != NULL; ep = ep->sibling)
251 epoptimize(ep);
252
253 if (epar->nkids > 4) { /* make list into array if > 4 kids */
254 int n = 1;
255 epar->v.kid = (EPNODE *)erealloc(epar->v.kid,
256 sizeof(EPNODE)*epar->nkids);
257 while (n < epar->nkids) {
258 ep = epar->v.kid[n-1].sibling;
259 epar->v.kid[n] = *ep;
260 efree(ep); /* not epfree()! */
261 epar->v.kid[n-1].sibling = epar->v.kid + n;
262 n++;
263 }
264 epar->nkids = -n;
265 }
266 }
267
268 /* the following used to be a switch */
269 static double
270 eargument(
271 EPNODE *ep
272 )
273 {
274 return(argument(ep->v.chan));
275 }
276
277 static double
278 enumber(
279 EPNODE *ep
280 )
281 {
282 return(ep->v.num);
283 }
284
285 static double
286 euminus(
287 EPNODE *ep
288 )
289 {
290 EPNODE *ep1 = ep->v.kid;
291
292 return(-evalue(ep1));
293 }
294
295 static double
296 echannel(
297 EPNODE *ep
298 )
299 {
300 return(chanvalue(ep->v.chan));
301 }
302
303 static double
304 eadd(
305 EPNODE *ep
306 )
307 {
308 double sum = 0;
309 EPNODE *ep1 = ep->v.kid;
310
311 do
312 sum += envalue(ep1);
313 while ((ep1 = ep1->sibling) != NULL);
314
315 return(sum);
316 }
317
318 static double
319 esubtr(
320 EPNODE *ep
321 )
322 {
323 EPNODE *ep1 = ep->v.kid;
324 EPNODE *ep2 = ep1->sibling;
325
326 return(envalue(ep1) - envalue(ep2));
327 }
328
329 static double
330 emult(
331 EPNODE *ep
332 )
333 {
334 double prod = 1;
335 EPNODE *ep1 = ep->v.kid;
336
337 do
338 prod *= envalue(ep1);
339 while ((ep1 = ep1->sibling) != NULL);
340
341 return(prod);
342 }
343
344 static double
345 edivi(
346 EPNODE *ep
347 )
348 {
349 EPNODE *ep1 = ep->v.kid;
350 EPNODE *ep2 = ep1->sibling;
351 double d;
352
353 d = evalue(ep2);
354 if (d == 0.0) {
355 wputs("Division by zero\n");
356 errno = ERANGE;
357 return(0.0);
358 }
359 return(envalue(ep1) / d);
360 }
361
362 static double
363 epow(
364 EPNODE *ep
365 )
366 {
367 EPNODE *ep1 = ep->v.kid;
368 double d;
369 int lasterrno;
370
371 lasterrno = errno;
372 errno = 0;
373 d = pow(evalue(ep1), evalue(ep1->sibling));
374 #ifdef isnan
375 if (errno == 0) {
376 if (isnan(d))
377 errno = EDOM;
378 else if (isinf(d))
379 errno = ERANGE;
380 }
381 #endif
382 if ((errno == EDOM) | (errno == ERANGE)) {
383 wputs("Illegal power\n");
384 return(0.0);
385 }
386 errno = lasterrno;
387 return(d);
388 }
389
390 static double
391 ebotch(
392 EPNODE *ep
393 )
394 {
395 eputs("Bad expression!\n");
396 quit(1);
397 return 0.0; /* pro forma return */
398 }
399
400
401 EPNODE *
402 ekid( /* return pointer to a node's nth kid */
403 EPNODE *ep,
404 int n
405 )
406 {
407 if (ep->nkids < 0) { /* allocated array? */
408 if (n >= -ep->nkids)
409 return(NULL);
410 return(ep->v.kid + n);
411 }
412 ep = ep->v.kid; /* else get from list */
413 while (n-- > 0)
414 if ((ep = ep->sibling) == NULL)
415 break;
416 return(ep);
417 }
418
419
420 void
421 initfile( /* prepare input file */
422 FILE *fp,
423 char *fn,
424 int ln
425 )
426 {
427 static char inpbuf[MAXLINE];
428
429 infp = fp;
430 linbuf = inpbuf;
431 infile = fn;
432 lineno = ln;
433 linepos = 0;
434 inpbuf[0] = '\0';
435 scan();
436 }
437
438
439 void
440 initstr( /* prepare input string */
441 char *s,
442 char *fn,
443 int ln
444 )
445 {
446 infp = NULL;
447 infile = fn;
448 lineno = ln;
449 linbuf = s;
450 linepos = 0;
451 scan();
452 }
453
454
455 void
456 getscanpos( /* return current scan position */
457 char **fnp,
458 int *lnp,
459 char **spp,
460 FILE **fpp
461 )
462 {
463 if (fnp != NULL) *fnp = infile;
464 if (lnp != NULL) *lnp = lineno;
465 if (spp != NULL) *spp = linbuf+linepos;
466 if (fpp != NULL) *fpp = infp;
467 }
468
469
470 int
471 scan(void) /* scan next character, return literal next */
472 {
473 int lnext = 0;
474
475 do {
476 if (linbuf[linepos] == '\0')
477 if (infp == NULL || fgets(linbuf, MAXLINE, infp) == NULL)
478 nextc = EOF;
479 else {
480 nextc = linbuf[0];
481 lineno++;
482 linepos = 1;
483 }
484 else
485 nextc = linbuf[linepos++];
486 if (!lnext)
487 lnext = nextc;
488 if (nextc == eofc) {
489 nextc = EOF;
490 break;
491 }
492 if (nextc == '{') {
493 scan();
494 while (nextc != '}')
495 if (nextc == EOF)
496 syntax("'}' expected");
497 else
498 scan();
499 scan();
500 }
501 } while (isspace(nextc));
502 return(lnext);
503 }
504
505
506 char *
507 long2ascii( /* convert long to ascii */
508 long l
509 )
510 {
511 static char buf[16];
512 char *cp;
513 int neg = 0;
514
515 if (l == 0)
516 return("0");
517 if (l < 0) {
518 l = -l;
519 neg++;
520 }
521 cp = buf + sizeof(buf);
522 *--cp = '\0';
523 while (l) {
524 *--cp = l % 10 + '0';
525 l /= 10;
526 }
527 if (neg)
528 *--cp = '-';
529 return(cp);
530 }
531
532
533 void
534 syntax( /* report syntax error and quit */
535 char *err
536 )
537 {
538 int i;
539
540 if ((infile != NULL) | (lineno != 0)) {
541 if (infile != NULL) eputs(infile);
542 if (lineno != 0) {
543 eputs(infile != NULL ? ", line " : "line ");
544 eputs(long2ascii((long)lineno));
545 }
546 eputs(":\n");
547 }
548 eputs(linbuf);
549 if (linbuf[strlen(linbuf)-1] != '\n')
550 eputs("\n");
551 for (i = 0; i < linepos-1; i++)
552 eputs(linbuf[i] == '\t' ? "\t" : " ");
553 eputs("^ ");
554 eputs(err);
555 eputs("\n");
556 quit(1);
557 }
558
559
560 void
561 addekid( /* add a child to ep */
562 EPNODE *ep,
563 EPNODE *ek
564 )
565 {
566 if (ep->nkids < 0) {
567 eputs("Cannot add kid to EPNODE array\n");
568 quit(1);
569 }
570 ep->nkids++;
571 if (ep->v.kid == NULL)
572 ep->v.kid = ek;
573 else {
574 for (ep = ep->v.kid; ep->sibling != NULL; ep = ep->sibling)
575 ;
576 ep->sibling = ek;
577 }
578 ek->sibling = NULL; /* shouldn't be necessary */
579 }
580
581
582 char *
583 getname(void) /* scan an identifier */
584 {
585 static char str[RMAXWORD+1];
586 int i, lnext;
587
588 lnext = nextc;
589 for (i = 0; i < RMAXWORD && isid(lnext); i++, lnext = scan())
590 str[i] = lnext;
591 str[i] = '\0';
592 while (isid(lnext)) /* skip rest of name */
593 lnext = scan();
594
595 return(str);
596 }
597
598
599 int
600 getinum(void) /* scan a positive integer */
601 {
602 int n, lnext;
603
604 n = 0;
605 lnext = nextc;
606 while (isdigit(lnext)) {
607 n = n * 10 + lnext - '0';
608 lnext = scan();
609 }
610 return(n);
611 }
612
613
614 double
615 getnum(void) /* scan a positive float */
616 {
617 int i, lnext;
618 char str[RMAXWORD+1];
619
620 i = 0;
621 lnext = nextc;
622 while (isdigit(lnext) && i < RMAXWORD) {
623 str[i++] = lnext;
624 lnext = scan();
625 }
626 if ((lnext == '.') & (i < RMAXWORD)) {
627 str[i++] = lnext;
628 lnext = scan();
629 if (i == 1 && !isdigit(lnext))
630 syntax("badly formed number");
631 while (isdigit(lnext) && i < RMAXWORD) {
632 str[i++] = lnext;
633 lnext = scan();
634 }
635 }
636 if ((lnext == 'e') | (lnext == 'E') && i < RMAXWORD) {
637 str[i++] = lnext;
638 lnext = scan();
639 if ((lnext == '-') | (lnext == '+') && i < RMAXWORD) {
640 str[i++] = lnext;
641 lnext = scan();
642 }
643 if (!isdigit(lnext))
644 syntax("missing exponent");
645 while (isdigit(lnext) && i < RMAXWORD) {
646 str[i++] = lnext;
647 lnext = scan();
648 }
649 }
650 str[i] = '\0';
651
652 return(atof(str));
653 }
654
655
656 EPNODE *
657 getE1(void) /* E1 -> E1 ADDOP E2 */
658 /* E2 */
659 {
660 EPNODE *ep1, *ep2;
661
662 ep1 = getE2();
663 while ((nextc == '+') | (nextc == '-')) {
664 ep2 = newnode();
665 ep2->type = nextc;
666 scan();
667 addekid(ep2, ep1);
668 addekid(ep2, getE2());
669 if (esupport&E_RCONST &&
670 (ep1->type == NUM) & (ep1->sibling->type == NUM))
671 ep2 = rconst(ep2);
672 ep1 = ep2;
673 }
674 return(ep1);
675 }
676
677
678 EPNODE *
679 getE2(void) /* E2 -> E2 MULOP E3 */
680 /* E3 */
681 {
682 EPNODE *ep1, *ep2;
683
684 ep1 = getE3();
685 while ((nextc == '*') | (nextc == '/')) {
686 ep2 = newnode();
687 ep2->type = nextc;
688 scan();
689 addekid(ep2, ep1);
690 addekid(ep2, getE3());
691 if (esupport&E_RCONST) {
692 EPNODE *ep3 = ep1->sibling;
693 if ((ep1->type == NUM) & (ep3->type == NUM)) {
694 ep2 = rconst(ep2);
695 } else if (ep3->type == NUM) {
696 if (ep2->type == '/') {
697 if (ep3->v.num == 0)
698 syntax("divide by zero constant");
699 ep2->type = '*'; /* for speed */
700 ep3->v.num = 1./ep3->v.num;
701 } else if (ep3->v.num == 0) {
702 ep1->sibling = NULL; /* (E2 * 0) */
703 epfree(ep2,1);
704 ep2 = ep3;
705 }
706 } else if (ep1->type == NUM && ep1->v.num == 0) {
707 epfree(ep3,1); /* (0 * E3) or (0 / E3) */
708 ep1->sibling = NULL;
709 efree(ep2);
710 ep2 = ep1;
711 }
712 }
713 ep1 = ep2;
714 }
715 return(ep1);
716 }
717
718
719 EPNODE *
720 getE3(void) /* E3 -> E4 ^ E3 */
721 /* E4 */
722 {
723 EPNODE *ep1, *ep2;
724
725 ep1 = getE4();
726 if (nextc != '^')
727 return(ep1);
728 ep2 = newnode();
729 ep2->type = nextc;
730 scan();
731 addekid(ep2, ep1);
732 addekid(ep2, getE3());
733 if (esupport&E_RCONST) {
734 EPNODE *ep3 = ep1->sibling;
735 if ((ep1->type == NUM) & (ep3->type == NUM)) {
736 ep2 = rconst(ep2);
737 } else if (ep1->type == NUM && ep1->v.num == 0) {
738 epfree(ep3,1); /* (0 ^ E3) */
739 ep1->sibling = NULL;
740 efree(ep2);
741 ep2 = ep1;
742 } else if ((ep3->type == NUM && ep3->v.num == 0) |
743 (ep1->type == NUM && ep1->v.num == 1)) {
744 epfree(ep2,1); /* (E4 ^ 0) or (1 ^ E3) */
745 ep2 = newnode();
746 ep2->type = NUM;
747 ep2->v.num = 1;
748 } else if (ep3->type == NUM && ep3->v.num == 1) {
749 efree(ep3); /* (E4 ^ 1) */
750 ep1->sibling = NULL;
751 efree(ep2);
752 ep2 = ep1;
753 }
754 }
755 return(ep2);
756 }
757
758
759 EPNODE *
760 getE4(void) /* E4 -> ADDOP E5 */
761 /* E5 */
762 {
763 EPNODE *ep1, *ep2;
764
765 if (nextc == '-') {
766 scan();
767 ep2 = getE5();
768 if (ep2->type == NUM) {
769 ep2->v.num = -ep2->v.num;
770 return(ep2);
771 }
772 if (ep2->type == UMINUS) { /* don't generate -(-E5) */
773 ep1 = ep2->v.kid;
774 efree(ep2);
775 return(ep1);
776 }
777 ep1 = newnode();
778 ep1->type = UMINUS;
779 addekid(ep1, ep2);
780 return(ep1);
781 }
782 if (nextc == '+')
783 scan();
784 return(getE5());
785 }
786
787
788 EPNODE *
789 getE5(void) /* E5 -> (E1) */
790 /* VAR */
791 /* NUM */
792 /* $N */
793 /* FUNC(E1,..) */
794 /* ARG */
795 {
796 int i;
797 char *nam;
798 EPNODE *ep1, *ep2;
799
800 if (nextc == '(') {
801 scan();
802 ep1 = getE1();
803 if (nextc != ')')
804 syntax("')' expected");
805 scan();
806 return(ep1);
807 }
808 if (esupport&E_INCHAN && nextc == '$') {
809 scan();
810 ep1 = newnode();
811 ep1->type = CHAN;
812 ep1->v.chan = getinum();
813 return(ep1);
814 }
815 if (esupport&(E_VARIABLE|E_FUNCTION) &&
816 (isalpha(nextc) | (nextc == CNTXMARK))) {
817 nam = getname();
818 ep1 = NULL;
819 if ((esupport&(E_VARIABLE|E_FUNCTION)) == (E_VARIABLE|E_FUNCTION)
820 && curfunc != NULL)
821 for (i = 1, ep2 = curfunc->v.kid->sibling;
822 ep2 != NULL; i++, ep2 = ep2->sibling)
823 if (!strcmp(ep2->v.name, nam)) {
824 ep1 = newnode();
825 ep1->type = ARG;
826 ep1->v.chan = i;
827 break;
828 }
829 if (ep1 == NULL) {
830 ep1 = newnode();
831 ep1->type = VAR;
832 ep1->v.ln = varinsert(nam);
833 }
834 if (esupport&E_FUNCTION && nextc == '(') {
835 ep2 = newnode();
836 ep2->type = FUNC;
837 addekid(ep2, ep1);
838 ep1 = ep2;
839 do {
840 scan();
841 addekid(ep1, getE1());
842 } while (nextc == ',');
843 if (nextc != ')')
844 syntax("')' expected");
845 scan();
846 } else if (!(esupport&E_VARIABLE))
847 syntax("'(' expected");
848 if (esupport&E_RCONST && isconstvar(ep1))
849 ep1 = rconst(ep1);
850 return(ep1);
851 }
852 if (isdecimal(nextc)) {
853 ep1 = newnode();
854 ep1->type = NUM;
855 ep1->v.num = getnum();
856 return(ep1);
857 }
858 syntax("unexpected character");
859 return NULL; /* pro forma return */
860 }
861
862
863 EPNODE *
864 rconst( /* reduce a constant expression */
865 EPNODE *epar
866 )
867 {
868 EPNODE *ep;
869
870 ep = newnode();
871 ep->type = NUM;
872 errno = 0;
873 ep->v.num = evalue(epar);
874 if ((errno == EDOM) | (errno == ERANGE))
875 syntax("bad constant expression");
876 epfree(epar,1);
877
878 return(ep);
879 }
880
881
882 int
883 isconstvar( /* is ep linked to a constant expression? */
884 EPNODE *ep
885 )
886 {
887 EPNODE *ep1;
888
889 if (esupport&E_FUNCTION && ep->type == FUNC) {
890 if (!isconstfun(ep->v.kid))
891 return(0);
892 for (ep1 = ep->v.kid->sibling; ep1 != NULL; ep1 = ep1->sibling)
893 if (ep1->type != NUM && !isconstfun(ep1))
894 return(0);
895 return(1);
896 }
897 if (ep->type != VAR)
898 return(0);
899 ep1 = ep->v.ln->def;
900 if (ep1 == NULL || ep1->type != ':')
901 return(0);
902 if (esupport&E_FUNCTION && ep1->v.kid->type != SYM)
903 return(0);
904 return(1);
905 }
906
907
908 int
909 isconstfun( /* is ep linked to a constant function? */
910 EPNODE *ep
911 )
912 {
913 EPNODE *dp;
914 LIBR *lp;
915
916 if (ep->type != VAR)
917 return(0);
918 if ((dp = ep->v.ln->def) != NULL) {
919 if (dp->v.kid->type == FUNC)
920 return(dp->type == ':');
921 else
922 return(0); /* don't identify masked library functions */
923 }
924 if ((lp = ep->v.ln->lib) != NULL)
925 return(lp->atyp == ':');
926 return(0);
927 }