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