diff options
Diffstat (limited to 'gnu/usr.bin/awk/builtin.c')
-rw-r--r-- | gnu/usr.bin/awk/builtin.c | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/gnu/usr.bin/awk/builtin.c b/gnu/usr.bin/awk/builtin.c new file mode 100644 index 0000000..9d5e3b3 --- /dev/null +++ b/gnu/usr.bin/awk/builtin.c @@ -0,0 +1,1133 @@ +/* + * builtin.c - Builtin functions and various utility procedures + */ + +/* + * Copyright (C) 1986, 1988, 1989, 1991, 1992 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 2 of the License, 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include "awk.h" + + +#ifndef SRANDOM_PROTO +extern void srandom P((int seed)); +#endif +#ifndef linux +extern char *initstate P((unsigned seed, char *state, int n)); +extern char *setstate P((char *state)); +extern long random P((void)); +#endif + +extern NODE **fields_arr; +extern int output_is_tty; + +static NODE *sub_common P((NODE *tree, int global)); + +#ifdef GFMT_WORKAROUND +char *gfmt P((double g, int prec, char *buf)); +#endif + +#ifdef _CRAY +/* Work around a problem in conversion of doubles to exact integers. */ +#include <float.h> +#define Floor(n) floor((n) * (1.0 + DBL_EPSILON)) +#define Ceil(n) ceil((n) * (1.0 + DBL_EPSILON)) + +/* Force the standard C compiler to use the library math functions. */ +extern double exp(double); +double (*Exp)() = exp; +#define exp(x) (*Exp)(x) +extern double log(double); +double (*Log)() = log; +#define log(x) (*Log)(x) +#else +#define Floor(n) floor(n) +#define Ceil(n) ceil(n) +#endif + +static void +efwrite(ptr, size, count, fp, from, rp, flush) +void *ptr; +unsigned size, count; +FILE *fp; +char *from; +struct redirect *rp; +int flush; +{ + errno = 0; + if (fwrite(ptr, size, count, fp) != count) + goto wrerror; + if (flush + && ((fp == stdout && output_is_tty) + || (rp && (rp->flag & RED_NOBUF)))) { + fflush(fp); + if (ferror(fp)) + goto wrerror; + } + return; + + wrerror: + fatal("%s to \"%s\" failed (%s)", from, + rp ? rp->value : "standard output", + errno ? strerror(errno) : "reason unknown"); +} + +/* Builtin functions */ +NODE * +do_exp(tree) +NODE *tree; +{ + NODE *tmp; + double d, res; +#ifndef exp + double exp P((double)); +#endif + + tmp= tree_eval(tree->lnode); + 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; + + + s1 = tree_eval(tree->lnode); + s2 = tree_eval(tree->rnode->lnode); + force_string(s1); + force_string(s2); + p1 = s1->stptr; + p2 = s2->stptr; + l1 = s1->stlen; + l2 = s2->stlen; + ret = 0; + if (IGNORECASE) { + while (l1) { + if (l2 > l1) + break; + if (casetable[(int)*p1] == casetable[(int)*p2] + && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) { + ret = 1 + s1->stlen - l1; + break; + } + l1--; + p1++; + } + } else { + while (l1) { + if (l2 > l1) + break; + if (*p1 == *p2 + && (l2 == 1 || 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 P((double)); + double ceil P((double)); + double d; + + tmp = tree_eval(tree->lnode); + d = force_number(tmp); + if (d >= 0) + d = Floor(d); + else + d = Ceil(d); + free_temp(tmp); + return tmp_number((AWKNUM) d); +} + +NODE * +do_length(tree) +NODE *tree; +{ + NODE *tmp; + int len; + + tmp = tree_eval(tree->lnode); + len = force_string(tmp)->stlen; + free_temp(tmp); + return tmp_number((AWKNUM) len); +} + +NODE * +do_log(tree) +NODE *tree; +{ + NODE *tmp; +#ifndef log + double log P((double)); +#endif + double d, arg; + + tmp = tree_eval(tree->lnode); + 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); +} + +/* %e and %f formats are not properly implemented. Someone should fix them */ +/* Actually, this whole thing should be reimplemented. */ + +NODE * +do_sprintf(tree) +NODE *tree; +{ +#define bchunk(s,l) if(l) {\ + while((l)>ofre) {\ + erealloc(obuf, char *, osiz*2, "do_sprintf");\ + 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) {\ + erealloc(obuf, char *, osiz*2, "do_sprintf");\ + 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) { toofew = 1; break; }\ + else {\ + arg=tree_eval(carg->lnode);\ + carg=carg->rnode;\ + }\ + } + + NODE *r; + int toofew = 0; + 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; + char signchar = 0; + int len; + + + emalloc(obuf, char *, 120, "do_sprintf"); + osiz = 120; + ofre = osiz - 1; + olen = 0; + sfmt = tree_eval(tree->lnode); + 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; + case '*': + if (cur == 0) + goto lose; + parse_next_arg(); + *cur = force_number(arg); + free_temp(arg); + goto retry; + case ' ': /* print ' ' or '-' */ + case '+': /* print '+' or '-' */ + signchar = *(s1-1); + goto retry; + 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 & NUMBER) { +#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 = '-'; + else if (signchar) + *--cp = signchar; + if (prec > fw) + fw = prec; + prec = cend - cp; + if (fw > prec && !lj) { + if (fill != sp && (*cp == '-' || signchar)) { + 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); + chksize(fw + prec + 9); /* 9==slop */ + + cp = cpbuf; + *cp++ = '%'; + if (lj) + *cp++ = '-'; + if (fill != sp) + *cp++ = '0'; +#ifndef GFMT_WORKAROUND + if (cur != &fw) { + (void) strcpy(cp, "*.*g"); + (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval); + } else { + (void) strcpy(cp, "*g"); + (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval); + } +#else /* GFMT_WORKAROUND */ + { + char *gptr, gbuf[120]; +#define DEFAULT_G_PRECISION 6 + if (fw + prec + 9 > sizeof gbuf) { /* 9==slop */ + emalloc(gptr, char *, fw+prec+9, "do_sprintf(gfmt)"); + } else + gptr = gbuf; + (void) gfmt((double) tmpval, cur != &fw ? + (int) prec : DEFAULT_G_PRECISION, gptr); + *cp++ = '*', *cp++ = 's', *cp = '\0'; + (void) sprintf(obuf + olen, cpbuf, (int) fw, gptr); + if (fill != sp && *gptr == ' ') { + char *p = gptr; + do { *p++ = '0'; } while (*p == ' '); + } + if (gptr != gbuf) free(gptr); + } +#endif /* GFMT_WORKAROUND */ + len = strlen(obuf + olen); + ofre -= len; + olen += len; + s0 = s1; + break; + + case 'f': + parse_next_arg(); + tmpval = force_number(arg); + free_temp(arg); + chksize(fw + prec + 9); /* 9==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); + } + len = strlen(obuf + olen); + ofre -= len; + olen += len; + s0 = s1; + break; + case 'e': + parse_next_arg(); + tmpval = force_number(arg); + free_temp(arg); + chksize(fw + prec + 9); /* 9==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); + } + len = strlen(obuf + olen); + ofre -= len; + olen += len; + s0 = s1; + break; + + default: + lose: + break; + } + if (toofew) + fatal("%s\n\t%s\n\t%*s%s", + "not enough arguments to satisfy format string", + sfmt->stptr, s1 - sfmt->stptr - 2, "", + "^ ran out for this one" + ); + } + if (do_lint && carg != NULL) + warning("too many arguments supplied for format string"); + bchunk(s0, s1 - s0); + free_temp(sfmt); + r = make_str_node(obuf, olen, ALREADY_MALLOCED); + r->flags |= TEMP; + return r; +} + +void +do_printf(tree) +register NODE *tree; +{ + struct redirect *rp = NULL; + register FILE *fp; + + if (tree->rnode) { + int errflg; /* not used, sigh */ + + rp = redirect(tree->rnode, &errflg); + if (rp) { + fp = rp->fp; + if (!fp) + return; + } else + return; + } else + fp = stdout; + tree = do_sprintf(tree->lnode); + efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp , 1); + free_temp(tree); +} + +NODE * +do_sqrt(tree) +NODE *tree; +{ + NODE *tmp; + double arg; + extern double sqrt P((double)); + + tmp = tree_eval(tree->lnode); + arg = (double) force_number(tmp); + free_temp(tmp); + if (arg < 0.0) + warning("sqrt called with negative argument %g", arg); + return tmp_number((AWKNUM) sqrt(arg)); +} + +NODE * +do_substr(tree) +NODE *tree; +{ + NODE *t1, *t2, *t3; + NODE *r; + register int indx; + size_t length; + + t1 = tree_eval(tree->lnode); + t2 = tree_eval(tree->rnode->lnode); + if (tree->rnode->rnode == NULL) /* third arg. missing */ + length = t1->stlen; + else { + t3 = tree_eval(tree->rnode->rnode->lnode); + length = (size_t) force_number(t3); + free_temp(t3); + } + indx = (int) force_number(t2) - 1; + free_temp(t2); + t1 = force_string(t1); + if (indx < 0) + indx = 0; + if (indx >= t1->stlen || length <= 0) { + free_temp(t1); + return Nnull_string; + } + if (indx + length > t1->stlen || LONG_MAX - indx < length) + length = t1->stlen - indx; + r = tmp_string(t1->stptr + indx, length); + free_temp(t1); + return r; +} + +NODE * +do_strftime(tree) +NODE *tree; +{ + NODE *t1, *t2; + struct tm *tm; + time_t fclock; + char buf[100]; + int ret; + + t1 = force_string(tree_eval(tree->lnode)); + + if (tree->rnode == NULL) /* second arg. missing, default */ + (void) time(&fclock); + else { + t2 = tree_eval(tree->rnode->lnode); + fclock = (time_t) force_number(t2); + free_temp(t2); + } + tm = localtime(&fclock); + + ret = strftime(buf, 100, t1->stptr, tm); + + return tmp_string(buf, ret); +} + +NODE * +do_systime(tree) +NODE *tree; +{ + time_t lclock; + + (void) time(&lclock); + return tmp_number((AWKNUM) lclock); +} + +NODE * +do_system(tree) +NODE *tree; +{ + NODE *tmp; + int ret = 0; + char *cmd; + + (void) flush_io (); /* so output is synchronous with gawk's */ + tmp = tree_eval(tree->lnode); + cmd = force_string(tmp)->stptr; + if (cmd && *cmd) { + ret = system(cmd); + ret = (ret >> 8) & 0xff; + } + free_temp(tmp); + return tmp_number((AWKNUM) ret); +} + +void +do_print(tree) +register NODE *tree; +{ + register NODE *t1; + struct redirect *rp = NULL; + register FILE *fp; + register char *s; + + if (tree->rnode) { + int errflg; /* not used, sigh */ + + rp = redirect(tree->rnode, &errflg); + if (rp) { + fp = rp->fp; + if (!fp) + return; + } else + return; + } else + fp = stdout; + tree = tree->lnode; + while (tree) { + t1 = tree_eval(tree->lnode); + if (t1->flags & NUMBER) { + if (OFMTidx == CONVFMTidx) + (void) force_string(t1); + else { + char buf[100]; + + sprintf(buf, OFMT, t1->numbr); + t1 = tmp_string(buf, strlen(buf)); + } + } + efwrite(t1->stptr, sizeof(char), t1->stlen, fp, "print", rp, 0); + free_temp(t1); + tree = tree->rnode; + if (tree) { + s = OFS; + if (OFSlen) + efwrite(s, sizeof(char), OFSlen, fp, "print", rp, 0); + } + } + s = ORS; + if (ORSlen) + efwrite(s, sizeof(char), ORSlen, fp, "print", rp, 1); +} + +NODE * +do_tolower(tree) +NODE *tree; +{ + NODE *t1, *t2; + register char *cp, *cp2; + + t1 = tree_eval(tree->lnode); + 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; + + t1 = tree_eval(tree->lnode); + 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; +} + +NODE * +do_atan2(tree) +NODE *tree; +{ + NODE *t1, *t2; + extern double atan2 P((double, double)); + double d1, d2; + + t1 = tree_eval(tree->lnode); + t2 = tree_eval(tree->rnode->lnode); + 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 P((double)); + double d; + + tmp = tree_eval(tree->lnode); + 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 P((double)); + double d; + + tmp = tree_eval(tree->lnode); + d = cos((double)force_number(tmp)); + free_temp(tmp); + return tmp_number((AWKNUM) d); +} + +static int firstrand = 1; +static char state[256]; + +/* 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() / LONG_MAX); +} + +NODE * +do_srand(tree) +NODE *tree; +{ + NODE *tmp; + static long save_seed = 0; + long ret = save_seed; /* SVR4 awk srand returns previous seed */ + + if (firstrand) + (void) initstate((unsigned) 1, state, sizeof state); + else + (void) setstate(state); + + if (!tree) + srandom((int) (save_seed = (long) time((time_t *) 0))); + else { + tmp = tree_eval(tree->lnode); + 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; + AWKNUM rlength; + Regexp *rp; + + t1 = force_string(tree_eval(tree->lnode)); + tree = tree->rnode->lnode; + rp = re_update(tree); + rstart = research(rp, t1->stptr, 0, t1->stlen, 1); + if (rstart >= 0) { /* match succeded */ + rstart++; /* 1-based indexing */ + rlength = REEND(rp, t1->stptr) - RESTART(rp, t1->stptr); + } else { /* match failed */ + rstart = 0; + rlength = -1.0; + } + free_temp(t1); + unref(RSTART_node->var_value); + RSTART_node->var_value = make_number((AWKNUM) rstart); + unref(RLENGTH_node->var_value); + RLENGTH_node->var_value = make_number(rlength); + return tmp_number((AWKNUM) rstart); +} + +static NODE * +sub_common(tree, global) +NODE *tree; +int global; +{ + register char *scan; + register char *bp, *cp; + char *buf; + int buflen; + register char *matchend; + register int len; + char *matchstart; + char *text; + int textlen; + char *repl; + char *replend; + int repllen; + int sofar; + int ampersands; + int matches = 0; + Regexp *rp; + NODE *s; /* subst. pattern */ + NODE *t; /* string to make sub. in; $0 if none given */ + NODE *tmp; + NODE **lhs = &tree; /* value not used -- just different from NULL */ + int priv = 0; + Func_ptr after_assign = NULL; + + tmp = tree->lnode; + rp = re_update(tmp); + + tree = tree->rnode; + s = tree->lnode; + + tree = tree->rnode; + tmp = tree->lnode; + t = force_string(tree_eval(tmp)); + + /* do the search early to avoid work on non-match */ + if (research(rp, t->stptr, 0, t->stlen, 1) == -1 || + (RESTART(rp, t->stptr) > t->stlen) && (matches = 1)) { + free_temp(t); + return tmp_number((AWKNUM) matches); + } + + if (tmp->type == Node_val) + lhs = NULL; + else + lhs = get_lhs(tmp, &after_assign); + t->flags |= STRING; + /* + * create a private copy of the string + */ + if (t->stref > 1 || (t->flags & PERM)) { + unsigned int saveflags; + + saveflags = t->flags; + t->flags &= ~MALLOC; + tmp = dupnode(t); + t->flags = saveflags; + t = tmp; + priv = 1; + } + text = t->stptr; + textlen = t->stlen; + buflen = textlen + 2; + + s = force_string(tree_eval(s)); + repl = s->stptr; + replend = repl + s->stlen; + repllen = replend - repl; + emalloc(buf, char *, buflen, "do_sub"); + ampersands = 0; + for (scan = repl; scan < replend; scan++) { + if (*scan == '&') { + repllen--; + ampersands++; + } else if (*scan == '\\' && (*(scan+1) == '&' || *(scan+1) == '\\')) { + repllen--; + scan++; + } + } + + bp = buf; + for (;;) { + matches++; + matchstart = t->stptr + RESTART(rp, t->stptr); + matchend = t->stptr + REEND(rp, t->stptr); + + /* + * create the result, copying in parts of the original + * string + */ + len = matchstart - text + repllen + + ampersands * (matchend - matchstart); + sofar = bp - buf; + while (buflen - sofar - len - 1 < 0) { + buflen *= 2; + erealloc(buf, char *, buflen, "do_sub"); + bp = buf + sofar; + } + for (scan = text; scan < matchstart; scan++) + *bp++ = *scan; + for (scan = repl; scan < replend; scan++) + if (*scan == '&') + for (cp = matchstart; cp < matchend; cp++) + *bp++ = *cp; + else if (*scan == '\\' && (*(scan+1) == '&' || *(scan+1) == '\\')) { + scan++; + *bp++ = *scan; + } else + *bp++ = *scan; + if (global && matchstart == matchend && matchend < text + textlen) { + *bp++ = *matchend; + matchend++; + } + textlen = text + textlen - matchend; + text = matchend; + if (!global || textlen <= 0 || + research(rp, t->stptr, text-t->stptr, textlen, 1) == -1) + break; + } + sofar = bp - buf; + if (buflen - sofar - textlen - 1) { + buflen = sofar + textlen + 2; + erealloc(buf, char *, buflen, "do_sub"); + bp = buf + sofar; + } + for (scan = matchend; scan < text + textlen; scan++) + *bp++ = *scan; + textlen = bp - buf; + free(t->stptr); + t->stptr = buf; + t->stlen = textlen; + + free_temp(s); + if (matches > 0 && lhs) { + if (priv) { + unref(*lhs); + *lhs = t; + } + if (after_assign) + (*after_assign)(); + t->flags &= ~(NUM|NUMBER); + } + 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); +} + +#ifdef GFMT_WORKAROUND + /* + * printf's %g format [can't rely on gcvt()] + * caveat: don't use as argument to *printf()! + */ +char * +gfmt(g, prec, buf) +double g; /* value to format */ +int prec; /* indicates desired significant digits, not decimal places */ +char *buf; /* return buffer; assumed big enough to hold result */ +{ + if (g == 0.0) { + (void) strcpy(buf, "0"); /* easy special case */ + } else { + register char *d, *e, *p; + + /* start with 'e' format (it'll provide nice exponent) */ + if (prec < 1) prec = 1; /* at least 1 significant digit */ + (void) sprintf(buf, "%.*e", prec - 1, g); + if ((e = strchr(buf, 'e')) != 0) { /* find exponent */ + int exp = atoi(e+1); /* fetch exponent */ + if (exp >= -4 && exp < prec) { /* per K&R2, B1.2 */ + /* switch to 'f' format and re-do */ + prec -= (exp + 1); /* decimal precision */ + (void) sprintf(buf, "%.*f", prec, g); + e = buf + strlen(buf); + } + if ((d = strchr(buf, '.')) != 0) { + /* remove trailing zeroes and decimal point */ + for (p = e; p > d && *--p == '0'; ) continue; + if (*p == '.') --p; + if (++p < e) /* copy exponent and NUL */ + while ((*p++ = *e++) != '\0') continue; + } + } + } + return buf; +} +#endif /* GFMT_WORKAROUND */ |