9216 awk: this statement may fall through
[unleashed.git] / usr / src / cmd / awk_xpg4 / awk4.c
blob96ec3d26ec83c38f3c3f55b0174192f1d9abf66c
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * awk -- functions
25 * Copyright (c) 1995, 1996 by Sun Microsystems, Inc.
26 * All rights reserved.
28 * Copyright 1986, 1994 by Mortice Kern Systems Inc. All rights reserved.
30 * Based on MKS awk(1) ported to be /usr/xpg4/bin/awk with POSIX/XCU4 changes
33 #include "awk.h"
34 #include "y.tab.h"
35 #include <time.h>
36 #include <sys/wait.h>
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);
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 */
49 * Return the value of exp(x).
50 * Usage: y = exp(x)
51 * y = exp()
53 NODE *
54 f_exp(NODE *np)
56 register uint na;
58 if ((na = nargs(np)) > 1)
59 awkerr(nargerr, s_exp);
60 return (realnode(exp(exprreal(na==0 ? field0 : getlist(&np)))));
64 * Return the integer part of the argument.
65 * Usage: i = int(r)
66 * i = int()
68 NODE *
69 f_int(NODE *np)
71 register uint na;
73 if ((na = nargs(np)) > 1)
74 awkerr(nargerr, s_int);
75 return (intnode(exprint(na==0 ? field0 : getlist(&np))));
79 * Logarithm function.
80 * Usage: y = log(x)
81 * y = log()
83 NODE *
84 f_log(NODE *np)
86 register uint na;
88 if ((na = nargs(np)) > 1)
89 awkerr(nargerr, s_log);
90 return (realnode(log(exprreal(na==0 ? field0 : getlist(&np)))));
94 * Square root function.
95 * Usage: y = sqrt(x)
96 * y = sqrt()
98 NODE *
99 f_sqrt(NODE *np)
101 register uint na;
103 if ((na = nargs(np)) > 1)
104 awkerr(nargerr, s_sqrt);
105 return (realnode(sqrt(exprreal(na==0 ? field0 : getlist(&np)))));
109 * Trigonometric sine function.
110 * Usage: y = sin(x)
112 NODE *
113 f_sin(NODE *np)
115 if (nargs(np) != 1)
116 awkerr(nargerr, s_sin);
117 return (realnode(sin(exprreal(getlist(&np)))));
121 * Trigonometric cosine function.
122 * Usage: y = cos(x)
124 NODE *
125 f_cos(NODE *np)
127 if (nargs(np) != 1)
128 awkerr(nargerr, s_cos);
129 return (realnode(cos(exprreal(getlist(&np)))));
133 * Arctangent of y/x.
134 * Usage: z = atan2(y, x)
136 NODE *
137 f_atan2(NODE *np)
139 double y, x;
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)));
149 * Set the seed for the random number generator function -- rand.
150 * Usage: srand(x)
151 * srand()
153 NODE *
154 f_srand(NODE *np)
156 register uint na;
157 register uint seed;
158 static uint oldseed = 0;
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));
172 * Generate a random number.
173 * Usage: x = rand()
175 NODE *
176 f_rand(NODE *np)
178 double result;
179 int expon;
180 ushort rint;
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)));
190 * Substitute function.
191 * Usage: n = sub(regex, replace, target)
192 * n = sub(regex, replace)
194 NODE *
195 f_sub(NODE *np)
197 return (dosub(np, 1));
201 * Global substitution function.
202 * Usage: n = gsub(regex, replace, target)
203 * n = gsub(regex, replace)
205 NODE *
206 f_gsub(NODE *np)
208 return (dosub(np, 0));
212 * Do actual substitutions.
213 * `glob' is the number to substitute, 0 for all.
215 static NODE *
216 dosub(NODE *np, int glob)
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;
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;
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"));
249 (void)assign(left, stringnode(buf, FNOALLOC, wcslen(buf)));
250 return (intnode((INT)n));
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)
259 NODE *
260 f_match(NODE *np)
262 register wchar_t *text;
263 register REGEXP rp;
264 register int pos, length;
265 REGWMATCH_T match[10];
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;
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)));
284 * Call shell or command interpreter.
285 * Usage: status = system(command)
287 NODE *
288 f_system(NODE *np)
290 int retcode;
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)));
300 * Search for string within string.
301 * Usage: pos = index(string1, string2)
303 NODE *
304 f_index(NODE *np)
306 register wchar_t *s1, *s2;
307 register int l1, l2;
308 register int result;
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--;
324 if (l2 > l1)
325 result = 0;
326 return (intnode((INT)result));
330 * Return length of argument or $0
331 * Usage: n = length(string)
332 * n = length()
333 * n = length
335 NODE *
336 f_length(NODE *np)
338 register uint na;
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));
349 * Split string into fields.
350 * Usage: nfields = split(string, array [, separator]);
352 NODE *
353 f_split(NODE *np)
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;
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;
381 case PARM:
382 break;
384 case VAR:
385 if (isstring(tnp->n_flags) && tnp->n_string==_null)
386 break;
387 /* FALLTHROUGH */
389 default:
390 awkerr(gettext(
391 "second parameter to \"split\" must be an array"));
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.)
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);
417 * Iterate over the record, extracting each field and assigning it to
418 * the corresponding element in the array.
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);
430 break;
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;
439 * Restore the old record separator/and or regular expression.
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));
450 if (saved)
451 free(saved);
452 return (intnode((INT)fcount));
456 * Sprintf function.
457 * Usage: string = sprintf(format, arg, ...)
459 NODE *
460 f_sprintf(NODE *np)
462 wchar_t *cp;
463 size_t length;
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);
473 * Substring.
474 * newstring = substr(string, start, [length])
476 NODE *
477 f_substr(NODE *np)
479 register STRING str;
480 register size_t n;
481 register int start;
482 register size_t len;
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);
513 * Close an output or input file stream.
515 NODE *
516 f_close(NODE *np)
518 register OFILE *op;
519 register char *name;
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;
529 if (op >= &ofiles[NIOSTREAM])
530 return (const1);
531 return (const0);
535 * Return the integer value of the first character of a string.
536 * Usage: char = ord(string)
538 NODE *
539 f_ord(NODE *np)
541 if (nargs(np) != 1)
542 awkerr(nargerr, s_ord);
543 return (intnode((INT)*exprstring(getlist(&np))));
547 * Return the argument string in lower case:
548 * Usage:
549 * lower = tolower(upper)
551 NODE *
552 f_tolower(NODE *np)
554 return (docasetr(np, 0));
558 * Return the argument string in upper case:
559 * Usage:
560 * upper = toupper(lower)
562 NODE *
563 f_toupper(NODE *np)
565 return (docasetr(np, 1));
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]
578 NODE *
579 f_asort(NODE *np)
581 NODE *array;
582 STRING funcname;
583 register size_t nel;
584 register NODE *tnp;
585 register NODE *funcnp;
586 register NODE **alist, **npp;
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);
602 * Count size of array, allowing one extra for NULL at end
604 nel = 1;
605 for (tnp = array->n_alink; tnp != NNULL; tnp = tnp->n_alink)
606 ++nel;
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
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;
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 *));
632 * Copy array into alist.
634 npp = alist;
635 for (tnp = array->n_alink; tnp != NNULL; tnp = tnp->n_alink)
636 *npp++ = tnp;
637 *npp = NNULL;
639 * Re-order array to this list
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);
652 * Return the number of arguments of a function.
654 static uint
655 nargs(NODE *np)
657 register int n;
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++;
666 return (n);
670 * Do case translation.
672 static NODE *
673 docasetr(NODE *np, int upper)
675 register int c;
676 register wchar_t *cp;
677 register wchar_t *str;
678 register uint na;
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);
691 return (stringnode((STRING)str, FNOALLOC, (size_t)(cp-str-1)));
695 * The comparison routine used by qsort inside f_asort()
697 static int
698 asortcmp(const void *npp1, const void *npp2)
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));
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
716 * Math error for awk.
719 matherr(struct exception *ep)
721 register uint type;
722 static char msgs[7][256];
723 static int first_time = 1;
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;
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);
744 #endif /*M_MATHERR*/