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