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