xref: /netbsd/external/historical/nawk/dist/tran.c (revision 96570944)
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