• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  #include "ytab.h"
33  
34  #define	FULLTAB	2	/* rehash when table gets this x full */
35  #define	GROWTAB 4	/* grow table by this factor */
36  
37  Array	*symtab;	/* main symbol table */
38  
39  char	**FS;		/* initial field sep */
40  char	**RS;		/* initial record sep */
41  char	**OFS;		/* output field sep */
42  char	**ORS;		/* output record sep */
43  char	**OFMT;		/* output format for numbers */
44  char	**CONVFMT;	/* format for conversions in getsval */
45  Awkfloat *NF;		/* number of fields in current record */
46  Awkfloat *NR;		/* number of current record */
47  Awkfloat *FNR;		/* number of current record in current file */
48  char	**FILENAME;	/* current filename argument */
49  Awkfloat *ARGC;		/* number of arguments from command line */
50  char	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
51  Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
52  Awkfloat *RLENGTH;	/* length of same */
53  
54  Cell	*fsloc;		/* FS */
55  Cell	*nrloc;		/* NR */
56  Cell	*nfloc;		/* NF */
57  Cell	*fnrloc;	/* FNR */
58  Cell	*ofsloc;	/* OFS */
59  Cell	*orsloc;	/* ORS */
60  Cell	*rsloc;		/* RS */
61  Array	*ARGVtab;	/* symbol table containing ARGV[...] */
62  Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
63  Cell	*rstartloc;	/* RSTART */
64  Cell	*rlengthloc;	/* RLENGTH */
65  Cell	*subseploc;	/* SUBSEP */
66  Cell	*symtabloc;	/* SYMTAB */
67  
68  Cell	*nullloc;	/* a guaranteed empty cell */
69  Node	*nullnode;	/* zero&null, converted into a node for comparisons */
70  Cell	*literal0;
71  
72  extern Cell **fldtab;
73  
74  static void
setfree(Cell * vp)75  setfree(Cell *vp)
76  {
77  	if (&vp->sval == FS || &vp->sval == RS ||
78  	    &vp->sval == OFS || &vp->sval == ORS ||
79  	    &vp->sval == OFMT || &vp->sval == CONVFMT ||
80  	    &vp->sval == FILENAME || &vp->sval == SUBSEP)
81  		vp->tval |= DONTFREE;
82  	else
83  		vp->tval &= ~DONTFREE;
84  }
85  
syminit(void)86  void syminit(void)	/* initialize symbol table with builtin vars */
87  {
88  	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
89  	/* this is used for if(x)... tests: */
90  	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
91  	nullnode = celltonode(nullloc, CCON);
92  
93  	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
94  	FS = &fsloc->sval;
95  	rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
96  	RS = &rsloc->sval;
97  	ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
98  	OFS = &ofsloc->sval;
99  	orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
100  	ORS = &orsloc->sval;
101  	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
102  	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
103  	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
104  	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
105  	NF = &nfloc->fval;
106  	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
107  	NR = &nrloc->fval;
108  	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
109  	FNR = &fnrloc->fval;
110  	subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
111  	SUBSEP = &subseploc->sval;
112  	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
113  	RSTART = &rstartloc->fval;
114  	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
115  	RLENGTH = &rlengthloc->fval;
116  	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
117  	symtabloc->sval = (char *) symtab;
118  }
119  
arginit(int ac,char ** av)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  	cp->sval = (char *) ARGVtab;
130  	for (i = 0; i < ac; i++) {
131  		sprintf(temp, "%d", i);
132  		if (is_number(*av))
133  			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
134  		else
135  			setsymtab(temp, *av, 0.0, STR, ARGVtab);
136  		av++;
137  	}
138  }
139  
envinit(char ** envp)140  void envinit(char **envp)	/* set up ENVIRON variable */
141  {
142  	Cell *cp;
143  	char *p;
144  
145  	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
146  	ENVtab = makesymtab(NSYMTAB);
147  	cp->sval = (char *) ENVtab;
148  	for ( ; *envp; envp++) {
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))
155  			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
156  		else
157  			setsymtab(*envp, p, 0.0, STR, ENVtab);
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(Array));
168  	tp = (Cell **) calloc(n, sizeof(Cell *));
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] = 0;
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(Cell));
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(Cell *));
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 = 0;	/* 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 = 0;	/* mark $0 invalid */
316  		setlastfld(f);
317  		dprintf( ("setting NF to %g\n", f) );
318  	} else if (isrec(vp)) {
319  		donefld = 0;	/* mark $1... invalid */
320  		donerec = 1;
321  	} else if (vp == ofsloc) {
322  		if (donerec == 0)
323  			recbld();
324  	}
325  	if (freeable(vp))
326  		xfree(vp->sval); /* free any previous string */
327  	vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
328  	vp->fmt = NULL;
329  	vp->tval |= NUM;	/* mark number ok */
330  	if (f == -0)  /* who would have thought this possible? */
331  		f = 0;
332  	   dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
333  	return vp->fval = f;
334  }
335  
funnyvar(Cell * vp,const char * rw)336  void funnyvar(Cell *vp, const char *rw)
337  {
338  	if (isarr(vp))
339  		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
340  	if (vp->tval & FCN)
341  		FATAL("can't %s %s; it's a function.", rw, vp->nval);
342  	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
343  		vp, vp->nval, vp->sval, vp->fval, vp->tval);
344  }
345  
setsval(Cell * vp,const char * s)346  char *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
347  {
348  	char *t;
349  	int fldno;
350  	Awkfloat f;
351  
352  	   dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
353  		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
354  	if ((vp->tval & (NUM | STR)) == 0)
355  		funnyvar(vp, "assign to");
356  	if (isfld(vp)) {
357  		donerec = 0;	/* mark $0 invalid */
358  		fldno = atoi(vp->nval);
359  		if (fldno > *NF)
360  			newfld(fldno);
361  		   dprintf( ("setting field %d to %s (%p)\n", fldno, s, (void *) s) );
362  	} else if (isrec(vp)) {
363  		donefld = 0;	/* mark $1... invalid */
364  		donerec = 1;
365  	} else if (vp == ofsloc) {
366  		if (donerec == 0)
367  			recbld();
368  	}
369  	t = s ? tostring(s) : tostring("");	/* in case it's self-assign */
370  	if (freeable(vp))
371  		xfree(vp->sval);
372  	vp->tval &= ~(NUM|CONVC|CONVO);
373  	vp->tval |= STR;
374  	vp->fmt = NULL;
375  	setfree(vp);
376  	   dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
377  		(void*)vp, NN(vp->nval), t, (void *) t, vp->tval, donerec, donefld) );
378  	vp->sval = t;
379  	if (&vp->fval == NF) {
380  		donerec = 0;	/* mark $0 invalid */
381  		f = getfval(vp);
382  		setlastfld(f);
383  		dprintf( ("setting NF to %g\n", f) );
384  	}
385  
386  	return(vp->sval);
387  }
388  
getfval(Cell * vp)389  Awkfloat getfval(Cell *vp)	/* get float val of a Cell */
390  {
391  	if ((vp->tval & (NUM | STR)) == 0)
392  		funnyvar(vp, "read value of");
393  	if (isfld(vp) && donefld == 0)
394  		fldbld();
395  	else if (isrec(vp) && donerec == 0)
396  		recbld();
397  	if (!isnum(vp)) {	/* not a number */
398  		vp->fval = atof(vp->sval);	/* best guess */
399  		if (is_number(vp->sval) && !(vp->tval&CON))
400  			vp->tval |= NUM;	/* make NUM only sparingly */
401  	}
402  	   dprintf( ("getfval %p: %s = %g, t=%o\n",
403  		(void*)vp, NN(vp->nval), vp->fval, vp->tval) );
404  	return(vp->fval);
405  }
406  
get_str_val(Cell * vp,char ** fmt)407  static char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
408  {
409  	char s[256];
410  	double dtemp;
411  
412  	if ((vp->tval & (NUM | STR)) == 0)
413  		funnyvar(vp, "read value of");
414  	if (isfld(vp) && donefld == 0)
415  		fldbld();
416  	else if (isrec(vp) && donerec == 0)
417  		recbld();
418  
419  	/*
420  	 * ADR: This is complicated and more fragile than is desirable.
421  	 * Retrieving a string value for a number associates the string
422  	 * value with the scalar.  Previously, the string value was
423  	 * sticky, meaning if converted via OFMT that became the value
424  	 * (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
425  	 * changed after a string value was retrieved, the original value
426  	 * was maintained and used.  Also not per POSIX.
427  	 *
428  	 * We work around this design by adding two additional flags,
429  	 * CONVC and CONVO, indicating how the string value was
430  	 * obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
431  	 * of the pointer to the xFMT format string used for the
432  	 * conversion.  This pointer is only read, **never** dereferenced.
433  	 * The next time we do a conversion, if it's coming from the same
434  	 * xFMT as last time, and the pointer value is different, we
435  	 * know that the xFMT format string changed, and we need to
436  	 * redo the conversion. If it's the same, we don't have to.
437  	 *
438  	 * There are also several cases where we don't do a conversion,
439  	 * such as for a field (see the checks below).
440  	 */
441  
442  	/* Don't duplicate the code for actually updating the value */
443  #define update_str_val(vp) \
444  	{ \
445  		if (freeable(vp)) \
446  			xfree(vp->sval); \
447  		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */ \
448  			snprintf(s, sizeof (s), "%.30g", vp->fval); \
449  		else \
450  			snprintf(s, sizeof (s), *fmt, vp->fval); \
451  		vp->sval = tostring(s); \
452  		vp->tval &= ~DONTFREE; \
453  		vp->tval |= STR; \
454  	}
455  
456  	if (isstr(vp) == 0) {
457  		update_str_val(vp);
458  		if (fmt == OFMT) {
459  			vp->tval &= ~CONVC;
460  			vp->tval |= CONVO;
461  		} else {
462  			/* CONVFMT */
463  			vp->tval &= ~CONVO;
464  			vp->tval |= CONVC;
465  		}
466  		vp->fmt = *fmt;
467  	} else if ((vp->tval & DONTFREE) != 0 || ! isnum(vp) || isfld(vp)) {
468  		goto done;
469  	} else if (isstr(vp)) {
470  		if (fmt == OFMT) {
471  			if ((vp->tval & CONVC) != 0
472  			    || ((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
473  				update_str_val(vp);
474  				vp->tval &= ~CONVC;
475  				vp->tval |= CONVO;
476  				vp->fmt = *fmt;
477  			}
478  		} else {
479  			/* CONVFMT */
480  			if ((vp->tval & CONVO) != 0
481  			    || ((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
482  				update_str_val(vp);
483  				vp->tval &= ~CONVO;
484  				vp->tval |= CONVC;
485  				vp->fmt = *fmt;
486  			}
487  		}
488  	}
489  done:
490  	   dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
491  		(void*)vp, NN(vp->nval), vp->sval, (void *) vp->sval, vp->tval) );
492  	return(vp->sval);
493  }
494  
getsval(Cell * vp)495  char *getsval(Cell *vp)       /* get string val of a Cell */
496  {
497        return get_str_val(vp, CONVFMT);
498  }
499  
getpssval(Cell * vp)500  char *getpssval(Cell *vp)     /* get string val of a Cell for print */
501  {
502        return get_str_val(vp, OFMT);
503  }
504  
505  
tostring(const char * s)506  char *tostring(const char *s)	/* make a copy of string s */
507  {
508  	char *p;
509  
510  	p = (char *) malloc(strlen(s)+1);
511  	if (p == NULL)
512  		FATAL("out of space in tostring on %s", s);
513  	strcpy(p, s);
514  	return(p);
515  }
516  
qstring(const char * is,int delim)517  char *qstring(const char *is, int delim)	/* collect string up to next delim */
518  {
519  	const char *os = is;
520  	int c, n;
521  	uschar *s = (uschar *) is;
522  	uschar *buf, *bp;
523  
524  	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
525  		FATAL( "out of space in qstring(%s)", s);
526  	for (bp = buf; (c = *s) != delim; s++) {
527  		if (c == '\n')
528  			SYNTAX( "newline in string %.20s...", os );
529  		else if (c != '\\')
530  			*bp++ = c;
531  		else {	/* \something */
532  			c = *++s;
533  			if (c == 0) {	/* \ at end */
534  				*bp++ = '\\';
535  				break;	/* for loop */
536  			}
537  			switch (c) {
538  			case '\\':	*bp++ = '\\'; break;
539  			case 'n':	*bp++ = '\n'; break;
540  			case 't':	*bp++ = '\t'; break;
541  			case 'b':	*bp++ = '\b'; break;
542  			case 'f':	*bp++ = '\f'; break;
543  			case 'r':	*bp++ = '\r'; break;
544  			default:
545  				if (!isdigit(c)) {
546  					*bp++ = c;
547  					break;
548  				}
549  				n = c - '0';
550  				if (isdigit(s[1])) {
551  					n = 8 * n + *++s - '0';
552  					if (isdigit(s[1]))
553  						n = 8 * n + *++s - '0';
554  				}
555  				*bp++ = n;
556  				break;
557  			}
558  		}
559  	}
560  	*bp++ = 0;
561  	return (char *) buf;
562  }
563  
flags2str(int flags)564  const char *flags2str(int flags)
565  {
566  	static const struct ftab {
567  		const char *name;
568  		int value;
569  	} flagtab[] = {
570  		{ "NUM", NUM },
571  		{ "STR", STR },
572  		{ "DONTFREE", DONTFREE },
573  		{ "CON", CON },
574  		{ "ARR", ARR },
575  		{ "FCN", FCN },
576  		{ "FLD", FLD },
577  		{ "REC", REC },
578  		{ "CONVC", CONVC },
579  		{ "CONVO", CONVO },
580  		{ NULL, 0 }
581  	};
582  	static char buf[100];
583  	int i;
584  	char *cp = buf;
585  
586  	for (i = 0; flagtab[i].name != NULL; i++) {
587  		if ((flags & flagtab[i].value) != 0) {
588  			if (cp > buf)
589  				*cp++ = '|';
590  			strcpy(cp, flagtab[i].name);
591  			cp += strlen(cp);
592  		}
593  	}
594  
595  	return buf;
596  }
597