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 -- functions 24 * 25 * Copyright (c) 1995, 1996 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 #include <time.h> 38 #include <sys/wait.h> 39 40 static uint nargs(NODE *np); 41 static NODE *dosub(NODE *np, int glob); 42 static NODE *docasetr(NODE *np, int upper); 43 static int asortcmp(const void *npp1, const void *npp2); 44 45 static char nargerr[] = "wrong number of arguments to function \"%s\""; 46 static NODE *asortfunc; /* Function call for asort() */ 47 static NODE *asnp1, *asnp2; /* index1, index2 nodes */ 48 static int asarraylen; /* strlen(array)+1 for asort */ 49 50 /* 51 * Return the value of exp(x). 52 * Usage: y = exp(x) 53 * y = exp() 54 */ 55 NODE * 56 f_exp(NODE *np) 57 { 58 register uint na; 59 60 if ((na = nargs(np)) > 1) 61 awkerr(nargerr, s_exp); 62 return (realnode(exp(exprreal(na==0 ? field0 : getlist(&np))))); 63 } 64 65 /* 66 * Return the integer part of the argument. 67 * Usage: i = int(r) 68 * i = int() 69 */ 70 NODE * 71 f_int(NODE *np) 72 { 73 register uint na; 74 75 if ((na = nargs(np)) > 1) 76 awkerr(nargerr, s_int); 77 return (intnode(exprint(na==0 ? field0 : getlist(&np)))); 78 } 79 80 /* 81 * Logarithm function. 82 * Usage: y = log(x) 83 * y = log() 84 */ 85 NODE * 86 f_log(NODE *np) 87 { 88 register uint na; 89 90 if ((na = nargs(np)) > 1) 91 awkerr(nargerr, s_log); 92 return (realnode(log(exprreal(na==0 ? field0 : getlist(&np))))); 93 } 94 95 /* 96 * Square root function. 97 * Usage: y = sqrt(x) 98 * y = sqrt() 99 */ 100 NODE * 101 f_sqrt(NODE *np) 102 { 103 register uint na; 104 105 if ((na = nargs(np)) > 1) 106 awkerr(nargerr, s_sqrt); 107 return (realnode(sqrt(exprreal(na==0 ? field0 : getlist(&np))))); 108 } 109 110 /* 111 * Trigonometric sine function. 112 * Usage: y = sin(x) 113 */ 114 NODE * 115 f_sin(NODE *np) 116 { 117 if (nargs(np) != 1) 118 awkerr(nargerr, s_sin); 119 return (realnode(sin(exprreal(getlist(&np))))); 120 } 121 122 /* 123 * Trigonometric cosine function. 124 * Usage: y = cos(x) 125 */ 126 NODE * 127 f_cos(NODE *np) 128 { 129 if (nargs(np) != 1) 130 awkerr(nargerr, s_cos); 131 return (realnode(cos(exprreal(getlist(&np))))); 132 } 133 134 /* 135 * Arctangent of y/x. 136 * Usage: z = atan2(y, x) 137 */ 138 NODE * 139 f_atan2(NODE *np) 140 { 141 double y, x; 142 143 if (nargs(np) != 2) 144 awkerr(nargerr, s_atan2); 145 y = (double)exprreal(getlist(&np)); 146 x = (double)exprreal(getlist(&np)); 147 return (realnode(atan2(y, x))); 148 } 149 150 /* 151 * Set the seed for the random number generator function -- rand. 152 * Usage: srand(x) 153 * srand() 154 */ 155 NODE * 156 f_srand(NODE *np) 157 { 158 register uint na; 159 register uint seed; 160 static uint oldseed = 0; 161 162 if ((na = nargs(np)) > 1) 163 awkerr(nargerr, s_srand); 164 if (na == 0) 165 seed = (uint)time((time_t *)0); else 166 seed = (uint)exprint(getlist(&np)); 167 srand(seed); 168 na = oldseed; 169 oldseed = seed; 170 return (intnode((INT)na)); 171 } 172 173 /* 174 * Generate a random number. 175 * Usage: x = rand() 176 */ 177 NODE * 178 f_rand(NODE *np) 179 { 180 double result; 181 int expon; 182 ushort rint; 183 184 if (nargs(np) != 0) 185 awkerr(nargerr, s_rand); 186 rint = rand() & SHRT_MAX; 187 result = frexp((double)rint, &expon); 188 return (realnode((REAL)ldexp(result, expon-15))); 189 } 190 191 /* 192 * Substitute function. 193 * Usage: n = sub(regex, replace, target) 194 * n = sub(regex, replace) 195 */ 196 NODE * 197 f_sub(NODE *np) 198 { 199 return (dosub(np, 1)); 200 } 201 202 /* 203 * Global substitution function. 204 * Usage: n = gsub(regex, replace, target) 205 * n = gsub(regex, replace) 206 */ 207 NODE * 208 f_gsub(NODE *np) 209 { 210 return (dosub(np, 0)); 211 } 212 213 /* 214 * Do actual substitutions. 215 * `glob' is the number to substitute, 0 for all. 216 */ 217 static NODE * 218 dosub(NODE *np, int glob) 219 { 220 wchar_t *text; 221 register wchar_t *sub; 222 register uint n; 223 register uint na; 224 register REGEXP rp; 225 NODE *left; 226 static wchar_t *buf; 227 228 if ((na = nargs(np)) != 2 && na != 3) 229 awkerr(nargerr, glob==0 ? s_gsub : s_sub); 230 rp = getregexp(getlist(&np)); 231 sub = exprstring(getlist(&np)); 232 if (na == 3) { 233 left = getlist(&np); 234 text = exprstring(left); 235 } else { 236 left = field0; 237 text = linebuf; 238 } 239 switch (REGWDOSUBA(rp, sub, text, &buf, 256, &glob)) { 240 case REG_OK: 241 case REG_NOMATCH: 242 n = glob; 243 break; 244 case REG_ESPACE: 245 if (buf != NULL) 246 free(buf); 247 awkerr(nomem); 248 default: 249 awkerr(gettext("regular expression error")); 250 } 251 (void)assign(left, stringnode(buf, FNOALLOC, wcslen(buf))); 252 return (intnode((INT)n)); 253 } 254 255 /* 256 * Match function. Return position (origin 1) or 0 for regular 257 * expression match in string. Set new variables RSTART and RLENGTH 258 * as well. 259 * Usage: pos = match(string, re) 260 */ 261 NODE * 262 f_match(NODE *np) 263 { 264 register wchar_t *text; 265 register REGEXP rp; 266 register int pos, length; 267 REGWMATCH_T match[10]; 268 269 if (nargs(np) != 2) 270 awkerr(nargerr, s_match); 271 text = exprstring(getlist(&np)); 272 rp = getregexp(getlist(&np)); 273 if (REGWEXEC(rp, text, 10, match, 0) == REG_OK) { 274 pos = match[0].rm_sp-text+1; 275 length = match[0].rm_ep - match[0].rm_sp; 276 } else { 277 pos = 0; 278 length = -1; 279 } 280 constant->n_int = length; 281 (void)assign(vlook(M_MB_L("RLENGTH")), constant); 282 return (assign(vlook(M_MB_L("RSTART")), intnode((INT)pos))); 283 } 284 285 /* 286 * Call shell or command interpreter. 287 * Usage: status = system(command) 288 */ 289 NODE * 290 f_system(NODE *np) 291 { 292 int retcode; 293 294 if (nargs(np) != 1) 295 awkerr(nargerr, s_system); 296 (void) fflush(stdout); 297 retcode = system(mbunconvert(exprstring(getlist(&np)))); 298 return (intnode((INT)WEXITSTATUS(retcode))); 299 } 300 301 /* 302 * Search for string within string. 303 * Usage: pos = index(string1, string2) 304 */ 305 NODE * 306 f_index(NODE *np) 307 { 308 register wchar_t *s1, *s2; 309 register int l1, l2; 310 register int result; 311 312 if (nargs(np) != 2) 313 awkerr(nargerr, s_index); 314 s1 = (wchar_t *)exprstring(getlist(&np)); 315 s2 = (wchar_t *)exprstring(getlist(&np)); 316 l1 = wcslen(s1); 317 l2 = wcslen(s2); 318 result = 1; 319 while (l2 <= l1) { 320 if (memcmp(s1, s2, l2 * sizeof(wchar_t)) == 0) 321 break; 322 result++; 323 s1++; 324 l1--; 325 } 326 if (l2 > l1) 327 result = 0; 328 return (intnode((INT)result)); 329 } 330 331 /* 332 * Return length of argument or $0 333 * Usage: n = length(string) 334 * n = length() 335 * n = length 336 */ 337 NODE * 338 f_length(NODE *np) 339 { 340 register uint na; 341 342 if ((na = nargs(np)) > 1) 343 awkerr(nargerr, s_length); 344 if (na == 0) 345 na = lbuflen; else 346 na = wcslen((wchar_t *)exprstring(getlist(&np))); 347 return (intnode((INT)na)); 348 } 349 350 /* 351 * Split string into fields. 352 * Usage: nfields = split(string, array [, separator]); 353 */ 354 NODE * 355 f_split(NODE *np) 356 { 357 register wchar_t *cp; 358 wchar_t *ep, *saved = 0; 359 register NODE *tnp, *snp, *otnp; 360 register NODE *sep; 361 REGEXP old_resep = 0; 362 size_t seplen; 363 uint n; 364 wint_t c; 365 wchar_t savesep[20]; 366 wchar_t *(*old_awkfield)(wchar_t **) = 0; 367 368 if ((n = nargs(np))<2 || n>3) 369 awkerr(nargerr, s_split); 370 ep = exprstring(snp = getlist(&np)); 371 tnp = getlist(&np); 372 if (snp->n_type == INDEX && snp->n_left == tnp) 373 ep = saved = wsdup(ep); 374 if (n == 3) { 375 sep = getlist(&np); 376 } else 377 sep = NNULL; 378 switch (tnp->n_type) { 379 case ARRAY: 380 delarray(tnp); 381 break; 382 383 case PARM: 384 break; 385 386 case VAR: 387 if (isstring(tnp->n_flags) && tnp->n_string==_null) 388 break; 389 default: 390 awkerr(gettext( 391 "second parameter to \"split\" must be an array")); 392 /*NOTREACHED*/ 393 } 394 /* 395 * If an argument has been passed in to be used as the 396 * field separator check to see if it is a constant regular 397 * expression. If so, use it directly otherwise reduce the 398 * expression, convert the result into a string and assign it 399 * to "FS" (after saving the old value for FS.) 400 */ 401 if (sep != NNULL) { 402 if (sep->n_type == PARM) 403 sep = sep->n_next; 404 if (sep->n_type == RE) { 405 old_resep = resep; 406 resep = sep->n_regexp; 407 old_awkfield = awkfield; 408 awkfield = refield; 409 } else { 410 sep = exprreduce(sep); 411 seplen = wcslen(cp = (wchar_t *)exprstring(varFS)); 412 (void) memcpy(savesep, cp, 413 (seplen+1) * sizeof(wchar_t)); 414 (void) assign(varFS, sep); 415 } 416 } 417 /* 418 * Iterate over the record, extracting each field and assigning it to 419 * the corresponding element in the array. 420 */ 421 otnp = tnp; /* save tnp for possible promotion */ 422 tnp = node(INDEX, tnp, constant); 423 fcount = 0; 424 for (;;) { 425 if ((cp = (*awkfield)(&ep)) == NULL) { 426 if (fcount == 0) { 427 if (otnp->n_type == PARM) 428 otnp = otnp->n_next; 429 promote(otnp); 430 } 431 break; 432 } 433 c = *ep; 434 *ep = '\0'; 435 constant->n_int = ++fcount; 436 (void)assign(tnp, stringnode(cp,FALLOC|FSENSE,(size_t)(ep-cp))); 437 *ep = c; 438 } 439 /* 440 * Restore the old record separator/and or regular expression. 441 */ 442 if (sep != NNULL) { 443 if (old_awkfield != 0) { 444 resep = old_resep; 445 awkfield = old_awkfield; 446 } else { 447 (void)assign(varFS, 448 stringnode(savesep, FSTATIC, seplen)); 449 } 450 } 451 if (saved) 452 free(saved); 453 return (intnode((INT)fcount)); 454 } 455 456 /* 457 * Sprintf function. 458 * Usage: string = sprintf(format, arg, ...) 459 */ 460 NODE * 461 f_sprintf(NODE *np) 462 { 463 wchar_t *cp; 464 size_t length; 465 466 if (nargs(np) == 0) 467 awkerr(nargerr, s_sprintf); 468 length = xprintf(np, (FILE *)NULL, &cp); 469 np = stringnode(cp, FNOALLOC, length); 470 return (np); 471 } 472 473 /* 474 * Substring. 475 * newstring = substr(string, start, [length]) 476 */ 477 NODE * 478 f_substr(NODE *np) 479 { 480 register STRING str; 481 register size_t n; 482 register int start; 483 register size_t len; 484 485 if ((n = nargs(np))<2 || n>3) 486 awkerr(nargerr, s_substr); 487 str = exprstring(getlist(&np)); 488 if ((start = (int)exprint(getlist(&np))-1) < 0) 489 start = 0; 490 if (n == 3) { 491 int x; 492 x = (int)exprint(getlist(&np)); 493 if (x < 0) 494 len = 0; 495 else 496 len = (size_t)x; 497 } else 498 len = LARGE; 499 n = wcslen((wchar_t *)str); 500 if (start > n) 501 start = n; 502 n -= start; 503 if (len > n) 504 len = n; 505 str += start; 506 n = str[len]; 507 str[len] = '\0'; 508 np = stringnode(str, FALLOC, len); 509 str[len] = n; 510 return (np); 511 } 512 513 /* 514 * Close an output or input file stream. 515 */ 516 NODE * 517 f_close(NODE *np) 518 { 519 register OFILE *op; 520 register char *name; 521 522 if (nargs(np) != 1) 523 awkerr(nargerr, s_close); 524 name = mbunconvert(exprstring(getlist(&np))); 525 for (op = &ofiles[0]; op < &ofiles[NIOSTREAM]; op++) 526 if (op->f_fp!=FNULL && strcmp(name, op->f_name)==0) { 527 awkclose(op); 528 break; 529 } 530 if (op >= &ofiles[NIOSTREAM]) 531 return (const1); 532 return (const0); 533 } 534 535 /* 536 * Return the integer value of the first character of a string. 537 * Usage: char = ord(string) 538 */ 539 NODE * 540 f_ord(NODE *np) 541 { 542 if (nargs(np) != 1) 543 awkerr(nargerr, s_ord); 544 return (intnode((INT)*exprstring(getlist(&np)))); 545 } 546 547 /* 548 * Return the argument string in lower case: 549 * Usage: 550 * lower = tolower(upper) 551 */ 552 NODE * 553 f_tolower(NODE *np) 554 { 555 return (docasetr(np, 0)); 556 } 557 558 /* 559 * Return the argument string in upper case: 560 * Usage: 561 * upper = toupper(lower) 562 */ 563 NODE * 564 f_toupper(NODE *np) 565 { 566 return (docasetr(np, 1)); 567 } 568 569 /* 570 * Sort the array into traversal order by the next "for (i in array)" loop. 571 * Usage: 572 * asort(array, "cmpfunc") 573 * cmpfunc(array, index1, index2) 574 * returns: 575 * <0 if array[index1] < array[index2] 576 * 0 if array[index1] == array[index2] 577 * >0 if array[index1] > array[index2] 578 */ 579 NODE * 580 f_asort(NODE *np) 581 { 582 NODE *array; 583 STRING funcname; 584 register size_t nel; 585 register NODE *tnp; 586 register NODE *funcnp; 587 register NODE **alist, **npp; 588 589 if (nargs(np) != 2) 590 awkerr(nargerr, s_asort); 591 array = getlist(&np); 592 if (array->n_type == PARM) 593 array = array->n_next; 594 if (array->n_type != ARRAY) 595 awkerr(gettext("%s function requires an array"), 596 s_asort); 597 funcname = exprstring(getlist(&np)); 598 if ((funcnp = vlookup(funcname, 1)) == NNULL 599 || funcnp->n_type != UFUNC) 600 awkerr(gettext("%s: %s is not a function\n"), 601 s_asort, funcname); 602 /* 603 * Count size of array, allowing one extra for NULL at end 604 */ 605 nel = 1; 606 for (tnp = array->n_alink; tnp != NNULL; tnp = tnp->n_alink) 607 ++nel; 608 /* 609 * Create UFUNC node that points at the funcnp on left and the 610 * list of three variables on right (array, index1, index2) 611 * UFUNC 612 * / \ 613 * funcnp COMMA 614 * / \ 615 * array COMMA 616 * / \ 617 * index1 index2 618 */ 619 if (asortfunc == NNULL) { 620 running = 0; 621 asortfunc = node(CALLUFUNC, NNULL, 622 node(COMMA, NNULL, 623 node(COMMA, 624 asnp1=stringnode(_null, FSTATIC, 0), 625 asnp2=stringnode(_null, FSTATIC, 0)))); 626 running = 1; 627 } 628 asortfunc->n_left = funcnp; 629 asortfunc->n_right->n_left = array; 630 asarraylen = wcslen(array->n_name)+1; 631 alist = (NODE **) emalloc(nel*sizeof(NODE *)); 632 /* 633 * Copy array into alist. 634 */ 635 npp = alist; 636 for (tnp = array->n_alink; tnp != NNULL; tnp = tnp->n_alink) 637 *npp++ = tnp; 638 *npp = NNULL; 639 /* 640 * Re-order array to this list 641 */ 642 qsort((wchar_t *)alist, nel-1, sizeof (NODE *), asortcmp); 643 tnp = array; 644 npp = alist; 645 do { 646 tnp = tnp->n_alink = *npp; 647 } while (*npp++ != NNULL); 648 free((wchar_t *)alist); 649 return (constundef); 650 } 651 652 /* 653 * Return the number of arguments of a function. 654 */ 655 static uint 656 nargs(NODE *np) 657 { 658 register int n; 659 660 if (np == NNULL) 661 return (0); 662 n = 1; 663 while (np!=NNULL && np->n_type==COMMA) { 664 np = np->n_right; 665 n++; 666 } 667 return (n); 668 } 669 670 /* 671 * Do case translation. 672 */ 673 static NODE * 674 docasetr(NODE *np, int upper) 675 { 676 register int c; 677 register wchar_t *cp; 678 register wchar_t *str; 679 register uint na; 680 681 if ((na = nargs(np)) > 1) 682 awkerr(nargerr, upper ? s_toupper : s_tolower); 683 str = strsave(na==0 ? linebuf : exprstring(getlist(&np))); 684 cp = str; 685 if (upper) { 686 while ((c = *cp++) != '\0') 687 cp[-1] = towupper(c); 688 } else { 689 while ((c = *cp++) != '\0') 690 cp[-1] = towlower(c); 691 } 692 return (stringnode((STRING)str, FNOALLOC, (size_t)(cp-str-1))); 693 } 694 695 /* 696 * The comparison routine used by qsort inside f_asort() 697 */ 698 static int 699 asortcmp(const void *npp1, const void *npp2) 700 { 701 asnp1->n_strlen = 702 wcslen(asnp1->n_string = (*(NODE **)npp1)->n_name+asarraylen); 703 asnp2->n_strlen = 704 wcslen(asnp2->n_string = (*(NODE **)npp2)->n_name+asarraylen); 705 return ((int)exprint(asortfunc)); 706 } 707 708 #if M_MATHERR 709 #if !defined(__BORLANDC__)&&defined(__TURBOC__)&&__COMPACT__&&__EMULATE__ 710 /* So it won't optimize registers our FP is using */ 711 #define flushesbx() (_BX = 0, _ES = _BX) 712 #else 713 #define flushesbx() (0) 714 #endif 715 716 /* 717 * Math error for awk. 718 */ 719 int 720 matherr(struct exception *ep) 721 { 722 register uint type; 723 static char msgs[7][256]; 724 static int first_time = 1; 725 726 if (first_time) { 727 msgs[0] = gettext("Unknown FP error"), 728 msgs[1] = gettext("Domain"), 729 msgs[2] = gettext("Singularity"), 730 msgs[3] = gettext("Overflow"), 731 msgs[4] = gettext("Underflow"), 732 msgs[5] = gettext("Total loss of precision"), 733 msgs[6] = gettext("Partial loss of precision") 734 first_time = 0; 735 } 736 737 if ((type = ep->type) > (uint)PLOSS) 738 type = 0; 739 (void)fprintf(stderr, "awk: %s", strmsg(msgs[type])); 740 (void)fprintf(stderr, gettext( 741 " error in function %s(%g) at NR=%lld\n"), 742 ((void) flushesbx(), ep->name), ep->arg1, (INT)exprint(varNR)); 743 return (1); 744 } 745 #endif /*M_MATHERR*/ 746