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