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