| /* |
| * 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 "gawk.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 = ≺ |
| 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); |
| } |
| 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); |
| } |
| 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); |
| } |
| |