185587Sobrien/****************************************************************
285587SobrienCopyright (C) Lucent Technologies 1997
385587SobrienAll Rights Reserved
485587Sobrien
585587SobrienPermission to use, copy, modify, and distribute this software and
685587Sobrienits documentation for any purpose and without fee is hereby
785587Sobriengranted, provided that the above copyright notice appear in all
885587Sobriencopies and that both that the copyright notice and this
985587Sobrienpermission notice and warranty disclaimer appear in supporting
1085587Sobriendocumentation, and that the name Lucent Technologies or any of
1185587Sobrienits entities not be used in advertising or publicity pertaining
1285587Sobriento distribution of the software without specific, written prior
1385587Sobrienpermission.
1485587Sobrien
1585587SobrienLUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1685587SobrienINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
1785587SobrienIN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
1885587SobrienSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1985587SobrienWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
2085587SobrienIN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2185587SobrienARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
2285587SobrienTHIS SOFTWARE.
2385587Sobrien****************************************************************/
2485587Sobrien
2585587Sobrien#define	DEBUG
2685587Sobrien#include <stdio.h>
2785587Sobrien#include <math.h>
2885587Sobrien#include <ctype.h>
2985587Sobrien#include <string.h>
3085587Sobrien#include <stdlib.h>
3185587Sobrien#include "awk.h"
3285587Sobrien#include "ytab.h"
3385587Sobrien
3485587Sobrien#define	FULLTAB	2	/* rehash when table gets this x full */
3585587Sobrien#define	GROWTAB 4	/* grow table by this factor */
3685587Sobrien
3785587SobrienArray	*symtab;	/* main symbol table */
3885587Sobrien
3985587Sobrienchar	**FS;		/* initial field sep */
4085587Sobrienchar	**RS;		/* initial record sep */
4185587Sobrienchar	**OFS;		/* output field sep */
4285587Sobrienchar	**ORS;		/* output record sep */
4385587Sobrienchar	**OFMT;		/* output format for numbers */
4485587Sobrienchar	**CONVFMT;	/* format for conversions in getsval */
4585587SobrienAwkfloat *NF;		/* number of fields in current record */
4685587SobrienAwkfloat *NR;		/* number of current record */
4785587SobrienAwkfloat *FNR;		/* number of current record in current file */
4885587Sobrienchar	**FILENAME;	/* current filename argument */
4985587SobrienAwkfloat *ARGC;		/* number of arguments from command line */
5085587Sobrienchar	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
5185587SobrienAwkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
5285587SobrienAwkfloat *RLENGTH;	/* length of same */
5385587Sobrien
54146299SruCell	*fsloc;		/* FS */
5585587SobrienCell	*nrloc;		/* NR */
5685587SobrienCell	*nfloc;		/* NF */
5785587SobrienCell	*fnrloc;	/* FNR */
5885587SobrienArray	*ARGVtab;	/* symbol table containing ARGV[...] */
5985587SobrienArray	*ENVtab;	/* symbol table containing ENVIRON[...] */
6085587SobrienCell	*rstartloc;	/* RSTART */
6185587SobrienCell	*rlengthloc;	/* RLENGTH */
6285587SobrienCell	*symtabloc;	/* SYMTAB */
6385587Sobrien
6485587SobrienCell	*nullloc;	/* a guaranteed empty cell */
6585587SobrienNode	*nullnode;	/* zero&null, converted into a node for comparisons */
6685587SobrienCell	*literal0;
6785587Sobrien
6885587Sobrienextern Cell **fldtab;
6985587Sobrien
7085587Sobrienvoid syminit(void)	/* initialize symbol table with builtin vars */
7185587Sobrien{
7285587Sobrien	literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
7385587Sobrien	/* this is used for if(x)... tests: */
7485587Sobrien	nullloc = setsymtab("$zero&null", "", 0.0, NUM|STR|CON|DONTFREE, symtab);
7585587Sobrien	nullnode = celltonode(nullloc, CCON);
7685587Sobrien
77146299Sru	fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
78146299Sru	FS = &fsloc->sval;
7985587Sobrien	RS = &setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
8085587Sobrien	OFS = &setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab)->sval;
8185587Sobrien	ORS = &setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab)->sval;
8285587Sobrien	OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
8385587Sobrien	CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
8485587Sobrien	FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
8585587Sobrien	nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
8685587Sobrien	NF = &nfloc->fval;
8785587Sobrien	nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
8885587Sobrien	NR = &nrloc->fval;
8985587Sobrien	fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
9085587Sobrien	FNR = &fnrloc->fval;
9185587Sobrien	SUBSEP = &setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab)->sval;
9285587Sobrien	rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
9385587Sobrien	RSTART = &rstartloc->fval;
9485587Sobrien	rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
9585587Sobrien	RLENGTH = &rlengthloc->fval;
9685587Sobrien	symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
9785587Sobrien	symtabloc->sval = (char *) symtab;
9885587Sobrien}
9985587Sobrien
10085587Sobrienvoid arginit(int ac, char **av)	/* set up ARGV and ARGC */
10185587Sobrien{
10285587Sobrien	Cell *cp;
10385587Sobrien	int i;
10485587Sobrien	char temp[50];
10585587Sobrien
10685587Sobrien	ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval;
10785587Sobrien	cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
10885587Sobrien	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
10985587Sobrien	cp->sval = (char *) ARGVtab;
11085587Sobrien	for (i = 0; i < ac; i++) {
11185587Sobrien		sprintf(temp, "%d", i);
11285587Sobrien		if (is_number(*av))
11385587Sobrien			setsymtab(temp, *av, atof(*av), STR|NUM, ARGVtab);
11485587Sobrien		else
11585587Sobrien			setsymtab(temp, *av, 0.0, STR, ARGVtab);
11685587Sobrien		av++;
11785587Sobrien	}
11885587Sobrien}
11985587Sobrien
12085587Sobrienvoid envinit(char **envp)	/* set up ENVIRON variable */
12185587Sobrien{
12285587Sobrien	Cell *cp;
12385587Sobrien	char *p;
12485587Sobrien
12585587Sobrien	cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
12685587Sobrien	ENVtab = makesymtab(NSYMTAB);
12785587Sobrien	cp->sval = (char *) ENVtab;
12885587Sobrien	for ( ; *envp; envp++) {
12985587Sobrien		if ((p = strchr(*envp, '=')) == NULL)
13085587Sobrien			continue;
13185587Sobrien		if( p == *envp ) /* no left hand side name in env string */
13285587Sobrien			continue;
13385587Sobrien		*p++ = 0;	/* split into two strings at = */
13485587Sobrien		if (is_number(p))
13585587Sobrien			setsymtab(*envp, p, atof(p), STR|NUM, ENVtab);
13685587Sobrien		else
13785587Sobrien			setsymtab(*envp, p, 0.0, STR, ENVtab);
13885587Sobrien		p[-1] = '=';	/* restore in case env is passed down to a shell */
13985587Sobrien	}
14085587Sobrien}
14185587Sobrien
14285587SobrienArray *makesymtab(int n)	/* make a new symbol table */
14385587Sobrien{
14485587Sobrien	Array *ap;
14585587Sobrien	Cell **tp;
14685587Sobrien
14785587Sobrien	ap = (Array *) malloc(sizeof(Array));
14885587Sobrien	tp = (Cell **) calloc(n, sizeof(Cell *));
14985587Sobrien	if (ap == NULL || tp == NULL)
15085587Sobrien		FATAL("out of space in makesymtab");
15185587Sobrien	ap->nelem = 0;
15285587Sobrien	ap->size = n;
15385587Sobrien	ap->tab = tp;
15485587Sobrien	return(ap);
15585587Sobrien}
15685587Sobrien
15785587Sobrienvoid freesymtab(Cell *ap)	/* free a symbol table */
15885587Sobrien{
15985587Sobrien	Cell *cp, *temp;
16085587Sobrien	Array *tp;
16185587Sobrien	int i;
16285587Sobrien
16385587Sobrien	if (!isarr(ap))
16485587Sobrien		return;
16585587Sobrien	tp = (Array *) ap->sval;
16685587Sobrien	if (tp == NULL)
16785587Sobrien		return;
16885587Sobrien	for (i = 0; i < tp->size; i++) {
16985587Sobrien		for (cp = tp->tab[i]; cp != NULL; cp = temp) {
17085587Sobrien			xfree(cp->nval);
17185587Sobrien			if (freeable(cp))
17285587Sobrien				xfree(cp->sval);
17385587Sobrien			temp = cp->cnext;	/* avoids freeing then using */
17485587Sobrien			free(cp);
17590902Sdes			tp->nelem--;
17685587Sobrien		}
177301289Spfg		tp->tab[i] = NULL;
17885587Sobrien	}
17990902Sdes	if (tp->nelem != 0)
18090902Sdes		WARNING("can't happen: inconsistent element count freeing %s", ap->nval);
18185587Sobrien	free(tp->tab);
18285587Sobrien	free(tp);
18385587Sobrien}
18485587Sobrien
185107806Sobrienvoid freeelem(Cell *ap, const char *s)	/* free elem s from ap (i.e., ap["s"] */
18685587Sobrien{
18785587Sobrien	Array *tp;
18885587Sobrien	Cell *p, *prev = NULL;
18985587Sobrien	int h;
19085587Sobrien
19185587Sobrien	tp = (Array *) ap->sval;
19285587Sobrien	h = hash(s, tp->size);
19385587Sobrien	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
19485587Sobrien		if (strcmp(s, p->nval) == 0) {
19585587Sobrien			if (prev == NULL)	/* 1st one */
19685587Sobrien				tp->tab[h] = p->cnext;
19785587Sobrien			else			/* middle somewhere */
19885587Sobrien				prev->cnext = p->cnext;
19985587Sobrien			if (freeable(p))
20085587Sobrien				xfree(p->sval);
20185587Sobrien			free(p->nval);
20285587Sobrien			free(p);
20385587Sobrien			tp->nelem--;
20485587Sobrien			return;
20585587Sobrien		}
20685587Sobrien}
20785587Sobrien
208107806SobrienCell *setsymtab(const char *n, const char *s, Awkfloat f, unsigned t, Array *tp)
20985587Sobrien{
21085587Sobrien	int h;
21185587Sobrien	Cell *p;
21285587Sobrien
21385587Sobrien	if (n != NULL && (p = lookup(n, tp)) != NULL) {
21485587Sobrien		   dprintf( ("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
215224731Sru			(void*)p, NN(p->nval), NN(p->sval), p->fval, p->tval) );
21685587Sobrien		return(p);
21785587Sobrien	}
21885587Sobrien	p = (Cell *) malloc(sizeof(Cell));
21985587Sobrien	if (p == NULL)
22085587Sobrien		FATAL("out of space for symbol table at %s", n);
22185587Sobrien	p->nval = tostring(n);
22285587Sobrien	p->sval = s ? tostring(s) : tostring("");
22385587Sobrien	p->fval = f;
22485587Sobrien	p->tval = t;
22585587Sobrien	p->csub = CUNK;
22685587Sobrien	p->ctype = OCELL;
22785587Sobrien	tp->nelem++;
22885587Sobrien	if (tp->nelem > FULLTAB * tp->size)
22985587Sobrien		rehash(tp);
23085587Sobrien	h = hash(n, tp->size);
23185587Sobrien	p->cnext = tp->tab[h];
23285587Sobrien	tp->tab[h] = p;
23385587Sobrien	   dprintf( ("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
234224731Sru		(void*)p, p->nval, p->sval, p->fval, p->tval) );
23585587Sobrien	return(p);
23685587Sobrien}
23785587Sobrien
238107806Sobrienint hash(const char *s, int n)	/* form hash value for string s */
23985587Sobrien{
24085587Sobrien	unsigned hashval;
24185587Sobrien
24285587Sobrien	for (hashval = 0; *s != '\0'; s++)
24385587Sobrien		hashval = (*s + 31 * hashval);
24485587Sobrien	return hashval % n;
24585587Sobrien}
24685587Sobrien
24785587Sobrienvoid rehash(Array *tp)	/* rehash items in small table into big one */
24885587Sobrien{
24985587Sobrien	int i, nh, nsz;
25085587Sobrien	Cell *cp, *op, **np;
25185587Sobrien
25285587Sobrien	nsz = GROWTAB * tp->size;
25385587Sobrien	np = (Cell **) calloc(nsz, sizeof(Cell *));
25485587Sobrien	if (np == NULL)		/* can't do it, but can keep running. */
25585587Sobrien		return;		/* someone else will run out later. */
25685587Sobrien	for (i = 0; i < tp->size; i++) {
25785587Sobrien		for (cp = tp->tab[i]; cp; cp = op) {
25885587Sobrien			op = cp->cnext;
25985587Sobrien			nh = hash(cp->nval, nsz);
26085587Sobrien			cp->cnext = np[nh];
26185587Sobrien			np[nh] = cp;
26285587Sobrien		}
26385587Sobrien	}
26485587Sobrien	free(tp->tab);
26585587Sobrien	tp->tab = np;
26685587Sobrien	tp->size = nsz;
26785587Sobrien}
26885587Sobrien
269107806SobrienCell *lookup(const char *s, Array *tp)	/* look for s in tp */
27085587Sobrien{
27185587Sobrien	Cell *p;
27285587Sobrien	int h;
27385587Sobrien
27485587Sobrien	h = hash(s, tp->size);
27585587Sobrien	for (p = tp->tab[h]; p != NULL; p = p->cnext)
27685587Sobrien		if (strcmp(s, p->nval) == 0)
27785587Sobrien			return(p);	/* found it */
27885587Sobrien	return(NULL);			/* not found */
27985587Sobrien}
28085587Sobrien
28185587SobrienAwkfloat setfval(Cell *vp, Awkfloat f)	/* set float val of a Cell */
28285587Sobrien{
28385587Sobrien	int fldno;
28485587Sobrien
28585587Sobrien	if ((vp->tval & (NUM | STR)) == 0)
28685587Sobrien		funnyvar(vp, "assign to");
28785587Sobrien	if (isfld(vp)) {
28885587Sobrien		donerec = 0;	/* mark $0 invalid */
28985587Sobrien		fldno = atoi(vp->nval);
29085587Sobrien		if (fldno > *NF)
29185587Sobrien			newfld(fldno);
29285587Sobrien		   dprintf( ("setting field %d to %g\n", fldno, f) );
29385587Sobrien	} else if (isrec(vp)) {
29485587Sobrien		donefld = 0;	/* mark $1... invalid */
29585587Sobrien		donerec = 1;
29685587Sobrien	}
29785587Sobrien	if (freeable(vp))
29885587Sobrien		xfree(vp->sval); /* free any previous string */
29985587Sobrien	vp->tval &= ~STR;	/* mark string invalid */
30085587Sobrien	vp->tval |= NUM;	/* mark number ok */
301244988Sdelphij	if (f == -0)  /* who would have thought this possible? */
302244988Sdelphij		f = 0;
303224731Sru	   dprintf( ("setfval %p: %s = %g, t=%o\n", (void*)vp, NN(vp->nval), f, vp->tval) );
30485587Sobrien	return vp->fval = f;
30585587Sobrien}
30685587Sobrien
307107806Sobrienvoid funnyvar(Cell *vp, const char *rw)
30885587Sobrien{
30985587Sobrien	if (isarr(vp))
31085587Sobrien		FATAL("can't %s %s; it's an array name.", rw, vp->nval);
31185587Sobrien	if (vp->tval & FCN)
31285587Sobrien		FATAL("can't %s %s; it's a function.", rw, vp->nval);
31385587Sobrien	WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
31485587Sobrien		vp, vp->nval, vp->sval, vp->fval, vp->tval);
31585587Sobrien}
31685587Sobrien
317107806Sobrienchar *setsval(Cell *vp, const char *s)	/* set string val of a Cell */
31885587Sobrien{
31985587Sobrien	char *t;
32085587Sobrien	int fldno;
32185587Sobrien
322146299Sru	   dprintf( ("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
323224731Sru		(void*)vp, NN(vp->nval), s, vp->tval, donerec, donefld) );
32485587Sobrien	if ((vp->tval & (NUM | STR)) == 0)
32585587Sobrien		funnyvar(vp, "assign to");
32685587Sobrien	if (isfld(vp)) {
32785587Sobrien		donerec = 0;	/* mark $0 invalid */
32885587Sobrien		fldno = atoi(vp->nval);
32985587Sobrien		if (fldno > *NF)
33085587Sobrien			newfld(fldno);
33185587Sobrien		   dprintf( ("setting field %d to %s (%p)\n", fldno, s, s) );
33285587Sobrien	} else if (isrec(vp)) {
33385587Sobrien		donefld = 0;	/* mark $1... invalid */
33485587Sobrien		donerec = 1;
33585587Sobrien	}
336301691Spfg	t = tostring(s);	/* in case it's self-assign */
337172958Sobrien	if (freeable(vp))
338172958Sobrien		xfree(vp->sval);
33985587Sobrien	vp->tval &= ~NUM;
34085587Sobrien	vp->tval |= STR;
34185587Sobrien	vp->tval &= ~DONTFREE;
342146299Sru	   dprintf( ("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
343224731Sru		(void*)vp, NN(vp->nval), t,t, vp->tval, donerec, donefld) );
34485587Sobrien	return(vp->sval = t);
34585587Sobrien}
34685587Sobrien
34785587SobrienAwkfloat getfval(Cell *vp)	/* get float val of a Cell */
34885587Sobrien{
34985587Sobrien	if ((vp->tval & (NUM | STR)) == 0)
35085587Sobrien		funnyvar(vp, "read value of");
35185587Sobrien	if (isfld(vp) && donefld == 0)
35285587Sobrien		fldbld();
35385587Sobrien	else if (isrec(vp) && donerec == 0)
35485587Sobrien		recbld();
35585587Sobrien	if (!isnum(vp)) {	/* not a number */
35685587Sobrien		vp->fval = atof(vp->sval);	/* best guess */
35785587Sobrien		if (is_number(vp->sval) && !(vp->tval&CON))
35885587Sobrien			vp->tval |= NUM;	/* make NUM only sparingly */
35985587Sobrien	}
360224731Sru	   dprintf( ("getfval %p: %s = %g, t=%o\n",
361224731Sru		(void*)vp, NN(vp->nval), vp->fval, vp->tval) );
36285587Sobrien	return(vp->fval);
36385587Sobrien}
36485587Sobrien
365146299Srustatic char *get_str_val(Cell *vp, char **fmt)        /* get string val of a Cell */
36685587Sobrien{
36785587Sobrien	char s[100];	/* BUG: unchecked */
36885587Sobrien	double dtemp;
36985587Sobrien
37085587Sobrien	if ((vp->tval & (NUM | STR)) == 0)
37185587Sobrien		funnyvar(vp, "read value of");
37285587Sobrien	if (isfld(vp) && donefld == 0)
37385587Sobrien		fldbld();
37485587Sobrien	else if (isrec(vp) && donerec == 0)
37585587Sobrien		recbld();
37685587Sobrien	if (isstr(vp) == 0) {
37785587Sobrien		if (freeable(vp))
37885587Sobrien			xfree(vp->sval);
37985587Sobrien		if (modf(vp->fval, &dtemp) == 0)	/* it's integral */
38085587Sobrien			sprintf(s, "%.30g", vp->fval);
38185587Sobrien		else
382107806Sobrien			sprintf(s, *fmt, vp->fval);
38385587Sobrien		vp->sval = tostring(s);
38485587Sobrien		vp->tval &= ~DONTFREE;
38585587Sobrien		vp->tval |= STR;
38685587Sobrien	}
387224731Sru	   dprintf( ("getsval %p: %s = \"%s (%p)\", t=%o\n",
388224731Sru		(void*)vp, NN(vp->nval), vp->sval, vp->sval, vp->tval) );
38985587Sobrien	return(vp->sval);
39085587Sobrien}
39185587Sobrien
392107806Sobrienchar *getsval(Cell *vp)       /* get string val of a Cell */
39385587Sobrien{
394107806Sobrien      return get_str_val(vp, CONVFMT);
395107806Sobrien}
396107806Sobrien
397107806Sobrienchar *getpssval(Cell *vp)     /* get string val of a Cell for print */
398107806Sobrien{
399107806Sobrien      return get_str_val(vp, OFMT);
400107806Sobrien}
401107806Sobrien
402107806Sobrien
403107806Sobrienchar *tostring(const char *s)	/* make a copy of string s */
404107806Sobrien{
40585587Sobrien	char *p;
40685587Sobrien
40785587Sobrien	p = (char *) malloc(strlen(s)+1);
40885587Sobrien	if (p == NULL)
40985587Sobrien		FATAL("out of space in tostring on %s", s);
41085587Sobrien	strcpy(p, s);
41185587Sobrien	return(p);
41285587Sobrien}
41385587Sobrien
414107806Sobrienchar *qstring(const char *is, int delim)	/* collect string up to next delim */
41585587Sobrien{
416107806Sobrien	const char *os = is;
41785587Sobrien	int c, n;
41885587Sobrien	uschar *s = (uschar *) is;
41985587Sobrien	uschar *buf, *bp;
42085587Sobrien
42190902Sdes	if ((buf = (uschar *) malloc(strlen(is)+3)) == NULL)
42285587Sobrien		FATAL( "out of space in qstring(%s)", s);
42385587Sobrien	for (bp = buf; (c = *s) != delim; s++) {
42485587Sobrien		if (c == '\n')
42585587Sobrien			SYNTAX( "newline in string %.20s...", os );
42685587Sobrien		else if (c != '\\')
42785587Sobrien			*bp++ = c;
42885587Sobrien		else {	/* \something */
42985587Sobrien			c = *++s;
43085587Sobrien			if (c == 0) {	/* \ at end */
43185587Sobrien				*bp++ = '\\';
43285587Sobrien				break;	/* for loop */
43385587Sobrien			}
43485587Sobrien			switch (c) {
43585587Sobrien			case '\\':	*bp++ = '\\'; break;
43685587Sobrien			case 'n':	*bp++ = '\n'; break;
43785587Sobrien			case 't':	*bp++ = '\t'; break;
43885587Sobrien			case 'b':	*bp++ = '\b'; break;
43985587Sobrien			case 'f':	*bp++ = '\f'; break;
44085587Sobrien			case 'r':	*bp++ = '\r'; break;
44185587Sobrien			default:
44285587Sobrien				if (!isdigit(c)) {
44385587Sobrien					*bp++ = c;
44485587Sobrien					break;
44585587Sobrien				}
44685587Sobrien				n = c - '0';
44785587Sobrien				if (isdigit(s[1])) {
44885587Sobrien					n = 8 * n + *++s - '0';
44985587Sobrien					if (isdigit(s[1]))
45085587Sobrien						n = 8 * n + *++s - '0';
45185587Sobrien				}
45285587Sobrien				*bp++ = n;
45385587Sobrien				break;
45485587Sobrien			}
45585587Sobrien		}
45685587Sobrien	}
45785587Sobrien	*bp++ = 0;
45885587Sobrien	return (char *) buf;
45985587Sobrien}
460