diff options
Diffstat (limited to 'builtin.c')
-rw-r--r-- | builtin.c | 2367 |
1 files changed, 661 insertions, 1706 deletions
@@ -1,5 +1,5 @@ /* - * builtin.c - Builtin functions and various utility procedures + * builtin.c - Builtin functions and various utility procedures. */ /* @@ -30,7 +30,6 @@ #endif #include <math.h> #include "random.h" -#include "floatmagic.h" #ifndef CHAR_BIT # define CHAR_BIT 8 @@ -55,45 +54,44 @@ #define SIZE_MAX ((size_t) -1) #endif -/* 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)); +/* Can declare these, since we always use the random shipped with gawk */ +extern char *initstate(unsigned long seed, char *state, long n); +extern char *setstate(char *state); +extern long random(void); +extern void srandom(unsigned long seed); +extern NODE **args_array; +extern int max_args; extern NODE **fields_arr; extern int output_is_tty; +extern FILE *output_fp; -static NODE *sub_common P((NODE *tree, long how_many, int backdigs)); +static NODE *sub_common(int nargs, long how_many, int backdigs); #ifdef _CRAY /* Force the standard C compiler to use the library math functions. */ extern double exp(double); -double (*Exp)() = exp; +double (*Exp)(double) = exp; #define exp(x) (*Exp)(x) extern double log(double); -double (*Log)() = log; +double (*Log)(double) = log; #define log(x) (*Log)(x) #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)); -static size_t mbc_byte_count P((const char *ptr, size_t numchars)); -static size_t mbc_char_count P((const char *ptr, size_t numbytes)); +static void efwrite(const void *ptr, size_t size, size_t count, FILE *fp, + const char *from, struct redirect *rp, int flush); + +#define r_format_arg NODE * format_tree +#define fmt_msg msg +#include "awkprintf.h" +#undef fmt_msg +#undef r_format_arg /* efwrite --- like fwrite, but with error checking */ @@ -110,8 +108,8 @@ efwrite(const void *ptr, if (fwrite(ptr, size, count, fp) != count) goto wrerror; if (flush - && ((fp == stdout && output_is_tty) - || (rp != NULL && (rp->flag & RED_NOBUF)))) { + && (output_is_tty + || (rp != NULL && (rp->flag & RED_NOBUF)))) { fflush(fp); if (ferror(fp)) goto wrerror; @@ -127,21 +125,21 @@ wrerror: /* do_exp --- exponential function */ NODE * -do_exp(NODE *tree) +do_exp(int nargs) { NODE *tmp; double d, res; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("exp: received non-numeric argument")); d = force_number(tmp); - free_temp(tmp); + DEREF(tmp); errno = 0; res = exp(d); if (errno == ERANGE) warning(_("exp: argument %g is out of range"), d); - return tmp_number((AWKNUM) res); + return make_number((AWKNUM) res); } /* stdfile --- return fp for a standard file */ @@ -168,7 +166,7 @@ stdfile(const char *name, size_t len) /* do_fflush --- flush output, either named file or pipe or everything */ NODE * -do_fflush(NODE *tree) +do_fflush(int nargs) { struct redirect *rp; NODE *tmp; @@ -177,20 +175,21 @@ do_fflush(NODE *tree) const char *file; /* fflush() --- flush stdout */ - if (tree == NULL) { + if (nargs == 0) { + if (output_fp != stdout) + (void) fflush(output_fp); status = fflush(stdout); - return tmp_number((AWKNUM) status); + return make_number((AWKNUM) status); } - tmp = tree_eval(tree->lnode); - tmp = force_string(tmp); + tmp = POP_STRING(); file = tmp->stptr; /* fflush("") --- flush all */ if (tmp->stlen == 0) { status = flush_io(); - free_temp(tmp); - return tmp_number((AWKNUM) status); + DEREF(tmp); + return make_number((AWKNUM) status); } rp = getredirect(tmp->stptr, tmp->stlen); @@ -203,8 +202,8 @@ do_fflush(NODE *tree) else warning(_("fflush: cannot flush: file `%s' opened for reading, not writing"), file); - free_temp(tmp); - return tmp_number((AWKNUM) status); + DEREF(tmp); + return make_number((AWKNUM) status); } fp = rp->fp; if (fp != NULL) @@ -215,37 +214,41 @@ do_fflush(NODE *tree) status = -1; warning(_("fflush: `%s' is not an open file, pipe or co-process"), file); } - free_temp(tmp); - return tmp_number((AWKNUM) status); + DEREF(tmp); + return make_number((AWKNUM) status); } #ifdef MBS_SUPPORT -/* strncasecmpmbs --- like strncasecmp(multibyte string version) */ +/* strncasecmpmbs --- like strncasecmp (multibyte string version) */ + int -strncasecmpmbs(const char *s1, mbstate_t mbs1, const char *s2, - mbstate_t mbs2, size_t n) +strncasecmpmbs(const char *s1, const char *s2, size_t n) { - int i1, i2, mbclen1, mbclen2, gap; + size_t i1, i2, mbclen1, mbclen2, gap; wchar_t wc1, wc2; + mbstate_t mbs1, mbs2; + + memset(& mbs1, 0, sizeof(mbs1)); + memset(& mbs2, 0, sizeof(mbs2)); for (i1 = i2 = 0 ; i1 < n && i2 < n ;i1 += mbclen1, i2 += mbclen2) { - mbclen1 = mbrtowc(&wc1, s1 + i1, n - i1, &mbs1); + mbclen1 = mbrtowc(& wc1, s1 + i1, n - i1, & mbs1); if (mbclen1 == (size_t) -1 || mbclen1 == (size_t) -2 || mbclen1 == 0) { - /* We treat it as a singlebyte character. */ + /* We treat it as a singlebyte character. */ mbclen1 = 1; wc1 = s1[i1]; } - mbclen2 = mbrtowc(&wc2, s2 + i2, n - i2, &mbs2); + mbclen2 = mbrtowc(& wc2, s2 + i2, n - i2, & mbs2); if (mbclen2 == (size_t) -1 || mbclen2 == (size_t) -2 || mbclen2 == 0) { - /* We treat it as a singlebyte character. */ + /* We treat it as a singlebyte character. */ mbclen2 = 1; wc2 = s2[i2]; } if ((gap = towlower(wc1) - towlower(wc2)) != 0) - /* s1 and s2 are not equivalent. */ + /* s1 and s2 are not equivalent. */ return gap; } - /* s1 and s2 are equivalent. */ + /* s1 and s2 are equivalent. */ return 0; } @@ -260,12 +263,12 @@ index_multibyte_buffer(char* src, char* dest, int len) { int idx, prev_idx; mbstate_t mbs, prevs; - memset(&prevs, 0, sizeof(mbstate_t)); + memset(& prevs, 0, sizeof(mbstate_t)); for (idx = prev_idx = 0 ; idx < len ; idx++) { size_t mbclen; mbs = prevs; - mbclen = mbrlen(src + prev_idx, idx - prev_idx + 1, &mbs); + mbclen = mbrlen(src + prev_idx, idx - prev_idx + 1, & mbs); if (mbclen == (size_t) -1 || mbclen == 1 || mbclen == 0) { /* singlebyte character. */ mbclen = 1; @@ -295,16 +298,25 @@ index_multibyte_buffer(char* src ATTRIBUTE_UNUSED, char* dest ATTRIBUTE_UNUSED, /* do_index --- find index of a string */ NODE * -do_index(NODE *tree) +do_index(int nargs) { NODE *s1, *s2; - register const char *p1, *p2; - register size_t l1, l2; + const char *p1, *p2; + size_t l1, l2; long ret; int do_single_byte = FALSE; +#ifdef MBS_SUPPORT + mbstate_t mbs1, mbs2; + + if (gawk_mb_cur_max > 1) { + memset(& mbs1, 0, sizeof(mbstate_t)); + memset(& mbs2, 0, sizeof(mbstate_t)); + } +#endif - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { if ((s1->flags & (STRING|STRCUR)) == 0) lintwarn(_("index: received non-string first argument")); @@ -360,8 +372,8 @@ do_index(NODE *tree) } else { #endif /* - * Could use tolower(*p1) == tolower(*p2) here. See discussion - * in eval.c as to why not. + * Could use tolower(*p1) == tolower(*p2) here. + * See discussion in eval.c as to why not. */ if (casetable[(unsigned char)*p1] == casetable[(unsigned char)*p2] && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) { @@ -404,9 +416,9 @@ do_index(NODE *tree) } } out: - free_temp(s1); - free_temp(s2); - return tmp_number((AWKNUM) ret); + DEREF(s1); + DEREF(s2); + return make_number((AWKNUM) ret); } /* double_to_int --- convert double to int, used several places */ @@ -424,1054 +436,238 @@ double_to_int(double d) /* do_int --- convert double to int for awk */ NODE * -do_int(NODE *tree) +do_int(int nargs) { NODE *tmp; double d; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|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); + DEREF(tmp); + return make_number((AWKNUM) d); } -/* do_length --- length of a string or $0 */ + +/* do_length --- length of a string, array or $0 */ NODE * -do_length(NODE *tree) +do_length(int nargs) { NODE *tmp; size_t len; - NODE *n; - n = tree->lnode; - if (n->type == Node_param_list) - n = stack_ptr[n->param_cnt]; + tmp = POP(); - if (n->type == Node_var_array - || n->type == Node_array_ref) { - NODE *array_var = n; + if (tmp->type == Node_var_array) { static short warned = FALSE; - if (array_var->type == Node_array_ref) - array_var = array_var->orig_array; - - if (do_lint && ! warned) { + if (do_lint && ! warned) { warned = TRUE; lintwarn(_("`length(array)' is a gawk extension")); } - if (do_posix) - goto normal; /* will die as fatal error */ - - if (array_var->type == Node_var_new) { - if (do_lint) - lintwarn(_("length: untyped parameter argument will be forced to scalar")); + return make_number((AWKNUM) tmp->table_size); + } - return tmp_number(0.0); - } + assert(tmp->type == Node_val); - return tmp_number((AWKNUM) array_var->table_size); - } else { -normal: - if (do_lint && n->type == Node_var_new) - lintwarn(_("length: untyped argument will be forced to scalar")); + if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) + lintwarn(_("length: received non-string argument")); + (void) force_string(tmp); - tmp = tree_eval(n); - if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) - lintwarn(_("length: received non-string argument")); - tmp = force_string(tmp); #ifdef MBS_SUPPORT - if (gawk_mb_cur_max > 1) { - tmp = force_wstring(tmp); - len = tmp->wstlen; - /* - * If the bytes don't make a valid wide character - * string, fall back to the bytes themselves. - */ - if (len == 0 && tmp->stlen > 0) - len = tmp->stlen; - } else + if (gawk_mb_cur_max > 1) { + tmp = force_wstring(tmp); + len = tmp->wstlen; + /* + * If the bytes don't make a valid wide character + * string, fall back to the bytes themselves. + */ + if (len == 0 && tmp->stlen > 0) + len = tmp->stlen; + } else #endif - len = tmp->stlen; + len = tmp->stlen; - free_temp(tmp); - return tmp_number((AWKNUM) len); - } + DEREF(tmp); + return make_number((AWKNUM) len); } /* do_log --- the log function */ NODE * -do_log(NODE *tree) +do_log(int nargs) { NODE *tmp; double d, arg; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|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); + DEREF(tmp); + return make_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. - */ +/* printf_common --- common code for sprintf and printf */ -NODE * -format_tree( - const char *fmt_string, - size_t n0, - register NODE *carg, - long num_args) +static NODE * +printf_common(int nargs) { -/* 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) { \ - size_t 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 < 1) { \ - size_t 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) { \ - size_t olen = obufout - obuf; \ - size_t delta = osiz+l-ofre; \ - erealloc(obuf, char *, osiz + delta, "format_tree"); \ - obufout = obuf + olen; \ - ofre += delta; \ - osiz += delta; \ -} - - 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; - const 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 - uintmax_t uval; - int sgn; - int base = 0; - /* - * Although this is an array, the elements serve two different - * purposes. The first element is the general buffer meant - * to hold the entire result string. The second one is a - * temporary buffer for large floating point values. They - * could just as easily be separate variables, and the - * code might arguably be clearer. - */ - struct { - char *buf; - size_t bufsize; - char stackbuf[30]; - } cpbufs[2]; -#define cpbuf cpbufs[0].buf - char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)]; - char *cp; - const char *fill; - AWKNUM tmpval; - char signchar = FALSE; - size_t len; - int zero_flag = FALSE; - int quote_flag = FALSE; - int ii, jj; - char *chp; - size_t copy_count, char_count; - static const char sp[] = " "; - static const char zero_string[] = "0"; - static const char lchbuf[] = "0123456789abcdef"; - static const char Uchbuf[] = "0123456789ABCDEF"; - -#define INITIAL_OUT_SIZE 512 - emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree"); - obufout = obuf; - osiz = INITIAL_OUT_SIZE; - ofre = osiz - 2; - - { - size_t k; - for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) { - cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf); - cpbufs[k].buf = cpbufs[k].stackbuf; - } - } - - /* - * The point of this goop is to grow the buffer - * holding the converted number, so that large - * values don't overflow a fixed length buffer. - */ -#define PREPEND(CH) do { \ - if (cp == cpbufs[0].buf) { \ - char *prev = cpbufs[0].buf; \ - emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \ - "format_tree"); \ - memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev, \ - cpbufs[0].bufsize); \ - cpbufs[0].bufsize *= 2; \ - if (prev != cpbufs[0].stackbuf) \ - free(prev); \ - cend = cpbufs[0].buf+cpbufs[0].bufsize; \ - } \ - *--cp = (CH); \ -} while(0) - - /* - * 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; - quote_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; - /* - * 29 Oct. 2002: - * The C99 standard pages 274 and 279 seem to imply that - * since there's no arg converted, the field width doesn't - * apply. The code already was that way, but this - * comment documents it, at least in the code. - */ - if (do_lint) { - const char *msg = NULL; - - if (fw && ! have_prec) - msg = _("field width is ignored for `%%%%' specifier"); - else if (fw == 0 && have_prec) - msg = _("precision is ignored for `%%%%' specifier"); - else if (fw && have_prec) - msg = _("field width and precision are ignored for `%%%%' specifier"); - - if (msg != NULL) - lintwarn(msg); - } - 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 %ld 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--; - } - if (val >= num_args) { - toofew = TRUE; - break; - } - 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 = ≺ - have_prec = TRUE; - goto retry; - case '#': - alt = TRUE; - goto check_pos; - case '\'': -#if defined(HAVE_LOCALE_H) - /* allow quote_flag if there is a thousands separator. */ - if (loc.thousands_sep[0] != '\0') - quote_flag = TRUE; - goto check_pos; -#else - goto retry; -#endif - case 'l': - if (big) - break; - else { - static short 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 short 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 short 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; - 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 = (uintmax_t) arg->numbr; -#endif - if (do_lint && uval > 255) { - lintwarn("[s]printf: value %g is too big for %%c format", - arg->numbr); - } - cpbuf[0] = uval; - prec = 1; - cp = cpbuf; - goto pr_tail; - } - /* - * As per POSIX, only output first character of a - * string value. Thus, we ignore any provided - * precision, forcing it to 1. (Didn't this - * used to work? 6/2003.) - */ - prec = 1; - cp = arg->stptr; - goto pr_tail; - case 's': - need_format = FALSE; - parse_next_arg(); - arg = force_string(arg); - if (fw == 0 && ! have_prec) - prec = arg->stlen; - else { - char_count = mbc_char_count(arg->stptr, arg->stlen); - if (! have_prec || prec > char_count) - prec = char_count; - } - cp = arg->stptr; - goto pr_tail; - case 'd': - case 'i': - need_format = FALSE; - parse_next_arg(); - tmpval = force_number(arg); - /* - * Check for Nan or Inf. - */ - if (isnan(tmpval) || isinf(tmpval)) - goto out_of_range; - else - tmpval = double_to_int(tmpval); - - /* - * ``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) { - tmpval = -tmpval; - sgn = TRUE; - } else { - if (tmpval == -0.0) - /* avoid printing -0 */ - tmpval = 0.0; - sgn = FALSE; - } - /* - * Use snprintf return value to tell if there - * is enough room in the buffer or not. - */ - while ((i = snprintf(cpbufs[1].buf, - cpbufs[1].bufsize, "%.0f", - tmpval)) >= - cpbufs[1].bufsize) { - if (cpbufs[1].buf == cpbufs[1].stackbuf) - cpbufs[1].buf = NULL; - if (i > 0) { - cpbufs[1].bufsize += ((i > cpbufs[1].bufsize) ? - i : cpbufs[1].bufsize); - } - else - cpbufs[1].bufsize *= 2; - assert(cpbufs[1].bufsize > 0); - erealloc(cpbufs[1].buf, char *, - cpbufs[1].bufsize, "format_tree"); - } - if (i < 1) - goto out_of_range; - chp = &cpbufs[1].buf[i-1]; - ii = jj = 0; - do { - PREPEND(*chp); - chp--; i--; -#if defined(HAVE_LOCALE_H) - if (quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (i) /* only add if more digits coming */ - PREPEND(loc.thousands_sep[0]); /* XXX - assumption it's one char */ - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = FALSE; - else { - ii++; - jj = 0; - } - } -#endif - } while (i > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (sgn) - PREPEND('-'); - else if (signchar) - PREPEND(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) { - uval = (uintmax_t) (intmax_t) tmpval; - if ((AWKNUM)(intmax_t)uval != - double_to_int(tmpval)) - goto out_of_range; - } else { - uval = (uintmax_t) tmpval; - if ((AWKNUM)uval != double_to_int(tmpval)) - goto out_of_range; - } - /* - * 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; - - ii = jj = 0; - do { - PREPEND(chbuf[uval % base]); - uval /= base; -#if defined(HAVE_LOCALE_H) - if (base == 10 && quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) { - if (uval) /* only add if more digits coming */ - PREPEND(loc.thousands_sep[0]); /* XXX --- assumption it's one char */ - if (loc.grouping[ii+1] == 0) - jj = 0; /* keep using current val in loc.grouping[ii] */ - else if (loc.grouping[ii+1] == CHAR_MAX) - quote_flag = FALSE; - else { - ii++; - jj = 0; - } - } -#endif - } while (uval > 0); - - /* add more output digits to match the precision */ - if (have_prec) { - while (cend - cp < prec) - PREPEND('0'); - } - - if (alt && tmpval != 0) { - if (base == 16) { - PREPEND(cs1); - PREPEND('0'); - if (fill != sp) { - bchunk(cp, 2); - cp += 2; - fw -= 2; - } - } else if (base == 8) - PREPEND('0'); - } - base = 0; - if (prec > fw) - fw = prec; - prec = cend - cp; - pr_tail: - if (! lj) { - while (fw > prec) { - bchunk_one(fill); - fw--; - } - } - copy_count = prec; - if (fw == 0 && ! have_prec) - ; - else if (gawk_mb_cur_max > 1 && (cs1 == 's' || cs1 == 'c')) { - int nchars_needed = 0; - - assert(cp == arg->stptr || cp == cpbuf); - - if (cs1 == 'c') - nchars_needed = 1; - else if (have_prec) - nchars_needed = prec; - else - nchars_needed = arg->stlen; - - copy_count = mbc_byte_count(arg->stptr, nchars_needed); - } - bchunk(cp, copy_count); - while (fw > prec) { - bchunk_one(fill); - fw--; - } - s0 = s1; - break; - - out_of_range: - /* out of range - emergency use of %g format */ - if (do_lint) - lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"), - (double) tmpval, cs1); - cs1 = 'g'; - goto format_float; - - case 'F': -#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1 - cs1 = 'f'; - /* FALL THROUGH */ -#endif - 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 */ -#ifdef VAXCRTL - /* pre-ANSI library doesn't handle '0' flag - correctly in many cases; reject it */ - if (zero_flag - && (lj || (signchar && signchar != '+'))) - zero_flag = FALSE; -#endif - cp = cpbuf; - *cp++ = '%'; - if (lj) - *cp++ = '-'; - if (signchar) - *cp++ = signchar; - if (alt) - *cp++ = '#'; - if (zero_flag) - *cp++ = '0'; - if (quote_flag) - *cp++ = '\''; - strcpy(cp, "*.*"); - cp += 3; - *cp++ = cs1; - *cp = '\0'; -#ifndef GFMT_WORKAROUND -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, ""); -#endif - { - int n; - while ((n = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(n) - } -#if defined(LC_NUMERIC) - if (quote_flag && ! use_lc_numeric) - setlocale(LC_NUMERIC, "C"); -#endif -#else /* GFMT_WORKAROUND */ - if (cs1 == 'g' || cs1 == 'G') - sgfmt(obufout, cpbuf, (int) alt, - (int) fw, (int) prec, (double) tmpval); - else { - int n; - while ((n = snprintf(obufout, ofre, cpbuf, - (int) fw, (int) prec, - (double) tmpval)) >= ofre) - chksize(n) - } -#endif /* GFMT_WORKAROUND */ - len = strlen(obufout); - ofre -= len; - obufout += len; - s0 = s1; - break; - default: - if (do_lint && isalpha(cs1)) - lintwarn(_("ignoring unknown format specifier character `%c': no argument converted"), cs1); - break; - } - if (toofew) { - free(obuf); /* silence valgrind */ - fatal("%s\n\t`%s'\n\t%*s%s", - _("not enough arguments to satisfy format string"), - fmt_string, (int) (s1 - fmt_string - 1), "", - _("^ ran out for this one")); - } - } - if (do_lint) { - if (need_format) - lintwarn( - _("[s]printf: format specifier does not have control letter")); - if (cur_arg < num_args) - 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; - } - - { - size_t k; - size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]); - for (k = 0; k < count; k++) { - if (cpbufs[k].buf != cpbufs[k].stackbuf) - free(cpbufs[k].buf); + NODE *r, *tmp; + + assert(nargs <= max_args); + for (i = 1; i <= nargs; i++) { + tmp = args_array[nargs - i] = POP(); + if (tmp->type == Node_var_array) { + while (--i > 0) + DEREF(args_array[nargs - i]); + fatal(_("attempt to use array `%s' in scalar context"), array_vname(tmp)); } } + force_string(args_array[0]); + r = format_tree(args_array[0]->stptr, args_array[0]->stlen, args_array, nargs); + for (i = 0; i < nargs; i++) + DEREF(args_array[i]); return r; } /* do_sprintf --- perform sprintf */ NODE * -do_sprintf(NODE *tree) +do_sprintf(int nargs) { 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); + r = printf_common(nargs); + if (r == NULL) + gawk_exit(EXIT_FATAL); return r; } -/* - * redirect_to_fp --- return fp for redirection, NULL on failure - * or stdout if no redirection, used by all print routines - */ - -static inline FILE * -redirect_to_fp(NODE *tree, struct redirect **rpp) -{ - int errflg; /* not used, sigh */ - struct redirect *rp; - - if (tree == NULL) - return stdout; - - rp = redirect(tree, &errflg); - if (rp != NULL) { - *rpp = rp; - return rp->fp; - } - - return NULL; -} /* do_printf --- perform printf, including redirection */ void -do_printf(NODE *tree) +do_printf(int nargs, int redirtype) { + FILE *fp = NULL; + NODE *tmp; struct redirect *rp = NULL; - register FILE *fp; + int errflg; /* not used, sigh */ + NODE *redir_exp = NULL; - if (tree->lnode == NULL) { + if (nargs == 0) { if (do_traditional) { if (do_lint) lintwarn(_("printf: no arguments")); + if (redirtype != 0) { + redir_exp = TOP(); + if (redir_exp->type != Node_val) + fatal(_("attempt to use array `%s' in scalar context"), array_vname(redir_exp)); + rp = redirect(redir_exp, redirtype, & errflg); + DEREF(redir_exp); + decr_sp(); + } return; /* bwk accepts it silently */ } fatal(_("printf: no arguments")); } - fp = redirect_to_fp(tree->rnode, & rp); - if (fp == NULL) - return; - 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); + if (redirtype != 0) { + redir_exp = PEEK(nargs); + if (redir_exp->type != Node_val) + fatal(_("attempt to use array `%s' in scalar context"), array_vname(redir_exp)); + rp = redirect(redir_exp, redirtype, & errflg); + if (rp != NULL) + fp = rp->fp; + } else + fp = output_fp; + + tmp = printf_common(nargs); + if (redir_exp != NULL) { + DEREF(redir_exp); + decr_sp(); + } + if (tmp != NULL) { + if (fp == NULL) { + DEREF(tmp); + return; + } + efwrite(tmp->stptr, sizeof(char), tmp->stlen, fp, "printf", rp, TRUE); + if (rp != NULL && (rp->flag & RED_TWOWAY) != 0) + fflush(rp->fp); + DEREF(tmp); + } else + gawk_exit(EXIT_FATAL); } /* do_sqrt --- do the sqrt function */ NODE * -do_sqrt(NODE *tree) +do_sqrt(int nargs) { NODE *tmp; double arg; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("sqrt: received non-numeric argument")); arg = (double) force_number(tmp); - free_temp(tmp); + DEREF(tmp); if (arg < 0.0) warning(_("sqrt: called with negative argument %g"), arg); - return tmp_number((AWKNUM) sqrt(arg)); + return make_number((AWKNUM) sqrt(arg)); } /* do_substr --- do the substr function */ NODE * -do_substr(NODE *tree) +do_substr(int nargs) { - NODE *t1, *t2, *t3; + NODE *t1; NODE *r; - register size_t indx; - size_t length; - double d_index, d_length; + size_t indx; + size_t length = 0; + double d_index = 0, d_length = 0; size_t src_len; - t1 = force_string(tree_eval(tree->lnode)); - t2 = tree_eval(tree->rnode->lnode); - d_index = force_number(t2); - free_temp(t2); + if (nargs == 3) + POP_NUMBER(d_length); + POP_NUMBER(d_index); + t1 = POP_STRING(); + + if (nargs == 3) { + if (! (d_length >= 1)) { + if (do_lint == LINT_ALL) + lintwarn(_("substr: length %g is not >= 1"), d_length); + else if (do_lint == LINT_INVALID && ! (d_length >= 0)) + lintwarn(_("substr: length %g is not >= 0"), d_length); + DEREF(t1); + return Nnull_string; + } + if (do_lint) { + if (double_to_int(d_length) != d_length) + lintwarn( + _("substr: non-integer length %g will be truncated"), + d_length); + + if (d_length > SIZE_MAX) + lintwarn( + _("substr: length %g too big for string indexing, truncating to %g"), + d_length, (double) SIZE_MAX); + } + if (d_length < SIZE_MAX) + length = d_length; + else + length = SIZE_MAX; + } /* the weird `! (foo)' tests help catch NaN values. */ if (! (d_index >= 1)) { @@ -1490,7 +686,7 @@ do_substr(NODE *tree) else indx = SIZE_MAX; - if (tree->rnode->rnode == NULL) { /* third arg. missing */ + if (nargs == 2) { /* third arg. missing */ /* use remainder of string */ length = t1->stlen - indx; /* default to bytes */ #ifdef MBS_SUPPORT @@ -1501,40 +697,13 @@ do_substr(NODE *tree) } #endif d_length = length; /* set here in case used in diagnostics, below */ - } else { - t3 = tree_eval(tree->rnode->rnode->lnode); - d_length = force_number(t3); - free_temp(t3); - if (! (d_length >= 1)) { - if (do_lint == LINT_ALL) - lintwarn(_("substr: length %g is not >= 1"), d_length); - else if (do_lint == LINT_INVALID && ! (d_length >= 0)) - lintwarn(_("substr: length %g is not >= 0"), d_length); - free_temp(t1); - return Nnull_string; - } - if (do_lint) { - if (double_to_int(d_length) != d_length) - lintwarn( - _("substr: non-integer length %g will be truncated"), - d_length); - - if (d_length > SIZE_MAX) - lintwarn( - _("substr: length %g too big for string indexing, truncating to %g"), - d_length, (double) SIZE_MAX); - } - if (d_length < SIZE_MAX) - length = d_length; - else - length = SIZE_MAX; } if (t1->stlen == 0) { /* substr("", 1, 0) produces a warning only if LINT_ALL */ if (do_lint && (do_lint == LINT_ALL || ((indx | length) != 0))) lintwarn(_("substr: source string is zero length")); - free_temp(t1); + DEREF(t1); return Nnull_string; } @@ -1551,7 +720,7 @@ do_substr(NODE *tree) if (do_lint) lintwarn(_("substr: start index %g is past end of string"), d_index); - free_temp(t1); + DEREF(t1); return Nnull_string; } if (length > src_len - indx) { @@ -1592,24 +761,23 @@ do_substr(NODE *tree) } *cp = '\0'; r = make_str_node(substr, cp - substr, ALREADY_MALLOCED); - r->flags |= TEMP; } else { /* single byte case, easy */ single_byte_case: - r = tmp_string(t1->stptr + indx, length); + r = make_string(t1->stptr + indx, length); } #else - r = tmp_string(t1->stptr + indx, length); + r = make_string(t1->stptr + indx, length); #endif - free_temp(t1); + DEREF(t1); return r; } /* do_strftime --- format a time stamp */ NODE * -do_strftime(NODE *tree) +do_strftime(int nargs) { NODE *t1, *t2, *t3, *ret; struct tm *tm; @@ -1617,8 +785,7 @@ do_strftime(NODE *tree) char *bufp; size_t buflen, bufsize; char buf[BUFSIZ]; - /* FIXME: One day make %d be %e, after C 99 is common. */ - static const char def_format[] = "%a %b %d %H:%M:%S %Z %Y"; + static const char def_format[] = "%a %b %e %H:%M:%S %Z %Y"; const char *format; int formatlen; int do_gmt; @@ -1626,50 +793,39 @@ do_strftime(NODE *tree) /* set defaults first */ format = def_format; /* traditional date format */ formatlen = strlen(format); - (void) time(&fclock); /* current time of day */ + (void) time(& fclock); /* current time of day */ do_gmt = FALSE; t1 = t2 = t3 = NULL; - if (tree != NULL) { /* have args */ - if (tree->lnode != NULL) { - NODE *tmp = tree_eval(tree->lnode); - if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) - lintwarn(_("strftime: received 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 (nargs > 0) { /* have args */ + NODE *tmp; + + if (nargs == 2) { + t2 = POP_SCALAR(); if (do_lint && (t2->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("strftime: received non-numeric second argument")); fclock = (time_t) force_number(t2); - free_temp(t2); - - if (tree->rnode->rnode != NULL) { - tree = tree->rnode->rnode; - t3 = tree_eval(tree->lnode); - if ((t3->flags & (NUMCUR|NUMBER)) != 0) - do_gmt = (t3->numbr != 0); - else - do_gmt = (t3->stlen > 0); + DEREF(t2); + } - free_temp(t3); - } + tmp = POP_SCALAR(); + if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) + lintwarn(_("strftime: received 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")); + DEREF(t1); + return make_string("", 0); } } if (do_gmt) - tm = gmtime(&fclock); + tm = gmtime(& fclock); else - tm = localtime(&fclock); + tm = localtime(& fclock); bufp = buf; bufsize = sizeof(buf); @@ -1692,29 +848,29 @@ do_strftime(NODE *tree) else erealloc(bufp, char *, bufsize, "do_strftime"); } - ret = tmp_string(bufp, buflen); + ret = make_string(bufp, buflen); if (bufp != buf) - free(bufp); + efree(bufp); if (t1) - free_temp(t1); + DEREF(t1); return ret; } /* do_systime --- get the time of day */ NODE * -do_systime(NODE *tree ATTRIBUTE_UNUSED) +do_systime(int nargs ATTRIBUTE_UNUSED) { time_t lclock; - (void) time(&lclock); - return tmp_number((AWKNUM) lclock); + (void) time(& lclock); + return make_number((AWKNUM) lclock); } /* do_mktime --- turn a time string into a timestamp */ NODE * -do_mktime(NODE *tree) +do_mktime(int nargs) { NODE *t1; struct tm then; @@ -1724,7 +880,7 @@ do_mktime(NODE *tree) time_t then_stamp; char save; - t1 = tree_eval(tree->lnode); + t1 = POP_SCALAR(); if (do_lint && (t1->flags & (STRING|STRCUR)) == 0) lintwarn(_("mktime: received non-string argument")); t1 = force_string(t1); @@ -1737,22 +893,22 @@ do_mktime(NODE *tree) & hour, & minute, & second, & dst); - if (do_lint /* Ready? Set! Go: */ - && ( (second < 0 || second > 60) - || (minute < 0 || minute > 60) - || (hour < 0 || hour > 23) - || (day < 1 || day > 31) - || (month < 1 || month > 12) )) - lintwarn(_("mktime: at least one of the values is out of the default range")); + if (do_lint /* Ready? Set! Go: */ + && ( (second < 0 || second > 60) + || (minute < 0 || minute > 60) + || (hour < 0 || hour > 23) + || (day < 1 || day > 31) + || (month < 1 || month > 12) )) + lintwarn(_("mktime: at least one of the values is out of the default range")); t1->stptr[t1->stlen] = save; - free_temp(t1); + DEREF(t1); if (count < 6 || month == INT_MIN || year < INT_MIN + 1900 || year - 1900 > INT_MAX) - return tmp_number((AWKNUM) -1); + return make_number((AWKNUM) -1); memset(& then, '\0', sizeof(then)); then.tm_sec = second; @@ -1764,13 +920,13 @@ do_mktime(NODE *tree) then.tm_isdst = dst; then_stamp = mktime(& then); - return tmp_number((AWKNUM) then_stamp); + return make_number((AWKNUM) then_stamp); } /* do_system --- run an external command */ NODE * -do_system(NODE *tree) +do_system(int nargs) { NODE *tmp; int ret = 0; @@ -1781,28 +937,13 @@ do_system(NODE *tree) fatal(_("'system' function not allowed in sandbox mode")); (void) flush_io(); /* so output is synchronous with gawk's */ - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0) lintwarn(_("system: received 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'; @@ -1815,8 +956,8 @@ do_system(NODE *tree) cmd[tmp->stlen] = save; } - free_temp(tmp); - return tmp_number((AWKNUM) ret); + DEREF(tmp); + return make_number((AWKNUM) ret); } extern NODE **fmt_list; /* declared in eval.c */ @@ -1824,54 +965,60 @@ extern NODE **fmt_list; /* declared in eval.c */ /* do_print --- print items, separated by OFS, terminated with ORS */ void -do_print(register NODE *tree) +do_print(int nargs, int redirtype) { - register NODE **t; struct redirect *rp = NULL; - register FILE *fp; - int numnodes, i; - NODE *save; - NODE *tval; - - fp = redirect_to_fp(tree->rnode, & rp); - if (fp == NULL) - return; - - /* - * 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; + int errflg; /* not used, sigh */ + FILE *fp = NULL; + int i; + NODE *redir_exp = NULL; + NODE *tmp; - /* Here lies the wumpus. R.I.P. */ - n = tree_eval(tree->lnode); - t[i] = dupnode(n); - free_temp(n); + assert(nargs <= max_args); - if ((t[i]->flags & (NUMBER|STRING)) == NUMBER) { + if (redirtype != 0) { + redir_exp = PEEK(nargs); + if (redir_exp->type != Node_val) + fatal(_("attempt to use array `%s' in scalar context"), array_vname(redir_exp)); + rp = redirect(redir_exp, redirtype, & errflg); + if (rp != NULL) + fp = rp->fp; + } else + fp = output_fp; + + for (i = 1; i <= nargs; i++) { + tmp = args_array[i] = POP(); + if (tmp->type == Node_var_array) { + while (--i > 0) + DEREF(args_array[i]); + fatal(_("attempt to use array `%s' in scalar context"), array_vname(tmp)); + } + if (do_lint && tmp->type == Node_var_new) + lintwarn(_("reference to uninitialized variable `%s'"), + tmp->vname); + if ((tmp->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); - } + (void) force_string(tmp); + else + args_array[i] = format_val(OFMT, OFMTidx, tmp); } } - for (i = 0; i < numnodes; i++) { - efwrite(t[i]->stptr, sizeof(char), t[i]->stlen, fp, "print", rp, FALSE); - unref(t[i]); + if (redir_exp != NULL) { + DEREF(redir_exp); + decr_sp(); + } - if (i != numnodes - 1 && OFSlen > 0) + if (fp == NULL) { + for (i = nargs; i > 0; i--) + DEREF(args_array[i]); + return; + } + + for (i = nargs; i > 0; i--) { + efwrite(args_array[i]->stptr, sizeof(char), args_array[i]->stlen, fp, "print", rp, FALSE); + DEREF(args_array[i]); + if (i != 1 && OFSlen > 0) efwrite(OFS, sizeof(char), (size_t) OFSlen, fp, "print", rp, FALSE); @@ -1881,20 +1028,30 @@ do_print(register NODE *tree) if (rp != NULL && (rp->flag & RED_TWOWAY) != 0) fflush(rp->fp); - - free(t); } /* do_print_rec --- special case printing of $0, for speed */ void -do_print_rec(register NODE *tree) +do_print_rec(int nargs, int redirtype) { + FILE *fp = NULL; + NODE *f0; struct redirect *rp = NULL; - register FILE *fp; - register NODE *f0; + int errflg; /* not used, sigh */ + NODE *redir_exp = NULL; + + assert(nargs == 0); + if (redirtype != 0) { + redir_exp = TOP(); + rp = redirect(redir_exp, redirtype, & errflg); + if (rp != NULL) + fp = rp->fp; + DEREF(redir_exp); + decr_sp(); + } else + fp = output_fp; - fp = redirect_to_fp(tree->rnode, & rp); if (fp == NULL) return; @@ -1915,161 +1072,128 @@ do_print_rec(register NODE *tree) fflush(rp->fp); } -#ifdef MBS_SUPPORT -/* wide_tolower_toupper --- lower- or uppercase a multibyte string */ - -typedef int (*isw_func)(wint_t); -typedef wint_t (*tow_func)(wint_t); - -static NODE * -wide_tolower_toupper(NODE *t1, isw_func iswu, tow_func towl) -{ - register unsigned char *cp, *cpe; - register unsigned char *cp2; - size_t mbclen; - mbstate_t mbs, prev_mbs; - wchar_t wc; - NODE *t2; - /* - * Since the lowercase char and its uppercase equivalent may occupy - * different number of bytes (Turkish `i'), we cannot say the length - * of the output string. - * This approach is adapted from format_tree(). - */ - unsigned char *obuf; - size_t osiz, ofre; - - /* - * Better 2 spare bytes than 1, consistently with make_str_node(). - * And we need gawk_mb_cur_max free bytes before we convert the last - * char, so we add (gawk_mb_cur_max - 1). - */ - osiz = t1->stlen + 2 + (gawk_mb_cur_max - 1); - ofre = osiz - 2; - emalloc(obuf, unsigned char *, osiz, "wide_tolower_toupper"); - - memset(&mbs, 0, sizeof(mbstate_t)); - cp = (unsigned char *)t1->stptr; - cpe = (unsigned char *)(t1->stptr + t1->stlen); - cp2 = obuf; - while (cp < cpe) { - if (ofre < gawk_mb_cur_max) { - size_t olen = cp2 - obuf; - ofre += osiz; - osiz *= 2; - erealloc(obuf, unsigned char *, osiz, "wide_tolower_toupper"); - cp2 = obuf + olen; - } - prev_mbs = mbs; - mbclen = (size_t) mbrtowc(&wc, (char *) cp, cpe - cp, - &mbs); - if (mbclen == 0 || mbclen == (size_t) -1 || mbclen == (size_t) -2) { - /* Null wide char, or a problem appeared. */ - *cp2++ = *cp++; - ofre--; - continue; - } - - /* If the character doesn't need change, copy it. */ - if (!(*iswu)(wc)) { - ofre -= mbclen; - while (mbclen--) - *cp2++ = *cp++; - continue; - } - - /* Increment the input pointer. */ - cp += mbclen; - - /* Write the modified wide character. */ - mbclen = wcrtomb((char *) cp2, (*towl)(wc), &prev_mbs); - - if (mbclen > 0 && mbclen < (size_t) -2) { - /* Increment the output pointer. */ - cp2 += mbclen; - ofre -= mbclen; - } else { - /* A problem appeared. */ - cp2++; - ofre--; - } - } - t2 = make_str_node((char *) obuf, cp2 - obuf, ALREADY_MALLOCED); - t2->flags |= TEMP; - return t2; -} -#endif +/* + * 11/2010: FIXME: Consider converting the whole string to wide + * characters, running through and converting to wide lower case + * and then coverting back. Might be more straightforward code. + */ /* do_tolower --- lower case a string */ NODE * -do_tolower(NODE *tree) +do_tolower(int nargs) { NODE *t1, *t2; + unsigned char *cp, *cp2; +#ifdef MBS_SUPPORT + size_t mbclen = 0; + mbstate_t mbs, prev_mbs; + + if (gawk_mb_cur_max > 1) + memset(& mbs, 0, sizeof(mbstate_t)); +#endif - t1 = tree_eval(tree->lnode); + t1 = POP_SCALAR(); if (do_lint && (t1->flags & (STRING|STRCUR)) == 0) lintwarn(_("tolower: received non-string argument")); t1 = force_string(t1); - + t2 = make_string(t1->stptr, t1->stlen); + for (cp = (unsigned char *)t2->stptr, + cp2 = (unsigned char *)(t2->stptr + t2->stlen); cp < cp2; cp++) #ifdef MBS_SUPPORT - if (gawk_mb_cur_max > 1) - t2 = wide_tolower_toupper(t1, iswupper, towlower); - else + if (gawk_mb_cur_max > 1) { + wchar_t wc; + + prev_mbs = mbs; + mbclen = (size_t) mbrtowc(& wc, (char *) cp, cp2 - cp, + & mbs); + if ((mbclen != 1) && (mbclen != (size_t) -1) && + (mbclen != (size_t) -2) && (mbclen != 0)) { + /* a multibyte character. */ + if (iswupper(wc)) { + wint_t junk; + + wc = towlower(wc); + junk = wcrtomb((char *) cp, wc, & prev_mbs); + } + /* Adjust the pointer. */ + cp += mbclen - 1; + } else { + /* Otherwise we treat it as a singlebyte character. */ + if (isupper(*cp)) + *cp = tolower(*cp); + } + } else #endif - { - register unsigned char *cp, *cpe; - - t2 = tmp_string(t1->stptr, t1->stlen); - for (cp = (unsigned char *)t2->stptr, - cpe = (unsigned char *)(t2->stptr + t2->stlen); cp < cpe; cp++) - if (isupper(*cp)) - *cp = tolower(*cp); - } - free_temp(t1); + if (isupper(*cp)) + *cp = tolower(*cp); + DEREF(t1); return t2; } /* do_toupper --- upper case a string */ NODE * -do_toupper(NODE *tree) +do_toupper(int nargs) { NODE *t1, *t2; + unsigned char *cp, *cp2; +#ifdef MBS_SUPPORT + size_t mbclen = 0; + mbstate_t mbs, prev_mbs; - t1 = tree_eval(tree->lnode); + if (gawk_mb_cur_max > 1) + memset(& mbs, 0, sizeof(mbstate_t)); +#endif + + t1 = POP_SCALAR(); if (do_lint && (t1->flags & (STRING|STRCUR)) == 0) lintwarn(_("toupper: received non-string argument")); t1 = force_string(t1); - + t2 = make_string(t1->stptr, t1->stlen); + for (cp = (unsigned char *)t2->stptr, + cp2 = (unsigned char *)(t2->stptr + t2->stlen); cp < cp2; cp++) #ifdef MBS_SUPPORT - if (gawk_mb_cur_max > 1) - t2 = wide_tolower_toupper(t1, iswlower, towupper); - else + if (gawk_mb_cur_max > 1) { + wchar_t wc; + + prev_mbs = mbs; + mbclen = (size_t) mbrtowc(& wc, (char *) cp, cp2 - cp, + & mbs); + if ((mbclen != 1) && (mbclen != (size_t) -1) && + (mbclen != (size_t) -2) && (mbclen != 0)) { + /* a multibyte character. */ + if (iswlower(wc)) { + wint_t junk; + + wc = towupper(wc); + junk = wcrtomb((char *) cp, wc, & prev_mbs); + } + /* Adjust the pointer. */ + cp += mbclen - 1; + } else { + /* Otherwise we treat it as a singlebyte character. */ + if (islower(*cp)) + *cp = toupper(*cp); + } + } else #endif - { - register unsigned char *cp, *cpe; - - t2 = tmp_string(t1->stptr, t1->stlen); - for (cp = (unsigned char *)t2->stptr, - cpe = (unsigned char *)(t2->stptr + t2->stlen); cp < cpe; cp++) - if (islower(*cp)) - *cp = toupper(*cp); - } - free_temp(t1); + if (islower(*cp)) + *cp = toupper(*cp); + DEREF(t1); return t2; } /* do_atan2 --- do the atan2 function */ NODE * -do_atan2(NODE *tree) +do_atan2(int nargs) { NODE *t1, *t2; double d1, d2; - t1 = tree_eval(tree->lnode); - t2 = tree_eval(tree->rnode->lnode); + t2 = POP_SCALAR(); + t1 = POP_SCALAR(); if (do_lint) { if ((t1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("atan2: received non-numeric first argument")); @@ -2078,41 +1202,41 @@ do_atan2(NODE *tree) } d1 = force_number(t1); d2 = force_number(t2); - free_temp(t1); - free_temp(t2); - return tmp_number((AWKNUM) atan2(d1, d2)); + DEREF(t1); + DEREF(t2); + return make_number((AWKNUM) atan2(d1, d2)); } /* do_sin --- do the sin function */ NODE * -do_sin(NODE *tree) +do_sin(int nargs) { NODE *tmp; double d; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("sin: received non-numeric argument")); d = sin((double) force_number(tmp)); - free_temp(tmp); - return tmp_number((AWKNUM) d); + DEREF(tmp); + return make_number((AWKNUM) d); } /* do_cos --- do the cos function */ NODE * -do_cos(NODE *tree) +do_cos(int nargs) { NODE *tmp; double d; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("cos: received non-numeric argument")); d = cos((double) force_number(tmp)); - free_temp(tmp); - return tmp_number((AWKNUM) d); + DEREF(tmp); + return make_number((AWKNUM) d); } /* do_rand --- do the rand function */ @@ -2125,7 +1249,7 @@ static char *const state = (char *const) istate; /* ARGSUSED */ NODE * -do_rand(NODE *tree ATTRIBUTE_UNUSED) +do_rand(int nargs ATTRIBUTE_UNUSED) { if (firstrand) { (void) initstate((unsigned) 1, state, SIZEOF_STATE); @@ -2138,13 +1262,13 @@ do_rand(NODE *tree ATTRIBUTE_UNUSED) * * 0 <= n < 1 */ - return tmp_number((AWKNUM) (random() % GAWK_RANDOM_MAX) / GAWK_RANDOM_MAX); + return make_number((AWKNUM) (random() % GAWK_RANDOM_MAX) / GAWK_RANDOM_MAX); } /* do_srand --- seed the random number generator */ NODE * -do_srand(NODE *tree) +do_srand(int nargs) { NODE *tmp; static long save_seed = 1; @@ -2157,16 +1281,16 @@ do_srand(NODE *tree) (void) setstate(state); } - if (tree == NULL) + if (nargs == 0) srandom((unsigned int) (save_seed = (long) time((time_t *) 0))); else { - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("srand: received non-numeric argument")); srandom((unsigned int) (save_seed = (long) force_number(tmp))); - free_temp(tmp); + DEREF(tmp); } - return tmp_number((AWKNUM) ret); + return make_number((AWKNUM) ret); } /* do_match --- match a regexp, set RSTART and RLENGTH, @@ -2175,9 +1299,9 @@ do_srand(NODE *tree) */ NODE * -do_match(NODE *tree) +do_match(int nargs) { - NODE *t1, *dest, *it; + NODE *tre, *t1, *dest, *it; int rstart, len, ii; int rlength; Regexp *rp; @@ -2189,18 +1313,16 @@ do_match(NODE *tree) char *subsepstr; size_t subseplen; - 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 = get_param(tree->rnode->lnode); + if (nargs == 3) { /* 3rd optional arg for the subpatterns */ + dest = POP_PARAM(); if (dest->type != Node_var_array) fatal(_("match: third argument is not an array")); - assoc_clear(dest); } + tre = POP(); + rp = re_update(tre); + t1 = POP_STRING(); rstart = research(rp, t1->stptr, 0, t1->stlen, RE_NEED_START); if (rstart >= 0) { /* match succeded */ @@ -2229,7 +1351,9 @@ do_match(NODE *tree) if ((s = SUBPATSTART(rp, t1->stptr, ii)) != -1) { size_t subpat_start; size_t subpat_len; - + NODE **lhs; + NODE *sub; + start = t1->stptr + s; subpat_start = s; subpat_len = len = SUBPATEND(rp, t1->stptr, ii) - s; @@ -2242,11 +1366,13 @@ do_match(NODE *tree) it = make_string(start, len); it->flags |= MAYBE_NUM; /* user input */ - /* - * assoc_lookup() does free_temp() on 2nd arg. - */ - *assoc_lookup(dest, tmp_number((AWKNUM) (ii)), FALSE) = it; - + + sub = make_number((AWKNUM) (ii)); + lhs = assoc_lookup(dest, sub, FALSE); + unref(*lhs); + *lhs = it; + unref(sub); + sprintf(buff, "%d", ii); ilen = strlen(buff); amt = ilen + subseplen + strlen("length") + 2; @@ -2264,7 +1390,11 @@ do_match(NODE *tree) slen = ilen + subseplen + 5; it = make_number((AWKNUM) subpat_start + 1); - *assoc_lookup(dest, tmp_string(buf, slen), FALSE) = it; + sub = make_string(buf, slen); + lhs = assoc_lookup(dest, sub, FALSE); + unref(*lhs); + *lhs = it; + unref(sub); memcpy(buf, buff, ilen); memcpy(buf + ilen, subsepstr, subseplen); @@ -2273,24 +1403,29 @@ do_match(NODE *tree) slen = ilen + subseplen + 6; it = make_number((AWKNUM) subpat_len); - *assoc_lookup(dest, tmp_string(buf, slen), FALSE) = it; + sub = make_string(buf, slen); + lhs = assoc_lookup(dest, sub, FALSE); + unref(*lhs); + *lhs = it; + unref(sub); } } - free(buf); + efree(buf); } if (wc_indices != NULL) - free(wc_indices); + efree(wc_indices); } else { /* match failed */ rstart = 0; rlength = -1; } - free_temp(t1); + + DEREF(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((AWKNUM) rlength); - return tmp_number((AWKNUM) rstart); + return make_number((AWKNUM) rstart); } /* sub_common --- the common code (does the work) for sub, gsub, and gensub */ @@ -2363,22 +1498,23 @@ do_match(NODE *tree) * 2001 standard: * * sub(ere, repl[, in ]) - * Substitute the string repl in place of the first instance of the extended regular - * expression ERE in string in and return the number of substitutions. An ampersand - * ('&') appearing in the string repl shall be replaced by the string from in that - * matches the ERE. An ampersand preceded with a backslash ('\') shall be - * interpreted as the literal ampersand character. An occurrence of two consecutive - * backslashes shall be interpreted as just a single literal backslash character. Any - * other occurrence of a backslash (for example, preceding any other character) shall - * be treated as a literal backslash character. Note that if repl is a string literal (the - * lexical token STRING; see Grammar (on page 170)), the handling of the - * ampersand character occurs after any lexical processing, including any lexical - * backslash escape sequence processing. If in is specified and it is not an lvalue (see - * Expressions in awk (on page 156)), the behavior is undefined. If in is omitted, awk - * shall use the current record ($0) in its place. + * Substitute the string repl in place of the first instance of the extended regular + * expression ERE in string in and return the number of substitutions. An ampersand + * ('&') appearing in the string repl shall be replaced by the string from in that + * matches the ERE. An ampersand preceded with a backslash ('\') shall be + * interpreted as the literal ampersand character. An occurrence of two consecutive + * backslashes shall be interpreted as just a single literal backslash character. Any + * other occurrence of a backslash (for example, preceding any other character) shall + * be treated as a literal backslash character. Note that if repl is a string literal (the + * lexical token STRING; see Grammar (on page 170)), the handling of the + * ampersand character occurs after any lexical processing, including any lexical + * backslash escape sequence processing. If in is specified and it is not an lvalue (see + * Expressions in awk (on page 156)), the behavior is undefined. If in is omitted, awk + * shall use the current record ($0) in its place. * - * Because gawk has had its behavior for 7+ years, that behavior is remaining as - * the default, with the POSIX behavior available for do_posix. Fun, fun, fun. + * 11/2010: The text in the 2008 standard is the same as just quoted. However, POSIX behavior + * is now the default. This can change the behavior of awk programs. The old behavior + * is not available. */ /* @@ -2386,14 +1522,14 @@ do_match(NODE *tree) */ static NODE * -sub_common(NODE *tree, long how_many, int backdigs) +sub_common(int nargs, long how_many, int backdigs) { - register char *scan; - register char *bp, *cp; + char *scan; + char *bp, *cp; char *buf; size_t buflen; - register char *matchend; - register size_t len; + char *matchend; + size_t len; char *matchstart; char *text; size_t textlen; @@ -2407,47 +1543,41 @@ sub_common(NODE *tree, long how_many, int backdigs) 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; - + NODE **lhs; int global = (how_many == -1); long current; int lastmatchnonzero; char *mb_indices = NULL; - tmp = tree->lnode; /* regexp */ + tmp = PEEK(2); /* take care of regexp early, in case re_update is fatal */ rp = re_update(tmp); + + /* original string */ + if (nargs == 4) { /* kludge: no of items on stack is really 3, + * See snode(..) in awkgram.y + */ + lhs = NULL; + t = POP_STRING(); + } else { + lhs = POP_ADDRESS(); + t = force_string(*lhs); + } - tree = tree->rnode; /* replacement text */ - s = tree->lnode; - s = force_string(tree_eval(s)); - tree = tree->rnode; /* original string */ - tmp = tree->lnode; - t = force_string(tree_eval(tmp)); + s = POP_STRING(); /* replacement text */ + decr_sp(); /* regexp, already updated above */ /* do the search early to avoid work on non-match */ if (research(rp, t->stptr, 0, t->stlen, RE_NEED_START) == -1 || RESTART(rp, t->stptr) > t->stlen) { - free_temp(t); - free_temp(s); - return tmp_number((AWKNUM) 0.0); + if (lhs == NULL) + DEREF(t); + DEREF(s); + return make_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; @@ -2487,7 +1617,7 @@ sub_common(NODE *tree, long how_many, int backdigs) repllen--; scan++; } - } else { /* was under if (do_posix) */ + } else { /* \& --> &, \\ --> \ */ if (scan[1] == '&' || scan[1] == '\\') { repllen--; @@ -2495,26 +1625,6 @@ sub_common(NODE *tree, long how_many, int backdigs) } /* else leave alone, it goes into the output */ } -#if 0 - else { - /* gawk default behavior since 1996 */ - 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 */ - } -#endif } } @@ -2561,18 +1671,18 @@ sub_common(NODE *tree, long how_many, int backdigs) */ for (scan = repl; scan < replend; scan++) if (*scan == '&' - /* - * Don't test repllen here. A simple "&" could - * end up with repllen == 0. - */ - && (gawk_mb_cur_max == 1 - || mb_indices[scan - repl] == 1) + /* + * Don't test repllen here. A simple "&" could + * end up with repllen == 0. + */ + && (gawk_mb_cur_max == 1 + || mb_indices[scan - repl] == 1) ) { - for (cp = matchstart; cp < matchend; cp++) - *bp++ = *cp; + for (cp = matchstart; cp < matchend; cp++) + *bp++ = *cp; } else if (*scan == '\\' - && (gawk_mb_cur_max == 1 - || (repllen > 0 && mb_indices[scan - repl] == 1)) + && (gawk_mb_cur_max == 1 + || (repllen > 0 && mb_indices[scan - repl] == 1)) ) { if (backdigs) { /* gensub, behave sanely */ if (isdigit(scan[1])) { @@ -2591,30 +1701,11 @@ sub_common(NODE *tree, long how_many, int backdigs) scan++; } else /* \q for any q --> q */ *bp++ = *++scan; - } else if (do_posix) { + } else { /* \& --> &, \\ --> \ */ if (scan[1] == '&' || scan[1] == '\\') scan++; *bp++ = *scan; - } else { - /* gawk default behavior since 1996 */ - 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; @@ -2653,65 +1744,53 @@ sub_common(NODE *tree, long how_many, int backdigs) *bp++ = *scan; *bp = '\0'; textlen = bp - buf; - free(t->stptr); - t->stptr = buf; - t->stlen = textlen; - free_wstr(t); - t->flags &= ~(NUMCUR|NUMBER); - - free_temp(s); - if (matches > 0 && lhs) { - if (priv) { + + DEREF(s); + + if (lhs != NULL) { + if (matches > 0) { unref(*lhs); - *lhs = t; - } - if (after_assign != NULL) - (*after_assign)(); + *lhs = make_str_node(buf, textlen, ALREADY_MALLOCED); + } else + efree(buf); + } else { + efree(buf); + DEREF(t); } + if (mb_indices != NULL) - free(mb_indices); + efree(mb_indices); - return tmp_number((AWKNUM) matches); + return make_number((AWKNUM) matches); } /* do_gsub --- global substitution */ NODE * -do_gsub(NODE *tree) +do_gsub(int nargs) { - return sub_common(tree, -1, FALSE); + return sub_common(nargs, -1, FALSE); } /* do_sub --- single substitution */ NODE * -do_sub(NODE *tree) +do_sub(int nargs) { - return sub_common(tree, 1, FALSE); + return sub_common(nargs, 1, FALSE); } /* do_gensub --- fix up the tree for sub_common for the gensub function */ NODE * -do_gensub(NODE *tree) +do_gensub(int nargs) { - NODE n1, n2, n3, *t, *tmp, *target, *ret; + NODE *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 */ + tmp = POP_STRING(); /* target */ + t = POP_SCALAR(); /* value of global flag */ /* * We make copy of the original target string, and pass that @@ -2719,12 +1798,10 @@ do_gensub(NODE *tree) * 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; + target = make_string(tmp->stptr, tmp->stlen); + DEREF(tmp); + PUSH_ADDRESS(& target); if ((t->flags & (STRCUR|STRING)) != 0) { if (t->stlen > 0 && (t->stptr[0] == 'g' || t->stptr[0] == 'G')) @@ -2750,117 +1827,43 @@ set_how_many: warning(_("gensub: third argument of 0 treated as 1")); } - free_temp(t); + DEREF(t); - ret = sub_common(&n1, how_many, TRUE); - free_temp(ret); + ret = sub_common(3, how_many, TRUE); + unref(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 */ -/* tmp_integer - Convert an integer to a temporary number node. */ +/* make_integer - Convert an integer to a number node. */ static NODE * -tmp_integer(uintmax_t n) +make_integer(uintmax_t n) { n = adjust_uint(n); - return tmp_number((AWKNUM) n); + return make_number((AWKNUM) n); } /* do_lshift --- perform a << operation */ NODE * -do_lshift(NODE *tree) +do_lshift(int nargs) { NODE *s1, *s2; uintmax_t uval, ushift, res; AWKNUM val, shift; - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("lshift: received non-numeric first argument")); @@ -2878,27 +1881,28 @@ do_lshift(NODE *tree) lintwarn(_("lshift(%lf, %lf): too large shift value will give strange results"), val, shift); } - free_temp(s1); - free_temp(s2); + DEREF(s1); + DEREF(s2); uval = (uintmax_t) val; ushift = (uintmax_t) shift; res = uval << ushift; - return tmp_integer(res); + return make_integer(res); } /* do_rshift --- perform a >> operation */ NODE * -do_rshift(NODE *tree) +do_rshift(int nargs) { NODE *s1, *s2; uintmax_t uval, ushift, res; AWKNUM val, shift; - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("rshift: received non-numeric first argument")); @@ -2916,27 +1920,28 @@ do_rshift(NODE *tree) lintwarn(_("rshift(%lf, %lf): too large shift value will give strange results"), val, shift); } - free_temp(s1); - free_temp(s2); + DEREF(s1); + DEREF(s2); uval = (uintmax_t) val; ushift = (uintmax_t) shift; res = uval >> ushift; - return tmp_integer(res); + return make_integer(res); } /* do_and --- perform an & operation */ NODE * -do_and(NODE *tree) +do_and(int nargs) { NODE *s1, *s2; uintmax_t uleft, uright, res; AWKNUM left, right; - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("and: received non-numeric first argument")); @@ -2952,27 +1957,28 @@ do_and(NODE *tree) lintwarn(_("and(%lf, %lf): fractional values will be truncated"), left, right); } - free_temp(s1); - free_temp(s2); + DEREF(s1); + DEREF(s2); uleft = (uintmax_t) left; uright = (uintmax_t) right; res = uleft & uright; - return tmp_integer(res); + return make_integer(res); } /* do_or --- perform an | operation */ NODE * -do_or(NODE *tree) +do_or(int nargs) { NODE *s1, *s2; uintmax_t uleft, uright, res; AWKNUM left, right; - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("or: received non-numeric first argument")); @@ -2988,27 +1994,30 @@ do_or(NODE *tree) lintwarn(_("or(%lf, %lf): fractional values will be truncated"), left, right); } - free_temp(s1); - free_temp(s2); + DEREF(s1); + DEREF(s2); uleft = (uintmax_t) left; uright = (uintmax_t) right; res = uleft | uright; - return tmp_integer(res); + return make_integer(res); } /* do_xor --- perform an ^ operation */ NODE * -do_xor(NODE *tree) +do_xor(int nargs) { NODE *s1, *s2; uintmax_t uleft, uright, res; AWKNUM left, right; - s1 = tree_eval(tree->lnode); - s2 = tree_eval(tree->rnode->lnode); + s2 = POP_SCALAR(); + s1 = POP_SCALAR(); + left = force_number(s1); + right = force_number(s2); + if (do_lint) { if ((s1->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("xor: received non-numeric first argument")); @@ -3024,32 +2033,34 @@ do_xor(NODE *tree) lintwarn(_("xor(%lf, %lf): fractional values will be truncated"), left, right); } - free_temp(s1); - free_temp(s2); + DEREF(s1); + DEREF(s2); uleft = (uintmax_t) left; uright = (uintmax_t) right; res = uleft ^ uright; - return tmp_integer(res); + return make_integer(res); } /* do_compl --- perform a ~ operation */ NODE * -do_compl(NODE *tree) +do_compl(int nargs) { NODE *tmp; double d; uintmax_t uval; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0) lintwarn(_("compl: received non-numeric argument")); d = force_number(tmp); - free_temp(tmp); + DEREF(tmp); if (do_lint) { + if ((tmp->flags & (NUMCUR|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) @@ -3058,18 +2069,18 @@ do_compl(NODE *tree) uval = (uintmax_t) d; uval = ~ uval; - return tmp_integer(uval); + return make_integer(uval); } /* do_strtonum --- the strtonum function */ NODE * -do_strtonum(NODE *tree) +do_strtonum(int nargs) { NODE *tmp; AWKNUM d; - tmp = tree_eval(tree->lnode); + tmp = POP_SCALAR(); if ((tmp->flags & (NUMBER|NUMCUR)) != 0) d = (AWKNUM) force_number(tmp); @@ -3078,8 +2089,8 @@ do_strtonum(NODE *tree) else d = (AWKNUM) force_number(tmp); - free_temp(tmp); - return tmp_number((AWKNUM) d); + DEREF(tmp); + return make_number((AWKNUM) d); } /* nondec2awknum --- convert octal or hex value to double */ @@ -3167,7 +2178,7 @@ done: #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT static int -localecategory_from_argument(NODE *tree) +localecategory_from_argument(NODE *t) { static const struct category_table { int val; @@ -3199,14 +2210,11 @@ localecategory_from_argument(NODE *tree) #endif /* LC_TIME */ }; - if (tree != NULL) { + if (t != NULL) { int low, high, i, mid; - NODE *tmp, *t; char *category; int lc_cat = -1; - tmp = tree->lnode; - t = force_string(tree_eval(tmp)); category = t->stptr; /* binary search the table */ @@ -3228,7 +2236,6 @@ localecategory_from_argument(NODE *tree) if (lc_cat == -1) /* not there */ fatal(_("dcgettext: `%s' is not a valid locale category"), category); - free_temp(t); return lc_cat; } else return LC_MESSAGES; @@ -3246,94 +2253,108 @@ localecategory_from_argument(NODE *tree) */ NODE * -do_dcgettext(NODE *tree) +do_dcgettext(int nargs) { - NODE *tmp, *t1, *t2; + NODE *tmp, *t1, *t2 = NULL; char *string; char *the_result; #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT int lc_cat; char *domain; -#endif /* ENABLE_NLS */ - tmp = tree->lnode; /* first argument */ - t1 = force_string(tree_eval(tmp)); - string = t1->stptr; + if (nargs == 3) { /* third argument */ + tmp = POP_STRING(); + lc_cat = localecategory_from_argument(tmp); + DEREF(tmp); + } else + lc_cat = LC_MESSAGES; - t2 = NULL; -#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT - tree = tree->rnode; /* second argument */ - if (tree != NULL) { - tmp = tree->lnode; - t2 = force_string(tree_eval(tmp)); + if (nargs >= 2) { /* second argument */ + t2 = POP_STRING(); domain = t2->stptr; } else domain = TEXTDOMAIN; +#else + if (nargs == 3) { + tmp = POP_STRING(); + DEREF(tmp); + } + if (nargs >= 2) { + t2 = POP_STRING(); + DEREF(t2); + } +#endif - if (tree && tree->rnode != NULL) { /* third argument */ - lc_cat = localecategory_from_argument(tree->rnode); - } else - lc_cat = LC_MESSAGES; + t1 = POP_STRING(); /* first argument */ + string = t1->stptr; +#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT the_result = dcgettext(domain, string, lc_cat); + if (t2 != NULL) + DEREF(t2); #else the_result = string; #endif - free_temp(t1); - if (t2 != NULL) - free_temp(t2); - - return tmp_string(the_result, strlen(the_result)); + DEREF(t1); + return make_string(the_result, strlen(the_result)); } + NODE * -do_dcngettext(NODE *tree) +do_dcngettext(int nargs) { NODE *tmp, *t1, *t2, *t3; char *string1, *string2; unsigned long number; + AWKNUM d; char *the_result; + #if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT int lc_cat; char *domain; -#endif /* ENABLE_NLS */ - - tmp = tree->lnode; /* first argument */ - t1 = force_string(tree_eval(tmp)); - string1 = t1->stptr; - - tmp = tree->rnode->lnode; /* second argument */ - t2 = force_string(tree_eval(tmp)); - string2 = t2->stptr; - tmp = tree->rnode->rnode->lnode; /* third argument */ - number = (unsigned long) double_to_int(force_number(tree_eval(tmp))); + if (nargs == 5) { /* fifth argument */ + tmp = POP_STRING(); + lc_cat = localecategory_from_argument(tmp); + DEREF(tmp); + } else + lc_cat = LC_MESSAGES; t3 = NULL; -#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT - tree = tree->rnode->rnode->rnode; /* fourth argument */ - if (tree != NULL) { - tmp = tree->lnode; - t3 = force_string(tree_eval(tmp)); + if (nargs >= 4) { /* fourth argument */ + t3 = POP_STRING(); domain = t3->stptr; } else domain = TEXTDOMAIN; +#else + if (nargs == 5) { + tmp = POP_STRING(); + DEREF(tmp); + } + if (nargs >= 4) { + t3 = POP_STRING(); + DEREF(t3); + } +#endif - if (tree && tree->rnode != NULL) { /* fifth argument */ - lc_cat = localecategory_from_argument(tree->rnode); - } else - lc_cat = LC_MESSAGES; + POP_NUMBER(d); /* third argument */ + number = (unsigned long) double_to_int(d); + t2 = POP_STRING(); /* second argument */ + string2 = t2->stptr; + t1 = POP_STRING(); /* first argument */ + string1 = t1->stptr; + +#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT the_result = dcngettext(domain, string1, string2, number, lc_cat); + if (t3 != NULL) + DEREF(t3); #else the_result = (number == 1 ? string1 : string2); #endif - free_temp(t1); - free_temp(t2); - if (t3 != NULL) - free_temp(t3); - - return tmp_string(the_result, strlen(the_result)); + DEREF(t1); + DEREF(t2); + return make_string(the_result, strlen(the_result)); } /* do_bindtextdomain --- set the directory for a text domain */ @@ -3348,9 +2369,9 @@ do_dcngettext(NODE *tree) */ NODE * -do_bindtextdomain(NODE *tree) +do_bindtextdomain(int nargs) { - NODE *tmp, *t1, *t2; + NODE *t1, *t2; char *directory, *domain; char *the_result; @@ -3359,87 +2380,21 @@ do_bindtextdomain(NODE *tree) 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)); + if (nargs == 2) { /* second argument */ + t2 = POP_STRING(); domain = t2->stptr; } + /* first argument */ + t1 = POP_STRING(); + if (t1->stlen > 0) + directory = t1->stptr; + the_result = bindtextdomain(domain, directory); - free_temp(t1); + DEREF(t1); if (t2 != NULL) - free_temp(t2); - - return tmp_string(the_result, strlen(the_result)); -} - -/* mbc_byte_count --- return number of bytes for corresponding numchars multibyte characters */ - -static size_t -mbc_byte_count(const char *ptr, size_t numchars) -{ -#ifdef MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - memset(& cur_state, 0, sizeof(cur_state)); - - assert(gawk_mb_cur_max > 1); - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numchars; /* no valid m.b. char */ - - for (; numchars > 0; numchars--) { - mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum += mb_len; - ptr += mb_len; - } - - return sum; -#else - return numchars; -#endif -} - -/* mbc_char_count --- return number of m.b. chars in string, up to numbytes bytes */ - -static size_t -mbc_char_count(const char *ptr, size_t numbytes) -{ -#ifdef MBS_SUPPORT - mbstate_t cur_state; - size_t sum = 0; - int mb_len; - - memset(& cur_state, 0, sizeof(cur_state)); + DEREF(t2); - if (gawk_mb_cur_max == 1) - return numbytes; - - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - return numbytes; /* no valid m.b. char */ - - for (; numbytes > 0; numbytes--) { - mb_len = mbrlen(ptr, numbytes * gawk_mb_cur_max, &cur_state); - if (mb_len <= 0) - break; - sum++; - ptr += mb_len; - } - - return sum; -#else - return numbytes; -#endif + return make_string(the_result, strlen(the_result)); } |