summaryrefslogtreecommitdiffstats
path: root/contrib/awk/builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/awk/builtin.c')
-rw-r--r--contrib/awk/builtin.c2499
1 files changed, 0 insertions, 2499 deletions
diff --git a/contrib/awk/builtin.c b/contrib/awk/builtin.c
deleted file mode 100644
index dcf3ac3..0000000
--- a/contrib/awk/builtin.c
+++ /dev/null
@@ -1,2499 +0,0 @@
-/*
- * builtin.c - Builtin functions and various utility procedures
- */
-
-/*
- * Copyright (C) 1986, 1988, 1989, 1991-2001 the Free Software Foundation, Inc.
- *
- * This file is part of GAWK, the GNU implementation of the
- * AWK Programming 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * $FreeBSD$
- */
-
-
-#include "awk.h"
-#if defined(HAVE_FCNTL_H)
-#include <fcntl.h>
-#endif
-#undef HUGE
-#undef CHARBITS
-#undef INTBITS
-#include <math.h>
-#ifndef __FreeBSD__
-#include "random.h"
-
-/* can declare these, since we always use the random shipped with gawk */
-extern char *initstate P((unsigned long seed, char *state, long n));
-extern char *setstate P((char *state));
-extern long random P((void));
-extern void srandom P((unsigned long seed));
-#endif
-
-extern NODE **fields_arr;
-extern int output_is_tty;
-
-static NODE *sub_common P((NODE *tree, int how_many, int backdigs));
-
-#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
-
-#define DEFAULT_G_PRECISION 6
-
-#ifdef GFMT_WORKAROUND
-/* semi-temporary hack, mostly to gracefully handle VMS */
-static void sgfmt P((char *buf, const char *format, int alt,
- int fwidth, int precision, double value));
-#endif /* GFMT_WORKAROUND */
-
-/*
- * Since we supply the version of random(), we know what
- * value to use here.
- */
-#define GAWK_RANDOM_MAX 0x7fffffffL
-
-static void efwrite P((const void *ptr, size_t size, size_t count, FILE *fp,
- const char *from, struct redirect *rp, int flush));
-
-/* efwrite --- like fwrite, but with error checking */
-
-static void
-efwrite(const void *ptr,
- size_t size,
- size_t count,
- FILE *fp,
- const 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 != NULL && (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"));
-}
-
-/* do_exp --- exponential function */
-
-NODE *
-do_exp(NODE *tree)
-{
- NODE *tmp;
- double d, res;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("exp: received non-numeric argument"));
- 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);
-}
-
-/* stdfile --- return fp for a standard file */
-
-/*
- * This function allows `fflush("/dev/stdout")' to work.
- * The other files will be available via getredirect().
- * /dev/stdin is not included, since fflush is only for output.
- */
-
-static FILE *
-stdfile(char *name, size_t len)
-{
- if (len == 11) {
- if (STREQN(name, "/dev/stderr", 11))
- return stderr;
- else if (STREQN(name, "/dev/stdout", 11))
- return stdout;
- }
-
- return NULL;
-}
-
-/* do_fflush --- flush output, either named file or pipe or everything */
-
-NODE *
-do_fflush(NODE *tree)
-{
- struct redirect *rp;
- NODE *tmp;
- FILE *fp;
- int status = 0;
- char *file;
-
- /* fflush() --- flush stdout */
- if (tree == NULL) {
- status = fflush(stdout);
- return tmp_number((AWKNUM) status);
- }
-
- tmp = tree_eval(tree->lnode);
- tmp = force_string(tmp);
- file = tmp->stptr;
-
- /* fflush("") --- flush all */
- if (tmp->stlen == 0) {
- status = flush_io();
- free_temp(tmp);
- return tmp_number((AWKNUM) status);
- }
-
- rp = getredirect(tmp->stptr, tmp->stlen);
- status = -1;
- if (rp != NULL) {
- if ((rp->flag & (RED_WRITE|RED_APPEND)) == 0) {
- if (rp->flag & RED_PIPE)
- warning(_("fflush: cannot flush: pipe `%s' opened for reading, not writing"),
- file);
- else
- warning(_("fflush: cannot flush: file `%s' opened for reading, not writing"),
- file);
- free_temp(tmp);
- return tmp_number((AWKNUM) status);
- }
- fp = rp->fp;
- if (fp != NULL)
- status = fflush(fp);
- } else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) {
- status = fflush(fp);
- } else {
- status = -1;
- warning(_("fflush: `%s' is not an open file, pipe or co-process"), file);
- }
- free_temp(tmp);
- return tmp_number((AWKNUM) status);
-}
-
-/* do_index --- find index of a string */
-
-NODE *
-do_index(NODE *tree)
-{
- NODE *s1, *s2;
- register char *p1, *p2;
- register size_t l1, l2;
- long ret;
-
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- if (do_lint) {
- if ((s1->flags & (STRING|STR)) == 0)
- lintwarn(_("index: received non-string first argument"));
- if ((s2->flags & (STRING|STR)) == 0)
- lintwarn(_("index: received non-string second argument"));
- }
- force_string(s1);
- force_string(s2);
- p1 = s1->stptr;
- p2 = s2->stptr;
- l1 = s1->stlen;
- l2 = s2->stlen;
- ret = 0;
-
- /* IGNORECASE will already be false if posix */
- if (IGNORECASE) {
- while (l1 > 0) {
- if (l2 > l1)
- break;
- if (casetable[(unsigned char)*p1] == casetable[(unsigned char)*p2]
- && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) {
- ret = 1 + s1->stlen - l1;
- break;
- }
- l1--;
- p1++;
- }
- } else {
- while (l1 > 0) {
- 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);
-}
-
-/* double_to_int --- convert double to int, used several places */
-
-double
-double_to_int(double d)
-{
- if (d >= 0)
- d = Floor(d);
- else
- d = Ceil(d);
- return d;
-}
-
-/* do_int --- convert double to int for awk */
-
-NODE *
-do_int(NODE *tree)
-{
- NODE *tmp;
- double d;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("int: received non-numeric argument"));
- d = force_number(tmp);
- d = double_to_int(d);
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
-}
-
-/* do_length --- length of a string or $0 */
-
-NODE *
-do_length(NODE *tree)
-{
- NODE *tmp;
- size_t len;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (STRING|STR)) == 0)
- lintwarn(_("length: received non-string argument"));
- len = force_string(tmp)->stlen;
- free_temp(tmp);
- return tmp_number((AWKNUM) len);
-}
-
-/* do_log --- the log function */
-
-NODE *
-do_log(NODE *tree)
-{
- NODE *tmp;
- double d, arg;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("log: received non-numeric argument"));
- arg = (double) force_number(tmp);
- if (arg < 0.0)
- warning(_("log: received negative argument %g"), arg);
- d = log(arg);
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
-}
-
-/*
- * format_tree() formats nodes of a tree, starting with a left node,
- * and accordingly to a fmt_string providing a format like in
- * printf family from C library. Returns a string node which value
- * is a formatted string. Called by sprintf function.
- *
- * It is one of the uglier parts of gawk. Thanks to Michal Jaegermann
- * for taming this beast and making it compatible with ANSI C.
- */
-
-NODE *
-format_tree(
- const char *fmt_string,
- int n0,
- register NODE *carg,
- int num_args)
-{
-/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
-/* difference of pointers should be of ptrdiff_t type, but let us be kind */
-#define bchunk(s, l) if (l) { \
- while ((l) > ofre) { \
- long olen = obufout - obuf; \
- erealloc(obuf, char *, osiz * 2, "format_tree"); \
- ofre += osiz; \
- osiz *= 2; \
- obufout = obuf + olen; \
- } \
- memcpy(obufout, s, (size_t) (l)); \
- obufout += (l); \
- ofre -= (l); \
-}
-
-/* copy one byte from 's' to 'obufout' checking for space in the process */
-#define bchunk_one(s) { \
- if (ofre <= 0) { \
- long olen = obufout - obuf; \
- erealloc(obuf, char *, osiz * 2, "format_tree"); \
- ofre += osiz; \
- osiz *= 2; \
- obufout = obuf + olen; \
- } \
- *obufout++ = *s; \
- --ofre; \
-}
-
-/* Is there space for something L big in the buffer? */
-#define chksize(l) if ((l) > ofre) { \
- long olen = obufout - obuf; \
- erealloc(obuf, char *, osiz * 2, "format_tree"); \
- obufout = obuf + olen; \
- ofre += osiz; \
- osiz *= 2; \
-}
-
- static NODE **the_args = 0;
- static size_t args_size = 0;
- size_t cur_arg = 0;
-
- auto NODE **save_args = 0;
- auto size_t save_args_size = 0;
- static int call_level = 0;
-
- NODE *r;
- int i;
- int toofew = FALSE;
- char *obuf, *obufout;
- size_t osiz, ofre;
- char *chbuf;
- const char *s0, *s1;
- int cs1;
- NODE *arg;
- long fw, prec, argnum;
- int used_dollar;
- int lj, alt, big, bigbig, small, have_prec, need_format;
- long *cur = NULL;
-#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 = 0;
- 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 signchar = FALSE;
- size_t len;
- int zero_flag = FALSE;
- static char sp[] = " ";
- static char zero_string[] = "0";
- static char lchbuf[] = "0123456789abcdef";
- static char Uchbuf[] = "0123456789ABCDEF";
-
-#define INITIAL_OUT_SIZE 512
- emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
- obufout = obuf;
- osiz = INITIAL_OUT_SIZE;
- ofre = osiz - 1;
-
- /*
- * Icky problem. If the args make a nested call to printf/sprintf,
- * we end up clobbering the static variable `the_args'. Not good.
- * We don't just malloc and free the_args each time, since most of the
- * time there aren't nested calls. But if this is a nested call,
- * save the memory pointed to by the_args and allocate a fresh
- * array. Then free it on end.
- */
- if (++call_level > 1) { /* nested */
- save_args = the_args;
- save_args_size = args_size;
-
- args_size = 0; /* force fresh allocation */
- }
-
- if (args_size == 0) {
- /* allocate array */
- emalloc(the_args, NODE **, (num_args+1) * sizeof(NODE *), "format_tree");
- args_size = num_args + 1;
- } else if (num_args + 1 > args_size) {
- /* grow it */
- erealloc(the_args, NODE **, (num_args+1) * sizeof(NODE *), "format_tree");
- args_size = num_args + 1;
- }
-
-
- /* fill it in */
- /*
- * We ignore the_args[0] since format strings use
- * 1-based numbers to indicate the arguments. It's
- * easiest to just convert to int and index, without
- * having to remember to subtract 1.
- */
- memset(the_args, '\0', num_args * sizeof(NODE *));
- for (i = 1; carg != NULL; i++, carg = carg->rnode) {
- NODE *tmp;
-
- /* Here lies the wumpus's other brother. R.I.P. */
- tmp = tree_eval(carg->lnode);
- the_args[i] = dupnode(tmp);
- free_temp(tmp);
- }
- assert(i == num_args);
- cur_arg = 1;
-
- /*
- * Check first for use of `count$'.
- * If plain argument retrieval was used earlier, choke.
- * Otherwise, return the requested argument.
- * If not `count$' now, but it was used earlier, choke.
- * If this format is more than total number of args, choke.
- * Otherwise, return the current argument.
- */
-#define parse_next_arg() { \
- if (argnum > 0) { \
- if (cur_arg > 1) \
- fatal(_("must use `count$' on all formats or none")); \
- arg = the_args[argnum]; \
- } else if (used_dollar) { \
- fatal(_("must use `count$' on all formats or none")); \
- arg = 0; /* shutup the compiler */ \
- } else if (cur_arg >= num_args) { \
- arg = 0; /* shutup the compiler */ \
- toofew = TRUE; \
- break; \
- } else { \
- arg = the_args[cur_arg]; \
- cur_arg++; \
- } \
-}
-
- need_format = FALSE;
- used_dollar = FALSE;
-
- s0 = s1 = fmt_string;
- while (n0-- > 0) {
- if (*s1 != '%') {
- s1++;
- continue;
- }
- need_format = TRUE;
- bchunk(s0, s1 - s0);
- s0 = s1;
- cur = &fw;
- fw = 0;
- prec = 0;
- argnum = 0;
- have_prec = FALSE;
- signchar = FALSE;
- zero_flag = FALSE;
- lj = alt = big = bigbig = small = FALSE;
- fill = sp;
- cp = cend;
- chbuf = lchbuf;
- s1++;
-
-retry:
- if (n0-- <= 0) /* ran out early! */
- break;
-
- switch (cs1 = *s1++) {
- case (-1): /* dummy case to allow for checking */
-check_pos:
- if (cur != &fw)
- break; /* reject as a valid format */
- goto retry;
- case '%':
- need_format = FALSE;
- bchunk_one("%");
- s0 = s1;
- break;
-
- case '0':
- /*
- * Only turn on zero_flag if we haven't seen
- * the field width or precision yet. Otherwise,
- * screws up floating point formatting.
- */
- if (cur == & fw)
- zero_flag = TRUE;
- if (lj)
- goto retry;
- /* FALL through */
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (cur == NULL)
- break;
- if (prec >= 0)
- *cur = cs1 - '0';
- /*
- * with a negative precision *cur is already set
- * to -1, so it will remain negative, but we have
- * to "eat" precision digits in any case
- */
- while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
- --n0;
- *cur = *cur * 10 + *s1++ - '0';
- }
- if (prec < 0) /* negative precision is discarded */
- have_prec = FALSE;
- if (cur == &prec)
- cur = NULL;
- if (n0 == 0) /* badly formatted control string */
- continue;
- goto retry;
- case '$':
- if (do_traditional)
- fatal(_("`$' is not permitted in awk formats"));
- if (cur == &fw) {
- argnum = fw;
- fw = 0;
- used_dollar = TRUE;
- if (argnum <= 0)
- fatal(_("arg count with `$' must be > 0"));
- if (argnum >= num_args)
- fatal(_("arg count %d greater than total number of supplied arguments"), argnum);
- } else
- fatal(_("`$' not permitted after period in format"));
- goto retry;
- case '*':
- if (cur == NULL)
- break;
- if (! do_traditional && ISDIGIT(*s1)) {
- int val = 0;
-
- for (; n0 > 0 && *s1 && ISDIGIT(*s1); s1++, n0--) {
- val *= 10;
- val += *s1 - '0';
- }
- if (*s1 != '$') {
- fatal(_("no `$' supplied for positional field width or precision"));
- } else {
- s1++;
- n0--;
- }
-
- arg = the_args[val];
- } else {
- parse_next_arg();
- }
- *cur = force_number(arg);
- if (*cur < 0 && cur == &fw) {
- *cur = -*cur;
- lj++;
- }
- if (cur == &prec) {
- if (*cur >= 0)
- have_prec = TRUE;
- else
- have_prec = FALSE;
- cur = NULL;
- }
- goto retry;
- case ' ': /* print ' ' or '-' */
- /* 'space' flag is ignored */
- /* if '+' already present */
- if (signchar != FALSE)
- goto check_pos;
- /* FALL THROUGH */
- case '+': /* print '+' or '-' */
- signchar = cs1;
- goto check_pos;
- case '-':
- if (prec < 0)
- break;
- if (cur == &prec) {
- prec = -1;
- goto retry;
- }
- fill = sp; /* if left justified then other */
- lj++; /* filling is ignored */
- goto check_pos;
- case '.':
- if (cur != &fw)
- break;
- cur = &prec;
- have_prec = TRUE;
- goto retry;
- case '#':
- alt = TRUE;
- goto check_pos;
- case 'l':
- if (big)
- break;
- else {
- static int warned = FALSE;
-
- if (do_lint && ! warned) {
- lintwarn(_("`l' is meaningless in awk formats; ignored"));
- warned = TRUE;
- }
- if (do_posix)
- fatal(_("`l' is not permitted in POSIX awk formats"));
- }
- big = TRUE;
- goto retry;
- case 'L':
- if (bigbig)
- break;
- else {
- static int warned = FALSE;
-
- if (do_lint && ! warned) {
- lintwarn(_("`L' is meaningless in awk formats; ignored"));
- warned = TRUE;
- }
- if (do_posix)
- fatal(_("`L' is not permitted in POSIX awk formats"));
- }
- bigbig = TRUE;
- goto retry;
- case 'h':
- if (small)
- break;
- else {
- static int warned = FALSE;
-
- if (do_lint && ! warned) {
- lintwarn(_("`h' is meaningless in awk formats; ignored"));
- warned = TRUE;
- }
- if (do_posix)
- fatal(_("`h' is not permitted in POSIX awk formats"));
- }
- small = TRUE;
- goto retry;
- case 'c':
- need_format = FALSE;
- if (zero_flag && ! lj)
- fill = zero_string;
- parse_next_arg();
- /* user input that looks numeric is numeric */
- if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM)
- (void) force_number(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;
- cp = cpbuf;
- goto pr_tail;
- }
- if (have_prec == FALSE)
- prec = 1;
- else if (prec > arg->stlen)
- prec = arg->stlen;
- cp = arg->stptr;
- goto pr_tail;
- case 's':
- need_format = FALSE;
- if (zero_flag && ! lj)
- fill = zero_string;
- parse_next_arg();
- arg = force_string(arg);
- if (! have_prec || prec > arg->stlen)
- prec = arg->stlen;
- cp = arg->stptr;
- goto pr_tail;
- case 'd':
- case 'i':
- need_format = FALSE;
- parse_next_arg();
- tmpval = force_number(arg);
-
- /*
- * ``The result of converting a zero value with a
- * precision of zero is no characters.''
- */
- if (have_prec && prec == 0 && tmpval == 0)
- goto pr_tail;
-
- if (tmpval < 0) {
- if (tmpval < LONG_MIN)
- goto out_of_range;
- sgn = TRUE;
- uval = - (unsigned long) (long) tmpval;
- } else {
- /* Use !, so that NaNs are out of range.
- The cast avoids a SunOS 4.1.x cc bug. */
- if (! (tmpval <= (unsigned long) ULONG_MAX))
- goto out_of_range;
- sgn = FALSE;
- uval = (unsigned long) tmpval;
- }
- do {
- *--cp = (char) ('0' + uval % 10);
- uval /= 10;
- } while (uval > 0);
-
- /* add more output digits to match the precision */
- if (have_prec) {
- while (cend - cp < prec)
- *--cp = '0';
- }
-
- if (sgn)
- *--cp = '-';
- else if (signchar)
- *--cp = signchar;
- /*
- * When to fill with zeroes is of course not simple.
- * First: No zero fill if left-justifying.
- * Next: There seem to be two cases:
- * A '0' without a precision, e.g. %06d
- * A precision with no field width, e.g. %.10d
- * Any other case, we don't want to fill with zeroes.
- */
- if (! lj
- && ((zero_flag && ! have_prec)
- || (fw == 0 && have_prec)))
- fill = zero_string;
- if (prec > fw)
- fw = prec;
- prec = cend - cp;
- if (fw > prec && ! lj && fill != sp
- && (*cp == '-' || signchar)) {
- bchunk_one(cp);
- cp++;
- prec--;
- fw--;
- }
- goto pr_tail;
- case 'X':
- chbuf = Uchbuf; /* FALL THROUGH */
- case 'x':
- base += 6; /* FALL THROUGH */
- case 'u':
- base += 2; /* FALL THROUGH */
- case 'o':
- base += 8;
- need_format = FALSE;
- parse_next_arg();
- tmpval = force_number(arg);
-
- /*
- * ``The result of converting a zero value with a
- * precision of zero is no characters.''
- *
- * If I remember the ANSI C standard, though,
- * it says that for octal conversions
- * the precision is artificially increased
- * to add an extra 0 if # is supplied.
- * Indeed, in C,
- * printf("%#.0o\n", 0);
- * prints a single 0.
- */
- if (! alt && have_prec && prec == 0 && tmpval == 0)
- goto pr_tail;
-
- if (tmpval < 0) {
- if (tmpval < LONG_MIN)
- goto out_of_range;
- uval = (unsigned long) (long) tmpval;
- } else {
- /* Use !, so that NaNs are out of range.
- The cast avoids a SunOS 4.1.x cc bug. */
- if (! (tmpval <= (unsigned long) ULONG_MAX))
- goto out_of_range;
- uval = (unsigned long) tmpval;
- }
- /*
- * When to fill with zeroes is of course not simple.
- * First: No zero fill if left-justifying.
- * Next: There seem to be two cases:
- * A '0' without a precision, e.g. %06d
- * A precision with no field width, e.g. %.10d
- * Any other case, we don't want to fill with zeroes.
- */
- if (! lj
- && ((zero_flag && ! have_prec)
- || (fw == 0 && have_prec)))
- fill = zero_string;
- do {
- *--cp = chbuf[uval % base];
- uval /= base;
- } while (uval > 0);
-
- /* add more output digits to match the precision */
- if (have_prec) {
- while (cend - cp < prec)
- *--cp = '0';
- }
-
- if (alt && tmpval != 0) {
- if (base == 16) {
- *--cp = cs1;
- *--cp = '0';
- if (fill != sp) {
- bchunk(cp, 2);
- cp += 2;
- fw -= 2;
- }
- } else if (base == 8)
- *--cp = '0';
- }
- base = 0;
- if (prec > fw)
- fw = prec;
- prec = cend - cp;
- pr_tail:
- if (! lj) {
- while (fw > prec) {
- bchunk_one(fill);
- fw--;
- }
- }
- bchunk(cp, (int) prec);
- while (fw > prec) {
- bchunk_one(fill);
- fw--;
- }
- s0 = s1;
- break;
-
- out_of_range:
- /* out of range - emergency use of %g format */
- cs1 = 'g';
- goto format_float;
-
- case 'g':
- case 'G':
- case 'e':
- case 'f':
- case 'E':
- need_format = FALSE;
- parse_next_arg();
- tmpval = force_number(arg);
- format_float:
- if (! have_prec)
- prec = DEFAULT_G_PRECISION;
- chksize(fw + prec + 9); /* 9 == slop */
-
- cp = cpbuf;
- *cp++ = '%';
- if (lj)
- *cp++ = '-';
- if (signchar)
- *cp++ = signchar;
- if (alt)
- *cp++ = '#';
- if (zero_flag)
- *cp++ = '0';
- strcpy(cp, "*.*");
- cp += 3;
- *cp++ = cs1;
- *cp = '\0';
-#ifndef GFMT_WORKAROUND
- (void) sprintf(obufout, cpbuf,
- (int) fw, (int) prec, (double) tmpval);
-#else /* GFMT_WORKAROUND */
- if (cs1 == 'g' || cs1 == 'G')
- sgfmt(obufout, cpbuf, (int) alt,
- (int) fw, (int) prec, (double) tmpval);
- else
- (void) sprintf(obufout, cpbuf,
- (int) fw, (int) prec, (double) tmpval);
-#endif /* GFMT_WORKAROUND */
- len = strlen(obufout);
- ofre -= len;
- obufout += len;
- s0 = s1;
- break;
- default:
- break;
- }
- if (toofew)
- fatal("%s\n\t`%s'\n\t%*s%s",
- _("not enough arguments to satisfy format string"),
- fmt_string, s1 - fmt_string - 2, "",
- _("^ ran out for this one"));
- }
- if (do_lint) {
- if (need_format)
- lintwarn(
- _("[s]printf: format specifier does not have control letter"));
- if (carg != NULL)
- lintwarn(
- _("too many arguments supplied for format string"));
- }
- bchunk(s0, s1 - s0);
- r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
- r->flags |= TEMP;
-
- for (i = 1; i < num_args; i++) {
- unref(the_args[i]);
- }
-
- if (call_level-- > 1) {
- free(the_args);
- the_args = save_args;
- args_size = save_args_size;
- }
-
- return r;
-}
-
-/* do_sprintf --- perform sprintf */
-
-NODE *
-do_sprintf(NODE *tree)
-{
- NODE *r;
- NODE *sfmt = force_string(tree_eval(tree->lnode));
-
- r = format_tree(sfmt->stptr, sfmt->stlen, tree->rnode, tree->printf_count);
- free_temp(sfmt);
- return r;
-}
-
-/* do_printf --- perform printf, including redirection */
-
-void
-do_printf(NODE *tree)
-{
- struct redirect *rp = NULL;
- register FILE *fp;
-
- if (tree->lnode == NULL) {
- if (do_traditional) {
- if (do_lint)
- lintwarn(_("printf: no arguments"));
- return; /* bwk accepts it silently */
- }
- fatal(_("printf: no arguments"));
- }
-
- if (tree->rnode != NULL) {
- int errflg; /* not used, sigh */
-
- rp = redirect(tree->rnode, &errflg);
- if (rp != NULL) {
- fp = rp->fp;
- if (fp == NULL)
- return;
- } else
- return;
- } else
- fp = stdout;
- tree->lnode->printf_count = tree->printf_count;
- tree = do_sprintf(tree->lnode);
- efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp, TRUE);
- if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
- fflush(rp->fp);
- free_temp(tree);
-}
-
-/* do_sqrt --- do the sqrt function */
-
-NODE *
-do_sqrt(NODE *tree)
-{
- NODE *tmp;
- double arg;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("sqrt: received non-numeric argument"));
- 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));
-}
-
-/* do_substr --- do the substr function */
-
-NODE *
-do_substr(NODE *tree)
-{
- NODE *t1, *t2, *t3;
- NODE *r;
- register size_t indx;
- size_t length;
- double d_index, d_length;
-
- t1 = force_string(tree_eval(tree->lnode));
- t2 = tree_eval(tree->rnode->lnode);
- d_index = force_number(t2);
- free_temp(t2);
-
- if (d_index < 1.0) {
- if (do_lint)
- lintwarn(_("substr: start index %g is invalid, using 1"),
- d_index);
- d_index = 1;
- }
- if (do_lint && double_to_int(d_index) != d_index)
- lintwarn(_("substr: non-integer start index %g will be truncated"),
- d_index);
-
- indx = d_index - 1; /* awk indices are from 1, C's are from 0 */
-
- if (tree->rnode->rnode == NULL) { /* third arg. missing */
- /* use remainder of string */
- length = t1->stlen - indx;
- } else {
- t3 = tree_eval(tree->rnode->rnode->lnode);
- d_length = force_number(t3);
- free_temp(t3);
- if (d_length <= 0.0) {
- if (do_lint)
- lintwarn(_("substr: length %g is <= 0"), d_length);
- free_temp(t1);
- return Nnull_string;
- }
- if (do_lint && double_to_int(d_length) != d_length)
- lintwarn(
- _("substr: non-integer length %g will be truncated"),
- d_length);
- length = d_length;
- }
-
- if (t1->stlen == 0) {
- if (do_lint)
- lintwarn(_("substr: source string is zero length"));
- free_temp(t1);
- return Nnull_string;
- }
- if ((indx + length) > t1->stlen) {
- if (do_lint)
- lintwarn(
- _("substr: length %d at start index %d exceeds length of first argument (%d)"),
- length, indx+1, t1->stlen);
- length = t1->stlen - indx;
- }
- if (indx >= t1->stlen) {
- if (do_lint)
- lintwarn(_("substr: start index %d is past end of string"),
- indx+1);
- free_temp(t1);
- return Nnull_string;
- }
- r = tmp_string(t1->stptr + indx, length);
- free_temp(t1);
- return r;
-}
-
-/* do_strftime --- format a time stamp */
-
-NODE *
-do_strftime(NODE *tree)
-{
- NODE *t1, *t2, *ret;
- struct tm *tm;
- time_t fclock;
- char *bufp;
- size_t buflen, bufsize;
- char buf[BUFSIZ];
- /* FIXME: One day make %d be %e, after C 99 is common. */
- static char def_format[] = "%a %b %d %H:%M:%S %Z %Y";
- char *format;
- int formatlen;
-
- /* set defaults first */
- format = def_format; /* traditional date format */
- formatlen = strlen(format);
- (void) time(&fclock); /* current time of day */
-
- t1 = t2 = NULL;
- if (tree != NULL) { /* have args */
- if (tree->lnode != NULL) {
- NODE *tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (STRING|STR)) == 0)
- lintwarn(_("strftime: recieved non-string first argument"));
- t1 = force_string(tmp);
- format = t1->stptr;
- formatlen = t1->stlen;
- if (formatlen == 0) {
- if (do_lint)
- lintwarn(_("strftime: received empty format string"));
- free_temp(t1);
- return tmp_string("", 0);
- }
- }
-
- if (tree->rnode != NULL) {
- t2 = tree_eval(tree->rnode->lnode);
- if (do_lint && (t2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("strftime: recieved non-numeric second argument"));
- fclock = (time_t) force_number(t2);
- free_temp(t2);
- }
- }
-
- tm = localtime(&fclock);
-
- bufp = buf;
- bufsize = sizeof(buf);
- for (;;) {
- *bufp = '\0';
- buflen = strftime(bufp, bufsize, format, tm);
- /*
- * buflen can be zero EITHER because there's not enough
- * room in the string, or because the control command
- * goes to the empty string. Make a reasonable guess that
- * if the buffer is 1024 times bigger than the length of the
- * format string, it's not failing for lack of room.
- * Thanks to Paul Eggert for pointing out this issue.
- */
- if (buflen > 0 || bufsize >= 1024 * formatlen)
- break;
- bufsize *= 2;
- if (bufp == buf)
- emalloc(bufp, char *, bufsize, "do_strftime");
- else
- erealloc(bufp, char *, bufsize, "do_strftime");
- }
- ret = tmp_string(bufp, buflen);
- if (bufp != buf)
- free(bufp);
- if (t1)
- free_temp(t1);
- return ret;
-}
-
-/* do_systime --- get the time of day */
-
-NODE *
-do_systime(NODE *tree)
-{
- time_t lclock;
-
- (void) time(&lclock);
- return tmp_number((AWKNUM) lclock);
-}
-
-/* do_mktime --- turn a time string into a timestamp */
-
-NODE *
-do_mktime(NODE *tree)
-{
- NODE *t1;
- struct tm then;
- long year;
- int month, day, hour, minute, second, count;
- int dst = -1; /* default is unknown */
- time_t then_stamp;
- char save;
-
- t1 = tree_eval(tree->lnode);
- if (do_lint && (t1->flags & (STRING|STR)) == 0)
- lintwarn(_("mktime: received non-string argument"));
- t1 = force_string(t1);
-
- save = t1->stptr[t1->stlen];
- t1->stptr[t1->stlen] = '\0';
-
- count = sscanf(t1->stptr, "%ld %d %d %d %d %d %d",
- & year, & month, & day,
- & hour, & minute, & second,
- & dst);
-
- t1->stptr[t1->stlen] = save;
- free_temp(t1);
-
- if (count < 6
- || month < month - 1
- || year < year - 1900 || year - 1900 != (int) (year - 1900))
- return tmp_number((AWKNUM) -1);
-
- memset(& then, '\0', sizeof(then));
- then.tm_sec = second;
- then.tm_min = minute;
- then.tm_hour = hour;
- then.tm_mday = day;
- then.tm_mon = month - 1;
- then.tm_year = year - 1900;
- then.tm_isdst = dst;
-
- then_stamp = mktime(& then);
- return tmp_number((AWKNUM) then_stamp);
-}
-
-/* do_system --- run an external command */
-
-NODE *
-do_system(NODE *tree)
-{
- NODE *tmp;
- int ret = 0;
- char *cmd;
- char save;
-
- (void) flush_io(); /* so output is synchronous with gawk's */
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (STRING|STR)) == 0)
- lintwarn(_("system: recieved non-string argument"));
- cmd = force_string(tmp)->stptr;
-
- if (cmd && *cmd) {
- /* insure arg to system is zero-terminated */
-
- /*
- * From: David Trueman <david@cs.dal.ca>
- * To: arnold@cc.gatech.edu (Arnold Robbins)
- * Date: Wed, 3 Nov 1993 12:49:41 -0400
- *
- * It may not be necessary to save the character, but
- * I'm not sure. It would normally be the field
- * separator. If the parse has not yet gone beyond
- * that, it could mess up (although I doubt it). If
- * FIELDWIDTHS is being used, it might be the first
- * character of the next field. Unless someone wants
- * to check it out exhaustively, I suggest saving it
- * for now...
- */
- save = cmd[tmp->stlen];
- cmd[tmp->stlen] = '\0';
-
- os_restore_mode(fileno(stdin));
- ret = system(cmd);
- ret = (ret >> 8) & 0xff;
- if ((BINMODE & 1) != 0)
- os_setbinmode(fileno(stdin), O_BINARY);
-
- cmd[tmp->stlen] = save;
- }
- free_temp(tmp);
- return tmp_number((AWKNUM) ret);
-}
-
-extern NODE **fmt_list; /* declared in eval.c */
-
-/* do_print --- print items, separated by OFS, terminated with ORS */
-
-void
-do_print(register NODE *tree)
-{
- register NODE **t;
- struct redirect *rp = NULL;
- register FILE *fp;
- int numnodes, i;
- NODE *save;
- NODE *tval;
-
- if (tree->rnode) {
- int errflg; /* not used, sigh */
-
- rp = redirect(tree->rnode, &errflg);
- if (rp != NULL) {
- fp = rp->fp;
- if (fp == NULL)
- return;
- } else
- return;
- } else
- fp = stdout;
-
- /*
- * General idea is to evaluate all the expressions first and
- * then print them, otherwise you get suprising behavior.
- * See test/prtoeval.awk for an example program.
- */
- save = tree = tree->lnode;
- for (numnodes = 0; tree != NULL; tree = tree->rnode)
- numnodes++;
- emalloc(t, NODE **, numnodes * sizeof(NODE *), "do_print");
-
- tree = save;
- for (i = 0; tree != NULL; i++, tree = tree->rnode) {
- NODE *n;
-
- /* Here lies the wumpus. R.I.P. */
- n = tree_eval(tree->lnode);
- t[i] = dupnode(n);
- free_temp(n);
-
- if ((t[i]->flags & (NUMBER|STRING)) == NUMBER) {
- if (OFMTidx == CONVFMTidx)
- (void) force_string(t[i]);
- else {
- tval = tmp_number(t[i]->numbr);
- unref(t[i]);
- t[i] = format_val(OFMT, OFMTidx, tval);
- }
- }
- }
-
- for (i = 0; i < numnodes; i++) {
- efwrite(t[i]->stptr, sizeof(char), t[i]->stlen, fp, "print", rp, FALSE);
- unref(t[i]);
-
- if (i != numnodes - 1 && OFSlen > 0)
- efwrite(OFS, sizeof(char), (size_t) OFSlen,
- fp, "print", rp, FALSE);
-
- }
- if (ORSlen > 0)
- efwrite(ORS, sizeof(char), (size_t) ORSlen, fp, "print", rp, TRUE);
-
- if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
- fflush(rp->fp);
-
- free(t);
-}
-
-/* do_tolower --- lower case a string */
-
-NODE *
-do_tolower(NODE *tree)
-{
- NODE *t1, *t2;
- register unsigned char *cp, *cp2;
-
- t1 = tree_eval(tree->lnode);
- if (do_lint && (t1->flags & (STRING|STR)) == 0)
- lintwarn(_("tolower: recieved non-string argument"));
- t1 = force_string(t1);
- t2 = tmp_string(t1->stptr, t1->stlen);
- for (cp = (unsigned char *)t2->stptr,
- cp2 = (unsigned char *)(t2->stptr + t2->stlen); cp < cp2; cp++)
- if (ISUPPER(*cp))
- *cp = TOLOWER(*cp);
- free_temp(t1);
- return t2;
-}
-
-/* do_toupper --- upper case a string */
-
-NODE *
-do_toupper(NODE *tree)
-{
- NODE *t1, *t2;
- register unsigned char *cp, *cp2;
-
- t1 = tree_eval(tree->lnode);
- if (do_lint && (t1->flags & (STRING|STR)) == 0)
- lintwarn(_("toupper: recieved non-string argument"));
- t1 = force_string(t1);
- t2 = tmp_string(t1->stptr, t1->stlen);
- for (cp = (unsigned char *)t2->stptr,
- cp2 = (unsigned char *)(t2->stptr + t2->stlen); cp < cp2; cp++)
- if (ISLOWER(*cp))
- *cp = TOUPPER(*cp);
- free_temp(t1);
- return t2;
-}
-
-/* do_atan2 --- do the atan2 function */
-
-NODE *
-do_atan2(NODE *tree)
-{
- NODE *t1, *t2;
- double d1, d2;
-
- t1 = tree_eval(tree->lnode);
- t2 = tree_eval(tree->rnode->lnode);
- if (do_lint) {
- if ((t1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("atan2: received non-numeric first argument"));
- if ((t2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("atan2: received non-numeric second argument"));
- }
- d1 = force_number(t1);
- d2 = force_number(t2);
- free_temp(t1);
- free_temp(t2);
- return tmp_number((AWKNUM) atan2(d1, d2));
-}
-
-/* do_sin --- do the sin function */
-
-NODE *
-do_sin(NODE *tree)
-{
- NODE *tmp;
- double d;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("sin: received non-numeric argument"));
- d = sin((double) force_number(tmp));
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
-}
-
-/* do_cos --- do the cos function */
-
-NODE *
-do_cos(NODE *tree)
-{
- NODE *tmp;
- double d;
-
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("cos: received non-numeric argument"));
- d = cos((double) force_number(tmp));
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
-}
-
-/* do_rand --- do the rand function */
-
-static int firstrand = TRUE;
-static char state[512];
-
-/* ARGSUSED */
-NODE *
-do_rand(NODE *tree)
-{
- if (firstrand) {
- (void) initstate((unsigned) 1, state, sizeof state);
- srandom(1);
- firstrand = FALSE;
- }
- return tmp_number((AWKNUM) random() / GAWK_RANDOM_MAX);
-}
-
-/* do_srand --- seed the random number generator */
-
-NODE *
-do_srand(NODE *tree)
-{
- NODE *tmp;
- static long save_seed = 1;
- long ret = save_seed; /* SVR4 awk srand returns previous seed */
-
- if (firstrand) {
- (void) initstate((unsigned) 1, state, sizeof state);
- /* don't need to srandom(1), we're changing the seed below */
- firstrand = FALSE;
- } else
- (void) setstate(state);
-
- if (tree == NULL)
-#ifdef __FreeBSD__
- srandom((unsigned int) (save_seed = (long) time((time_t *) 0)
- ^ (getpid() << 16)));
-#else
- srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
-#endif
- else {
- tmp = tree_eval(tree->lnode);
- if (do_lint && (tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("srand: received non-numeric argument"));
- srandom((unsigned int) (save_seed = (long) force_number(tmp)));
- free_temp(tmp);
- }
- return tmp_number((AWKNUM) ret);
-}
-
-/* do_match --- match a regexp, set RSTART and RLENGTH,
- * optional third arg is array filled with text of
- * subpatterns enclosed in parens.
- */
-
-NODE *
-do_match(NODE *tree)
-{
- NODE *t1, *dest, *it;
- int rstart, len, ii;
- AWKNUM rlength;
- Regexp *rp;
- regoff_t s;
- char *start;
-
- t1 = force_string(tree_eval(tree->lnode));
- tree = tree->rnode;
- rp = re_update(tree->lnode);
-
- dest = NULL;
- if (tree->rnode != NULL) { /* 3rd optional arg for the subpatterns */
- dest = tree->rnode->lnode;
- if (dest->type == Node_param_list)
- dest = stack_ptr[dest->param_cnt];
- if (dest->type == Node_array_ref)
- dest = dest->orig_array;
- if (dest->type != Node_var && dest->type != Node_var_array)
- fatal(_("match: third argument is not an array"));
- dest->type = Node_var_array;
- assoc_clear(dest);
- }
-
- rstart = research(rp, t1->stptr, 0, t1->stlen, TRUE);
- if (rstart >= 0) { /* match succeded */
- rstart++; /* 1-based indexing */
- rlength = REEND(rp, t1->stptr) - RESTART(rp, t1->stptr);
-
- /* Build the array only if the caller wants the optional subpatterns */
- if (dest != NULL) {
- for (ii = 0; (s = SUBPATSTART(rp, t1->stptr, ii)) != -1; ii++) {
- start = t1->stptr + s;
- len = SUBPATEND(rp, t1->stptr, ii) - s;
-
- it = make_string(start, len);
- it->flags |= MAYBE_NUM;
- *assoc_lookup(dest, tmp_number((AWKNUM) (ii)), FALSE) = it;
- }
- }
- } 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);
-}
-
-/* sub_common --- the common code (does the work) for sub, gsub, and gensub */
-
-/*
- * Gsub can be tricksy; particularly when handling the case of null strings.
- * The following awk code was useful in debugging problems. It is too bad
- * that it does not readily translate directly into the C code, below.
- *
- * #! /usr/local/bin/mawk -f
- *
- * BEGIN {
- * TRUE = 1; FALSE = 0
- * print "--->", mygsub("abc", "b+", "FOO")
- * print "--->", mygsub("abc", "x*", "X")
- * print "--->", mygsub("abc", "b*", "X")
- * print "--->", mygsub("abc", "c", "X")
- * print "--->", mygsub("abc", "c+", "X")
- * print "--->", mygsub("abc", "x*$", "X")
- * }
- *
- * function mygsub(str, regex, replace, origstr, newstr, eosflag, nonzeroflag)
- * {
- * origstr = str;
- * eosflag = nonzeroflag = FALSE
- * while (match(str, regex)) {
- * if (RLENGTH > 0) { # easy case
- * nonzeroflag = TRUE
- * if (RSTART == 1) { # match at front of string
- * newstr = newstr replace
- * } else {
- * newstr = newstr substr(str, 1, RSTART-1) replace
- * }
- * str = substr(str, RSTART+RLENGTH)
- * } else if (nonzeroflag) {
- * # last match was non-zero in length, and at the
- * # current character, we get a zero length match,
- * # which we don't really want, so skip over it
- * newstr = newstr substr(str, 1, 1)
- * str = substr(str, 2)
- * nonzeroflag = FALSE
- * } else {
- * # 0-length match
- * if (RSTART == 1) {
- * newstr = newstr replace substr(str, 1, 1)
- * str = substr(str, 2)
- * } else {
- * return newstr str replace
- * }
- * }
- * if (length(str) == 0)
- * if (eosflag)
- * break;
- * else
- * eosflag = TRUE
- * }
- * if (length(str) > 0)
- * newstr = newstr str # rest of string
- *
- * return newstr
- * }
- */
-
-/*
- * NB: `howmany' conflicts with a SunOS 4.x macro in <sys/param.h>.
- */
-
-static NODE *
-sub_common(NODE *tree, int how_many, int backdigs)
-{
- register char *scan;
- register char *bp, *cp;
- char *buf;
- size_t buflen;
- register char *matchend;
- register size_t len;
- char *matchstart;
- char *text;
- size_t textlen;
- char *repl;
- char *replend;
- size_t 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 = FALSE;
- Func_ptr after_assign = NULL;
-
- int global = (how_many == -1);
- long current;
- int lastmatchnonzero;
-
- 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, TRUE) == -1 ||
- RESTART(rp, t->stptr) > t->stlen) {
- free_temp(t);
- return tmp_number((AWKNUM) 0.0);
- }
-
- if (tmp->type == Node_val)
- lhs = NULL;
- else
- lhs = get_lhs(tmp, &after_assign, FALSE);
- t->flags |= STRING;
- /*
- * create a private copy of the string
- */
- if (t->stref > 1 || (t->flags & (PERM|FIELD)) != 0) {
- tmp = copynode(t);
- t = tmp;
- priv = TRUE;
- }
- 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 + 2, "sub_common");
- buf[buflen] = '\0';
- buf[buflen + 1] = '\0';
- ampersands = 0;
- for (scan = repl; scan < replend; scan++) {
- if (*scan == '&') {
- repllen--;
- ampersands++;
- } else if (*scan == '\\') {
- if (backdigs) { /* gensub, behave sanely */
- if (ISDIGIT(scan[1])) {
- ampersands++;
- scan++;
- } else { /* \q for any q --> q */
- repllen--;
- scan++;
- }
- } else { /* (proposed) posix '96 mode */
- if (strncmp(scan, "\\\\\\&", 4) == 0) {
- /* \\\& --> \& */
- repllen -= 2;
- scan += 3;
- } else if (strncmp(scan, "\\\\&", 3) == 0) {
- /* \\& --> \<string> */
- ampersands++;
- repllen--;
- scan += 2;
- } else if (scan[1] == '&') {
- /* \& --> & */
- repllen--;
- scan++;
- } /* else
- leave alone, it goes into the output */
- }
- }
- }
-
- lastmatchnonzero = FALSE;
- bp = buf;
- for (current = 1;; current++) {
- 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)) {
- buflen *= 2;
- erealloc(buf, char *, buflen, "sub_common");
- bp = buf + sofar;
- }
- for (scan = text; scan < matchstart; scan++)
- *bp++ = *scan;
- if (global || current == how_many) {
- /*
- * If the current match matched the null string,
- * and the last match didn't and did a replacement,
- * then skip this one.
- */
- if (lastmatchnonzero && matchstart == matchend) {
- lastmatchnonzero = FALSE;
- matches--;
- goto empty;
- }
- /*
- * If replacing all occurrences, or this is the
- * match we want, copy in the replacement text,
- * making substitutions as we go.
- */
- for (scan = repl; scan < replend; scan++)
- if (*scan == '&')
- for (cp = matchstart; cp < matchend; cp++)
- *bp++ = *cp;
- else if (*scan == '\\') {
- if (backdigs) { /* gensub, behave sanely */
- if (ISDIGIT(scan[1])) {
- int dig = scan[1] - '0';
- char *start, *end;
-
- start = t->stptr
- + SUBPATSTART(rp, t->stptr, dig);
- end = t->stptr
- + SUBPATEND(rp, t->stptr, dig);
-
- for (cp = start; cp < end; cp++)
- *bp++ = *cp;
- scan++;
- } else /* \q for any q --> q */
- *bp++ = *++scan;
- } else { /* posix '96 mode, bleah */
- if (strncmp(scan, "\\\\\\&", 4) == 0) {
- /* \\\& --> \& */
- *bp++ = '\\';
- *bp++ = '&';
- scan += 3;
- } else if (strncmp(scan, "\\\\&", 3) == 0) {
- /* \\& --> \<string> */
- *bp++ = '\\';
- for (cp = matchstart; cp < matchend; cp++)
- *bp++ = *cp;
- scan += 2;
- } else if (scan[1] == '&') {
- /* \& --> & */
- *bp++ = '&';
- scan++;
- } else
- *bp++ = *scan;
- }
- } else
- *bp++ = *scan;
- if (matchstart != matchend)
- lastmatchnonzero = TRUE;
- } else {
- /*
- * don't want this match, skip over it by copying
- * in current text.
- */
- for (cp = matchstart; cp < matchend; cp++)
- *bp++ = *cp;
- }
- empty:
- /* catch the case of gsub(//, "blah", whatever), i.e. empty regexp */
- if (matchstart == matchend && matchend < text + textlen) {
- *bp++ = *matchend;
- matchend++;
- }
- textlen = text + textlen - matchend;
- text = matchend;
-
- if ((current >= how_many && !global)
- || ((long) textlen <= 0 && matchstart == matchend)
- || research(rp, t->stptr, text - t->stptr, textlen, TRUE) == -1)
- break;
-
- }
- sofar = bp - buf;
- if (buflen - sofar - textlen - 1) {
- buflen = sofar + textlen + 2;
- erealloc(buf, char *, buflen, "sub_common");
- bp = buf + sofar;
- }
- for (scan = matchend; scan < text + textlen; scan++)
- *bp++ = *scan;
- *bp = '\0';
- 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 != NULL)
- (*after_assign)();
- t->flags &= ~(NUM|NUMBER);
- }
- return tmp_number((AWKNUM) matches);
-}
-
-/* do_gsub --- global substitution */
-
-NODE *
-do_gsub(NODE *tree)
-{
- return sub_common(tree, -1, FALSE);
-}
-
-/* do_sub --- single substitution */
-
-NODE *
-do_sub(NODE *tree)
-{
- return sub_common(tree, 1, FALSE);
-}
-
-/* do_gensub --- fix up the tree for sub_common for the gensub function */
-
-NODE *
-do_gensub(NODE *tree)
-{
- NODE n1, n2, n3, *t, *tmp, *target, *ret;
- long how_many = 1; /* default is one substitution */
- double d;
-
- /*
- * We have to pull out the value of the global flag, and
- * build up a tree without the flag in it, turning it into the
- * kind of tree that sub_common() expects. It helps to draw
- * a picture of this ...
- */
- n1 = *tree;
- n2 = *(tree->rnode);
- n1.rnode = & n2;
-
- t = tree_eval(n2.rnode->lnode); /* value of global flag */
-
- tmp = force_string(tree_eval(n2.rnode->rnode->lnode)); /* target */
-
- /*
- * We make copy of the original target string, and pass that
- * in to sub_common() as the target to make the substitution in.
- * We will then return the result string as the return value of
- * this function.
- */
- target = make_string(tmp->stptr, tmp->stlen);
- free_temp(tmp);
-
- n3 = *(n2.rnode->rnode);
- n3.lnode = target;
- n2.rnode = & n3;
-
- if ((t->flags & (STR|STRING)) != 0) {
- if (t->stlen > 0 && (t->stptr[0] == 'g' || t->stptr[0] == 'G'))
- how_many = -1;
- else
- how_many = 1;
- } else {
- d = force_number(t);
- if (d > 0)
- how_many = d;
- else
- how_many = 1;
- if (d == 0)
- warning(_("gensub: 3rd argument of 0 treated as 1"));
- }
-
- free_temp(t);
-
- ret = sub_common(&n1, how_many, TRUE);
- free_temp(ret);
-
- /*
- * Note that we don't care what sub_common() returns, since the
- * easiest thing for the programmer is to return the string, even
- * if no substitutions were done.
- */
- target->flags |= TEMP;
- return target;
-}
-
-#ifdef GFMT_WORKAROUND
-/*
- * printf's %g format [can't rely on gcvt()]
- * caveat: don't use as argument to *printf()!
- * 'format' string HAS to be of "<flags>*.*g" kind, or we bomb!
- */
-static void
-sgfmt(char *buf, /* return buffer; assumed big enough to hold result */
- const char *format,
- int alt, /* use alternate form flag */
- int fwidth, /* field width in a format */
- int prec, /* indicates desired significant digits, not decimal places */
- double g) /* value to format */
-{
- char dform[40];
- register char *gpos;
- register char *d, *e, *p;
- int again = FALSE;
-
- strncpy(dform, format, sizeof dform - 1);
- dform[sizeof dform - 1] = '\0';
- gpos = strrchr(dform, '.');
-
- if (g == 0.0 && ! alt) { /* easy special case */
- *gpos++ = 'd';
- *gpos = '\0';
- (void) sprintf(buf, dform, fwidth, 0);
- return;
- }
-
- /* advance to location of 'g' in the format */
- while (*gpos && *gpos != 'g' && *gpos != 'G')
- gpos++;
-
- if (prec <= 0) /* negative precision is ignored */
- prec = (prec < 0 ? DEFAULT_G_PRECISION : 1);
-
- if (*gpos == 'G')
- again = TRUE;
- /* start with 'e' format (it'll provide nice exponent) */
- *gpos = 'e';
- prec--;
- (void) sprintf(buf, dform, fwidth, prec, g);
- if ((e = strrchr(buf, 'e')) != NULL) { /* find exponent */
- int expn = atoi(e+1); /* fetch exponent */
- if (expn >= -4 && expn <= prec) { /* per K&R2, B1.2 */
- /* switch to 'f' format and re-do */
- *gpos = 'f';
- prec -= expn; /* decimal precision */
- (void) sprintf(buf, dform, fwidth, prec, g);
- e = buf + strlen(buf);
- while (*--e == ' ')
- continue;
- e++;
- }
- else if (again)
- *gpos = 'E';
-
- /* if 'alt' in force, then trailing zeros are not removed */
- if (! alt && (d = strrchr(buf, '.')) != NULL) {
- /* throw away an excess of precision */
- for (p = e; p > d && *--p == '0'; )
- prec--;
- if (d == p)
- prec--;
- if (prec < 0)
- prec = 0;
- /* and do that once again */
- again = TRUE;
- }
- if (again)
- (void) sprintf(buf, dform, fwidth, prec, g);
- }
-}
-#endif /* GFMT_WORKAROUND */
-
-#define BITS_PER_BYTE 8 /* if not true, you lose. too bad. */
-
-/* do_lshift --- perform a << operation */
-
-NODE *
-do_lshift(NODE *tree)
-{
- NODE *s1, *s2;
- unsigned long uval, ushift, res;
- AWKNUM val, shift;
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- val = force_number(s1);
- shift = force_number(s2);
-
- if (do_lint) {
- if ((s1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("lshift: received non-numeric first argument"));
- if ((s2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("lshift: received non-numeric first argument"));
- if (val < 0 || shift < 0)
- lintwarn(_("lshift(%lf, %lf): negative values will give strange results"), val, shift);
- if (double_to_int(val) != val || double_to_int(shift) != shift)
- lintwarn(_("lshift(%lf, %lf): fractional values will be truncated"), val, shift);
- if (shift > (sizeof(unsigned long) * BITS_PER_BYTE))
- lintwarn(_("lshift(%lf, %lf): too large shift value will give strange results"), val, shift);
- }
-
- free_temp(s1);
- free_temp(s2);
-
- uval = (unsigned long) val;
- ushift = (unsigned long) shift;
-
- res = uval << ushift;
- return tmp_number((AWKNUM) res);
-}
-
-/* do_rshift --- perform a >> operation */
-
-NODE *
-do_rshift(NODE *tree)
-{
- NODE *s1, *s2;
- unsigned long uval, ushift, res;
- AWKNUM val, shift;
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- val = force_number(s1);
- shift = force_number(s2);
-
- if (do_lint) {
- if ((s1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("rshift: received non-numeric first argument"));
- if ((s2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("rshift: received non-numeric first argument"));
- if (val < 0 || shift < 0)
- lintwarn(_("rshift(%lf, %lf): negative values will give strange results"), val, shift);
- if (double_to_int(val) != val || double_to_int(shift) != shift)
- lintwarn(_("rshift(%lf, %lf): fractional values will be truncated"), val, shift);
- if (shift > (sizeof(unsigned long) * BITS_PER_BYTE))
- lintwarn(_("rshift(%lf, %lf): too large shift value will give strange results"), val, shift);
- }
-
- free_temp(s1);
- free_temp(s2);
-
- uval = (unsigned long) val;
- ushift = (unsigned long) shift;
-
- res = uval >> ushift;
- return tmp_number((AWKNUM) res);
-}
-
-/* do_and --- perform an & operation */
-
-NODE *
-do_and(NODE *tree)
-{
- NODE *s1, *s2;
- unsigned long uleft, uright, res;
- AWKNUM left, right;
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- left = force_number(s1);
- right = force_number(s2);
-
- if (do_lint) {
- if ((s1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("and: received non-numeric first argument"));
- if ((s2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("and: received non-numeric first argument"));
- if (left < 0 || right < 0)
- lintwarn(_("and(%lf, %lf): negative values will give strange results"), left, right);
- if (double_to_int(left) != left || double_to_int(right) != right)
- lintwarn(_("and(%lf, %lf): fractional values will be truncated"), left, right);
- }
-
- free_temp(s1);
- free_temp(s2);
-
- uleft = (unsigned long) left;
- uright = (unsigned long) right;
-
- res = uleft & uright;
- return tmp_number((AWKNUM) res);
-}
-
-/* do_or --- perform an | operation */
-
-NODE *
-do_or(NODE *tree)
-{
- NODE *s1, *s2;
- unsigned long uleft, uright, res;
- AWKNUM left, right;
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- left = force_number(s1);
- right = force_number(s2);
-
- if (do_lint) {
- if ((s1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("or: received non-numeric first argument"));
- if ((s2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("or: received non-numeric first argument"));
- if (left < 0 || right < 0)
- lintwarn(_("or(%lf, %lf): negative values will give strange results"), left, right);
- if (double_to_int(left) != left || double_to_int(right) != right)
- lintwarn(_("or(%lf, %lf): fractional values will be truncated"), left, right);
- }
-
- free_temp(s1);
- free_temp(s2);
-
- uleft = (unsigned long) left;
- uright = (unsigned long) right;
-
- res = uleft | uright;
- return tmp_number((AWKNUM) res);
-}
-
-/* do_xor --- perform an ^ operation */
-
-NODE *
-do_xor(NODE *tree)
-{
- NODE *s1, *s2;
- unsigned long uleft, uright, res;
- AWKNUM left, right;
-
- s1 = tree_eval(tree->lnode);
- s2 = tree_eval(tree->rnode->lnode);
- left = force_number(s1);
- right = force_number(s2);
-
- if (do_lint) {
- if ((s1->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("xor: received non-numeric first argument"));
- if ((s2->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("xor: received non-numeric first argument"));
- if (left < 0 || right < 0)
- lintwarn(_("xor(%lf, %lf): negative values will give strange results"), left, right);
- if (double_to_int(left) != left || double_to_int(right) != right)
- lintwarn(_("xor(%lf, %lf): fractional values will be truncated"), left, right);
- }
-
- free_temp(s1);
- free_temp(s2);
-
- uleft = (unsigned long) left;
- uright = (unsigned long) right;
-
- res = uleft ^ uright;
- return tmp_number((AWKNUM) res);
-}
-
-/* do_compl --- perform a ~ operation */
-
-NODE *
-do_compl(NODE *tree)
-{
- NODE *tmp;
- double d;
- unsigned long uval;
-
- tmp = tree_eval(tree->lnode);
- d = force_number(tmp);
- free_temp(tmp);
-
- if (do_lint) {
- if ((tmp->flags & (NUM|NUMBER)) == 0)
- lintwarn(_("compl: received non-numeric argument"));
- if (d < 0)
- lintwarn(_("compl(%lf): negative value will give strange results"), d);
- if (double_to_int(d) != d)
- lintwarn(_("compl(%lf): fractional value will be truncated"), d);
- }
-
- uval = (unsigned long) d;
- uval = ~ uval;
- return tmp_number((AWKNUM) uval);
-}
-
-/* do_strtonum --- the strtonum function */
-
-NODE *
-do_strtonum(NODE *tree)
-{
- NODE *tmp;
- double d;
-
- tmp = tree_eval(tree->lnode);
-
- if ((tmp->flags & (NUM|NUMBER)) != 0)
- d = (double) force_number(tmp);
- else if (isnondecimal(tmp->stptr))
- d = nondec2awknum(tmp->stptr, tmp->stlen);
- else
- d = (double) force_number(tmp);
-
- free_temp(tmp);
- return tmp_number((AWKNUM) d);
-}
-
-/* nondec2awknum --- convert octal or hex value to double */
-
-/*
- * Because of awk's concatenation rules and the way awk.y:yylex()
- * collects a number, this routine has to be willing to stop on the
- * first invalid character.
- */
-
-AWKNUM
-nondec2awknum(char *str, size_t len)
-{
- AWKNUM retval = 0.0;
- char save;
- short val;
- char *start = str;
-
- if (*str == '0' && (str[1] == 'x' || str[1] == 'X')) {
- assert(len > 2);
-
- for (str += 2, len -= 2; len > 0; len--, str++) {
- switch (*str) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- val = *str - '0';
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- val = *str - 'a' + 10;
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- val = *str - 'A' + 10;
- break;
- default:
- goto done;
- }
- retval = (retval * 16) + val;
- }
- } else if (*str == '0') {
- if (strchr(str, '8') != NULL || strchr(str, '9') != NULL)
- goto decimal;
- for (; len > 0; len--) {
- if (! ISDIGIT(*str))
- goto done;
- else if (*str == '8' || *str == '9') {
- str = start;
- goto decimal;
- }
- retval = (retval * 8) + (*str - '0');
- str++;
- }
- } else {
-decimal:
- save = str[len];
- retval = strtod(str, NULL);
- str[len] = save;
- }
-done:
- return retval;
-}
-
-/* do_dcgettext --- handle i18n translations */
-
-/*
- * awk usage is
- *
- * str = dcgettext(string [, domain [, category]])
- *
- * Default domain is TEXTDOMAIN, default category is LC_MESSAGES.
- */
-
-NODE *
-do_dcgettext(NODE *tree)
-{
- NODE *tmp, *t1, *t2;
- char *string;
- char *the_result;
-#if ENABLE_NLS && HAVE_LC_MESSAGES && HAVE_DCGETTEXT
- int lc_cat = -1;
- char *category, *domain;
- static struct category_table {
- int val;
- char *name;
- } cat_tab[] = {
-#ifdef LC_ALL
- { LC_ALL, "LC_ALL" },
-#endif /* LC_ALL */
-#ifdef LC_COLLATE
- { LC_COLLATE, "LC_COLLATE" },
-#endif /* LC_COLLATE */
-#ifdef LC_CTYPE
- { LC_CTYPE, "LC_CTYPE" },
-#endif /* LC_CTYPE */
-#ifdef LC_MESSAGES
- { LC_MESSAGES, "LC_MESSAGES" },
-#endif /* LC_MESSAGES */
-#ifdef LC_MONETARY
- { LC_MONETARY, "LC_MONETARY" },
-#endif /* LC_MONETARY */
-#ifdef LC_NUMERIC
- { LC_NUMERIC, "LC_NUMERIC" },
-#endif /* LC_NUMERIC */
-#ifdef LC_RESPONSE
- { LC_RESPONSE, "LC_RESPONSE" },
-#endif /* LC_RESPONSE */
-#ifdef LC_TIME
- { LC_TIME, "LC_TIME" },
-#endif /* LC_TIME */
- };
-#endif /* ENABLE_NLS */
-
- tmp = tree->lnode; /* first argument */
- t1 = force_string(tree_eval(tmp));
- string = t1->stptr;
-
- t2 = NULL;
-#if ENABLE_NLS && HAVE_LC_MESSAGES && HAVE_DCGETTEXT
- tree = tree->rnode; /* second argument */
- if (tree != NULL) {
- tmp = tree->lnode;
- t2 = force_string(tree_eval(tmp));
- domain = t2->stptr;
- } else
- domain = TEXTDOMAIN;
-
- if (tree != NULL && tree->rnode != NULL) { /* third argument */
- int low, high, i, mid;
- NODE *t;
-
- tree = tree->rnode;
- tmp = tree->lnode;
- t = force_string(tree_eval(tmp));
- category = t->stptr;
-
- /* binary search the table */
- low = 0;
- high = (sizeof(cat_tab) / sizeof(cat_tab[0])) - 1;
- while (low <= high) {
- mid = (low + high) / 2;
- i = strcmp(category, cat_tab[mid].name);
-
- if (i < 0) /* category < mid */
- high = mid - 1;
- else if (i > 0) /* category > mid */
- low = mid + 1;
- else {
- lc_cat = cat_tab[mid].val;
- break;
- }
- }
- if (lc_cat == -1) /* not there */
- fatal(_("dcgettext: `%s' is not a valid locale category"), category);
-
- free_temp(t);
- } else
- lc_cat = LC_MESSAGES;
-
- the_result = dcgettext(domain, string, lc_cat);
-#else
- the_result = string;
-#endif
- free_temp(t1);
- if (t2 != NULL)
- free_temp(t2);
-
- return tmp_string(the_result, strlen(the_result));
-}
-
-/* do_bindtextdomain --- set the directory for a text domain */
-
-/*
- * awk usage is
- *
- * binding = bindtextdomain(dir [, domain])
- *
- * If dir is "", pass NULL to C version.
- * Default domain is TEXTDOMAIN.
- */
-
-NODE *
-do_bindtextdomain(NODE *tree)
-{
- NODE *tmp, *t1, *t2;
- char *directory, *domain;
- char *the_result;
-
- t1 = t2 = NULL;
- /* set defaults */
- directory = NULL;
- domain = TEXTDOMAIN;
-
- tmp = tree->lnode; /* first argument */
- t1 = force_string(tree_eval(tmp));
- if (t1->stlen > 0)
- directory = t1->stptr;
-
- tree = tree->rnode; /* second argument */
- if (tree != NULL) {
- tmp = tree->lnode;
- t2 = force_string(tree_eval(tmp));
- domain = t2->stptr;
- }
-
- the_result = bindtextdomain(domain, directory);
-
- free_temp(t1);
- if (t2 != NULL)
- free_temp(t1);
-
- return tmp_string(the_result, strlen(the_result));
-}
OpenPOWER on IntegriCloud