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