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