aboutsummaryrefslogtreecommitdiffstats
path: root/builtin.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-15 23:12:49 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-15 23:12:49 +0300
commit3697ec5ca140f686643d204a54181a5ddbf9a799 (patch)
tree592873e8614475012ddd5f4e6d0482acadbfc9e2 /builtin.c
parentf3d9dd233ac07f764a554528c85be3768a1d1ddb (diff)
downloadegawk-3697ec5ca140f686643d204a54181a5ddbf9a799.tar.gz
egawk-3697ec5ca140f686643d204a54181a5ddbf9a799.tar.bz2
egawk-3697ec5ca140f686643d204a54181a5ddbf9a799.zip
Moved to gawk 2.11.
Diffstat (limited to 'builtin.c')
-rw-r--r--builtin.c1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/builtin.c b/builtin.c
new file mode 100644
index 00000000..b949a33b
--- /dev/null
+++ b/builtin.c
@@ -0,0 +1,1057 @@
+/*
+ * builtin.c - Builtin functions and various utility procedures
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Progamming Language.
+ *
+ * GAWK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * GAWK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GAWK; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "awk.h"
+
+extern void srandom();
+extern char *initstate();
+extern char *setstate();
+extern long random();
+
+extern NODE **fields_arr;
+
+static void get_one();
+static void get_two();
+static int get_three();
+
+/* Builtin functions */
+NODE *
+do_exp(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double d, res;
+ double exp();
+
+ get_one(tree, &tmp);
+ d = force_number(tmp);
+ free_temp(tmp);
+ errno = 0;
+ res = exp(d);
+ if (errno == ERANGE)
+ warning("exp argument %g is out of range", d);
+ return tmp_number((AWKNUM) res);
+}
+
+NODE *
+do_index(tree)
+NODE *tree;
+{
+ NODE *s1, *s2;
+ register char *p1, *p2;
+ register int l1, l2;
+ long ret;
+
+
+ get_two(tree, &s1, &s2);
+ force_string(s1);
+ force_string(s2);
+ p1 = s1->stptr;
+ p2 = s2->stptr;
+ l1 = s1->stlen;
+ l2 = s2->stlen;
+ ret = 0;
+ if (! strict && IGNORECASE_node->var_value->numbr != 0.0) {
+ while (l1) {
+ if (casetable[*p1] == casetable[*p2]
+ && strncasecmp(p1, p2, l2) == 0) {
+ ret = 1 + s1->stlen - l1;
+ break;
+ }
+ l1--;
+ p1++;
+ }
+ } else {
+ while (l1) {
+ if (STREQN(p1, p2, l2)) {
+ ret = 1 + s1->stlen - l1;
+ break;
+ }
+ l1--;
+ p1++;
+ }
+ }
+ free_temp(s1);
+ free_temp(s2);
+ return tmp_number((AWKNUM) ret);
+}
+
+NODE *
+do_int(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double floor();
+ double d;
+
+ get_one(tree, &tmp);
+ d = floor((double)force_number(tmp));
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+NODE *
+do_length(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ int len;
+
+ get_one(tree, &tmp);
+ len = force_string(tmp)->stlen;
+ free_temp(tmp);
+ return tmp_number((AWKNUM) len);
+}
+
+NODE *
+do_log(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double log();
+ double d, arg;
+
+ get_one(tree, &tmp);
+ arg = (double) force_number(tmp);
+ if (arg < 0.0)
+ warning("log called with negative argument %g", arg);
+ d = log(arg);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+/*
+ * Note that the output buffer cannot be static because sprintf may get
+ * called recursively by force_string. Hence the wasteful alloca calls
+ */
+
+/* %e and %f formats are not properly implemented. Someone should fix them */
+NODE *
+do_sprintf(tree)
+NODE *tree;
+{
+#define bchunk(s,l) if(l) {\
+ while((l)>ofre) {\
+ char *tmp;\
+ tmp=(char *)alloca(osiz*2);\
+ memcpy(tmp,obuf,olen);\
+ obuf=tmp;\
+ ofre+=osiz;\
+ osiz*=2;\
+ }\
+ memcpy(obuf+olen,s,(l));\
+ olen+=(l);\
+ ofre-=(l);\
+ }
+
+ /* Is there space for something L big in the buffer? */
+#define chksize(l) if((l)>ofre) {\
+ char *tmp;\
+ tmp=(char *)alloca(osiz*2);\
+ memcpy(tmp,obuf,olen);\
+ obuf=tmp;\
+ ofre+=osiz;\
+ osiz*=2;\
+ }
+
+ /*
+ * Get the next arg to be formatted. If we've run out of args,
+ * return "" (Null string)
+ */
+#define parse_next_arg() {\
+ if(!carg) arg= Nnull_string;\
+ else {\
+ get_one(carg,&arg);\
+ carg=carg->rnode;\
+ }\
+ }
+
+ char *obuf;
+ int osiz, ofre, olen;
+ static char chbuf[] = "0123456789abcdef";
+ static char sp[] = " ";
+ char *s0, *s1;
+ int n0;
+ NODE *sfmt, *arg;
+ register NODE *carg;
+ long fw, prec, lj, alt, big;
+ long *cur;
+ long val;
+#ifdef sun386 /* Can't cast unsigned (int/long) from ptr->value */
+ long tmp_uval; /* on 386i 4.0.1 C compiler -- it just hangs */
+#endif
+ unsigned long uval;
+ int sgn;
+ int base;
+ char cpbuf[30]; /* if we have numbers bigger than 30 */
+ char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */
+ char *cp;
+ char *fill;
+ double tmpval;
+ char *pr_str;
+ int ucasehex = 0;
+ extern char *gcvt();
+
+
+ obuf = (char *) alloca(120);
+ osiz = 120;
+ ofre = osiz;
+ olen = 0;
+ get_one(tree, &sfmt);
+ sfmt = force_string(sfmt);
+ carg = tree->rnode;
+ for (s0 = s1 = sfmt->stptr, n0 = sfmt->stlen; n0-- > 0;) {
+ if (*s1 != '%') {
+ s1++;
+ continue;
+ }
+ bchunk(s0, s1 - s0);
+ s0 = s1;
+ cur = &fw;
+ fw = 0;
+ prec = 0;
+ lj = alt = big = 0;
+ fill = sp;
+ cp = cend;
+ s1++;
+
+retry:
+ --n0;
+ switch (*s1++) {
+ case '%':
+ bchunk("%", 1);
+ s0 = s1;
+ break;
+
+ case '0':
+ if (fill != sp || lj)
+ goto lose;
+ if (cur == &fw)
+ fill = "0"; /* FALL through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (cur == 0)
+ goto lose;
+ *cur = s1[-1] - '0';
+ while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
+ --n0;
+ *cur = *cur * 10 + *s1++ - '0';
+ }
+ goto retry;
+#ifdef not_yet
+ case ' ': /* print ' ' or '-' */
+ case '+': /* print '+' or '-' */
+#endif
+ case '-':
+ if (lj || fill != sp)
+ goto lose;
+ lj++;
+ goto retry;
+ case '.':
+ if (cur != &fw)
+ goto lose;
+ cur = &prec;
+ goto retry;
+ case '#':
+ if (alt)
+ goto lose;
+ alt++;
+ goto retry;
+ case 'l':
+ if (big)
+ goto lose;
+ big++;
+ goto retry;
+ case 'c':
+ parse_next_arg();
+ if (arg->flags & NUMERIC) {
+#ifdef sun386
+ tmp_uval = arg->numbr;
+ uval= (unsigned long) tmp_uval;
+#else
+ uval = (unsigned long) arg->numbr;
+#endif
+ cpbuf[0] = uval;
+ prec = 1;
+ pr_str = cpbuf;
+ goto dopr_string;
+ }
+ if (! prec)
+ prec = 1;
+ else if (prec > arg->stlen)
+ prec = arg->stlen;
+ pr_str = arg->stptr;
+ goto dopr_string;
+ case 's':
+ parse_next_arg();
+ arg = force_string(arg);
+ if (!prec || prec > arg->stlen)
+ prec = arg->stlen;
+ pr_str = arg->stptr;
+
+ dopr_string:
+ if (fw > prec && !lj) {
+ while (fw > prec) {
+ bchunk(sp, 1);
+ fw--;
+ }
+ }
+ bchunk(pr_str, (int) prec);
+ if (fw > prec) {
+ while (fw > prec) {
+ bchunk(sp, 1);
+ fw--;
+ }
+ }
+ s0 = s1;
+ free_temp(arg);
+ break;
+ case 'd':
+ case 'i':
+ parse_next_arg();
+ val = (long) force_number(arg);
+ free_temp(arg);
+ if (val < 0) {
+ sgn = 1;
+ val = -val;
+ } else
+ sgn = 0;
+ do {
+ *--cp = '0' + val % 10;
+ val /= 10;
+ } while (val);
+ if (sgn)
+ *--cp = '-';
+ if (prec > fw)
+ fw = prec;
+ prec = cend - cp;
+ if (fw > prec && !lj) {
+ if (fill != sp && *cp == '-') {
+ bchunk(cp, 1);
+ cp++;
+ prec--;
+ fw--;
+ }
+ while (fw > prec) {
+ bchunk(fill, 1);
+ fw--;
+ }
+ }
+ bchunk(cp, (int) prec);
+ if (fw > prec) {
+ while (fw > prec) {
+ bchunk(fill, 1);
+ fw--;
+ }
+ }
+ s0 = s1;
+ break;
+ case 'u':
+ base = 10;
+ goto pr_unsigned;
+ case 'o':
+ base = 8;
+ goto pr_unsigned;
+ case 'X':
+ ucasehex = 1;
+ case 'x':
+ base = 16;
+ goto pr_unsigned;
+ pr_unsigned:
+ parse_next_arg();
+ uval = (unsigned long) force_number(arg);
+ free_temp(arg);
+ do {
+ *--cp = chbuf[uval % base];
+ if (ucasehex && isalpha(*cp))
+ *cp = toupper(*cp);
+ uval /= base;
+ } while (uval);
+ if (alt && (base == 8 || base == 16)) {
+ if (base == 16) {
+ if (ucasehex)
+ *--cp = 'X';
+ else
+ *--cp = 'x';
+ }
+ *--cp = '0';
+ }
+ prec = cend - cp;
+ if (fw > prec && !lj) {
+ while (fw > prec) {
+ bchunk(fill, 1);
+ fw--;
+ }
+ }
+ bchunk(cp, (int) prec);
+ if (fw > prec) {
+ while (fw > prec) {
+ bchunk(fill, 1);
+ fw--;
+ }
+ }
+ s0 = s1;
+ break;
+ case 'g':
+ parse_next_arg();
+ tmpval = force_number(arg);
+ free_temp(arg);
+ if (prec == 0)
+ prec = 13;
+ (void) gcvt(tmpval, (int) prec, cpbuf);
+ prec = strlen(cpbuf);
+ cp = cpbuf;
+ if (fw > prec && !lj) {
+ if (fill != sp && *cp == '-') {
+ bchunk(cp, 1);
+ cp++;
+ prec--;
+ } /* Deal with .5 as 0.5 */
+ if (fill == sp && *cp == '.') {
+ --fw;
+ while (--fw >= prec) {
+ bchunk(fill, 1);
+ }
+ bchunk("0", 1);
+ } else
+ while (fw-- > prec)
+ bchunk(fill, 1);
+ } else {/* Turn .5 into 0.5 */
+ /* FOO */
+ if (*cp == '.' && fill == sp) {
+ bchunk("0", 1);
+ --fw;
+ }
+ }
+ bchunk(cp, (int) prec);
+ if (fw > prec)
+ while (fw-- > prec)
+ bchunk(fill, 1);
+ s0 = s1;
+ break;
+ case 'f':
+ parse_next_arg();
+ tmpval = force_number(arg);
+ free_temp(arg);
+ chksize(fw + prec + 5); /* 5==slop */
+
+ cp = cpbuf;
+ *cp++ = '%';
+ if (lj)
+ *cp++ = '-';
+ if (fill != sp)
+ *cp++ = '0';
+ if (cur != &fw) {
+ (void) strcpy(cp, "*.*f");
+ (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval);
+ } else {
+ (void) strcpy(cp, "*f");
+ (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval);
+ }
+ cp = obuf + olen;
+ ofre -= strlen(obuf + olen);
+ olen += strlen(obuf + olen); /* There may be nulls */
+ s0 = s1;
+ break;
+ case 'e':
+ parse_next_arg();
+ tmpval = force_number(arg);
+ free_temp(arg);
+ chksize(fw + prec + 5); /* 5==slop */
+ cp = cpbuf;
+ *cp++ = '%';
+ if (lj)
+ *cp++ = '-';
+ if (fill != sp)
+ *cp++ = '0';
+ if (cur != &fw) {
+ (void) strcpy(cp, "*.*e");
+ (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval);
+ } else {
+ (void) strcpy(cp, "*e");
+ (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval);
+ }
+ cp = obuf + olen;
+ ofre -= strlen(obuf + olen);
+ olen += strlen(obuf + olen); /* There may be nulls */
+ s0 = s1;
+ break;
+
+ default:
+ lose:
+ break;
+ }
+ }
+ bchunk(s0, s1 - s0);
+ free_temp(sfmt);
+ return tmp_string(obuf, olen);
+}
+
+void
+do_printf(tree)
+NODE *tree;
+{
+ struct redirect *rp = NULL;
+ register FILE *fp = stdout;
+ int errflg = 0; /* not used, sigh */
+
+ if (tree->rnode) {
+ rp = redirect(tree->rnode, &errflg);
+ if (rp)
+ fp = rp->fp;
+ }
+ if (fp)
+ print_simple(do_sprintf(tree->lnode), fp);
+ if (rp && (rp->flag & RED_NOBUF))
+ fflush(fp);
+}
+
+NODE *
+do_sqrt(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ double sqrt();
+ double d, arg;
+
+ get_one(tree, &tmp);
+ arg = (double) force_number(tmp);
+ if (arg < 0.0)
+ warning("sqrt called with negative argument %g", arg);
+ d = sqrt(arg);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+NODE *
+do_substr(tree)
+NODE *tree;
+{
+ NODE *t1, *t2, *t3;
+ NODE *r;
+ register int indx, length;
+
+ t1 = t2 = t3 = NULL;
+ length = -1;
+ if (get_three(tree, &t1, &t2, &t3) == 3)
+ length = (int) force_number(t3);
+ indx = (int) force_number(t2) - 1;
+ t1 = force_string(t1);
+ if (length == -1)
+ length = t1->stlen;
+ if (indx < 0)
+ indx = 0;
+ if (indx >= t1->stlen || length <= 0) {
+ if (t3)
+ free_temp(t3);
+ free_temp(t2);
+ free_temp(t1);
+ return Nnull_string;
+ }
+ if (indx + length > t1->stlen)
+ length = t1->stlen - indx;
+ if (t3)
+ free_temp(t3);
+ free_temp(t2);
+ r = tmp_string(t1->stptr + indx, length);
+ free_temp(t1);
+ return r;
+}
+
+NODE *
+do_system(tree)
+NODE *tree;
+{
+#if defined(unix) || defined(MSDOS) /* || defined(gnu) */
+ NODE *tmp;
+ int ret;
+
+ (void) flush_io (); /* so output is synchronous with gawk's */
+ get_one(tree, &tmp);
+ ret = system(force_string(tmp)->stptr);
+ ret = (ret >> 8) & 0xff;
+ free_temp(tmp);
+ return tmp_number((AWKNUM) ret);
+#else
+ fatal("the \"system\" function is not supported.");
+ /* NOTREACHED */
+#endif
+}
+
+void
+do_print(tree)
+register NODE *tree;
+{
+ struct redirect *rp = NULL;
+ register FILE *fp = stdout;
+ int errflg = 0; /* not used, sigh */
+
+ if (tree->rnode) {
+ rp = redirect(tree->rnode, &errflg);
+ if (rp)
+ fp = rp->fp;
+ }
+ if (!fp)
+ return;
+ tree = tree->lnode;
+ if (!tree)
+ tree = WHOLELINE;
+ if (tree->type != Node_expression_list) {
+ if (!(tree->flags & STR))
+ cant_happen();
+ print_simple(tree, fp);
+ } else {
+ while (tree) {
+ print_simple(force_string(tree_eval(tree->lnode)), fp);
+ tree = tree->rnode;
+ if (tree)
+ print_simple(OFS_node->var_value, fp);
+ }
+ }
+ print_simple(ORS_node->var_value, fp);
+ if (rp && (rp->flag & RED_NOBUF))
+ fflush(fp);
+}
+
+NODE *
+do_tolower(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ register char *cp, *cp2;
+
+ get_one(tree, &t1);
+ t1 = force_string(t1);
+ t2 = tmp_string(t1->stptr, t1->stlen);
+ for (cp = t2->stptr, cp2 = t2->stptr + t2->stlen; cp < cp2; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ free_temp(t1);
+ return t2;
+}
+
+NODE *
+do_toupper(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ register char *cp;
+
+ get_one(tree, &t1);
+ t1 = force_string(t1);
+ t2 = tmp_string(t1->stptr, t1->stlen);
+ for (cp = t2->stptr; cp < t2->stptr + t2->stlen; cp++)
+ if (islower(*cp))
+ *cp = toupper(*cp);
+ free_temp(t1);
+ return t2;
+}
+
+/*
+ * Get the arguments to functions. No function cares if you give it too many
+ * args (they're ignored). Only a few fuctions complain about being given
+ * too few args. The rest have defaults.
+ */
+
+static void
+get_one(tree, res)
+NODE *tree, **res;
+{
+ if (!tree) {
+ *res = WHOLELINE;
+ return;
+ }
+ *res = tree_eval(tree->lnode);
+}
+
+static void
+get_two(tree, res1, res2)
+NODE *tree, **res1, **res2;
+{
+ if (!tree) {
+ *res1 = WHOLELINE;
+ return;
+ }
+ *res1 = tree_eval(tree->lnode);
+ if (!tree->rnode)
+ return;
+ tree = tree->rnode;
+ *res2 = tree_eval(tree->lnode);
+}
+
+static int
+get_three(tree, res1, res2, res3)
+NODE *tree, **res1, **res2, **res3;
+{
+ if (!tree) {
+ *res1 = WHOLELINE;
+ return 0;
+ }
+ *res1 = tree_eval(tree->lnode);
+ if (!tree->rnode)
+ return 1;
+ tree = tree->rnode;
+ *res2 = tree_eval(tree->lnode);
+ if (!tree->rnode)
+ return 2;
+ tree = tree->rnode;
+ *res3 = tree_eval(tree->lnode);
+ return 3;
+}
+
+int
+a_get_three(tree, res1, res2, res3)
+NODE *tree, **res1, **res2, **res3;
+{
+ if (!tree) {
+ *res1 = WHOLELINE;
+ return 0;
+ }
+ *res1 = tree_eval(tree->lnode);
+ if (!tree->rnode)
+ return 1;
+ tree = tree->rnode;
+ *res2 = tree->lnode;
+ if (!tree->rnode)
+ return 2;
+ tree = tree->rnode;
+ *res3 = tree_eval(tree->lnode);
+ return 3;
+}
+
+void
+print_simple(tree, fp)
+NODE *tree;
+FILE *fp;
+{
+ if (fwrite(tree->stptr, sizeof(char), tree->stlen, fp) != tree->stlen)
+ warning("fwrite: %s", strerror(errno));
+ free_temp(tree);
+}
+
+NODE *
+do_atan2(tree)
+NODE *tree;
+{
+ NODE *t1, *t2;
+ extern double atan2();
+ double d1, d2;
+
+ get_two(tree, &t1, &t2);
+ d1 = force_number(t1);
+ d2 = force_number(t2);
+ free_temp(t1);
+ free_temp(t2);
+ return tmp_number((AWKNUM) atan2(d1, d2));
+}
+
+NODE *
+do_sin(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ extern double sin();
+ double d;
+
+ get_one(tree, &tmp);
+ d = sin((double)force_number(tmp));
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+NODE *
+do_cos(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ extern double cos();
+ double d;
+
+ get_one(tree, &tmp);
+ d = cos((double)force_number(tmp));
+ free_temp(tmp);
+ return tmp_number((AWKNUM) d);
+}
+
+static int firstrand = 1;
+static char state[256];
+
+#define MAXLONG 2147483647 /* maximum value for long int */
+
+/* ARGSUSED */
+NODE *
+do_rand(tree)
+NODE *tree;
+{
+ if (firstrand) {
+ (void) initstate((unsigned) 1, state, sizeof state);
+ srandom(1);
+ firstrand = 0;
+ }
+ return tmp_number((AWKNUM) random() / MAXLONG);
+}
+
+NODE *
+do_srand(tree)
+NODE *tree;
+{
+ NODE *tmp;
+ static long save_seed = 1;
+ long ret = save_seed; /* SVR4 awk srand returns previous seed */
+ extern long time();
+
+ if (firstrand)
+ (void) initstate((unsigned) 1, state, sizeof state);
+ else
+ (void) setstate(state);
+
+ if (!tree)
+ srandom((int) (save_seed = time((long *) 0)));
+ else {
+ get_one(tree, &tmp);
+ srandom((int) (save_seed = (long) force_number(tmp)));
+ free_temp(tmp);
+ }
+ firstrand = 0;
+ return tmp_number((AWKNUM) ret);
+}
+
+NODE *
+do_match(tree)
+NODE *tree;
+{
+ NODE *t1;
+ int rstart;
+ struct re_registers reregs;
+ struct re_pattern_buffer *rp;
+ int need_to_free = 0;
+
+ t1 = force_string(tree_eval(tree->lnode));
+ tree = tree->rnode;
+ if (tree == NULL || tree->lnode == NULL)
+ fatal("match called with only one argument");
+ tree = tree->lnode;
+ if (tree->type == Node_regex) {
+ rp = tree->rereg;
+ if (!strict && ((IGNORECASE_node->var_value->numbr != 0)
+ ^ (tree->re_case != 0))) {
+ /* recompile since case sensitivity differs */
+ rp = tree->rereg =
+ mk_re_parse(tree->re_text,
+ (IGNORECASE_node->var_value->numbr != 0));
+ tree->re_case =
+ (IGNORECASE_node->var_value->numbr != 0);
+ }
+ } else {
+ need_to_free = 1;
+ rp = make_regexp(force_string(tree_eval(tree)),
+ (IGNORECASE_node->var_value->numbr != 0));
+ if (rp == NULL)
+ cant_happen();
+ }
+ rstart = re_search(rp, t1->stptr, t1->stlen, 0, t1->stlen, &reregs);
+ free_temp(t1);
+ if (rstart >= 0) {
+ rstart++; /* 1-based indexing */
+ /* RSTART set to rstart below */
+ RLENGTH_node->var_value->numbr =
+ (AWKNUM) (reregs.end[0] - reregs.start[0]);
+ } else {
+ /*
+ * Match failed. Set RSTART to 0, RLENGTH to -1.
+ * Return the value of RSTART.
+ */
+ rstart = 0; /* used as return value */
+ RLENGTH_node->var_value->numbr = -1.0;
+ }
+ RSTART_node->var_value->numbr = (AWKNUM) rstart;
+ if (need_to_free) {
+ free(rp->buffer);
+ free(rp->fastmap);
+ free((char *) rp);
+ }
+ return tmp_number((AWKNUM) rstart);
+}
+
+static NODE *
+sub_common(tree, global)
+NODE *tree;
+int global;
+{
+ register int len;
+ register char *scan;
+ register char *bp, *cp;
+ int search_start = 0;
+ int match_length;
+ int matches = 0;
+ char *buf;
+ struct re_pattern_buffer *rp;
+ NODE *s; /* subst. pattern */
+ NODE *t; /* string to make sub. in; $0 if none given */
+ struct re_registers reregs;
+ unsigned int saveflags;
+ NODE *tmp;
+ NODE **lhs;
+ char *lastbuf;
+ int need_to_free = 0;
+
+ if (tree == NULL)
+ fatal("sub or gsub called with 0 arguments");
+ tmp = tree->lnode;
+ if (tmp->type == Node_regex) {
+ rp = tmp->rereg;
+ if (! strict && ((IGNORECASE_node->var_value->numbr != 0)
+ ^ (tmp->re_case != 0))) {
+ /* recompile since case sensitivity differs */
+ rp = tmp->rereg =
+ mk_re_parse(tmp->re_text,
+ (IGNORECASE_node->var_value->numbr != 0));
+ tmp->re_case = (IGNORECASE_node->var_value->numbr != 0);
+ }
+ } else {
+ need_to_free = 1;
+ rp = make_regexp(force_string(tree_eval(tmp)),
+ (IGNORECASE_node->var_value->numbr != 0));
+ if (rp == NULL)
+ cant_happen();
+ }
+ tree = tree->rnode;
+ if (tree == NULL)
+ fatal("sub or gsub called with only 1 argument");
+ s = force_string(tree_eval(tree->lnode));
+ tree = tree->rnode;
+ deref = 0;
+ field_num = -1;
+ if (tree == NULL) {
+ t = node0_valid ? fields_arr[0] : *get_field(0, 0);
+ lhs = &fields_arr[0];
+ field_num = 0;
+ deref = t;
+ } else {
+ t = tree->lnode;
+ lhs = get_lhs(t, 1);
+ t = force_string(tree_eval(t));
+ }
+ /*
+ * create a private copy of the string
+ */
+ if (t->stref > 1 || (t->flags & PERM)) {
+ saveflags = t->flags;
+ t->flags &= ~MALLOC;
+ tmp = dupnode(t);
+ t->flags = saveflags;
+ do_deref();
+ t = tmp;
+ if (lhs)
+ *lhs = tmp;
+ }
+ lastbuf = t->stptr;
+ do {
+ if (re_search(rp, t->stptr, t->stlen, search_start,
+ t->stlen-search_start, &reregs) == -1
+ || reregs.start[0] == reregs.end[0])
+ break;
+ matches++;
+
+ /*
+ * first, make a pass through the sub. pattern, to calculate
+ * the length of the string after substitution
+ */
+ match_length = reregs.end[0] - reregs.start[0];
+ len = t->stlen - match_length;
+ for (scan = s->stptr; scan < s->stptr + s->stlen; scan++)
+ if (*scan == '&')
+ len += match_length;
+ else if (*scan == '\\' && *(scan+1) == '&') {
+ scan++;
+ len++;
+ } else
+ len++;
+ emalloc(buf, char *, len + 1, "do_sub");
+ bp = buf;
+
+ /*
+ * now, create the result, copying in parts of the original
+ * string
+ */
+ for (scan = t->stptr; scan < t->stptr + reregs.start[0]; scan++)
+ *bp++ = *scan;
+ for (scan = s->stptr; scan < s->stptr + s->stlen; scan++)
+ if (*scan == '&')
+ for (cp = t->stptr + reregs.start[0];
+ cp < t->stptr + reregs.end[0]; cp++)
+ *bp++ = *cp;
+ else if (*scan == '\\' && *(scan+1) == '&') {
+ scan++;
+ *bp++ = *scan;
+ } else
+ *bp++ = *scan;
+ search_start = bp - buf;
+ for (scan = t->stptr + reregs.end[0];
+ scan < t->stptr + t->stlen; scan++)
+ *bp++ = *scan;
+ *bp = '\0';
+ free(lastbuf);
+ t->stptr = buf;
+ lastbuf = buf;
+ t->stlen = len;
+ } while (global && search_start < t->stlen);
+
+ free_temp(s);
+ if (need_to_free) {
+ free(rp->buffer);
+ free(rp->fastmap);
+ free((char *) rp);
+ }
+ if (matches > 0) {
+ if (field_num == 0)
+ set_record(fields_arr[0]->stptr, fields_arr[0]->stlen);
+ t->flags &= ~(NUM|NUMERIC);
+ }
+ field_num = -1;
+ return tmp_number((AWKNUM) matches);
+}
+
+NODE *
+do_gsub(tree)
+NODE *tree;
+{
+ return sub_common(tree, 1);
+}
+
+NODE *
+do_sub(tree)
+NODE *tree;
+{
+ return sub_common(tree, 0);
+}
+