1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * awk -- process input files, field extraction, output 24 * 25 * Copyright (c) 1995, 1996-2000 by Sun Microsystems, Inc. 26 * All rights reserved. 27 * 28 * Copyright 1986, 1994 by Mortice Kern Systems Inc. All rights reserved. 29 * 30 * Based on MKS awk(1) ported to be /usr/xpg4/bin/awk with POSIX/XCU4 changes 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "awk.h" 36 #include "y.tab.h" 37 38 static FILE *awkinfp; /* Input file pointer */ 39 static int reclen; /* Length of last record */ 40 static int exstat; /* Exit status */ 41 42 static FILE *openfile(NODE *np, int flag, int fatal); 43 static FILE *newfile(void); 44 static NODE *nextarg(NODE **npp); 45 static void adjust_buf(wchar_t **,int *, wchar_t **, char *, size_t); 46 static void awk_putwc(wchar_t, FILE *); 47 48 /* 49 * mainline for awk execution 50 */ 51 void 52 awk() 53 { 54 running = 1; 55 dobegin(); 56 while (nextrecord(linebuf, awkinfp) > 0) 57 execute(yytree); 58 doend(exstat); 59 } 60 61 /* 62 * "cp" is the buffer to fill. There is a special case if this buffer is 63 * "linebuf" ($0) 64 * Return 1 if OK, zero on EOF, -1 on error. 65 */ 66 int 67 nextrecord(wchar_t *cp, FILE *fp) 68 { 69 wchar_t *ep = cp; 70 71 nextfile: 72 if (fp==FNULL && (fp=newfile())==FNULL) 73 return (0); 74 if ((*awkrecord)(ep, NLINE, fp) == NULL) { 75 if (fp == awkinfp) { 76 if (fp != stdin) 77 (void)fclose(awkinfp); 78 awkinfp = fp = FNULL; 79 goto nextfile; 80 } 81 if (ferror(fp)) 82 return (-1); 83 return (0); 84 } 85 if (fp == awkinfp) { 86 if (varNR->n_flags & FINT) 87 ++varNR->n_int; else 88 (void)exprreduce(incNR); 89 if (varFNR->n_flags & FINT) 90 ++varFNR->n_int; else 91 (void)exprreduce(incFNR); 92 } 93 if (cp == linebuf) { 94 lbuflen = reclen; 95 splitdone = 0; 96 if (needsplit) 97 fieldsplit(); 98 } 99 /* if record length is too long then bail out */ 100 if (reclen > NLINE - 2) { 101 awkerr(gettext("Record too long (LIMIT: %d bytes)"), 102 NLINE - 1); 103 /* Not Reached */ 104 } 105 return (1); 106 } 107 108 /* 109 * Return the next file from the command line. 110 * Return FNULL when no more files. 111 * Sets awkinfp variable to the new current input file. 112 */ 113 static FILE * 114 newfile() 115 { 116 static int argindex = 1; 117 static int filedone; 118 register wchar_t *ap; 119 register int argc; 120 wchar_t *arg; 121 extern void strescape(wchar_t *); 122 123 argc = (int)exprint(varARGC); 124 for (;;) { 125 if (argindex >= argc) { 126 if (filedone) 127 return (FNULL); 128 ++filedone; 129 awkinfp = stdin; 130 arg = M_MB_L("-"); 131 break; 132 } 133 constant->n_int = argindex++; 134 arg = (wchar_t *)exprstring(ARGVsubi); 135 if ((ap = wcschr(arg, '=')) != NULL) { 136 *ap = '\0'; 137 strescape(ap+1); 138 strassign(vlook(arg),linebuf,FALLOC|FSENSE, 139 wcslen(linebuf)); 140 *ap = '='; 141 continue; 142 } 143 if (arg[0] == '\0') 144 continue; 145 ++filedone; 146 if (arg[0]=='-' && arg[1]=='\0') { 147 awkinfp = stdin; 148 break; 149 } 150 if ((awkinfp = fopen(mbunconvert(arg), r)) == FNULL) { 151 (void) fprintf(stderr, gettext("input file \"%s\""), 152 mbunconvert(arg)); 153 exstat = 1; 154 continue; 155 } 156 break; 157 } 158 strassign(varFILENAME, arg, FALLOC, wcslen(arg)); 159 if (varFNR->n_flags & FINT) 160 varFNR->n_int = 0; else 161 (void)exprreduce(clrFNR); 162 return (awkinfp); 163 } 164 165 /* 166 * Default record reading code 167 * Uses fgets for potential speedups found in some (e.g. MKS) 168 * stdio packages. 169 */ 170 wchar_t * 171 defrecord(wchar_t *bp, int lim, FILE *fp) 172 { 173 register wchar_t *endp; 174 175 if (fgetws(bp, lim, fp) == NULL) { 176 *bp = '\0'; 177 return (NULL); 178 } 179 /* XXXX 180 switch (fgetws(bp, lim, fp)) { 181 case M_FGETS_EOF: 182 *bp = '\0'; 183 return (NULL); 184 case M_FGETS_BINARY: 185 awkerr(gettext("file is binary")); 186 case M_FGETS_LONG: 187 awkerr(gettext("line too long: limit %d"), 188 lim); 189 case M_FGETS_ERROR: 190 awkperr(gettext("error reading file")); 191 } 192 */ 193 194 if (*(endp = (bp + (reclen = wcslen(bp))-1)) == '\n') { 195 *endp = '\0'; 196 reclen--; 197 } 198 return (bp); 199 } 200 201 /* 202 * Read a record separated by one character in the RS. 203 * Compatible calling sequence with fgets, but don't include 204 * record separator character in string. 205 */ 206 wchar_t * 207 charrecord(wchar_t *abp, int alim, FILE *fp) 208 { 209 register wchar_t *bp; 210 register wint_t c; 211 register int limit = alim; 212 register wint_t endc; 213 214 bp = abp; 215 endc = *(wchar_t *)varRS->n_string; 216 while (--limit>0 && (c = getwc(fp))!=endc && c!=WEOF) 217 *bp++ = c; 218 *bp = '\0'; 219 reclen = bp-abp; 220 return (c==WEOF && bp==abp ? NULL : abp); 221 } 222 223 /* 224 * Special routine for multiple line records. 225 */ 226 wchar_t * 227 multirecord(wchar_t *abp, int limit, FILE *fp) 228 { 229 register wchar_t *bp; 230 register int c; 231 232 while ((c = getwc(fp)) == '\n') 233 ; 234 bp = abp; 235 if (c != WEOF) do { 236 if (--limit == 0) 237 break; 238 if (c=='\n' && bp[-1]=='\n') 239 break; 240 241 *bp++ = c; 242 } while ((c = getwc(fp)) != WEOF); 243 *bp = '\0'; 244 if (bp > abp) 245 *--bp = '\0'; 246 reclen = bp-abp; 247 return (c==WEOF && bp==abp ? NULL : abp); 248 } 249 250 /* 251 * Look for fields separated by spaces, tabs or newlines. 252 * Extract the next field, given pointer to start address. 253 * Return pointer to beginning of field or NULL. 254 * Reset end of field reference, which is the beginning of the 255 * next field. 256 */ 257 wchar_t * 258 whitefield(wchar_t **endp) 259 { 260 register wchar_t *sp; 261 register wchar_t *ep; 262 263 sp = *endp; 264 while (*sp==' ' || *sp=='\t' || *sp=='\n') 265 ++sp; 266 if (*sp == '\0') 267 return (NULL); 268 for (ep = sp; *ep!=' ' && *ep!='\0' && *ep!='\t' && *ep!='\n'; ++ep) 269 ; 270 *endp = ep; 271 return (sp); 272 } 273 274 /* 275 * Look for fields separated by non-whitespace characters. 276 * Same calling sequence as whitefield(). 277 */ 278 wchar_t * 279 blackfield(wchar_t **endp) 280 { 281 register wchar_t *cp; 282 register int endc; 283 284 endc = *(wchar_t *)varFS->n_string; 285 cp = *endp; 286 if (*cp == '\0') 287 return (NULL); 288 if (*cp==endc && fcount!=0) 289 cp++; 290 if ((*endp = wcschr(cp, endc)) == NULL) 291 *endp = wcschr(cp, '\0'); 292 return (cp); 293 } 294 295 /* 296 * This field separation routine uses the same logic as 297 * blackfield but uses a regular expression to separate 298 * the fields. 299 */ 300 wchar_t * 301 refield(wchar_t **endpp) 302 { 303 register wchar_t *cp, *start; 304 register int flags; 305 static REGWMATCH_T match[10]; 306 int result; 307 308 cp = *endpp; 309 if (*cp == '\0') { 310 match[0].rm_ep = NULL; 311 return (NULL); 312 } 313 if (match[0].rm_ep != NULL) { 314 flags = REG_NOTBOL; 315 cp = (wchar_t *)match[0].rm_ep; 316 } else 317 flags = 0; 318 start = cp; 319 again: 320 switch ((result = REGWEXEC(resep, cp, 10, match, flags))) { 321 case REG_OK: 322 /* 323 * Check to see if a null string was matched. If this is the 324 * case, then move the current pointer beyond this position. 325 */ 326 if (match[0].rm_sp == match[0].rm_ep) { 327 cp = (wchar_t *)match[0].rm_sp; 328 if (*cp++ != '\0') { 329 goto again; 330 } 331 } 332 *endpp = (wchar_t *)match[0].rm_sp; 333 break; 334 case REG_NOMATCH: 335 match[0].rm_ep = NULL; 336 *endpp = wcschr(cp, '\0'); 337 break; 338 default: 339 regerror(result, resep, (char *)linebuf, sizeof(linebuf)); 340 awkerr(gettext("error splitting record: %s"), 341 (char *)linebuf); 342 } 343 return (start); 344 } 345 346 /* 347 * do begin processing 348 */ 349 void 350 dobegin() 351 { 352 /*l 353 * Free all keyword nodes to save space. 354 */ 355 { 356 NODE *np; 357 int nbuck; 358 register NODE *knp; 359 360 np = NNULL; 361 nbuck = 0; 362 while ((knp = symwalk(&nbuck, &np)) != NNULL) 363 if (knp->n_type == KEYWORD) 364 delsymtab(knp, 1); 365 } 366 /*l 367 * Copy ENVIRON array only if needed. 368 * Note the convoluted work to assign to an array 369 * and that the temporary nodes will be freed by 370 * freetemps() because we are "running". 371 */ 372 if (needenviron) { 373 register char **app; 374 register wchar_t *name, *value; 375 register NODE *namep = stringnode(_null, FSTATIC, 0); 376 register NODE *valuep = stringnode(_null, FSTATIC, 0); 377 register NODE *ENVsubname = node(INDEX, varENVIRON, namep); 378 extern char **environ; 379 380 /* (void) m_setenv(); XXX what's this do? */ 381 for (app = environ; *app != NULL;) { 382 name = mbstowcsdup(*app++); 383 384 if ((value = wcschr(name, '=')) != NULL) { 385 *value++ = '\0'; 386 valuep->n_strlen = wcslen(value); 387 valuep->n_string = value; 388 } else { 389 valuep->n_strlen = 0; 390 valuep->n_string = _null; 391 } 392 namep->n_strlen = wcslen(namep->n_string = name); 393 (void)assign(ENVsubname, valuep); 394 if (value != NULL) 395 value[-1] = '='; 396 } 397 } 398 phase = BEGIN; 399 execute(yytree); 400 phase = 0; 401 if (npattern == 0) 402 doend(0); 403 /* 404 * Delete all pattern/action rules that are BEGIN at this 405 * point to save space. 406 * NOTE: this is not yet implemented. 407 */ 408 } 409 410 /* 411 * Do end processing. 412 * Exit with a status 413 */ 414 void 415 doend(int s) 416 { 417 register OFILE *op; 418 419 if (phase != END) { 420 phase = END; 421 awkinfp = stdin; 422 execute(yytree); 423 } 424 for (op = &ofiles[0]; op < &ofiles[NIOSTREAM]; op++) 425 if (op->f_fp != FNULL) 426 awkclose(op); 427 if (awkinfp == stdin) 428 (void) fflush(awkinfp); 429 exit(s); 430 } 431 432 /* 433 * Print statement. 434 */ 435 void 436 s_print(NODE *np) 437 { 438 register FILE *fp; 439 NODE *listp; 440 register char *ofs; 441 register int notfirst = 0; 442 443 fp = openfile(np->n_right, 1, 1); 444 if (np->n_left == NNULL) 445 (void)fputs(mbunconvert(linebuf), fp); 446 else { 447 ofs = wcstombsdup((isstring(varOFS->n_flags)) ? 448 (wchar_t *)varOFS->n_string : 449 (wchar_t *)exprstring(varOFS)); 450 listp = np->n_left; 451 while ((np = getlist(&listp)) != NNULL) { 452 if (notfirst++) 453 (void)fputs(ofs, fp); 454 np = exprreduce(np); 455 if (np->n_flags & FINT) 456 (void) fprintf(fp, "%lld", (INT)np->n_int); 457 else if (isstring(np->n_flags)) 458 (void) fprintf(fp, "%S", 459 np->n_string); 460 else 461 (void) fprintf(fp, 462 mbunconvert((wchar_t *)exprstring(varOFMT)), 463 (double)np->n_real); 464 } 465 free(ofs); 466 } 467 (void)fputs(mbunconvert(isstring(varORS->n_flags) ? 468 (wchar_t *)varORS->n_string : (wchar_t *)exprstring(varORS)), 469 fp); 470 if (ferror(fp)) 471 awkperr("error on print"); 472 } 473 474 /* 475 * printf statement. 476 */ 477 void 478 s_prf(NODE *np) 479 { 480 register FILE *fp; 481 482 fp = openfile(np->n_right, 1, 1); 483 (void)xprintf(np->n_left, fp, (wchar_t **)NULL); 484 if (ferror(fp)) 485 awkperr("error on printf"); 486 } 487 488 /* 489 * Get next input line. 490 * Read into variable on left of node (or $0 if NULL). 491 * Read from pipe or file on right of node (or from regular 492 * input if NULL). 493 * This is an oddball inasmuch as it is a function 494 * but parses more like the keywords print, etc. 495 */ 496 NODE * 497 f_getline(NODE *np) 498 { 499 register wchar_t *cp; 500 register INT ret; 501 register FILE *fp; 502 register size_t len; 503 504 if (np->n_right == NULL && phase == END) { 505 /* Pretend we've reached end of (the non-existant) file. */ 506 return intnode(0); 507 } 508 509 if ((fp = openfile(np->n_right, 0, 0)) != FNULL) { 510 if (np->n_left == NNULL) { 511 ret = nextrecord(linebuf, fp); 512 } else { 513 cp = emalloc(NLINE * sizeof(wchar_t)); 514 ret = nextrecord(cp, fp); 515 np = np->n_left; 516 len = wcslen(cp); 517 cp = erealloc(cp, (len+1)*sizeof(wchar_t)); 518 if (isleaf(np->n_flags)) { 519 if (np->n_type == PARM) 520 np = np->n_next; 521 strassign(np, cp, FNOALLOC, len); 522 } else 523 (void)assign(np, stringnode(cp, FNOALLOC, len)); 524 } 525 } else 526 ret = -1; 527 return (intnode(ret)); 528 } 529 530 /* 531 * Open a file. Flag is non-zero for output. 532 */ 533 static FILE * 534 openfile(NODE *np, int flag, int fatal) 535 { 536 register OFILE *op; 537 register char *cp; 538 register FILE *fp; 539 register int type; 540 register OFILE *fop; 541 542 if (np == NNULL) { 543 if (flag) 544 return (stdout); 545 if (awkinfp == FNULL) 546 awkinfp = newfile(); 547 return (awkinfp); 548 } 549 if ((type = np->n_type) == APPEND) 550 type = WRITE; 551 cp = mbunconvert(exprstring(np->n_left)); 552 fop = (OFILE *)NULL; 553 for (op = &ofiles[0]; op < &ofiles[NIOSTREAM]; op++) { 554 if (op->f_fp == FNULL) { 555 if (fop == (OFILE *)NULL) 556 fop = op; 557 continue; 558 } 559 if (op->f_mode == type 560 && strcmp(op->f_name, cp)==0) 561 return (op->f_fp); 562 } 563 if (fop == (OFILE *)NULL) 564 awkerr(gettext("too many open streams to %s onto \"%s\""), 565 flag ? "print/printf" : "getline", cp); 566 (void) fflush(stdout); 567 op = fop; 568 if (cp[0]=='-' && cp[1]=='\0') { 569 fp = flag ? stdout : stdin; 570 } else { 571 switch (np->n_type) { 572 case WRITE: 573 if ((fp = fopen(cp, w)) != FNULL) { 574 if (isatty(fileno(fp))) 575 (void) setvbuf(fp, 0, _IONBF, 0); 576 } 577 break; 578 579 case APPEND: 580 fp = fopen(cp, "a"); 581 break; 582 583 case PIPE: 584 fp = popen(cp, w); 585 (void) setvbuf(fp, (char *) 0, _IOLBF, 0); 586 break; 587 588 case PIPESYM: 589 fp = popen(cp, r); 590 break; 591 592 case LT: 593 fp = fopen(cp, r); 594 break; 595 596 default: 597 awkerr(interr, "openfile"); 598 } 599 } 600 if (fp != FNULL) { 601 op->f_name = strdup(cp); 602 op->f_fp = fp; 603 op->f_mode = type; 604 } else if (fatal) { 605 awkperr(flag ? gettext("output file \"%s\"") : 606 gettext("input file \"%s\""), cp); 607 } 608 return (fp); 609 } 610 611 /* 612 * Close a stream. 613 */ 614 void 615 awkclose(OFILE *op) 616 { 617 if (op->f_mode==PIPE || op->f_mode==PIPESYM) 618 (void)pclose(op->f_fp); 619 else if (fclose(op->f_fp) == EOF) 620 awkperr("error on stream \"%s\"", op->f_name); 621 op->f_fp = FNULL; 622 free(op->f_name); 623 op->f_name = NULL; 624 } 625 626 /* 627 * Internal routine common to printf, sprintf. 628 * The node is that describing the arguments. 629 * Returns the number of characters written to file 630 * pointer `fp' or the length of the string return 631 * in cp. If cp is NULL then the file pointer is used. If 632 * cp points to a string pointer, a pointer to an allocated 633 * buffer will be returned in it. 634 */ 635 size_t 636 xprintf(NODE *np, FILE *fp, wchar_t **cp) 637 { 638 register wchar_t *fmt; 639 register int c; 640 wchar_t *bptr = (wchar_t *)NULL; 641 char fmtbuf[40]; 642 register size_t length = 0; 643 register char *ofmtp; 644 register NODE *fnp; 645 register wchar_t *fmtsave; 646 int slen; 647 int cplen; 648 649 fnp = getlist(&np); 650 if (isleaf(fnp->n_flags) && fnp->n_type==PARM) 651 fnp = fnp->n_next; 652 if (isstring(fnp->n_flags)) { 653 fmt = fnp->n_string; 654 fmtsave = NULL; 655 } else 656 fmtsave = fmt = (wchar_t *)strsave(exprstring(fnp)); 657 658 /* 659 * if a char * pointer has been passed in then allocate an initial 660 * buffer for the string. Make it LINE_MAX plus the length of 661 * the format string but do reallocs only based LINE_MAX. 662 */ 663 if (cp != (wchar_t **)NULL) { 664 cplen = LINE_MAX; 665 bptr = *cp = emalloc(sizeof(wchar_t) * (cplen + wcslen(fmt))); 666 } 667 668 while ((c = *fmt++) != '\0') { 669 if (c != '%') { 670 if (bptr == (wchar_t *)NULL) 671 awk_putwc(c, fp); 672 else 673 *bptr++ = c; 674 ++length; 675 continue; 676 } 677 ofmtp = fmtbuf; 678 *ofmtp++ = (char)c; 679 nextc: 680 switch (c = *fmt++) { 681 case '%': 682 if (bptr == (wchar_t *)NULL) 683 awk_putwc(c, fp); 684 else 685 *bptr++ = c; 686 ++length; 687 continue; 688 689 case 'c': 690 *ofmtp++ = 'w'; 691 *ofmtp++ = 'c'; 692 *ofmtp = '\0'; 693 fnp = exprreduce(nextarg(&np)); 694 if (isnumber(fnp->n_flags)) 695 c = exprint(fnp); 696 else 697 c = *(wchar_t *)exprstring(fnp); 698 if (bptr == (wchar_t *)NULL) 699 length += fprintf(fp, fmtbuf, c); 700 else { 701 /* 702 * Make sure that the buffer is long 703 * enough to hold the formatted string. 704 */ 705 adjust_buf(cp, &cplen, &bptr, fmtbuf, 0); 706 /* 707 * Since the call to adjust_buf() has already 708 * guaranteed that the buffer will be long 709 * enough, just pass in INT_MAX as 710 * the length. 711 */ 712 (void) wsprintf(bptr, (const char *) fmtbuf, c); 713 bptr += (slen = wcslen(bptr)); 714 length += slen; 715 } 716 continue; 717 /* XXXX Is this bogus? Figure out what s & S mean - look at original code */ 718 case 's': 719 case 'S': 720 *ofmtp++ = 'w'; 721 *ofmtp++ = 's'; 722 *ofmtp = '\0'; 723 if (bptr == (wchar_t *)NULL) 724 length += fprintf(fp, fmtbuf, 725 (wchar_t *)exprstring(nextarg(&np))); 726 else { 727 wchar_t *ts = exprstring(nextarg(&np)); 728 729 adjust_buf(cp, &cplen, &bptr, fmtbuf, 730 wcslen(ts)); 731 (void) wsprintf(bptr, (const char *) fmtbuf, 732 ts); 733 bptr += (slen = wcslen(bptr)); 734 length += slen; 735 } 736 continue; 737 738 case 'o': 739 case 'O': 740 case 'X': 741 case 'x': 742 case 'd': 743 case 'i': 744 case 'D': 745 case 'U': 746 case 'u': 747 *ofmtp++ = 'l'; 748 *ofmtp++ = 'l'; /* now dealing with long longs */ 749 *ofmtp++ = c; 750 *ofmtp = '\0'; 751 if (bptr == (wchar_t *)NULL) 752 length += fprintf(fp, fmtbuf, 753 exprint(nextarg(&np))); 754 else { 755 adjust_buf(cp, &cplen, &bptr, fmtbuf, 0); 756 (void) wsprintf(bptr, (const char *) fmtbuf, 757 exprint(nextarg(&np))); 758 bptr += (slen = wcslen(bptr)); 759 length += slen; 760 } 761 continue; 762 763 case 'e': 764 case 'E': 765 case 'f': 766 case 'F': 767 case 'g': 768 case 'G': 769 *ofmtp++ = c; 770 *ofmtp = '\0'; 771 if (bptr == (wchar_t *)NULL) 772 length += fprintf(fp, fmtbuf, 773 exprreal(nextarg(&np))); 774 else { 775 adjust_buf(cp, &cplen, &bptr, fmtbuf, 0); 776 (void) wsprintf(bptr, (const char *) fmtbuf, 777 exprreal(nextarg(&np))); 778 bptr += (slen = wcslen(bptr)); 779 length += slen; 780 } 781 continue; 782 783 case 'l': 784 case 'L': 785 break; 786 787 case '*': 788 #ifdef M_BSD_SPRINTF 789 sprintf(ofmtp, "%lld", (INT)exprint(nextarg(&np))); 790 ofmtp += strlen(ofmtp); 791 #else 792 ofmtp += sprintf(ofmtp, "%lld", (INT)exprint(nextarg(&np))); 793 #endif 794 break; 795 796 default: 797 if(c=='\0') { 798 *ofmtp = (wchar_t)NULL; 799 fprintf(fp,"%s",fmtbuf); 800 continue; 801 } 802 else { 803 *ofmtp++ = (wchar_t)c; 804 break; 805 } 806 } 807 goto nextc; 808 } 809 if (fmtsave != NULL) 810 free(fmtsave); 811 /* 812 * If printing to a character buffer then make sure it is 813 * null-terminated and only uses as much space as required. 814 */ 815 if (bptr != (wchar_t *)NULL) { 816 *bptr = '\0'; 817 *cp = erealloc(*cp, (length+1) * sizeof(wchar_t)); 818 } 819 return (length); 820 } 821 822 /* 823 * Return the next argument from the list. 824 */ 825 static NODE * 826 nextarg(NODE **npp) 827 { 828 register NODE *np; 829 830 if ((np = getlist(npp)) == NNULL) 831 awkerr(gettext("insufficient arguments to printf or sprintf")); 832 if (isleaf(np->n_flags) && np->n_type==PARM) 833 return (np->n_next); 834 return (np); 835 } 836 837 838 /* 839 * Check and adjust the length of the buffer that has been passed in 840 * to make sure that it has space to accomodate the sequence string 841 * described in fmtstr. This routine is used by xprintf() to allow 842 * for arbitrarily long sprintf() strings. 843 * 844 * bp = start of current buffer 845 * len = length of current buffer 846 * offset = offset in current buffer 847 * fmtstr = format string to check 848 * slen = size of string for %s formats 849 */ 850 static void 851 adjust_buf(wchar_t **bp, int *len, wchar_t **offset, char *fmtstr, size_t slen) 852 { 853 int ioff; 854 int width = 0; 855 int prec = 0; 856 857 do { 858 fmtstr++; 859 } while (strchr("-+ 0", *fmtstr) != (char *)0 || 860 *fmtstr == ('#')); 861 if (*fmtstr != '*') { 862 if (isdigit(*fmtstr)) { 863 width = *fmtstr-'0'; 864 while (isdigit(*++fmtstr)) 865 width = width * 10 + *fmtstr - '0'; 866 } 867 } else 868 fmtstr++; 869 if (*fmtstr == '.') { 870 if (*++fmtstr != '*') { 871 prec = *fmtstr-'0'; 872 while (isdigit(*++fmtstr)) 873 prec = prec * 10 + *fmtstr - '0'; 874 } else 875 fmtstr++; 876 } 877 if (strchr("Llh", *fmtstr) != (char *)0) 878 fmtstr++; 879 if (*fmtstr == 'S') 880 { 881 if (width && slen < width) 882 slen = width; 883 if (prec && slen > prec) 884 slen = prec; 885 width = slen+1; 886 } else 887 if (width == 0) 888 width = NUMSIZE; 889 890 if (*offset+ width > *bp+ *len) { 891 ioff = *offset-*bp; 892 *len += width+1; 893 *bp = erealloc(*bp, *len * sizeof(wchar_t)); 894 *offset = *bp+ioff; 895 } 896 } 897 898 static void 899 awk_putwc(wchar_t c, FILE *fp) 900 { 901 char mb[MB_LEN_MAX]; 902 size_t mbl; 903 904 if ((mbl = wctomb(mb, c)) > 0) { 905 mb[mbl] = '\0'; 906 (void)fputs(mb, fp); 907 } else 908 awkerr(gettext("invalid wide character %x"), c); 909 } 910 911