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
38static uint	nargs(NODE *np);
39static NODE	*dosub(NODE *np, int glob);
40static NODE	*docasetr(NODE *np, int upper);
41static int	asortcmp(const void *npp1, const void *npp2);
42
43static char	nargerr[] = "wrong number of arguments to function \"%s\"";
44static NODE	*asortfunc;		/* Function call for asort() */
45static NODE	*asnp1, *asnp2;		/* index1, index2 nodes */
46static 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 */
53NODE *
54f_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 */
68NODE *
69f_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 */
83NODE *
84f_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 */
98NODE *
99f_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 */
112NODE *
113f_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 */
124NODE *
125f_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 */
136NODE *
137f_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 */
153NODE *
154f_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 */
175NODE *
176f_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 */
194NODE *
195f_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 */
205NODE *
206f_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 */
215static NODE *
216dosub(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 */
259NODE *
260f_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 */
287NODE *
288f_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 */
303NODE *
304f_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 */
335NODE *
336f_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 */
352NODE *
353f_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 */
459NODE *
460f_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 */
476NODE *
477f_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 */
515NODE *
516f_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 */
538NODE *
539f_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 */
551NODE *
552f_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 */
562NODE *
563f_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 */
578NODE *
579f_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 */
654static uint
655nargs(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 */
672static NODE *
673docasetr(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 */
697static int
698asortcmp(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 */
718int
719matherr(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