xref: /illumos-gate/usr/src/cmd/awk/run.c (revision f22acdfff536d452df49dd85c5ecd42092b8fcad)
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 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #define	tempfree(x, s)	if (istemp(x)) tfree(x, s)
34 
35 #define	execute(p) r_execute(p)
36 
37 #define	DEBUG
38 #include	"awk.h"
39 #include	<math.h>
40 #include	"y.tab.h"
41 #include	<stdio.h>
42 #include	<ctype.h>
43 #include	<setjmp.h>
44 #include	<time.h>
45 
46 #ifndef	FOPEN_MAX
47 #define	FOPEN_MAX	15	/* max number of open files, from ANSI std. */
48 #endif
49 
50 
51 static jmp_buf env;
52 
53 static	Cell	*r_execute(Node *);
54 static	Cell	*gettemp(char *), *copycell(Cell *);
55 static	FILE	*openfile(int, uchar *), *redirect(int, Node *);
56 
57 int	paircnt;
58 Node	*winner = NULL;
59 
60 static Cell	*tmps;
61 
62 static Cell	truecell	= { OBOOL, BTRUE, 0, 0, 1.0, NUM };
63 Cell	*true	= &truecell;
64 static Cell	falsecell	= { OBOOL, BFALSE, 0, 0, 0.0, NUM };
65 Cell	*false	= &falsecell;
66 static Cell	breakcell	= { OJUMP, JBREAK, 0, 0, 0.0, NUM };
67 Cell	*jbreak	= &breakcell;
68 static Cell	contcell	= { OJUMP, JCONT, 0, 0, 0.0, NUM };
69 Cell	*jcont	= &contcell;
70 static Cell	nextcell	= { OJUMP, JNEXT, 0, 0, 0.0, NUM };
71 Cell	*jnext	= &nextcell;
72 static Cell	exitcell	= { OJUMP, JEXIT, 0, 0, 0.0, NUM };
73 Cell	*jexit	= &exitcell;
74 static Cell	retcell		= { OJUMP, JRET, 0, 0, 0.0, NUM };
75 Cell	*jret	= &retcell;
76 static Cell	tempcell	= { OCELL, CTEMP, 0, 0, 0.0, NUM };
77 
78 Node	*curnode = NULL;	/* the node being executed, for debugging */
79 
80 static	void	tfree(Cell *, char *);
81 static	void	closeall(void);
82 static	double	ipow(double, int);
83 
84 void
85 run(Node *a)
86 {
87 	(void) execute(a);
88 	closeall();
89 }
90 
91 static Cell *
92 r_execute(Node *u)
93 {
94 	register Cell *(*proc)();
95 	register Cell *x;
96 	register Node *a;
97 
98 	if (u == NULL)
99 		return (true);
100 	for (a = u; ; a = a->nnext) {
101 		curnode = a;
102 		if (isvalue(a)) {
103 			x = (Cell *) (a->narg[0]);
104 			if ((x->tval & FLD) && !donefld)
105 				fldbld();
106 			else if ((x->tval & REC) && !donerec)
107 				recbld();
108 			return (x);
109 		}
110 		/* probably a Cell* but too risky to print */
111 		if (notlegal(a->nobj))
112 			ERROR "illegal statement" FATAL;
113 		proc = proctab[a->nobj-FIRSTTOKEN];
114 		x = (*proc)(a->narg, a->nobj);
115 		if ((x->tval & FLD) && !donefld)
116 			fldbld();
117 		else if ((x->tval & REC) && !donerec)
118 			recbld();
119 		if (isexpr(a))
120 			return (x);
121 		/* a statement, goto next statement */
122 		if (isjump(x))
123 			return (x);
124 		if (a->nnext == (Node *)NULL)
125 			return (x);
126 		tempfree(x, "execute");
127 	}
128 }
129 
130 /*ARGSUSED*/
131 Cell *
132 program(Node **a, int n)
133 {
134 	register Cell *x;
135 
136 	if (setjmp(env) != 0)
137 		goto ex;
138 	if (a[0]) {		/* BEGIN */
139 		x = execute(a[0]);
140 		if (isexit(x))
141 			return (true);
142 		if (isjump(x)) {
143 			ERROR "illegal break, continue or next from BEGIN"
144 			    FATAL;
145 		}
146 		tempfree(x, "");
147 	}
148 loop:
149 	if (a[1] || a[2])
150 		while (getrec(&record, &record_size) > 0) {
151 			x = execute(a[1]);
152 			if (isexit(x))
153 				break;
154 			tempfree(x, "");
155 		}
156 ex:
157 	if (setjmp(env) != 0)
158 		goto ex1;
159 	if (a[2]) {		/* END */
160 		x = execute(a[2]);
161 		if (iscont(x))	/* read some more */
162 			goto loop;
163 		if (isbreak(x) || isnext(x))
164 			ERROR "illegal break or next from END" FATAL;
165 		tempfree(x, "");
166 	}
167 ex1:
168 	return (true);
169 }
170 
171 struct Frame {
172 	int nargs;	/* number of arguments in this call */
173 	Cell *fcncell;	/* pointer to Cell for function */
174 	Cell **args;	/* pointer to array of arguments after execute */
175 	Cell *retval;	/* return value */
176 };
177 
178 #define	NARGS	30
179 
180 struct Frame *frame = NULL; /* base of stack frames; dynamically allocated */
181 int	nframe = 0;		/* number of frames allocated */
182 struct Frame *fp = NULL;	/* frame pointer. bottom level unused */
183 
184 /*ARGSUSED*/
185 Cell *
186 call(Node **a, int n)
187 {
188 	static Cell newcopycell =
189 		{ OCELL, CCOPY, 0, (uchar *) "", 0.0, NUM|STR|DONTFREE };
190 	int i, ncall, ndef, freed = 0;
191 	Node *x;
192 	Cell *args[NARGS], *oargs[NARGS], *y, *z, *fcn;
193 	uchar *s;
194 
195 	fcn = execute(a[0]);	/* the function itself */
196 	s = fcn->nval;
197 	if (!isfunc(fcn))
198 		ERROR "calling undefined function %s", s FATAL;
199 	if (frame == NULL) {
200 		fp = frame = (struct Frame *)calloc(nframe += 100,
201 		    sizeof (struct Frame));
202 		if (frame == NULL) {
203 			ERROR "out of space for stack frames calling %s",
204 			    s FATAL;
205 		}
206 	}
207 	for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */
208 		ncall++;
209 	ndef = (int)fcn->fval;			/* args in defn */
210 	dprintf(("calling %s, %d args (%d in defn), fp=%d\n",
211 	    s, ncall, ndef, fp-frame));
212 	if (ncall > ndef) {
213 		ERROR "function %s called with %d args, uses only %d",
214 		    s, ncall, ndef WARNING;
215 	}
216 	if (ncall + ndef > NARGS) {
217 		ERROR "function %s has %d arguments, limit %d",
218 		    s, ncall+ndef, NARGS FATAL;
219 	}
220 	for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) {
221 		/* get call args */
222 		dprintf(("evaluate args[%d], fp=%d:\n", i, fp-frame));
223 		y = execute(x);
224 		oargs[i] = y;
225 		dprintf(("args[%d]: %s %f <%s>, t=%o\n",
226 		    i, y->nval, y->fval,
227 		    isarr(y) ? "(array)" : (char *)y->sval, y->tval));
228 		if (isfunc(y)) {
229 			ERROR "can't use function %s as argument in %s",
230 			    y->nval, s FATAL;
231 		}
232 		if (isarr(y))
233 			args[i] = y;	/* arrays by ref */
234 		else
235 			args[i] = copycell(y);
236 		tempfree(y, "callargs");
237 	}
238 	for (; i < ndef; i++) { /* add null args for ones not provided */
239 		args[i] = gettemp("nullargs");
240 		*args[i] = newcopycell;
241 	}
242 	fp++;	/* now ok to up frame */
243 	if (fp >= frame + nframe) {
244 		int dfp = fp - frame;	/* old index */
245 		frame = (struct Frame *)
246 		    realloc(frame, (nframe += 100) * sizeof (struct Frame));
247 		if (frame == NULL)
248 			ERROR "out of space for stack frames in %s", s FATAL;
249 		fp = frame + dfp;
250 	}
251 	fp->fcncell = fcn;
252 	fp->args = args;
253 	fp->nargs = ndef;	/* number defined with (excess are locals) */
254 	fp->retval = gettemp("retval");
255 
256 	dprintf(("start exec of %s, fp=%d\n", s, fp-frame));
257 	/*LINTED align*/
258 	y = execute((Node *)(fcn->sval));	/* execute body */
259 	dprintf(("finished exec of %s, fp=%d\n", s, fp-frame));
260 
261 	for (i = 0; i < ndef; i++) {
262 		Cell *t = fp->args[i];
263 		if (isarr(t)) {
264 			if (t->csub == CCOPY) {
265 				if (i >= ncall) {
266 					freesymtab(t);
267 					t->csub = CTEMP;
268 				} else {
269 					oargs[i]->tval = t->tval;
270 					oargs[i]->tval &= ~(STR|NUM|DONTFREE);
271 					oargs[i]->sval = t->sval;
272 					tempfree(t, "oargsarr");
273 				}
274 			}
275 		} else {
276 			t->csub = CTEMP;
277 			tempfree(t, "fp->args");
278 			if (t == y) freed = 1;
279 		}
280 	}
281 	tempfree(fcn, "call.fcn");
282 	if (isexit(y) || isnext(y))
283 		return (y);
284 	if (!freed)
285 		tempfree(y, "fcn ret"); /* this can free twice! */
286 	z = fp->retval;			/* return value */
287 	dprintf(("%s returns %g |%s| %o\n",
288 	    s, getfval(z), getsval(z), z->tval));
289 	fp--;
290 	return (z);
291 }
292 
293 static Cell *
294 copycell(Cell *x)	/* make a copy of a cell in a temp */
295 {
296 	Cell *y;
297 
298 	y = gettemp("copycell");
299 	y->csub = CCOPY;	/* prevents freeing until call is over */
300 	y->nval = x->nval;
301 	y->sval = x->sval ? tostring(x->sval) : NULL;
302 	y->fval = x->fval;
303 	/* copy is not constant or field is DONTFREE right? */
304 	y->tval = x->tval & ~(CON|FLD|REC|DONTFREE);
305 	return (y);
306 }
307 
308 /*ARGSUSED*/
309 Cell *
310 arg(Node **a, int nnn)
311 {
312 	int n;
313 
314 	n = (int)a[0];	/* argument number, counting from 0 */
315 	dprintf(("arg(%d), fp->nargs=%d\n", n, fp->nargs));
316 	if (n+1 > fp->nargs) {
317 		ERROR "argument #%d of function %s was not supplied",
318 		    n+1, fp->fcncell->nval FATAL;
319 	}
320 	return (fp->args[n]);
321 }
322 
323 Cell *
324 jump(Node **a, int n)
325 {
326 	register Cell *y;
327 
328 	switch (n) {
329 	case EXIT:
330 		if (a[0] != NULL) {
331 			y = execute(a[0]);
332 			errorflag = getfval(y);
333 			tempfree(y, "");
334 		}
335 		longjmp(env, 1);
336 		/*NOTREACHED*/
337 	case RETURN:
338 		if (a[0] != NULL) {
339 			y = execute(a[0]);
340 			if ((y->tval & (STR|NUM)) == (STR|NUM)) {
341 				(void) setsval(fp->retval, getsval(y));
342 				fp->retval->fval = getfval(y);
343 				fp->retval->tval |= NUM;
344 			} else if (y->tval & STR)
345 				(void) setsval(fp->retval, getsval(y));
346 			else if (y->tval & NUM)
347 				(void) setfval(fp->retval, getfval(y));
348 			tempfree(y, "");
349 		}
350 		return (jret);
351 	case NEXT:
352 		return (jnext);
353 	case BREAK:
354 		return (jbreak);
355 	case CONTINUE:
356 		return (jcont);
357 	default:	/* can't happen */
358 		ERROR "illegal jump type %d", n FATAL;
359 	}
360 	/*NOTREACHED*/
361 	return (NULL);
362 }
363 
364 Cell *
365 getline(Node **a, int n)
366 {
367 	/* a[0] is variable, a[1] is operator, a[2] is filename */
368 	register Cell *r, *x;
369 	uchar *buf;
370 	FILE *fp;
371 	size_t len;
372 
373 	(void) fflush(stdout);	/* in case someone is waiting for a prompt */
374 	r = gettemp("");
375 	if (a[1] != NULL) {		/* getline < file */
376 		x = execute(a[2]);		/* filename */
377 		if ((int)a[1] == '|')	/* input pipe */
378 			a[1] = (Node *)LE;	/* arbitrary flag */
379 		fp = openfile((int)a[1], getsval(x));
380 		tempfree(x, "");
381 		buf = NULL;
382 		if (fp == NULL)
383 			n = -1;
384 		else
385 			n = readrec(&buf, &len, fp);
386 		if (n > 0) {
387 			if (a[0] != NULL) {	/* getline var <file */
388 				(void) setsval(execute(a[0]), buf);
389 			} else {			/* getline <file */
390 				if (!(recloc->tval & DONTFREE))
391 					xfree(recloc->sval);
392 				expand_buf(&record, &record_size, len);
393 				(void) memcpy(record, buf, len);
394 				record[len] = '\0';
395 				recloc->sval = record;
396 				recloc->tval = REC | STR | DONTFREE;
397 				donerec = 1; donefld = 0;
398 			}
399 		}
400 		if (buf != NULL)
401 			free(buf);
402 	} else {			/* bare getline; use current input */
403 		if (a[0] == NULL)	/* getline */
404 			n = getrec(&record, &record_size);
405 		else {			/* getline var */
406 			init_buf(&buf, &len, LINE_INCR);
407 			n = getrec(&buf, &len);
408 			(void) setsval(execute(a[0]), buf);
409 			free(buf);
410 		}
411 	}
412 	(void) setfval(r, (Awkfloat)n);
413 	return (r);
414 }
415 
416 /*ARGSUSED*/
417 Cell *
418 getnf(Node **a, int n)
419 {
420 	if (donefld == 0)
421 		fldbld();
422 	return ((Cell *)a[0]);
423 }
424 
425 /*ARGSUSED*/
426 Cell *
427 array(Node **a, int n)
428 {
429 	register Cell *x, *y, *z;
430 	register uchar *s;
431 	register Node *np;
432 	uchar	*buf;
433 	size_t	bsize, tlen, len, slen;
434 
435 	x = execute(a[0]);	/* Cell* for symbol table */
436 	init_buf(&buf, &bsize, LINE_INCR);
437 	buf[0] = '\0';
438 	tlen = 0;
439 	slen = strlen((char *)*SUBSEP);
440 	for (np = a[1]; np; np = np->nnext) {
441 		y = execute(np);	/* subscript */
442 		s = getsval(y);
443 		len = strlen((char *)s);
444 		expand_buf(&buf, &bsize, tlen + len + slen);
445 		(void) memcpy(&buf[tlen], s, len);
446 		tlen += len;
447 		if (np->nnext) {
448 			(void) memcpy(&buf[tlen], *SUBSEP, slen);
449 			tlen += slen;
450 		}
451 		buf[tlen] = '\0';
452 		tempfree(y, "");
453 	}
454 	if (!isarr(x)) {
455 		dprintf(("making %s into an array\n", x->nval));
456 		if (freeable(x))
457 			xfree(x->sval);
458 		x->tval &= ~(STR|NUM|DONTFREE);
459 		x->tval |= ARR;
460 		x->sval = (uchar *) makesymtab(NSYMTAB);
461 	}
462 	/*LINTED align*/
463 	z = setsymtab(buf, (uchar *)"", 0.0, STR|NUM, (Array *)x->sval);
464 	z->ctype = OCELL;
465 	z->csub = CVAR;
466 	tempfree(x, "");
467 	free(buf);
468 	return (z);
469 }
470 
471 /*ARGSUSED*/
472 Cell *
473 delete(Node **a, int n)
474 {
475 	Cell *x, *y;
476 	Node *np;
477 	uchar *buf, *s;
478 	size_t bsize, tlen, slen, len;
479 
480 	x = execute(a[0]);	/* Cell* for symbol table */
481 	if (!isarr(x))
482 		return (true);
483 	init_buf(&buf, &bsize, LINE_INCR);
484 	buf[0] = '\0';
485 	tlen = 0;
486 	slen = strlen((char *)*SUBSEP);
487 	for (np = a[1]; np; np = np->nnext) {
488 		y = execute(np);	/* subscript */
489 		s = getsval(y);
490 		len = strlen((char *)s);
491 		expand_buf(&buf, &bsize, tlen + len + slen);
492 		(void) memcpy(&buf[tlen], s, len);
493 		tlen += len;
494 		if (np->nnext) {
495 			(void) memcpy(&buf[tlen], *SUBSEP, slen);
496 			tlen += slen;
497 		}
498 		buf[tlen] = '\0';
499 		tempfree(y, "");
500 	}
501 	freeelem(x, buf);
502 	tempfree(x, "");
503 	free(buf);
504 	return (true);
505 }
506 
507 /*ARGSUSED*/
508 Cell *
509 intest(Node **a, int n)
510 {
511 	register Cell *x, *ap, *k;
512 	Node *p;
513 	uchar *buf;
514 	uchar *s;
515 	size_t bsize, tlen, slen, len;
516 
517 	ap = execute(a[1]);	/* array name */
518 	if (!isarr(ap))
519 		ERROR "%s is not an array", ap->nval FATAL;
520 	init_buf(&buf, &bsize, LINE_INCR);
521 	buf[0] = 0;
522 	tlen = 0;
523 	slen = strlen((char *)*SUBSEP);
524 	for (p = a[0]; p; p = p->nnext) {
525 		x = execute(p);	/* expr */
526 		s = getsval(x);
527 		len = strlen((char *)s);
528 		expand_buf(&buf, &bsize, tlen + len + slen);
529 		(void) memcpy(&buf[tlen], s, len);
530 		tlen += len;
531 		tempfree(x, "");
532 		if (p->nnext) {
533 			(void) memcpy(&buf[tlen], *SUBSEP, slen);
534 			tlen += slen;
535 		}
536 		buf[tlen] = '\0';
537 	}
538 	/*LINTED align*/
539 	k = lookup(buf, (Array *)ap->sval);
540 	tempfree(ap, "");
541 	free(buf);
542 	if (k == NULL)
543 		return (false);
544 	else
545 		return (true);
546 }
547 
548 
549 Cell *
550 matchop(Node **a, int n)
551 {
552 	register Cell *x, *y;
553 	register uchar *s, *t;
554 	register int i;
555 	fa *pfa;
556 	int (*mf)() = match, mode = 0;
557 
558 	if (n == MATCHFCN) {
559 		mf = pmatch;
560 		mode = 1;
561 	}
562 	x = execute(a[1]);
563 	s = getsval(x);
564 	if (a[0] == 0)
565 		i = (*mf)(a[2], s);
566 	else {
567 		y = execute(a[2]);
568 		t = getsval(y);
569 		pfa = makedfa(t, mode);
570 		i = (*mf)(pfa, s);
571 		tempfree(y, "");
572 	}
573 	tempfree(x, "");
574 	if (n == MATCHFCN) {
575 		int start = patbeg - s + 1;
576 		if (patlen < 0)
577 			start = 0;
578 		(void) setfval(rstartloc, (Awkfloat)start);
579 		(void) setfval(rlengthloc, (Awkfloat)patlen);
580 		x = gettemp("");
581 		x->tval = NUM;
582 		x->fval = start;
583 		return (x);
584 	} else if (n == MATCH && i == 1 || n == NOTMATCH && i == 0)
585 		return (true);
586 	else
587 		return (false);
588 }
589 
590 
591 Cell *
592 boolop(Node **a, int n)
593 {
594 	register Cell *x, *y;
595 	register int i;
596 
597 	x = execute(a[0]);
598 	i = istrue(x);
599 	tempfree(x, "");
600 	switch (n) {
601 	case BOR:
602 		if (i)
603 			return (true);
604 		y = execute(a[1]);
605 		i = istrue(y);
606 		tempfree(y, "");
607 		return (i ? true : false);
608 	case AND:
609 		if (!i)
610 			return (false);
611 		y = execute(a[1]);
612 		i = istrue(y);
613 		tempfree(y, "");
614 		return (i ? true : false);
615 	case NOT:
616 		return (i ? false : true);
617 	default:	/* can't happen */
618 		ERROR "unknown boolean operator %d", n FATAL;
619 	}
620 	/*NOTREACHED*/
621 	return (NULL);
622 }
623 
624 Cell *
625 relop(Node **a, int n)
626 {
627 	register int i;
628 	register Cell *x, *y;
629 	Awkfloat j;
630 
631 	x = execute(a[0]);
632 	y = execute(a[1]);
633 	if (x->tval&NUM && y->tval&NUM) {
634 		j = x->fval - y->fval;
635 		i = j < 0 ? -1: (j > 0 ? 1: 0);
636 	} else {
637 		i = strcmp((char *)getsval(x), (char *)getsval(y));
638 	}
639 	tempfree(x, "");
640 	tempfree(y, "");
641 	switch (n) {
642 	case LT:	return (i < 0 ? true : false);
643 	case LE:	return (i <= 0 ? true : false);
644 	case NE:	return (i != 0 ? true : false);
645 	case EQ:	return (i == 0 ? true : false);
646 	case GE:	return (i >= 0 ? true : false);
647 	case GT:	return (i > 0 ? true : false);
648 	default:	/* can't happen */
649 		ERROR "unknown relational operator %d", n FATAL;
650 	}
651 	/*NOTREACHED*/
652 	return (false);
653 }
654 
655 static void
656 tfree(Cell *a, char *s)
657 {
658 	if (dbg > 1) {
659 		(void) printf("## tfree %.8s %06lo %s\n",
660 		    s, (ulong_t)a, a->sval ? a->sval : (uchar *)"");
661 	}
662 	if (freeable(a))
663 		xfree(a->sval);
664 	if (a == tmps)
665 		ERROR "tempcell list is curdled" FATAL;
666 	a->cnext = tmps;
667 	tmps = a;
668 }
669 
670 static Cell *
671 gettemp(char *s)
672 {
673 	int i;
674 	register Cell *x;
675 
676 	if (!tmps) {
677 		tmps = (Cell *)calloc(100, sizeof (Cell));
678 		if (!tmps)
679 			ERROR "no space for temporaries" FATAL;
680 		for (i = 1; i < 100; i++)
681 			tmps[i-1].cnext = &tmps[i];
682 		tmps[i-1].cnext = 0;
683 	}
684 	x = tmps;
685 	tmps = x->cnext;
686 	*x = tempcell;
687 	if (dbg > 1)
688 		(void) printf("## gtemp %.8s %06lo\n", s, (ulong_t)x);
689 	return (x);
690 }
691 
692 /*ARGSUSED*/
693 Cell *
694 indirect(Node **a, int n)
695 {
696 	register Cell *x;
697 	register int m;
698 	register uchar *s;
699 
700 	x = execute(a[0]);
701 	m = getfval(x);
702 	if (m == 0 && !is_number(s = getsval(x)))	/* suspicion! */
703 		ERROR "illegal field $(%s)", s FATAL;
704 	tempfree(x, "");
705 	x = fieldadr(m);
706 	x->ctype = OCELL;
707 	x->csub = CFLD;
708 	return (x);
709 }
710 
711 /*ARGSUSED*/
712 Cell *
713 substr(Node **a, int nnn)
714 {
715 	register int k, m, n;
716 	register uchar *s;
717 	int temp;
718 	register Cell *x, *y, *z;
719 
720 	x = execute(a[0]);
721 	y = execute(a[1]);
722 	if (a[2] != 0)
723 		z = execute(a[2]);
724 	s = getsval(x);
725 	k = strlen((char *)s) + 1;
726 	if (k <= 1) {
727 		tempfree(x, "");
728 		tempfree(y, "");
729 		if (a[2] != 0)
730 			tempfree(z, "");
731 		x = gettemp("");
732 		(void) setsval(x, (uchar *)"");
733 		return (x);
734 	}
735 	m = getfval(y);
736 	if (m <= 0)
737 		m = 1;
738 	else if (m > k)
739 		m = k;
740 	tempfree(y, "");
741 	if (a[2] != 0) {
742 		n = getfval(z);
743 		tempfree(z, "");
744 	} else
745 		n = k - 1;
746 	if (n < 0)
747 		n = 0;
748 	else if (n > k - m)
749 		n = k - m;
750 	dprintf(("substr: m=%d, n=%d, s=%s\n", m, n, s));
751 	y = gettemp("");
752 	temp = s[n + m - 1];	/* with thanks to John Linderman */
753 	s[n + m - 1] = '\0';
754 	(void) setsval(y, s + m - 1);
755 	s[n + m - 1] = temp;
756 	tempfree(x, "");
757 	return (y);
758 }
759 
760 /*ARGSUSED*/
761 Cell *
762 sindex(Node **a, int nnn)
763 {
764 	register Cell *x, *y, *z;
765 	register uchar *s1, *s2, *p1, *p2, *q;
766 	Awkfloat v = 0.0;
767 
768 	x = execute(a[0]);
769 	s1 = getsval(x);
770 	y = execute(a[1]);
771 	s2 = getsval(y);
772 
773 	z = gettemp("");
774 	for (p1 = s1; *p1 != '\0'; p1++) {
775 		for (q = p1, p2 = s2; *p2 != '\0' && *q == *p2; q++, p2++)
776 			;
777 		if (*p2 == '\0') {
778 			v = (Awkfloat) (p1 - s1 + 1);	/* origin 1 */
779 			break;
780 		}
781 	}
782 	tempfree(x, "");
783 	tempfree(y, "");
784 	(void) setfval(z, v);
785 	return (z);
786 }
787 
788 void
789 format(uchar **bufp, uchar *s, Node *a)
790 {
791 	uchar *fmt;
792 	register uchar *os;
793 	register Cell *x;
794 	int flag = 0, len;
795 	uchar_t	*buf;
796 	size_t bufsize, fmtsize, cnt, tcnt, ret;
797 
798 	init_buf(&buf, &bufsize, LINE_INCR);
799 	init_buf(&fmt, &fmtsize, LINE_INCR);
800 	os = s;
801 	cnt = 0;
802 	while (*s) {
803 		if (*s != '%') {
804 			expand_buf(&buf, &bufsize, cnt);
805 			buf[cnt++] = *s++;
806 			continue;
807 		}
808 		if (*(s+1) == '%') {
809 			expand_buf(&buf, &bufsize, cnt);
810 			buf[cnt++] = '%';
811 			s += 2;
812 			continue;
813 		}
814 		for (tcnt = 0; ; s++) {
815 			expand_buf(&fmt, &fmtsize, tcnt);
816 			fmt[tcnt++] = *s;
817 			if (*s == '\0')
818 				break;
819 			if (isalpha(*s) && *s != 'l' && *s != 'h' && *s != 'L')
820 				break;	/* the ansi panoply */
821 			if (*s == '*') {
822 				if (a == NULL) {
823 					ERROR
824 		"not enough args in printf(%s) or sprintf(%s)", os, os FATAL;
825 				}
826 				x = execute(a);
827 				a = a->nnext;
828 				tcnt--;
829 				expand_buf(&fmt, &fmtsize, tcnt + 12);
830 				ret = sprintf((char *)&fmt[tcnt], "%d",
831 				    (int)getfval(x));
832 				tcnt += ret;
833 				tempfree(x, "");
834 			}
835 		}
836 		fmt[tcnt] = '\0';
837 
838 		switch (*s) {
839 		case 'f': case 'e': case 'g': case 'E': case 'G':
840 			flag = 1;
841 			break;
842 		case 'd': case 'i':
843 			flag = 2;
844 			if (*(s-1) == 'l')
845 				break;
846 			fmt[tcnt - 1] = 'l';
847 			expand_buf(&fmt, &fmtsize, tcnt);
848 			fmt[tcnt++] = 'd';
849 			fmt[tcnt] = '\0';
850 			break;
851 		case 'o': case 'x': case 'X': case 'u':
852 			flag = *(s-1) == 'l' ? 2 : 3;
853 			break;
854 		case 's':
855 			flag = 4;
856 			break;
857 		case 'c':
858 			flag = 5;
859 			break;
860 		default:
861 			flag = 0;
862 			break;
863 		}
864 		if (flag == 0) {
865 			len = strlen((char *)fmt);
866 			expand_buf(&buf, &bufsize, cnt + len);
867 			(void) memcpy(&buf[cnt], fmt, len);
868 			cnt += len;
869 			buf[cnt] = '\0';
870 			continue;
871 		}
872 		if (a == NULL) {
873 			ERROR
874 	"not enough args in printf(%s) or sprintf(%s)", os, os FATAL;
875 		}
876 		x = execute(a);
877 		a = a->nnext;
878 		for (;;) {
879 			/* make sure we have at least 1 byte space */
880 			expand_buf(&buf, &bufsize, cnt + 1);
881 			len = bufsize - cnt;
882 			switch (flag) {
883 			case 1:
884 				/*LINTED*/
885 				ret = snprintf((char *)&buf[cnt], len,
886 				    (char *)fmt, getfval(x));
887 				break;
888 			case 2:
889 				/*LINTED*/
890 				ret = snprintf((char *)&buf[cnt], len,
891 				    (char *)fmt, (long)getfval(x));
892 				break;
893 			case 3:
894 				/*LINTED*/
895 				ret = snprintf((char *)&buf[cnt], len,
896 				    (char *)fmt, (int)getfval(x));
897 				break;
898 			case 4:
899 				/*LINTED*/
900 				ret = snprintf((char *)&buf[cnt], len,
901 				    (char *)fmt, getsval(x));
902 				break;
903 			case 5:
904 				if (isnum(x)) {
905 					/*LINTED*/
906 					ret = snprintf((char *)&buf[cnt], len,
907 					    (char *)fmt, (int)getfval(x));
908 				} else {
909 					/*LINTED*/
910 					ret = snprintf((char *)&buf[cnt], len,
911 					    (char *)fmt, getsval(x)[0]);
912 				}
913 				break;
914 			default:
915 				ret = 0;
916 			}
917 			if (ret < len)
918 				break;
919 			expand_buf(&buf, &bufsize, cnt + ret);
920 		}
921 		tempfree(x, "");
922 		cnt += ret;
923 		s++;
924 	}
925 	buf[cnt] = '\0';
926 	for (; a; a = a->nnext)	/* evaluate any remaining args */
927 		(void) execute(a);
928 	*bufp = tostring(buf);
929 	free(buf);
930 	free(fmt);
931 }
932 
933 /*ARGSUSED*/
934 Cell *
935 asprintf(Node **a, int n)
936 {
937 	register Cell *x;
938 	register Node *y;
939 	uchar *buf;
940 
941 	y = a[0]->nnext;
942 	x = execute(a[0]);
943 	format(&buf, getsval(x), y);
944 	tempfree(x, "");
945 	x = gettemp("");
946 	x->sval = buf;
947 	x->tval = STR;
948 	return (x);
949 }
950 
951 /*ARGSUSED*/
952 Cell *
953 aprintf(Node **a, int n)
954 {
955 	FILE *fp;
956 	register Cell *x;
957 	register Node *y;
958 	uchar *buf;
959 
960 	y = a[0]->nnext;
961 	x = execute(a[0]);
962 	format(&buf, getsval(x), y);
963 	tempfree(x, "");
964 	if (a[1] == NULL)
965 		(void) fputs((char *)buf, stdout);
966 	else {
967 		fp = redirect((int)a[1], a[2]);
968 		(void) fputs((char *)buf, fp);
969 		(void) fflush(fp);
970 	}
971 	free(buf);
972 	return (true);
973 }
974 
975 Cell *
976 arith(Node **a, int n)
977 {
978 	Awkfloat i, j;
979 	double v;
980 	register Cell *x, *y, *z;
981 
982 	x = execute(a[0]);
983 	i = getfval(x);
984 	tempfree(x, "");
985 	if (n != UMINUS) {
986 		y = execute(a[1]);
987 		j = getfval(y);
988 		tempfree(y, "");
989 	}
990 	z = gettemp("");
991 	switch (n) {
992 	case ADD:
993 		i += j;
994 		break;
995 	case MINUS:
996 		i -= j;
997 		break;
998 	case MULT:
999 		i *= j;
1000 		break;
1001 	case DIVIDE:
1002 		if (j == 0)
1003 			ERROR "division by zero" FATAL;
1004 		i /= j;
1005 		break;
1006 	case MOD:
1007 		if (j == 0)
1008 			ERROR "division by zero in mod" FATAL;
1009 		(void) modf(i/j, &v);
1010 		i = i - j * v;
1011 		break;
1012 	case UMINUS:
1013 		i = -i;
1014 		break;
1015 	case POWER:
1016 		if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
1017 			i = ipow(i, (int)j);
1018 		else
1019 			i = errcheck(pow(i, j), "pow");
1020 		break;
1021 	default:	/* can't happen */
1022 		ERROR "illegal arithmetic operator %d", n FATAL;
1023 	}
1024 	(void) setfval(z, i);
1025 	return (z);
1026 }
1027 
1028 static double
1029 ipow(double x, int n)
1030 {
1031 	double v;
1032 
1033 	if (n <= 0)
1034 		return (1.0);
1035 	v = ipow(x, n/2);
1036 	if (n % 2 == 0)
1037 		return (v * v);
1038 	else
1039 		return (x * v * v);
1040 }
1041 
1042 Cell *
1043 incrdecr(Node **a, int n)
1044 {
1045 	register Cell *x, *z;
1046 	register int k;
1047 	Awkfloat xf;
1048 
1049 	x = execute(a[0]);
1050 	xf = getfval(x);
1051 	k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
1052 	if (n == PREINCR || n == PREDECR) {
1053 		(void) setfval(x, xf + k);
1054 		return (x);
1055 	}
1056 	z = gettemp("");
1057 	(void) setfval(z, xf);
1058 	(void) setfval(x, xf + k);
1059 	tempfree(x, "");
1060 	return (z);
1061 }
1062 
1063 Cell *
1064 assign(Node **a, int n)
1065 {
1066 	register Cell *x, *y;
1067 	Awkfloat xf, yf;
1068 	double v;
1069 
1070 	y = execute(a[1]);
1071 	x = execute(a[0]);	/* order reversed from before... */
1072 	if (n == ASSIGN) {	/* ordinary assignment */
1073 		if ((y->tval & (STR|NUM)) == (STR|NUM)) {
1074 			(void) setsval(x, getsval(y));
1075 			x->fval = getfval(y);
1076 			x->tval |= NUM;
1077 		} else if (y->tval & STR)
1078 			(void) setsval(x, getsval(y));
1079 		else if (y->tval & NUM)
1080 			(void) setfval(x, getfval(y));
1081 		else
1082 			funnyvar(y, "read value of");
1083 		tempfree(y, "");
1084 		return (x);
1085 	}
1086 	xf = getfval(x);
1087 	yf = getfval(y);
1088 	switch (n) {
1089 	case ADDEQ:
1090 		xf += yf;
1091 		break;
1092 	case SUBEQ:
1093 		xf -= yf;
1094 		break;
1095 	case MULTEQ:
1096 		xf *= yf;
1097 		break;
1098 	case DIVEQ:
1099 		if (yf == 0)
1100 			ERROR "division by zero in /=" FATAL;
1101 		xf /= yf;
1102 		break;
1103 	case MODEQ:
1104 		if (yf == 0)
1105 			ERROR "division by zero in %%=" FATAL;
1106 		(void) modf(xf/yf, &v);
1107 		xf = xf - yf * v;
1108 		break;
1109 	case POWEQ:
1110 		if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */
1111 			xf = ipow(xf, (int)yf);
1112 		else
1113 			xf = errcheck(pow(xf, yf), "pow");
1114 		break;
1115 	default:
1116 		ERROR "illegal assignment operator %d", n FATAL;
1117 		break;
1118 	}
1119 	tempfree(y, "");
1120 	(void) setfval(x, xf);
1121 	return (x);
1122 }
1123 
1124 /*ARGSUSED*/
1125 Cell *
1126 cat(Node **a, int q)
1127 {
1128 	register Cell *x, *y, *z;
1129 	register int n1, n2;
1130 	register uchar *s;
1131 
1132 	x = execute(a[0]);
1133 	y = execute(a[1]);
1134 	(void) getsval(x);
1135 	(void) getsval(y);
1136 	n1 = strlen((char *)x->sval);
1137 	n2 = strlen((char *)y->sval);
1138 	s = (uchar *)malloc(n1 + n2 + 1);
1139 	if (s == NULL) {
1140 		ERROR "out of space concatenating %.15s and %.15s",
1141 		    x->sval, y->sval FATAL;
1142 	}
1143 	(void) strcpy((char *)s, (char *)x->sval);
1144 	(void) strcpy((char *)s + n1, (char *)y->sval);
1145 	tempfree(y, "");
1146 	z = gettemp("");
1147 	z->sval = s;
1148 	z->tval = STR;
1149 	tempfree(x, "");
1150 	return (z);
1151 }
1152 
1153 /*ARGSUSED*/
1154 Cell *
1155 pastat(Node **a, int n)
1156 {
1157 	register Cell *x;
1158 
1159 	if (a[0] == 0)
1160 		x = execute(a[1]);
1161 	else {
1162 		x = execute(a[0]);
1163 		if (istrue(x)) {
1164 			tempfree(x, "");
1165 			x = execute(a[1]);
1166 		}
1167 	}
1168 	return (x);
1169 }
1170 
1171 /*ARGSUSED*/
1172 Cell *
1173 dopa2(Node **a, int n)
1174 {
1175 	Cell	*x;
1176 	int	pair;
1177 	static int	*pairstack = NULL;
1178 
1179 	if (!pairstack) {
1180 		/* first time */
1181 		dprintf(("paircnt: %d\n", paircnt));
1182 		pairstack = (int *)malloc(sizeof (int) * paircnt);
1183 		if (!pairstack)
1184 			ERROR "out of space in dopa2" FATAL;
1185 		(void) memset(pairstack, 0, sizeof (int) * paircnt);
1186 	}
1187 
1188 	pair = (int)a[3];
1189 	if (pairstack[pair] == 0) {
1190 		x = execute(a[0]);
1191 		if (istrue(x))
1192 			pairstack[pair] = 1;
1193 		tempfree(x, "");
1194 	}
1195 	if (pairstack[pair] == 1) {
1196 		x = execute(a[1]);
1197 		if (istrue(x))
1198 			pairstack[pair] = 0;
1199 		tempfree(x, "");
1200 		x = execute(a[2]);
1201 		return (x);
1202 	}
1203 	return (false);
1204 }
1205 
1206 /*ARGSUSED*/
1207 Cell *
1208 split(Node **a, int nnn)
1209 {
1210 	Cell *x, *y, *ap;
1211 	register uchar *s;
1212 	register int sep;
1213 	uchar *t, temp, num[11], *fs;
1214 	int n, tempstat;
1215 
1216 	y = execute(a[0]);	/* source string */
1217 	s = getsval(y);
1218 	if (a[2] == 0)		/* fs string */
1219 		fs = *FS;
1220 	else if ((int)a[3] == STRING) {	/* split(str,arr,"string") */
1221 		x = execute(a[2]);
1222 		fs = getsval(x);
1223 	} else if ((int)a[3] == REGEXPR)
1224 		fs = (uchar *)"(regexpr)";	/* split(str,arr,/regexpr/) */
1225 	else
1226 		ERROR "illegal type of split()" FATAL;
1227 	sep = *fs;
1228 	ap = execute(a[1]);	/* array name */
1229 	freesymtab(ap);
1230 	dprintf(("split: s=|%s|, a=%s, sep=|%s|\n", s, ap->nval, fs));
1231 	ap->tval &= ~STR;
1232 	ap->tval |= ARR;
1233 	ap->sval = (uchar *)makesymtab(NSYMTAB);
1234 
1235 	n = 0;
1236 	if (*s != '\0' && strlen((char *)fs) > 1 || (int)a[3] == REGEXPR) {
1237 		/* reg expr */
1238 		fa *pfa;
1239 		if ((int)a[3] == REGEXPR) {	/* it's ready already */
1240 			pfa = (fa *)a[2];
1241 		} else {
1242 			pfa = makedfa(fs, 1);
1243 		}
1244 		if (nematch(pfa, s)) {
1245 			tempstat = pfa->initstat;
1246 			pfa->initstat = 2;
1247 			do {
1248 				n++;
1249 				(void) sprintf((char *)num, "%d", n);
1250 				temp = *patbeg;
1251 				*patbeg = '\0';
1252 				if (is_number(s)) {
1253 					(void) setsymtab(num, s,
1254 					    atof((char *)s),
1255 					    /*LINTED align*/
1256 					    STR|NUM, (Array *)ap->sval);
1257 				} else {
1258 					(void) setsymtab(num, s, 0.0,
1259 					    /*LINTED align*/
1260 					    STR, (Array *)ap->sval);
1261 				}
1262 				*patbeg = temp;
1263 				s = patbeg + patlen;
1264 				if (*(patbeg+patlen-1) == 0 || *s == 0) {
1265 					n++;
1266 					(void) sprintf((char *)num, "%d", n);
1267 					(void) setsymtab(num, (uchar *)"", 0.0,
1268 					    /*LINTED align*/
1269 					    STR, (Array *)ap->sval);
1270 					pfa->initstat = tempstat;
1271 					goto spdone;
1272 				}
1273 			} while (nematch(pfa, s));
1274 		}
1275 		n++;
1276 		(void) sprintf((char *)num, "%d", n);
1277 		if (is_number(s)) {
1278 			(void) setsymtab(num, s, atof((char *)s),
1279 			    /*LINTED align*/
1280 			    STR|NUM, (Array *)ap->sval);
1281 		} else {
1282 			/*LINTED align*/
1283 			(void) setsymtab(num, s, 0.0, STR, (Array *)ap->sval);
1284 		}
1285 spdone:
1286 		pfa = NULL;
1287 	} else if (sep == ' ') {
1288 		for (n = 0; ; ) {
1289 			while (*s == ' ' || *s == '\t' || *s == '\n')
1290 				s++;
1291 			if (*s == 0)
1292 				break;
1293 			n++;
1294 			t = s;
1295 			do
1296 				s++;
1297 			while (*s != ' ' && *s != '\t' &&
1298 			    *s != '\n' && *s != '\0')
1299 				;
1300 			temp = *s;
1301 			*s = '\0';
1302 			(void) sprintf((char *)num, "%d", n);
1303 			if (is_number(t)) {
1304 				(void) setsymtab(num, t, atof((char *)t),
1305 				    /*LINTED align*/
1306 				    STR|NUM, (Array *)ap->sval);
1307 			} else {
1308 				(void) setsymtab(num, t, 0.0,
1309 				    /*LINTED align*/
1310 				    STR, (Array *)ap->sval);
1311 			}
1312 			*s = temp;
1313 			if (*s != 0)
1314 				s++;
1315 		}
1316 	} else if (*s != 0) {
1317 		for (;;) {
1318 			n++;
1319 			t = s;
1320 			while (*s != sep && *s != '\n' && *s != '\0')
1321 				s++;
1322 			temp = *s;
1323 			*s = '\0';
1324 			(void) sprintf((char *)num, "%d", n);
1325 			if (is_number(t)) {
1326 				(void) setsymtab(num, t, atof((char *)t),
1327 				    /*LINTED align*/
1328 				    STR|NUM, (Array *)ap->sval);
1329 			} else {
1330 				(void) setsymtab(num, t, 0.0,
1331 				    /*LINTED align*/
1332 				    STR, (Array *)ap->sval);
1333 			}
1334 			*s = temp;
1335 			if (*s++ == 0)
1336 				break;
1337 		}
1338 	}
1339 	tempfree(ap, "");
1340 	tempfree(y, "");
1341 	if (a[2] != 0 && (int)a[3] == STRING)
1342 		tempfree(x, "");
1343 	x = gettemp("");
1344 	x->tval = NUM;
1345 	x->fval = n;
1346 	return (x);
1347 }
1348 
1349 /*ARGSUSED*/
1350 Cell *
1351 condexpr(Node **a, int n)
1352 {
1353 	register Cell *x;
1354 
1355 	x = execute(a[0]);
1356 	if (istrue(x)) {
1357 		tempfree(x, "");
1358 		x = execute(a[1]);
1359 	} else {
1360 		tempfree(x, "");
1361 		x = execute(a[2]);
1362 	}
1363 	return (x);
1364 }
1365 
1366 /*ARGSUSED*/
1367 Cell *
1368 ifstat(Node **a, int n)
1369 {
1370 	register Cell *x;
1371 
1372 	x = execute(a[0]);
1373 	if (istrue(x)) {
1374 		tempfree(x, "");
1375 		x = execute(a[1]);
1376 	} else if (a[2] != 0) {
1377 		tempfree(x, "");
1378 		x = execute(a[2]);
1379 	}
1380 	return (x);
1381 }
1382 
1383 /*ARGSUSED*/
1384 Cell *
1385 whilestat(Node **a, int n)
1386 {
1387 	register Cell *x;
1388 
1389 	for (;;) {
1390 		x = execute(a[0]);
1391 		if (!istrue(x))
1392 			return (x);
1393 		tempfree(x, "");
1394 		x = execute(a[1]);
1395 		if (isbreak(x)) {
1396 			x = true;
1397 			return (x);
1398 		}
1399 		if (isnext(x) || isexit(x) || isret(x))
1400 			return (x);
1401 		tempfree(x, "");
1402 	}
1403 }
1404 
1405 /*ARGSUSED*/
1406 Cell *
1407 dostat(Node **a, int n)
1408 {
1409 	register Cell *x;
1410 
1411 	for (;;) {
1412 		x = execute(a[0]);
1413 		if (isbreak(x))
1414 			return (true);
1415 		if (isnext(x) || isexit(x) || isret(x))
1416 			return (x);
1417 		tempfree(x, "");
1418 		x = execute(a[1]);
1419 		if (!istrue(x))
1420 			return (x);
1421 		tempfree(x, "");
1422 	}
1423 }
1424 
1425 /*ARGSUSED*/
1426 Cell *
1427 forstat(Node **a, int n)
1428 {
1429 	register Cell *x;
1430 
1431 	x = execute(a[0]);
1432 	tempfree(x, "");
1433 	for (;;) {
1434 		if (a[1] != 0) {
1435 			x = execute(a[1]);
1436 			if (!istrue(x))
1437 				return (x);
1438 			else
1439 				tempfree(x, "");
1440 		}
1441 		x = execute(a[3]);
1442 		if (isbreak(x))		/* turn off break */
1443 			return (true);
1444 		if (isnext(x) || isexit(x) || isret(x))
1445 			return (x);
1446 		tempfree(x, "");
1447 		x = execute(a[2]);
1448 		tempfree(x, "");
1449 	}
1450 }
1451 
1452 /*ARGSUSED*/
1453 Cell *
1454 instat(Node **a, int n)
1455 {
1456 	register Cell *x, *vp, *arrayp, *cp, *ncp;
1457 	Array *tp;
1458 	int i;
1459 
1460 	vp = execute(a[0]);
1461 	arrayp = execute(a[1]);
1462 	if (!isarr(arrayp))
1463 		ERROR "%s is not an array", arrayp->nval FATAL;
1464 	/*LINTED align*/
1465 	tp = (Array *)arrayp->sval;
1466 	tempfree(arrayp, "");
1467 	for (i = 0; i < tp->size; i++) { /* this routine knows too much */
1468 		for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
1469 			(void) setsval(vp, cp->nval);
1470 			ncp = cp->cnext;
1471 			x = execute(a[2]);
1472 			if (isbreak(x)) {
1473 				tempfree(vp, "");
1474 				return (true);
1475 			}
1476 			if (isnext(x) || isexit(x) || isret(x)) {
1477 				tempfree(vp, "");
1478 				return (x);
1479 			}
1480 			tempfree(x, "");
1481 		}
1482 	}
1483 	return (true);
1484 }
1485 
1486 /*ARGSUSED*/
1487 Cell *
1488 bltin(Node **a, int n)
1489 {
1490 	register Cell *x, *y;
1491 	Awkfloat u;
1492 	register int t;
1493 	uchar *p, *buf;
1494 	Node *nextarg;
1495 
1496 	t = (int)a[0];
1497 	x = execute(a[1]);
1498 	nextarg = a[1]->nnext;
1499 	switch (t) {
1500 	case FLENGTH:
1501 		u = (Awkfloat)strlen((char *)getsval(x)); break;
1502 	case FLOG:
1503 		u = errcheck(log(getfval(x)), "log"); break;
1504 	case FINT:
1505 		(void) modf(getfval(x), &u); break;
1506 	case FEXP:
1507 		u = errcheck(exp(getfval(x)), "exp"); break;
1508 	case FSQRT:
1509 		u = errcheck(sqrt(getfval(x)), "sqrt"); break;
1510 	case FSIN:
1511 		u = sin(getfval(x)); break;
1512 	case FCOS:
1513 		u = cos(getfval(x)); break;
1514 	case FATAN:
1515 		if (nextarg == 0) {
1516 			ERROR "atan2 requires two arguments; returning 1.0"
1517 			    WARNING;
1518 			u = 1.0;
1519 		} else {
1520 			y = execute(a[1]->nnext);
1521 			u = atan2(getfval(x), getfval(y));
1522 			tempfree(y, "");
1523 			nextarg = nextarg->nnext;
1524 		}
1525 		break;
1526 	case FSYSTEM:
1527 		/* in case something is buffered already */
1528 		(void) fflush(stdout);
1529 		/* 256 is unix-dep */
1530 		u = (Awkfloat)system((char *)getsval(x)) / 256;
1531 		break;
1532 	case FRAND:
1533 		u = (Awkfloat)(rand() % 32767) / 32767.0;
1534 		break;
1535 	case FSRAND:
1536 		if (x->tval & REC)	/* no argument provided */
1537 			u = time((time_t *)0);
1538 		else
1539 			u = getfval(x);
1540 		srand((int)u); u = (int)u;
1541 		break;
1542 	case FTOUPPER:
1543 	case FTOLOWER:
1544 		buf = tostring(getsval(x));
1545 		if (t == FTOUPPER) {
1546 			for (p = buf; *p; p++)
1547 				if (islower(*p))
1548 					*p = toupper(*p);
1549 		} else {
1550 			for (p = buf; *p; p++)
1551 				if (isupper(*p))
1552 					*p = tolower(*p);
1553 		}
1554 		tempfree(x, "");
1555 		x = gettemp("");
1556 		(void) setsval(x, buf);
1557 		free(buf);
1558 		return (x);
1559 	default:	/* can't happen */
1560 		ERROR "illegal function type %d", t FATAL;
1561 		break;
1562 	}
1563 	tempfree(x, "");
1564 	x = gettemp("");
1565 	(void) setfval(x, u);
1566 	if (nextarg != 0) {
1567 		ERROR "warning: function has too many arguments" WARNING;
1568 		for (; nextarg; nextarg = nextarg->nnext)
1569 			(void) execute(nextarg);
1570 	}
1571 	return (x);
1572 }
1573 
1574 /*ARGSUSED*/
1575 Cell *
1576 print(Node **a, int n)
1577 {
1578 	register Node *x;
1579 	register Cell *y;
1580 	FILE *fp;
1581 
1582 	if (a[1] == 0)
1583 		fp = stdout;
1584 	else
1585 		fp = redirect((int)a[1], a[2]);
1586 	for (x = a[0]; x != NULL; x = x->nnext) {
1587 		y = execute(x);
1588 		(void) fputs((char *)getsval(y), fp);
1589 		tempfree(y, "");
1590 		if (x->nnext == NULL)
1591 			(void) fputs((char *)*ORS, fp);
1592 		else
1593 			(void) fputs((char *)*OFS, fp);
1594 	}
1595 	if (a[1] != 0)
1596 		(void) fflush(fp);
1597 	return (true);
1598 }
1599 
1600 /*ARGSUSED*/
1601 Cell *
1602 nullproc(Node **a, int n)
1603 {
1604 	return (0);
1605 }
1606 
1607 struct {
1608 	FILE	*fp;
1609 	uchar	*fname;
1610 	int	mode;	/* '|', 'a', 'w' */
1611 } files[FOPEN_MAX];
1612 
1613 static FILE *
1614 redirect(int a, Node *b)
1615 {
1616 	FILE *fp;
1617 	Cell *x;
1618 	uchar *fname;
1619 
1620 	x = execute(b);
1621 	fname = getsval(x);
1622 	fp = openfile(a, fname);
1623 	if (fp == NULL)
1624 		ERROR "can't open file %s", fname FATAL;
1625 	tempfree(x, "");
1626 	return (fp);
1627 }
1628 
1629 static FILE *
1630 openfile(int a, uchar *s)
1631 {
1632 	register int i, m;
1633 	register FILE *fp;
1634 
1635 	if (*s == '\0')
1636 		ERROR "null file name in print or getline" FATAL;
1637 	for (i = 0; i < FOPEN_MAX; i++) {
1638 		if (files[i].fname &&
1639 		    strcmp((char *)s, (char *)files[i].fname) == 0) {
1640 			if (a == files[i].mode ||
1641 			    a == APPEND && files[i].mode == GT) {
1642 				return (files[i].fp);
1643 			}
1644 		}
1645 	}
1646 	for (i = 0; i < FOPEN_MAX; i++) {
1647 		if (files[i].fp == 0)
1648 			break;
1649 	}
1650 	if (i >= FOPEN_MAX)
1651 		ERROR "%s makes too many open files", s FATAL;
1652 	(void) fflush(stdout);	/* force a semblance of order */
1653 	m = a;
1654 	if (a == GT) {
1655 		fp = fopen((char *)s, "w");
1656 	} else if (a == APPEND) {
1657 		fp = fopen((char *)s, "a");
1658 		m = GT;	/* so can mix > and >> */
1659 	} else if (a == '|') {	/* output pipe */
1660 		fp = popen((char *)s, "w");
1661 	} else if (a == LE) {	/* input pipe */
1662 		fp = popen((char *)s, "r");
1663 	} else if (a == LT) {	/* getline <file */
1664 		fp = strcmp((char *)s, "-") == 0 ?
1665 		    stdin : fopen((char *)s, "r");	/* "-" is stdin */
1666 	} else	/* can't happen */
1667 		ERROR "illegal redirection" FATAL;
1668 	if (fp != NULL) {
1669 		files[i].fname = tostring(s);
1670 		files[i].fp = fp;
1671 		files[i].mode = m;
1672 	}
1673 	return (fp);
1674 }
1675 
1676 /*ARGSUSED*/
1677 Cell *
1678 closefile(Node **a, int n)
1679 {
1680 	register Cell *x;
1681 	int i, stat;
1682 
1683 	x = execute(a[0]);
1684 	(void) getsval(x);
1685 	for (i = 0; i < FOPEN_MAX; i++) {
1686 		if (files[i].fname &&
1687 		    strcmp((char *)x->sval, (char *)files[i].fname) == 0) {
1688 			if (ferror(files[i].fp)) {
1689 				ERROR "i/o error occurred on %s",
1690 				    files[i].fname WARNING;
1691 			}
1692 			if (files[i].mode == '|' || files[i].mode == LE)
1693 				stat = pclose(files[i].fp);
1694 			else
1695 				stat = fclose(files[i].fp);
1696 			if (stat == EOF) {
1697 				ERROR "i/o error occurred closing %s",
1698 				    files[i].fname WARNING;
1699 			}
1700 			xfree(files[i].fname);
1701 			/* watch out for ref thru this */
1702 			files[i].fname = NULL;
1703 			files[i].fp = NULL;
1704 		}
1705 	}
1706 	tempfree(x, "close");
1707 	return (true);
1708 }
1709 
1710 static void
1711 closeall(void)
1712 {
1713 	int i, stat;
1714 
1715 	for (i = 0; i < FOPEN_MAX; i++) {
1716 		if (files[i].fp) {
1717 			if (ferror(files[i].fp)) {
1718 				ERROR "i/o error occurred on %s",
1719 				    files[i].fname WARNING;
1720 			}
1721 			if (files[i].mode == '|' || files[i].mode == LE)
1722 				stat = pclose(files[i].fp);
1723 			else
1724 				stat = fclose(files[i].fp);
1725 			if (stat == EOF) {
1726 				ERROR "i/o error occurred while closing %s",
1727 				    files[i].fname WARNING;
1728 			}
1729 		}
1730 	}
1731 }
1732 
1733 /*ARGSUSED*/
1734 Cell *
1735 sub(Node **a, int nnn)
1736 {
1737 	register uchar *sptr;
1738 	register Cell *x, *y, *result;
1739 	uchar *buf, *t;
1740 	fa *pfa;
1741 	size_t	bsize, cnt, len;
1742 
1743 	x = execute(a[3]);	/* target string */
1744 	t = getsval(x);
1745 	if (a[0] == 0)
1746 		pfa = (fa *)a[1];	/* regular expression */
1747 	else {
1748 		y = execute(a[1]);
1749 		pfa = makedfa(getsval(y), 1);
1750 		tempfree(y, "");
1751 	}
1752 	y = execute(a[2]);	/* replacement string */
1753 	result = false;
1754 	if (pmatch(pfa, t)) {
1755 		init_buf(&buf, &bsize, LINE_INCR);
1756 		cnt = 0;
1757 		sptr = t;
1758 		len = patbeg - sptr;
1759 		if (len > 0) {
1760 			expand_buf(&buf, &bsize, cnt + len);
1761 			(void) memcpy(buf, sptr, len);
1762 			cnt += len;
1763 		}
1764 		sptr = getsval(y);
1765 		while (*sptr != 0) {
1766 			expand_buf(&buf, &bsize, cnt);
1767 			if (*sptr == '\\' && *(sptr+1) == '&') {
1768 				sptr++;		/* skip \, */
1769 				buf[cnt++] = *sptr++; /* add & */
1770 			} else if (*sptr == '&') {
1771 				expand_buf(&buf, &bsize, cnt + patlen);
1772 				sptr++;
1773 				(void) memcpy(&buf[cnt], patbeg, patlen);
1774 				cnt += patlen;
1775 			} else {
1776 				buf[cnt++] = *sptr++;
1777 			}
1778 		}
1779 		sptr = patbeg + patlen;
1780 		if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
1781 			len = strlen((char *)sptr);
1782 			expand_buf(&buf, &bsize, cnt + len);
1783 			(void) memcpy(&buf[cnt], sptr, len);
1784 			cnt += len;
1785 		}
1786 		buf[cnt] = '\0';
1787 		(void) setsval(x, buf);
1788 		free(buf);
1789 		result = true;
1790 	}
1791 	tempfree(x, "");
1792 	tempfree(y, "");
1793 	return (result);
1794 }
1795 
1796 /*ARGSUSED*/
1797 Cell *
1798 gsub(Node **a, int nnn)
1799 {
1800 	register Cell *x, *y;
1801 	register uchar *rptr, *sptr, *t;
1802 	uchar *buf;
1803 	register fa *pfa;
1804 	int mflag, tempstat, num;
1805 	size_t	bsize, cnt, len;
1806 
1807 	mflag = 0;	/* if mflag == 0, can replace empty string */
1808 	num = 0;
1809 	x = execute(a[3]);	/* target string */
1810 	t = getsval(x);
1811 	if (a[0] == 0)
1812 		pfa = (fa *) a[1];	/* regular expression */
1813 	else {
1814 		y = execute(a[1]);
1815 		pfa = makedfa(getsval(y), 1);
1816 		tempfree(y, "");
1817 	}
1818 	y = execute(a[2]);	/* replacement string */
1819 	if (pmatch(pfa, t)) {
1820 		tempstat = pfa->initstat;
1821 		pfa->initstat = 2;
1822 		init_buf(&buf, &bsize, LINE_INCR);
1823 		rptr = getsval(y);
1824 		cnt = 0;
1825 		do {
1826 			if (patlen == 0 && *patbeg != 0) {
1827 				/* matched empty string */
1828 				if (mflag == 0) {	/* can replace empty */
1829 					num++;
1830 					sptr = rptr;
1831 					while (*sptr != 0) {
1832 						expand_buf(&buf, &bsize, cnt);
1833 						if (*sptr == '\\' &&
1834 						    *(sptr+1) == '&') {
1835 							sptr++;
1836 							buf[cnt++] = *sptr++;
1837 						} else if (*sptr == '&') {
1838 							expand_buf(&buf,
1839 							    &bsize,
1840 							    cnt + patlen);
1841 							sptr++;
1842 							(void) memcpy(&buf[cnt],
1843 							    patbeg, patlen);
1844 							cnt += patlen;
1845 						} else {
1846 							buf[cnt++] = *sptr++;
1847 						}
1848 					}
1849 				}
1850 				if (*t == 0)	/* at end */
1851 					goto done;
1852 				expand_buf(&buf, &bsize, cnt);
1853 				buf[cnt++] = *t++;
1854 				mflag = 0;
1855 			} else {	/* matched nonempty string */
1856 				num++;
1857 				sptr = t;
1858 				len = patbeg - sptr;
1859 				if (len > 0) {
1860 					expand_buf(&buf, &bsize, cnt + len);
1861 					(void) memcpy(&buf[cnt], sptr, len);
1862 					cnt += len;
1863 				}
1864 				sptr = rptr;
1865 				while (*sptr != 0) {
1866 					expand_buf(&buf, &bsize, cnt);
1867 					if (*sptr == '\\' && *(sptr+1) == '&') {
1868 						sptr++;
1869 						buf[cnt++] = *sptr++;
1870 					} else if (*sptr == '&') {
1871 						expand_buf(&buf, &bsize,
1872 						    cnt + patlen);
1873 						sptr++;
1874 						(void) memcpy(&buf[cnt],
1875 						    patbeg, patlen);
1876 						cnt += patlen;
1877 					} else {
1878 						buf[cnt++] = *sptr++;
1879 					}
1880 				}
1881 				t = patbeg + patlen;
1882 				if ((*(t-1) == 0) || (*t == 0))
1883 					goto done;
1884 				mflag = 1;
1885 			}
1886 		} while (pmatch(pfa, t));
1887 		sptr = t;
1888 		len = strlen((char *)sptr);
1889 		expand_buf(&buf, &bsize, len + cnt);
1890 		(void) memcpy(&buf[cnt], sptr, len);
1891 		cnt += len;
1892 	done:
1893 		buf[cnt] = '\0';
1894 		(void) setsval(x, buf);
1895 		free(buf);
1896 		pfa->initstat = tempstat;
1897 	}
1898 	tempfree(x, "");
1899 	tempfree(y, "");
1900 	x = gettemp("");
1901 	x->tval = NUM;
1902 	x->fval = num;
1903 	return (x);
1904 }
1905