1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24
25 #if HAVE_NBTOOL_CONFIG_H
26 #include "nbtool_config.h"
27 #endif
28
29 #define DEBUG
30 #include <stdio.h>
31 #include <math.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include "awk.h"
36 #include "awkgram.h"
37
38 #define FULLTAB 2 /* rehash when table gets this x full */
39 #define GROWTAB 4 /* grow table by this factor */
40
41 Array *symtab; /* main symbol table */
42
43 char **FS; /* initial field sep */
44 char **RS; /* initial record sep */
45 char **OFS; /* output field sep */
46 char **ORS; /* output record sep */
47 char **OFMT; /* output format for numbers */
48 char **CONVFMT; /* format for conversions in getsval */
49 Awkfloat *NF; /* number of fields in current record */
50 Awkfloat *NR; /* number of current record */
51 Awkfloat *FNR; /* number of current record in current file */
52 char **FILENAME; /* current filename argument */
53 Awkfloat *ARGC; /* number of arguments from command line */
54 char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
55 Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
56 Awkfloat *RLENGTH; /* length of same */
57
58 Cell *fsloc; /* FS */
59 Cell *nrloc; /* NR */
60 Cell *nfloc; /* NF */
61 Cell *fnrloc; /* FNR */
62 Cell *ofsloc; /* OFS */
63 Cell *orsloc; /* ORS */
64 Cell *rsloc; /* RS */
65 Array *ARGVtab; /* symbol table containing ARGV[...] */
66 Array *ENVtab; /* symbol table containing ENVIRON[...] */
67 Cell *rstartloc; /* RSTART */
68 Cell *rlengthloc; /* RLENGTH */
69 Cell *subseploc; /* SUBSEP */
70 Cell *symtabloc; /* SYMTAB */
71
72 Cell *nullloc; /* a guaranteed empty cell */
73 Node *nullnode; /* zero&null, converted into a node for comparisons */
74 Cell *literal0;
75
76 extern Cell **fldtab;
77
78 static void
setfree(Cell * vp)79 setfree(Cell *vp)
80 {
81 if (&vp->sval == FS || &vp->sval == RS ||
82 &vp->sval == OFS || &vp->sval == ORS ||
83 &vp->sval == OFMT || &vp->sval == CONVFMT ||
84 &vp->sval == FILENAME || &vp->sval == SUBSEP)
85 vp->tval |= DONTFREE;
86 else
87 vp->tval &= ~DONTFREE;
88 }
89
syminit(void)90 void syminit(void) /* initialize symbol table with builtin vars */
91 {
92 literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
93 /* this is used for if(x)... tests: */
94 nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
95 nullnode = celltonode(nullloc, CCON);
96
97 fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
98 FS = &fsloc->sval;
99 rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
100 RS = &rsloc->sval;
101 ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
102 OFS = &ofsloc->sval;
103 orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
104 ORS = &orsloc->sval;
105 OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
106 CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
107 FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
108 nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
109 NF = &nfloc->fval;
110 nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
111 NR = &nrloc->fval;
112 fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
113 FNR = &fnrloc->fval;
114 subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
115 SUBSEP = &subseploc->sval;
116 rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
117 RSTART = &rstartloc->fval;
118 rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
119 RLENGTH = &rlengthloc->fval;
120 symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
121 free(symtabloc->sval);
122 symtabloc->sval = (char *) symtab;
123 }
124
arginit(int ac,char ** av)125 void arginit(int ac, char **av) /* set up ARGV and ARGC */
126 {
127 Cell *cp;
128 int i;
129 char temp[50];
130
131 ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
132 cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
133 ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
134 free(cp->sval);
135 cp->sval = (char *) ARGVtab;
136 for (i = 0; i < ac; i++) {
137 sprintf(temp, "%d", i);
138 if (is_number(*av))
139 setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
140 else
141 setsymtab(temp, *av, 0.0, STR, ARGVtab);
142 av++;
143 }
144 }
145
envinit(char ** envp)146 void envinit(char **envp) /* set up ENVIRON variable */
147 {
148 Cell *cp;
149 char *p;
150
151 cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
152 ENVtab = makesymtab(NSYMTAB);
153 free(cp->sval);
154 cp->sval = (char *) ENVtab;
155 for ( ; *envp; envp++) {
156 if ((p = strchr(*envp, '=')) == NULL)
157 continue;
158 if( p == *envp ) /* no left hand side name in env string */
159 continue;
160 *p++ = 0; /* split into two strings at = */
161 if (is_number(p))
162 setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
163 else
164 setsymtab(*envp, p, 0.0, STR, ENVtab);
165 p[-1] = '='; /* restore in case env is passed down to a shell */
166 }
167 }
168
makesymtab(int n)169 Array *makesymtab(int n) /* make a new symbol table */
170 {
171 Array *ap;
172 Cell **tp;
173
174 ap = malloc(sizeof(*ap));
175 tp = calloc(n, sizeof(*tp));
176 if (ap == NULL || tp == NULL)
177 FATAL("out of space in makesymtab");
178 ap->nelem = 0;
179 ap->size = n;
180 ap->tab = tp;
181 return(ap);
182 }
183
freesymtab(Cell * ap)184 void freesymtab(Cell *ap) /* free a symbol table */
185 {
186 Cell *cp, *temp;
187 Array *tp;
188 int i;
189
190 if (!isarr(ap))
191 return;
192 tp = (Array *) ap->sval;
193 if (tp == NULL)
194 return;
195 for (i = 0; i < tp->size; i++) {
196 for (cp = tp->tab[i]; cp != NULL; cp = temp) {
197 xfree(cp->nval);
198 if (freeable(cp))
199 xfree(cp->sval);
200 temp = cp->cnext; /* avoids freeing then using */
201 free(cp);
202 tp->nelem--;
203 }
204 tp->tab[i] = NULL;
205 }
206 if (tp->nelem != 0)
207 WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
208 free(tp->tab);
209 free(tp);
210 }
211
freeelem(Cell * ap,const char * s)212 void freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
213 {
214 Array *tp;
215 Cell *p, *prev = NULL;
216 int h;
217
218 tp = (Array *) ap->sval;
219 h = hash(s, tp->size);
220 for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
221 if (strcmp(s, p->nval) == 0) {
222 if (prev == NULL) /* 1st one */
223 tp->tab[h] = p->cnext;
224 else /* middle somewhere */
225 prev->cnext = p->cnext;
226 if (freeable(p))
227 xfree(p->sval);
228 free(p->nval);
229 free(p);
230 tp->nelem--;
231 return;
232 }
233 }
234
setsymtab(const char * n,const char * s,Awkfloat f,unsigned t,Array * tp)235 Cell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
236 {
237 int h;
238 Cell *p;
239
240 if (n != NULL && (p = lookup(n, tp)) != NULL) {
241 dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
242 (void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
243 return(p);
244 }
245 p = malloc(sizeof(*p));
246 if (p == NULL)
247 FATAL("out of space for symbol table at %s", n);
248 p->nval = tostring(n);
249 p->sval = s ? tostring(s) : tostring("");
250 p->fval = f;
251 p->tval = t;
252 p->csub = CUNK;
253 p->ctype = OCELL;
254 tp->nelem++;
255 if (tp->nelem > FULLTAB * tp->size)
256 rehash(tp);
257 h = hash(n, tp->size);
258 p->cnext = tp->tab[h];
259 tp->tab[h] = p;
260 dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
261 (void*)p, p->nval, p->sval, p->fval, p->tval) );
262 return(p);
263 }
264
hash(const char * s,int n)265 int hash(const char *s, int n) /* form hash value for string s */
266 {
267 unsigned hashval;
268
269 for (hashval = 0; *s != '\0'; s++)
270 hashval = (*s + 31 * hashval);
271 return hashval % n;
272 }
273
rehash(Array * tp)274 void rehash(Array *tp) /* rehash items in small table into big one */
275 {
276 int i, nh, nsz;
277 Cell *cp, *op, **np;
278
279 nsz = GROWTAB * tp->size;
280 np = calloc(nsz, sizeof(*np));
281 if (np == NULL) /* can't do it, but can keep running. */
282 return; /* someone else will run out later. */
283 for (i = 0; i < tp->size; i++) {
284 for (cp = tp->tab[i]; cp; cp = op) {
285 op = cp->cnext;
286 nh = hash(cp->nval, nsz);
287 cp->cnext = np[nh];
288 np[nh] = cp;
289 }
290 }
291 free(tp->tab);
292 tp->tab = np;
293 tp->size = nsz;
294 }
295
lookup(const char * s,Array * tp)296 Cell *lookup(const char *s, Array *tp) /* look for s in tp */
297 {
298 Cell *p;
299 int h;
300
301 h = hash(s, tp->size);
302 for (p = tp->tab[h]; p != NULL; p = p->cnext)
303 if (strcmp(s, p->nval) == 0)
304 return(p); /* found it */
305 return(NULL); /* not found */
306 }
307
setfval(Cell * vp,Awkfloat f)308 Awkfloat setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
309 {
310 int fldno;
311
312 f += 0.0; /* normalise negative zero to positive zero */
313 if ((vp->tval & (NUM | STR)) == 0)
314 funnyvar(vp, "assign to");
315 if (isfld(vp)) {
316 donerec = false; /* mark $0 invalid */
317 fldno = atoi(vp->nval);
318 if (fldno > *NF)
319 newfld(fldno);
320 dprintf( ("setting field %d to %g\n", fldno, f) );
321 } else if (&vp->fval == NF) {
322 donerec = false; /* mark $0 invalid */
323 setlastfld(f);
324 dprintf( ("setting NF to %g\n", f) );
325 } else if (isrec(vp)) {
326 donefld = false; /* mark $1... invalid */
327 donerec = true;
328 savefs();
329 } else if (vp == ofsloc) {
330 if (!donerec)
331 recbld();
332 }
333 if (freeable(vp))
334 xfree(vp->sval); /* free any previous string */
335 vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
336 vp->fmt = NULL;
337 vp->tval |= NUM; /* mark number ok */
338 if (f == -0) /* who would have thought this possible? */
339 f = 0;
340 dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
341 return vp->fval = f;
342 }
343
funnyvar(Cell * vp,const char * rw)344 void funnyvar(Cell *vp, const char *rw)
345 {
346 if (isarr(vp))
347 FATAL("can't %s %s; it's an array name.", rw, vp->nval);
348 if (vp->tval & FCN)
349 FATAL("can't %s %s; it's a function.", rw, vp->nval);
350 WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
351 (void *)vp, vp->nval, vp->sval, vp->fval, vp->tval);
352 }
353
setsval(Cell * vp,const char * s)354 char *setsval(Cell *vp, const char *s) /* set string val of a Cell */
355 {
356 char *t;
357 int fldno;
358 Awkfloat f;
359
360 dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
361 (void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
362 if ((vp->tval & (NUM | STR)) == 0)
363 funnyvar(vp, "assign to");
364 if (isfld(vp)) {
365 donerec = false; /* mark $0 invalid */
366 fldno = atoi(vp->nval);
367 if (fldno > *NF)
368 newfld(fldno);
369 dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
370 } else if (isrec(vp)) {
371 donefld = false; /* mark $1... invalid */
372 donerec = true;
373 savefs();
374 } else if (vp == ofsloc) {
375 if (!donerec)
376 recbld();
377 }
378 t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
379 if (freeable(vp))
380 xfree(vp->sval);
381 vp->tval &= ~(NUM|CONVC|CONVO);
382 vp->tval |= STR;
383 vp->fmt = NULL;
384 setfree(vp);
385 dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
386 (void*)vp, NN(vp->nval), t, t, vp->tval, donerec, donefld) );
387 vp->sval = t;
388 if (&vp->fval == NF) {
389 donerec = false; /* mark $0 invalid */
390 f = getfval(vp);
391 setlastfld(f);
392 dprintf( ("setting NF to %g\n", f) );
393 }
394
395 return(vp->sval);
396 }
397
checkstr(const char * s,const char * v)398 static int checkstr(const char *s, const char *v)
399 {
400 while (*s && tolower((unsigned char)*s) == *v)
401 s++, v++;
402 while (isspace((unsigned char)*s))
403 s++;
404 return !(*s || *v);
405 }
406
checkinfnan(const char * s)407 static int checkinfnan(const char *s)
408 {
409 while (isspace((unsigned char)*s))
410 s++;
411 if (*s == '+' || *s == '-')
412 s++;
413 switch (tolower((unsigned char)*s)) {
414 case 'i':
415 return checkstr(s, "inf") || checkstr(s, "infinity");
416 case 'n':
417 return checkstr(s, "nan");
418 default:
419 return 1;
420 }
421 }
422
getfval(Cell * vp)423 Awkfloat getfval(Cell *vp) /* get float val of a Cell */
424 {
425 if ((vp->tval & (NUM | STR)) == 0)
426 funnyvar(vp, "read value of");
427 if (isfld(vp) && !donefld)
428 fldbld();
429 else if (isrec(vp) && !donerec)
430 recbld();
431 if (!isnum(vp)) { /* not a number */
432 if (checkinfnan(vp->sval))
433 vp->fval = atof(vp->sval); /* best guess */
434 else
435 vp->fval = 0.0;
436 if (is_number(vp->sval) && !(vp->tval&CON)) {
437 vp->tval |= NUM; /* make NUM only sparingly */
438 }
439 }
440 dprintf( ("getfval %p: %s = %g, t=%o\n",
441 (void*)vp, NN(vp->nval), vp->fval, vp->tval) );
442 return(vp->fval);
443 }
444
get_str_val(Cell * vp,char ** fmt)445 static char *get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
446 {
447 char s[256];
448 double dtemp;
449
450 if ((vp->tval & (NUM | STR)) == 0)
451 funnyvar(vp, "read value of");
452 if (isfld(vp) && ! donefld)
453 fldbld();
454 else if (isrec(vp) && ! donerec)
455 recbld();
456
457 /*
458 * ADR: This is complicated and more fragile than is desirable.
459 * Retrieving a string value for a number associates the string
460 * value with the scalar. Previously, the string value was
461 * sticky, meaning if converted via OFMT that became the value
462 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
463 * changed after a string value was retrieved, the original value
464 * was maintained and used. Also not per POSIX.
465 *
466 * We work around this design by adding two additional flags,
467 * CONVC and CONVO, indicating how the string value was
468 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
469 * of the pointer to the xFMT format string used for the
470 * conversion. This pointer is only read, **never** dereferenced.
471 * The next time we do a conversion, if it's coming from the same
472 * xFMT as last time, and the pointer value is different, we
473 * know that the xFMT format string changed, and we need to
474 * redo the conversion. If it's the same, we don't have to.
475 *
476 * There are also several cases where we don't do a conversion,
477 * such as for a field (see the checks below).
478 */
479
480 /* Don't duplicate the code for actually updating the value */
481 #define update_str_val(vp) \
482 { \
483 if (freeable(vp)) \
484 xfree(vp->sval); \
485 if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
486 snprintf(s, sizeof (s), "%.30g", vp->fval); \
487 else \
488 snprintf(s, sizeof (s), *fmt, vp->fval); \
489 vp->sval = tostring(s); \
490 vp->tval &= ~DONTFREE; \
491 vp->tval |= STR; \
492 }
493
494 if (isstr(vp) == 0) {
495 update_str_val(vp);
496 if (fmt == OFMT) {
497 vp->tval &= ~CONVC;
498 vp->tval |= CONVO;
499 } else {
500 /* CONVFMT */
501 vp->tval &= ~CONVO;
502 vp->tval |= CONVC;
503 }
504 vp->fmt = *fmt;
505 } else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
506 goto done;
507 } else if (isstr(vp)) {
508 if (fmt == OFMT) {
509 if ((vp->tval & CONVC) != 0
510 || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
511 update_str_val(vp);
512 vp->tval &= ~CONVC;
513 vp->tval |= CONVO;
514 vp->fmt = *fmt;
515 }
516 } else {
517 /* CONVFMT */
518 if ((vp->tval & CONVO) != 0
519 || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
520 update_str_val(vp);
521 vp->tval &= ~CONVO;
522 vp->tval |= CONVC;
523 vp->fmt = *fmt;
524 }
525 }
526 }
527 done:
528 dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
529 (void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
530 return(vp->sval);
531 }
532
getsval(Cell * vp)533 char *getsval(Cell *vp) /* get string val of a Cell */
534 {
535 return get_str_val(vp, CONVFMT);
536 }
537
getpssval(Cell * vp)538 char *getpssval(Cell *vp) /* get string val of a Cell for print */
539 {
540 return get_str_val(vp, OFMT);
541 }
542
543
tostring(const char * s)544 char *tostring(const char *s) /* make a copy of string s */
545 {
546 char *p = strdup(s);
547 if (p == NULL)
548 FATAL("out of space in tostring on %s", s);
549 return(p);
550 }
551
tostringN(const char * s,size_t n)552 char *tostringN(const char *s, size_t n) /* make a copy of string s */
553 {
554 char *p;
555
556 p = malloc(n);
557 if (p == NULL)
558 FATAL("out of space in tostring on %s", s);
559 strcpy(p, s);
560 return(p);
561 }
562
catstr(Cell * a,Cell * b)563 Cell *catstr(Cell *a, Cell *b) /* concatenate a and b */
564 {
565 Cell *c;
566 char *p;
567 char *sa = getsval(a);
568 char *sb = getsval(b);
569 size_t l = strlen(sa) + strlen(sb) + 1;
570 p = malloc(l);
571 if (p == NULL)
572 FATAL("out of space concatenating %s and %s", sa, sb);
573 snprintf(p, l, "%s%s", sa, sb);
574
575 l++; // add room for ' '
576 char *newbuf = malloc(l);
577 if (newbuf == NULL)
578 FATAL("out of space concatenating %s and %s", sa, sb);
579 // See string() in lex.c; a string "xx" is stored in the symbol
580 // table as "xx ".
581 snprintf(newbuf, l, "%s ", p);
582 c = setsymtab(newbuf, p, 0.0, CON|STR|DONTFREE, symtab);
583 free(p);
584 free(newbuf);
585 return c;
586 }
587
qstring(const char * is,int delim)588 char *qstring(const char *is, int delim) /* collect string up to next delim */
589 {
590 const char *os = is;
591 int c, n;
592 const uschar *s = (const uschar *) is;
593 uschar *buf, *bp;
594
595 if ((buf = malloc(strlen(is)+3)) == NULL)
596 FATAL( "out of space in qstring(%s)", s);
597 for (bp = buf; (c = *s) != delim; s++) {
598 if (c == '\n')
599 SYNTAX( "newline in string %.20s...", os );
600 else if (c != '\\')
601 *bp++ = c;
602 else { /* \something */
603 c = *++s;
604 if (c == 0) { /* \ at end */
605 *bp++ = '\\';
606 break; /* for loop */
607 }
608 switch (c) {
609 case '\\': *bp++ = '\\'; break;
610 case 'n': *bp++ = '\n'; break;
611 case 't': *bp++ = '\t'; break;
612 case 'b': *bp++ = '\b'; break;
613 case 'f': *bp++ = '\f'; break;
614 case 'r': *bp++ = '\r'; break;
615 case 'v': *bp++ = '\v'; break;
616 case 'a': *bp++ = '\a'; break;
617 default:
618 if (!isdigit(c)) {
619 *bp++ = c;
620 break;
621 }
622 n = c - '0';
623 if (isdigit(s[1])) {
624 n = 8 * n + *++s - '0';
625 if (isdigit(s[1]))
626 n = 8 * n + *++s - '0';
627 }
628 *bp++ = n;
629 break;
630 }
631 }
632 }
633 *bp++ = 0;
634 return (char *) buf;
635 }
636
flags2str(int flags)637 const char *flags2str(int flags)
638 {
639 static const struct ftab {
640 const char *name;
641 int value;
642 } flagtab[] = {
643 { "NUM", NUM },
644 { "STR", STR },
645 { "DONTFREE", DONTFREE },
646 { "CON", CON },
647 { "ARR", ARR },
648 { "FCN", FCN },
649 { "FLD", FLD },
650 { "REC", REC },
651 { "CONVC", CONVC },
652 { "CONVO", CONVO },
653 { NULL, 0 }
654 };
655 static char buf[100];
656 int i;
657 char *cp = buf;
658
659 for (i = 0; flagtab[i].name != NULL; i++) {
660 if ((flags & flagtab[i].value) != 0) {
661 if (cp > buf)
662 *cp++ = '|';
663 strcpy(cp, flagtab[i].name);
664 cp += strlen(cp);
665 }
666 }
667
668 return buf;
669 }
670