Blame builtin.c

Packit 575503
/*
Packit 575503
 * builtin.c - Builtin functions and various utility procedures.
Packit 575503
 */
Packit 575503
Packit 575503
/*
Packit 575503
 * Copyright (C) 1986, 1988, 1989, 1991-2018 the Free Software Foundation, Inc.
Packit 575503
 *
Packit 575503
 * This file is part of GAWK, the GNU implementation of the
Packit 575503
 * AWK Programming Language.
Packit 575503
 *
Packit 575503
 * GAWK is free software; you can redistribute it and/or modify
Packit 575503
 * it under the terms of the GNU General Public License as published by
Packit 575503
 * the Free Software Foundation; either version 3 of the License, or
Packit 575503
 * (at your option) any later version.
Packit 575503
 *
Packit 575503
 * GAWK is distributed in the hope that it will be useful,
Packit 575503
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 575503
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 575503
 * GNU General Public License for more details.
Packit 575503
 *
Packit 575503
 * You should have received a copy of the GNU General Public License
Packit 575503
 * along with this program; if not, write to the Free Software
Packit 575503
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
Packit 575503
 */
Packit 575503
Packit 575503
Packit 575503
#include "awk.h"
Packit 575503
#if defined(HAVE_FCNTL_H)
Packit 575503
#include <fcntl.h>
Packit 575503
#endif
Packit 575503
#include <math.h>
Packit 575503
#include "random.h"
Packit 575503
#include "floatmagic.h"
Packit 575503
Packit 575503
#if defined(HAVE_POPEN_H)
Packit 575503
#include "popen.h"
Packit 575503
#endif
Packit 575503
Packit 575503
#ifndef CHAR_BIT
Packit 575503
# define CHAR_BIT 8
Packit 575503
#endif
Packit 575503
Packit 575503
/* The extra casts work around common compiler bugs.  */
Packit 575503
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
Packit 575503
/* Note:  these assume that negative integers are represented internally
Packit 575503
   via 2's complement, which is not mandated by C.  They also ignore the
Packit 575503
   fact that signed integer arithmetic overflow can trigger exceptions,
Packit 575503
   unlike unsigned which is guaranteed not to do so. */
Packit 575503
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
Packit 575503
			      ? ~ (uintmax_t) 0 << (sizeof (t) * CHAR_BIT - 1) \
Packit 575503
			      : 0))
Packit 575503
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
Packit 575503
Packit 575503
#ifndef INTMAX_MIN
Packit 575503
# define INTMAX_MIN TYPE_MINIMUM (intmax_t)
Packit 575503
#endif
Packit 575503
#ifndef UINTMAX_MAX
Packit 575503
# define UINTMAX_MAX TYPE_MAXIMUM (uintmax_t)
Packit 575503
#endif
Packit 575503
Packit 575503
#ifndef SIZE_MAX	/* C99 constant, can't rely on it everywhere */
Packit 575503
#define SIZE_MAX ((size_t) -1)
Packit 575503
#endif
Packit 575503
Packit 575503
#define DEFAULT_G_PRECISION 6
Packit 575503
Packit 575503
static size_t mbc_byte_count(const char *ptr, size_t numchars);
Packit 575503
static size_t mbc_char_count(const char *ptr, size_t numbytes);
Packit 575503
Packit 575503
/* Can declare these, since we always use the random shipped with gawk */
Packit 575503
extern char *initstate(unsigned long seed, char *state, long n);
Packit 575503
extern char *setstate(char *state);
Packit 575503
extern long random(void);
Packit 575503
extern void srandom(unsigned long seed);
Packit 575503
Packit 575503
extern NODE **args_array;
Packit 575503
extern int max_args;
Packit 575503
extern NODE **fields_arr;
Packit 575503
extern bool output_is_tty;
Packit 575503
extern FILE *output_fp;
Packit 575503
Packit 575503
Packit 575503
#define POP_TWO_SCALARS(s1, s2) \
Packit 575503
s2 = POP_SCALAR(); \
Packit 575503
s1 = POP(); \
Packit 575503
do { if (s1->type == Node_var_array) { \
Packit 575503
DEREF(s2); \
Packit 575503
fatal(_("attempt to use array `%s' in a scalar context"), array_vname(s1)); \
Packit 575503
}} while (false)
Packit 575503
Packit 575503
Packit 575503
/*
Packit 575503
 * Since we supply the version of random(), we know what
Packit 575503
 * value to use here.
Packit 575503
 */
Packit 575503
#define GAWK_RANDOM_MAX 0x7fffffffL
Packit 575503
Packit 575503
/* efwrite --- like fwrite, but with error checking */
Packit 575503
Packit 575503
static void
Packit 575503
efwrite(const void *ptr,
Packit 575503
	size_t size,
Packit 575503
	size_t count,
Packit 575503
	FILE *fp,
Packit 575503
	const char *from,
Packit 575503
	struct redirect *rp,
Packit 575503
	bool flush)
Packit 575503
{
Packit 575503
	errno = 0;
Packit 575503
	if (rp != NULL) {
Packit 575503
		if (rp->output.gawk_fwrite(ptr, size, count, fp, rp->output.opaque) != count)
Packit 575503
			goto wrerror;
Packit 575503
	} else if (fwrite(ptr, size, count, fp) != count)
Packit 575503
		goto wrerror;
Packit 575503
	if (flush
Packit 575503
	  && ((fp == stdout && output_is_tty)
Packit 575503
	      || (rp != NULL && (rp->flag & RED_NOBUF) != 0))) {
Packit 575503
		if (rp != NULL) {
Packit 575503
			rp->output.gawk_fflush(fp, rp->output.opaque);
Packit 575503
			if (rp->output.gawk_ferror(fp, rp->output.opaque))
Packit 575503
				goto wrerror;
Packit 575503
		} else {
Packit 575503
			fflush(fp);
Packit 575503
			if (ferror(fp))
Packit 575503
				goto wrerror;
Packit 575503
		}
Packit 575503
	}
Packit 575503
	return;
Packit 575503
Packit 575503
wrerror:
Packit 575503
#ifdef __MINGW32__
Packit 575503
	if (errno == 0 || errno == EINVAL)
Packit 575503
		w32_maybe_set_errno();
Packit 575503
#endif
Packit 575503
	/* for stdout, die with a real SIGPIPE, like other awks */
Packit 575503
	if (fp == stdout && errno == EPIPE)
Packit 575503
		die_via_sigpipe();
Packit 575503
Packit 575503
	/* otherwise die verbosely */
Packit 575503
	if ((rp != NULL) ? is_non_fatal_redirect(rp->value, strlen(rp->value)) : is_non_fatal_std(fp))
Packit 575503
		update_ERRNO_int(errno);
Packit 575503
	else
Packit 575503
		fatal(_("%s to \"%s\" failed (%s)"), from,
Packit 575503
			rp != NULL
Packit 575503
				? rp->value
Packit 575503
				: fp == stdout
Packit 575503
					? _("standard output")
Packit 575503
					: _("standard error"),
Packit 575503
			errno ? strerror(errno) : _("reason unknown"));
Packit 575503
}
Packit 575503
Packit 575503
/* do_exp --- exponential function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_exp(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d, res;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("exp: received non-numeric argument"));
Packit 575503
	d = force_number(tmp)->numbr;
Packit 575503
	DEREF(tmp);
Packit 575503
	errno = 0;
Packit 575503
	res = exp(d);
Packit 575503
	if (errno == ERANGE)
Packit 575503
		warning(_("exp: argument %g is out of range"), d);
Packit 575503
	return make_number((AWKNUM) res);
Packit 575503
}
Packit 575503
Packit 575503
/* stdfile --- return fp for a standard file */
Packit 575503
Packit 575503
/*
Packit 575503
 * This function allows `fflush("/dev/stdout")' to work.
Packit 575503
 * The other files will be available via getredirect().
Packit 575503
 * /dev/stdin is not included, since fflush is only for output.
Packit 575503
 */
Packit 575503
Packit 575503
static FILE *
Packit 575503
stdfile(const char *name, size_t len)
Packit 575503
{
Packit 575503
	if (len == 11) {
Packit 575503
		if (strncmp(name, "/dev/stderr", 11) == 0)
Packit 575503
			return stderr;
Packit 575503
		else if (strncmp(name, "/dev/stdout", 11) == 0)
Packit 575503
			return stdout;
Packit 575503
	}
Packit 575503
Packit 575503
	return NULL;
Packit 575503
}
Packit 575503
Packit 575503
/* do_fflush --- flush output, either named file or pipe or everything */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_fflush(int nargs)
Packit 575503
{
Packit 575503
	struct redirect *rp;
Packit 575503
	NODE *tmp;
Packit 575503
	FILE *fp;
Packit 575503
	int status = 0;
Packit 575503
	const char *file;
Packit 575503
	int len;
Packit 575503
Packit 575503
	/*
Packit 575503
	 * November, 2012.
Packit 575503
	 * It turns out that circa 2002, when BWK
Packit 575503
	 * added fflush() and fflush("") to his awk, he made both of
Packit 575503
	 * them flush everything.
Packit 575503
	 *
Packit 575503
	 * Now, with our inside agent getting ready to try to get fflush()
Packit 575503
	 * standardized in POSIX, we are going to make our awk consistent
Packit 575503
	 * with his.  This should not really affect anyone, as flushing
Packit 575503
	 * everything also flushes stdout.
Packit 575503
	 *
Packit 575503
	 * So. Once upon a time:
Packit 575503
	 * 	fflush()	--- flush stdout
Packit 575503
	 * 	fflush("")	--- flush everything
Packit 575503
	 * Now, both calls flush everything.
Packit 575503
	 */
Packit 575503
Packit 575503
	/* fflush() */
Packit 575503
	if (nargs == 0) {
Packit 575503
		status = flush_io();	// ERRNO updated
Packit 575503
		return make_number((AWKNUM) status);
Packit 575503
	}
Packit 575503
Packit 575503
	tmp = POP_STRING();
Packit 575503
	file = tmp->stptr;
Packit 575503
	len = tmp->stlen;
Packit 575503
Packit 575503
	/* fflush("") */
Packit 575503
	if (tmp->stlen == 0) {
Packit 575503
		status = flush_io();	// ERRNO updated
Packit 575503
		DEREF(tmp);
Packit 575503
		return make_number((AWKNUM) status);
Packit 575503
	}
Packit 575503
Packit 575503
	/* fflush("/some/path") */
Packit 575503
	rp = getredirect(tmp->stptr, tmp->stlen);
Packit 575503
	status = -1;
Packit 575503
	if (rp != NULL) {
Packit 575503
		if ((rp->flag & (RED_WRITE|RED_APPEND)) == 0) {
Packit 575503
			if ((rp->flag & RED_PIPE) != 0)
Packit 575503
				warning(_("fflush: cannot flush: pipe `%.*s' opened for reading, not writing"),
Packit 575503
					len, file);
Packit 575503
			else
Packit 575503
				warning(_("fflush: cannot flush: file `%.*s' opened for reading, not writing"),
Packit 575503
					len, file);
Packit 575503
			DEREF(tmp);
Packit 575503
			return make_number((AWKNUM) status);
Packit 575503
		}
Packit 575503
		fp = rp->output.fp;
Packit 575503
		if (fp != NULL) {
Packit 575503
			status = rp->output.gawk_fflush(fp, rp->output.opaque);
Packit 575503
Packit 575503
			if (status != 0) {
Packit 575503
				if (! is_non_fatal_redirect(tmp->stptr, tmp->stlen))
Packit 575503
					fatal(_("fflush: cannot flush file `%.*s': %s"),
Packit 575503
						len, file, strerror(errno));
Packit 575503
				update_ERRNO_int(errno);
Packit 575503
			}
Packit 575503
		} else if ((rp->flag & RED_TWOWAY) != 0)
Packit 575503
				warning(_("fflush: cannot flush: two-way pipe `%.*s' has closed write end"),
Packit 575503
					len, file);
Packit 575503
	} else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) {
Packit 575503
		status = (non_fatal_flush_std_file(fp) == false);
Packit 575503
	} else {
Packit 575503
		status = -1;
Packit 575503
		warning(_("fflush: `%.*s' is not an open file, pipe or co-process"), len, file);
Packit 575503
	}
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) status);
Packit 575503
}
Packit 575503
Packit 575503
/* strncasecmpmbs --- like strncasecmp (multibyte string version)  */
Packit 575503
Packit 575503
int
Packit 575503
strncasecmpmbs(const unsigned char *s1, const unsigned char *s2, size_t n)
Packit 575503
{
Packit 575503
	size_t i1, i2, mbclen1, mbclen2, gap;
Packit 575503
	wchar_t wc1, wc2;
Packit 575503
	mbstate_t mbs1, mbs2;
Packit 575503
Packit 575503
	memset(& mbs1, 0, sizeof(mbs1));
Packit 575503
	memset(& mbs2, 0, sizeof(mbs2));
Packit 575503
Packit 575503
	for (i1 = i2 = 0 ; i1 < n && i2 < n ;i1 += mbclen1, i2 += mbclen2) {
Packit 575503
		if (is_valid_character(s1[i1])) {
Packit 575503
			mbclen1 = 1;
Packit 575503
			wc1 = btowc_cache(s1[i1]);
Packit 575503
		} else {
Packit 575503
			mbclen1 = mbrtowc(& wc1, (const char *)s1 + i1,
Packit 575503
					  n - i1, & mbs1);
Packit 575503
			if (mbclen1 == (size_t) -1 || mbclen1 == (size_t) -2 || mbclen1 == 0) {
Packit 575503
				/* We treat it as a singlebyte character. */
Packit 575503
				mbclen1 = 1;
Packit 575503
				wc1 = btowc_cache(s1[i1]);
Packit 575503
			}
Packit 575503
		}
Packit 575503
		if (is_valid_character(s2[i2])) {
Packit 575503
			mbclen2 = 1;
Packit 575503
			wc2 = btowc_cache(s2[i2]);
Packit 575503
		} else {
Packit 575503
			mbclen2 = mbrtowc(& wc2, (const char *)s2 + i2,
Packit 575503
					  n - i2, & mbs2);
Packit 575503
			if (mbclen2 == (size_t) -1 || mbclen2 == (size_t) -2 || mbclen2 == 0) {
Packit 575503
				/* We treat it as a singlebyte character. */
Packit 575503
				mbclen2 = 1;
Packit 575503
				wc2 = btowc_cache(s2[i2]);
Packit 575503
			}
Packit 575503
		}
Packit 575503
		if ((gap = towlower(wc1) - towlower(wc2)) != 0)
Packit 575503
			/* s1 and s2 are not equivalent. */
Packit 575503
			return gap;
Packit 575503
	}
Packit 575503
	/* s1 and s2 are equivalent. */
Packit 575503
	return 0;
Packit 575503
}
Packit 575503
Packit 575503
/* Inspect the buffer `src' and write the index of each byte to `dest'.
Packit 575503
   Caller must allocate `dest'.
Packit 575503
   e.g. str = <mb1(1)>, <mb1(2)>, a, b, <mb2(1)>, <mb2(2)>, <mb2(3)>, c
Packit 575503
        where mb(i) means the `i'-th byte of a multibyte character.
Packit 575503
		dest =       1,        2, 1, 1,        1,        2,        3. 1
Packit 575503
*/
Packit 575503
static void
Packit 575503
index_multibyte_buffer(char* src, char* dest, int len)
Packit 575503
{
Packit 575503
	int idx, prev_idx;
Packit 575503
	mbstate_t mbs, prevs;
Packit 575503
Packit 575503
	memset(& prevs, 0, sizeof(mbstate_t));
Packit 575503
	for (idx = prev_idx = 0 ; idx < len ; idx++) {
Packit 575503
		size_t mbclen;
Packit 575503
		mbs = prevs;
Packit 575503
		mbclen = mbrlen(src + prev_idx, idx - prev_idx + 1, & mbs);
Packit 575503
		if (mbclen == (size_t) -1 || mbclen == 1 || mbclen == 0) {
Packit 575503
			/* singlebyte character.  */
Packit 575503
			mbclen = 1;
Packit 575503
			prev_idx = idx + 1;
Packit 575503
		} else if (mbclen == (size_t) -2) {
Packit 575503
			/* a part of a multibyte character.  */
Packit 575503
			mbclen = idx - prev_idx + 1;
Packit 575503
		} else if (mbclen > 1) {
Packit 575503
			/* the end of a multibyte character.  */
Packit 575503
			prev_idx = idx + 1;
Packit 575503
			prevs = mbs;
Packit 575503
		} else {
Packit 575503
			/* Can't reach.  */
Packit 575503
		}
Packit 575503
		dest[idx] = mbclen;
Packit 575503
    }
Packit 575503
}
Packit 575503
Packit 575503
/* do_index --- find index of a string */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_index(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1, *s2;
Packit 575503
	const char *p1, *p2;
Packit 575503
	size_t l1, l2;
Packit 575503
	long ret;
Packit 575503
	bool do_single_byte = false;
Packit 575503
	mbstate_t mbs1, mbs2;
Packit 575503
Packit 575503
	if (gawk_mb_cur_max > 1) {
Packit 575503
		memset(& mbs1, 0, sizeof(mbstate_t));
Packit 575503
		memset(& mbs2, 0, sizeof(mbstate_t));
Packit 575503
	}
Packit 575503
Packit 575503
	POP_TWO_SCALARS(s1, s2);
Packit 575503
Packit 575503
	if (do_lint) {
Packit 575503
		if ((fixtype(s1)->flags & STRING) == 0)
Packit 575503
			lintwarn(_("index: received non-string first argument"));
Packit 575503
		if ((fixtype(s2)->flags & STRING) == 0)
Packit 575503
			lintwarn(_("index: received non-string second argument"));
Packit 575503
	}
Packit 575503
Packit 575503
	s1 = force_string(s1);
Packit 575503
	s2 = force_string(s2);
Packit 575503
Packit 575503
	p1 = s1->stptr;
Packit 575503
	p2 = s2->stptr;
Packit 575503
	l1 = s1->stlen;
Packit 575503
	l2 = s2->stlen;
Packit 575503
	ret = 0;
Packit 575503
Packit 575503
	/*
Packit 575503
	 * Icky special case, index(foo, "") should return 1,
Packit 575503
	 * since both bwk awk and mawk do, and since match("foo", "")
Packit 575503
	 * returns 1. This makes index("", "") work, too, fwiw.
Packit 575503
	 */
Packit 575503
	if (l2 == 0) {
Packit 575503
		ret = 1;
Packit 575503
		goto out;
Packit 575503
	}
Packit 575503
Packit 575503
	if (gawk_mb_cur_max > 1) {
Packit 575503
		s1 = force_wstring(s1);
Packit 575503
		s2 = force_wstring(s2);
Packit 575503
		/*
Packit 575503
		 * If we don't have valid wide character strings, use
Packit 575503
		 * the real bytes.
Packit 575503
		 */
Packit 575503
		do_single_byte = ((s1->wstlen == 0 && s1->stlen > 0)
Packit 575503
					|| (s2->wstlen == 0 && s2->stlen > 0));
Packit 575503
	}
Packit 575503
Packit 575503
	/* IGNORECASE will already be false if posix */
Packit 575503
	if (IGNORECASE) {
Packit 575503
		while (l1 > 0) {
Packit 575503
			if (l2 > l1)
Packit 575503
				break;
Packit 575503
			if (! do_single_byte && gawk_mb_cur_max > 1) {
Packit 575503
				const wchar_t *pos;
Packit 575503
Packit 575503
				pos = wcasestrstr(s1->wstptr, s1->wstlen, s2->wstptr, s2->wstlen);
Packit 575503
				if (pos == NULL)
Packit 575503
					ret = 0;
Packit 575503
				else
Packit 575503
					ret = pos - s1->wstptr + 1;	/* 1-based */
Packit 575503
				goto out;
Packit 575503
			} else {
Packit 575503
				/*
Packit 575503
				 * Could use tolower(*p1) == tolower(*p2) here.
Packit 575503
				 * See discussion in eval.c as to why not.
Packit 575503
				 */
Packit 575503
				if (casetable[(unsigned char)*p1] == casetable[(unsigned char)*p2]
Packit 575503
				    && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) {
Packit 575503
					ret = 1 + s1->stlen - l1;
Packit 575503
					break;
Packit 575503
				}
Packit 575503
				l1--;
Packit 575503
				p1++;
Packit 575503
			}
Packit 575503
		}
Packit 575503
	} else {
Packit 575503
		while (l1 > 0) {
Packit 575503
			if (l2 > l1)
Packit 575503
				break;
Packit 575503
			if (*p1 == *p2
Packit 575503
			    && (l2 == 1 || (l2 > 0 && memcmp(p1, p2, l2) == 0))) {
Packit 575503
				ret = 1 + s1->stlen - l1;
Packit 575503
				break;
Packit 575503
			}
Packit 575503
			if (! do_single_byte && gawk_mb_cur_max > 1) {
Packit 575503
				const wchar_t *pos;
Packit 575503
Packit 575503
				pos = wstrstr(s1->wstptr, s1->wstlen, s2->wstptr, s2->wstlen);
Packit 575503
				if (pos == NULL)
Packit 575503
					ret = 0;
Packit 575503
				else
Packit 575503
					ret = pos - s1->wstptr + 1;	/* 1-based */
Packit 575503
				goto out;
Packit 575503
			} else {
Packit 575503
				l1--;
Packit 575503
				p1++;
Packit 575503
			}
Packit 575503
		}
Packit 575503
	}
Packit 575503
out:
Packit 575503
	DEREF(s1);
Packit 575503
	DEREF(s2);
Packit 575503
	return make_number((AWKNUM) ret);
Packit 575503
}
Packit 575503
Packit 575503
/* double_to_int --- convert double to int, used several places */
Packit 575503
Packit 575503
double
Packit 575503
double_to_int(double d)
Packit 575503
{
Packit 575503
	if (d >= 0)
Packit 575503
		d = floor(d);
Packit 575503
	else
Packit 575503
		d = ceil(d);
Packit 575503
	return d;
Packit 575503
}
Packit 575503
Packit 575503
/* do_int --- convert double to int for awk */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_int(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("int: received non-numeric argument"));
Packit 575503
	d = force_number(tmp)->numbr;
Packit 575503
	d = double_to_int(d);
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) d);
Packit 575503
}
Packit 575503
Packit 575503
/* do_isarray --- check if argument is array */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_isarray(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	int ret = 1;
Packit 575503
Packit 575503
	tmp = POP();
Packit 575503
	if (tmp->type != Node_var_array) {
Packit 575503
		ret = 0;
Packit 575503
		// could be Node_var_new
Packit 575503
		if (tmp->type == Node_val)
Packit 575503
			DEREF(tmp);
Packit 575503
	}
Packit 575503
	return make_number((AWKNUM) ret);
Packit 575503
}
Packit 575503
Packit 575503
/* do_length --- length of a string, array or $0 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_length(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	size_t len;
Packit 575503
Packit 575503
	tmp = POP();
Packit 575503
	if (tmp->type == Node_var_array) {
Packit 575503
		static bool warned = false;
Packit 575503
		unsigned long size;
Packit 575503
Packit 575503
		if (do_posix)
Packit 575503
			fatal(_("length: received array argument"));
Packit 575503
   		if (do_lint && ! warned) {
Packit 575503
			warned = true;
Packit 575503
			lintwarn(_("`length(array)' is a gawk extension"));
Packit 575503
		}
Packit 575503
Packit 575503
		/*
Packit 575503
		 * Support for deferred loading of array elements requires that
Packit 575503
		 * we use the array length interface even though it isn't
Packit 575503
		 * necessary for the built-in array types.
Packit 575503
		 *
Packit 575503
		 * 1/2015: The deferred arrays are gone, but this is probably
Packit 575503
		 * still a good idea.
Packit 575503
		 */
Packit 575503
Packit 575503
		size = assoc_length(tmp);
Packit 575503
		return make_number(size);
Packit 575503
	}
Packit 575503
Packit 575503
	assert(tmp->type == Node_val);
Packit 575503
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & STRING) == 0)
Packit 575503
		lintwarn(_("length: received non-string argument"));
Packit 575503
	tmp = force_string(tmp);
Packit 575503
Packit 575503
	if (gawk_mb_cur_max > 1) {
Packit 575503
		tmp = force_wstring(tmp);
Packit 575503
		len = tmp->wstlen;
Packit 575503
		/*
Packit 575503
		 * If the bytes don't make a valid wide character
Packit 575503
		 * string, fall back to the bytes themselves.
Packit 575503
		 */
Packit 575503
		 if (len == 0 && tmp->stlen > 0)
Packit 575503
			 len = tmp->stlen;
Packit 575503
	} else
Packit 575503
		len = tmp->stlen;
Packit 575503
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) len);
Packit 575503
}
Packit 575503
Packit 575503
/* do_log --- the log function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_log(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d, arg;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("log: received non-numeric argument"));
Packit 575503
	arg = force_number(tmp)->numbr;
Packit 575503
	if (arg < 0.0)
Packit 575503
		warning(_("log: received negative argument %g"), arg);
Packit 575503
	d = log(arg);
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) d);
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
Packit 575503
/*
Packit 575503
 * mpz2mpfr --- convert an arbitrary-precision integer to a float
Packit 575503
 *	without any loss of precision. The returned value is only
Packit 575503
 * 	good for temporary use.
Packit 575503
 */
Packit 575503
Packit 575503
Packit 575503
static mpfr_ptr
Packit 575503
mpz2mpfr(mpz_ptr zi)
Packit 575503
{
Packit 575503
	size_t prec;
Packit 575503
	static mpfr_t mpfrval;
Packit 575503
	static bool inited = false;
Packit 575503
	int tval;
Packit 575503
Packit 575503
	/* estimate minimum precision for exact conversion */
Packit 575503
	prec = mpz_sizeinbase(zi, 2);	/* most significant 1 bit position starting at 1 */
Packit 575503
	prec -= (size_t) mpz_scan1(zi, 0);	/* least significant 1 bit index starting at 0 */
Packit 575503
	if (prec < MPFR_PREC_MIN)
Packit 575503
		prec = MPFR_PREC_MIN;
Packit 575503
	else if (prec > MPFR_PREC_MAX)
Packit 575503
		prec = MPFR_PREC_MAX;
Packit 575503
Packit 575503
	if (! inited) {
Packit 575503
		mpfr_init2(mpfrval, prec);
Packit 575503
		inited = true;
Packit 575503
	} else
Packit 575503
		mpfr_set_prec(mpfrval, prec);
Packit 575503
	tval = mpfr_set_z(mpfrval, zi, ROUND_MODE);
Packit 575503
	IEEE_FMT(mpfrval, tval);
Packit 575503
	return mpfrval;
Packit 575503
}
Packit 575503
#endif
Packit 575503
Packit 575503
/*
Packit 575503
 * format_tree() formats arguments of sprintf,
Packit 575503
 * and accordingly to a fmt_string providing a format like in
Packit 575503
 * printf family from C library.  Returns a string node which value
Packit 575503
 * is a formatted string.  Called by  sprintf function.
Packit 575503
 *
Packit 575503
 * It is one of the uglier parts of gawk.  Thanks to Michal Jaegermann
Packit 575503
 * for taming this beast and making it compatible with ANSI C.
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
format_tree(
Packit 575503
	const char *fmt_string,
Packit 575503
	size_t n0,
Packit 575503
	NODE **the_args,
Packit 575503
	long num_args)
Packit 575503
{
Packit 575503
/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
Packit 575503
/* difference of pointers should be of ptrdiff_t type, but let us be kind */
Packit 575503
#define bchunk(s, l) if (l) { \
Packit 575503
	while ((l) > ofre) { \
Packit 575503
		size_t olen = obufout - obuf; \
Packit 575503
		erealloc(obuf, char *, osiz * 2, "format_tree"); \
Packit 575503
		ofre += osiz; \
Packit 575503
		osiz *= 2; \
Packit 575503
		obufout = obuf + olen; \
Packit 575503
	} \
Packit 575503
	memcpy(obufout, s, (size_t) (l)); \
Packit 575503
	obufout += (l); \
Packit 575503
	ofre -= (l); \
Packit 575503
}
Packit 575503
Packit 575503
/* copy one byte from 's' to 'obufout' checking for space in the process */
Packit 575503
#define bchunk_one(s) { \
Packit 575503
	if (ofre < 1) { \
Packit 575503
		size_t olen = obufout - obuf; \
Packit 575503
		erealloc(obuf, char *, osiz * 2, "format_tree"); \
Packit 575503
		ofre += osiz; \
Packit 575503
		osiz *= 2; \
Packit 575503
		obufout = obuf + olen; \
Packit 575503
	} \
Packit 575503
	*obufout++ = *s; \
Packit 575503
	--ofre; \
Packit 575503
}
Packit 575503
Packit 575503
/* Is there space for something L big in the buffer? */
Packit 575503
#define chksize(l)  if ((l) >= ofre) { \
Packit 575503
	size_t olen = obufout - obuf; \
Packit 575503
	size_t delta = osiz+l-ofre; \
Packit 575503
	erealloc(obuf, char *, osiz + delta, "format_tree"); \
Packit 575503
	obufout = obuf + olen; \
Packit 575503
	ofre += delta; \
Packit 575503
	osiz += delta; \
Packit 575503
}
Packit 575503
Packit 575503
	size_t cur_arg = 0;
Packit 575503
	NODE *r = NULL;
Packit 575503
	int i, nc;
Packit 575503
	bool toofew = false;
Packit 575503
	char *obuf, *obufout;
Packit 575503
	size_t osiz, ofre, olen_final;
Packit 575503
	const char *chbuf;
Packit 575503
	const char *s0, *s1;
Packit 575503
	int cs1;
Packit 575503
	NODE *arg;
Packit 575503
	long fw, prec, argnum;
Packit 575503
	bool used_dollar;
Packit 575503
	bool lj, alt, big_flag, bigbig_flag, small_flag, have_prec, need_format;
Packit 575503
	long *cur = NULL;
Packit 575503
	uintmax_t uval;
Packit 575503
	bool sgn;
Packit 575503
	int base;
Packit 575503
	/*
Packit 575503
	 * Although this is an array, the elements serve two different
Packit 575503
	 * purposes. The first element is the general buffer meant
Packit 575503
	 * to hold the entire result string.  The second one is a
Packit 575503
	 * temporary buffer for large floating point values. They
Packit 575503
	 * could just as easily be separate variables, and the
Packit 575503
	 * code might arguably be clearer.
Packit 575503
	 */
Packit 575503
	struct {
Packit 575503
		char *buf;
Packit 575503
		size_t bufsize;
Packit 575503
		char stackbuf[30];
Packit 575503
	} cpbufs[2];
Packit 575503
#define cpbuf	cpbufs[0].buf
Packit 575503
	char *cend = &cpbufs[0].stackbuf[sizeof(cpbufs[0].stackbuf)];
Packit 575503
	char *cp;
Packit 575503
	const char *fill;
Packit 575503
	AWKNUM tmpval = 0.0;
Packit 575503
	char signchar = '\0';
Packit 575503
	size_t len;
Packit 575503
	bool zero_flag = false;
Packit 575503
	bool quote_flag = false;
Packit 575503
	int ii, jj;
Packit 575503
	char *chp;
Packit 575503
	size_t copy_count, char_count;
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
	mpz_ptr zi;
Packit 575503
	mpfr_ptr mf;
Packit 575503
#endif
Packit 575503
	enum { MP_NONE = 0, MP_INT_WITH_PREC = 1, MP_INT_WITHOUT_PREC, MP_FLOAT } fmt_type;
Packit 575503
Packit 575503
	static const char sp[] = " ";
Packit 575503
	static const char zero_string[] = "0";
Packit 575503
	static const char lchbuf[] = "0123456789abcdef";
Packit 575503
	static const char Uchbuf[] = "0123456789ABCDEF";
Packit 575503
Packit 575503
#define INITIAL_OUT_SIZE	512
Packit 575503
	emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
Packit 575503
	obufout = obuf;
Packit 575503
	osiz = INITIAL_OUT_SIZE;
Packit 575503
	ofre = osiz - 1;
Packit 575503
Packit 575503
	cur_arg = 1;
Packit 575503
Packit 575503
	{
Packit 575503
		size_t k;
Packit 575503
		for (k = 0; k < sizeof(cpbufs)/sizeof(cpbufs[0]); k++) {
Packit 575503
			cpbufs[k].bufsize = sizeof(cpbufs[k].stackbuf);
Packit 575503
			cpbufs[k].buf = cpbufs[k].stackbuf;
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	/*
Packit 575503
	 * The point of this goop is to grow the buffer
Packit 575503
	 * holding the converted number, so that large
Packit 575503
	 * values don't overflow a fixed length buffer.
Packit 575503
	 */
Packit 575503
#define PREPEND(CH) do {	\
Packit 575503
	if (cp == cpbufs[0].buf) {	\
Packit 575503
		char *prev = cpbufs[0].buf;	\
Packit 575503
		emalloc(cpbufs[0].buf, char *, 2*cpbufs[0].bufsize, \
Packit 575503
		 	"format_tree");	\
Packit 575503
		memcpy((cp = cpbufs[0].buf+cpbufs[0].bufsize), prev,	\
Packit 575503
		       cpbufs[0].bufsize);	\
Packit 575503
		cpbufs[0].bufsize *= 2;	\
Packit 575503
		if (prev != cpbufs[0].stackbuf)	\
Packit 575503
			efree(prev);	\
Packit 575503
		cend = cpbufs[0].buf+cpbufs[0].bufsize;	\
Packit 575503
	}	\
Packit 575503
	*--cp = (CH);	\
Packit 575503
} while(0)
Packit 575503
Packit 575503
	/*
Packit 575503
	 * Check first for use of `count$'.
Packit 575503
	 * If plain argument retrieval was used earlier, choke.
Packit 575503
	 *	Otherwise, return the requested argument.
Packit 575503
	 * If not `count$' now, but it was used earlier, choke.
Packit 575503
	 * If this format is more than total number of args, choke.
Packit 575503
	 * Otherwise, return the current argument.
Packit 575503
	 */
Packit 575503
#define parse_next_arg() { \
Packit 575503
	if (argnum > 0) { \
Packit 575503
		if (cur_arg > 1) { \
Packit 575503
			msg(_("fatal: must use `count$' on all formats or none")); \
Packit 575503
			goto out; \
Packit 575503
		} \
Packit 575503
		arg = the_args[argnum]; \
Packit 575503
	} else if (used_dollar) { \
Packit 575503
		msg(_("fatal: must use `count$' on all formats or none")); \
Packit 575503
		arg = 0; /* shutup the compiler */ \
Packit 575503
		goto out; \
Packit 575503
	} else if (cur_arg >= num_args) { \
Packit 575503
		arg = 0; /* shutup the compiler */ \
Packit 575503
		toofew = true; \
Packit 575503
		break; \
Packit 575503
	} else { \
Packit 575503
		arg = the_args[cur_arg]; \
Packit 575503
		cur_arg++; \
Packit 575503
	} \
Packit 575503
}
Packit 575503
Packit 575503
	need_format = false;
Packit 575503
	used_dollar = false;
Packit 575503
Packit 575503
	s0 = s1 = fmt_string;
Packit 575503
	while (n0-- > 0) {
Packit 575503
		if (*s1 != '%') {
Packit 575503
			s1++;
Packit 575503
			continue;
Packit 575503
		}
Packit 575503
		need_format = true;
Packit 575503
		bchunk(s0, s1 - s0);
Packit 575503
		s0 = s1;
Packit 575503
		cur = &fw;
Packit 575503
		fw = 0;
Packit 575503
		prec = 0;
Packit 575503
		base = 0;
Packit 575503
		argnum = 0;
Packit 575503
		base = 0;
Packit 575503
		have_prec = false;
Packit 575503
		signchar = '\0';
Packit 575503
		zero_flag = false;
Packit 575503
		quote_flag = false;
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
		mf = NULL;
Packit 575503
		zi = NULL;
Packit 575503
#endif
Packit 575503
		fmt_type = MP_NONE;
Packit 575503
Packit 575503
		lj = alt = big_flag = bigbig_flag = small_flag = false;
Packit 575503
		fill = sp;
Packit 575503
		cp = cend;
Packit 575503
		chbuf = lchbuf;
Packit 575503
		s1++;
Packit 575503
Packit 575503
retry:
Packit 575503
		if (n0-- == 0)	/* ran out early! */
Packit 575503
			break;
Packit 575503
Packit 575503
		switch (cs1 = *s1++) {
Packit 575503
		case (-1):	/* dummy case to allow for checking */
Packit 575503
check_pos:
Packit 575503
			if (cur != &fw)
Packit 575503
				break;		/* reject as a valid format */
Packit 575503
			goto retry;
Packit 575503
		case '%':
Packit 575503
			need_format = false;
Packit 575503
			/*
Packit 575503
			 * 29 Oct. 2002:
Packit 575503
			 * The C99 standard pages 274 and 279 seem to imply that
Packit 575503
			 * since there's no arg converted, the field width doesn't
Packit 575503
			 * apply.  The code already was that way, but this
Packit 575503
			 * comment documents it, at least in the code.
Packit 575503
			 */
Packit 575503
			if (do_lint) {
Packit 575503
				const char *msg = NULL;
Packit 575503
Packit 575503
				if (fw && ! have_prec)
Packit 575503
					msg = _("field width is ignored for `%%' specifier");
Packit 575503
				else if (fw == 0 && have_prec)
Packit 575503
					msg = _("precision is ignored for `%%' specifier");
Packit 575503
				else if (fw && have_prec)
Packit 575503
					msg = _("field width and precision are ignored for `%%' specifier");
Packit 575503
Packit 575503
				if (msg != NULL)
Packit 575503
					lintwarn("%s", msg);
Packit 575503
			}
Packit 575503
			bchunk_one("%");
Packit 575503
			s0 = s1;
Packit 575503
			break;
Packit 575503
Packit 575503
		case '0':
Packit 575503
			/*
Packit 575503
			 * Only turn on zero_flag if we haven't seen
Packit 575503
			 * the field width or precision yet.  Otherwise,
Packit 575503
			 * screws up floating point formatting.
Packit 575503
			 */
Packit 575503
			if (cur == & fw)
Packit 575503
				zero_flag = true;
Packit 575503
			if (lj)
Packit 575503
				goto retry;
Packit 575503
			/* FALL through */
Packit 575503
		case '1':
Packit 575503
		case '2':
Packit 575503
		case '3':
Packit 575503
		case '4':
Packit 575503
		case '5':
Packit 575503
		case '6':
Packit 575503
		case '7':
Packit 575503
		case '8':
Packit 575503
		case '9':
Packit 575503
			if (cur == NULL)
Packit 575503
				break;
Packit 575503
			if (prec >= 0)
Packit 575503
				*cur = cs1 - '0';
Packit 575503
			/*
Packit 575503
			 * with a negative precision *cur is already set
Packit 575503
			 * to -1, so it will remain negative, but we have
Packit 575503
			 * to "eat" precision digits in any case
Packit 575503
			 */
Packit 575503
			while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
Packit 575503
				--n0;
Packit 575503
				*cur = *cur * 10 + *s1++ - '0';
Packit 575503
			}
Packit 575503
			if (prec < 0) 	/* negative precision is discarded */
Packit 575503
				have_prec = false;
Packit 575503
			if (cur == &prec)
Packit 575503
				cur = NULL;
Packit 575503
			if (n0 == 0)	/* badly formatted control string */
Packit 575503
				continue;
Packit 575503
			goto retry;
Packit 575503
		case '$':
Packit 575503
			if (do_traditional) {
Packit 575503
				msg(_("fatal: `$' is not permitted in awk formats"));
Packit 575503
				goto out;
Packit 575503
			}
Packit 575503
Packit 575503
			if (cur == &fw) {
Packit 575503
				argnum = fw;
Packit 575503
				fw = 0;
Packit 575503
				used_dollar = true;
Packit 575503
				if (argnum <= 0) {
Packit 575503
					msg(_("fatal: arg count with `$' must be > 0"));
Packit 575503
					goto out;
Packit 575503
				}
Packit 575503
				if (argnum >= num_args) {
Packit 575503
					msg(_("fatal: arg count %ld greater than total number of supplied arguments"), argnum);
Packit 575503
					goto out;
Packit 575503
				}
Packit 575503
			} else {
Packit 575503
				msg(_("fatal: `$' not permitted after period in format"));
Packit 575503
				goto out;
Packit 575503
			}
Packit 575503
Packit 575503
			goto retry;
Packit 575503
		case '*':
Packit 575503
			if (cur == NULL)
Packit 575503
				break;
Packit 575503
			if (! do_traditional && used_dollar && ! isdigit((unsigned char) *s1)) {
Packit 575503
				fatal(_("fatal: must use `count$' on all formats or none"));
Packit 575503
				break;	/* silence warnings */
Packit 575503
			} else if (! do_traditional && isdigit((unsigned char) *s1)) {
Packit 575503
				int val = 0;
Packit 575503
Packit 575503
				for (; n0 > 0 && *s1 && isdigit((unsigned char) *s1); s1++, n0--) {
Packit 575503
					val *= 10;
Packit 575503
					val += *s1 - '0';
Packit 575503
				}
Packit 575503
				if (*s1 != '$') {
Packit 575503
					msg(_("fatal: no `$' supplied for positional field width or precision"));
Packit 575503
					goto out;
Packit 575503
				} else {
Packit 575503
					s1++;
Packit 575503
					n0--;
Packit 575503
				}
Packit 575503
				if (val >= num_args) {
Packit 575503
					toofew = true;
Packit 575503
					break;
Packit 575503
				}
Packit 575503
				arg = the_args[val];
Packit 575503
			} else {
Packit 575503
				parse_next_arg();
Packit 575503
			}
Packit 575503
			(void) force_number(arg);
Packit 575503
			*cur = get_number_si(arg);
Packit 575503
			if (*cur < 0 && cur == &fw) {
Packit 575503
				*cur = -*cur;
Packit 575503
				lj = true;
Packit 575503
			}
Packit 575503
			if (cur == &prec) {
Packit 575503
				if (*cur >= 0)
Packit 575503
					have_prec = true;
Packit 575503
				else
Packit 575503
					have_prec = false;
Packit 575503
				cur = NULL;
Packit 575503
			}
Packit 575503
			goto retry;
Packit 575503
		case ' ':		/* print ' ' or '-' */
Packit 575503
					/* 'space' flag is ignored */
Packit 575503
					/* if '+' already present  */
Packit 575503
			if (signchar != false)
Packit 575503
				goto check_pos;
Packit 575503
			/* FALL THROUGH */
Packit 575503
		case '+':		/* print '+' or '-' */
Packit 575503
			signchar = cs1;
Packit 575503
			goto check_pos;
Packit 575503
		case '-':
Packit 575503
			if (prec < 0)
Packit 575503
				break;
Packit 575503
			if (cur == &prec) {
Packit 575503
				prec = -1;
Packit 575503
				goto retry;
Packit 575503
			}
Packit 575503
			fill = sp;      /* if left justified then other */
Packit 575503
			lj = true;	/* filling is ignored */
Packit 575503
			goto check_pos;
Packit 575503
		case '.':
Packit 575503
			if (cur != &fw)
Packit 575503
				break;
Packit 575503
			cur = ≺
Packit 575503
			have_prec = true;
Packit 575503
			goto retry;
Packit 575503
		case '#':
Packit 575503
			alt = true;
Packit 575503
			goto check_pos;
Packit 575503
		case '\'':
Packit 575503
#if defined(HAVE_LOCALE_H)
Packit 575503
			quote_flag = true;
Packit 575503
			goto check_pos;
Packit 575503
#else
Packit 575503
			goto retry;
Packit 575503
#endif
Packit 575503
		case 'l':
Packit 575503
			if (big_flag)
Packit 575503
				break;
Packit 575503
			else {
Packit 575503
				static bool warned = false;
Packit 575503
Packit 575503
				if (do_lint && ! warned) {
Packit 575503
					lintwarn(_("`l' is meaningless in awk formats; ignored"));
Packit 575503
					warned = true;
Packit 575503
				}
Packit 575503
				if (do_posix) {
Packit 575503
					msg(_("fatal: `l' is not permitted in POSIX awk formats"));
Packit 575503
					goto out;
Packit 575503
				}
Packit 575503
			}
Packit 575503
			big_flag = true;
Packit 575503
			goto retry;
Packit 575503
		case 'L':
Packit 575503
			if (bigbig_flag)
Packit 575503
				break;
Packit 575503
			else {
Packit 575503
				static bool warned = false;
Packit 575503
Packit 575503
				if (do_lint && ! warned) {
Packit 575503
					lintwarn(_("`L' is meaningless in awk formats; ignored"));
Packit 575503
					warned = true;
Packit 575503
				}
Packit 575503
				if (do_posix) {
Packit 575503
					msg(_("fatal: `L' is not permitted in POSIX awk formats"));
Packit 575503
					goto out;
Packit 575503
				}
Packit 575503
			}
Packit 575503
			bigbig_flag = true;
Packit 575503
			goto retry;
Packit 575503
		case 'h':
Packit 575503
			if (small_flag)
Packit 575503
				break;
Packit 575503
			else {
Packit 575503
				static bool warned = false;
Packit 575503
Packit 575503
				if (do_lint && ! warned) {
Packit 575503
					lintwarn(_("`h' is meaningless in awk formats; ignored"));
Packit 575503
					warned = true;
Packit 575503
				}
Packit 575503
				if (do_posix) {
Packit 575503
					msg(_("fatal: `h' is not permitted in POSIX awk formats"));
Packit 575503
					goto out;
Packit 575503
				}
Packit 575503
			}
Packit 575503
			small_flag = true;
Packit 575503
			goto retry;
Packit 575503
		case 'c':
Packit 575503
			need_format = false;
Packit 575503
			parse_next_arg();
Packit 575503
			/* user input that looks numeric is numeric */
Packit 575503
			fixtype(arg);
Packit 575503
			if ((arg->flags & NUMBER) != 0) {
Packit 575503
				uval = get_number_uj(arg);
Packit 575503
				if (gawk_mb_cur_max > 1) {
Packit 575503
					char buf[100];
Packit 575503
					wchar_t wc;
Packit 575503
					mbstate_t mbs;
Packit 575503
					size_t count;
Packit 575503
Packit 575503
					memset(& mbs, 0, sizeof(mbs));
Packit 575503
Packit 575503
					/* handle systems with too small wchar_t */
Packit 575503
					if (sizeof(wchar_t) < 4 && uval > 0xffff) {
Packit 575503
						if (do_lint)
Packit 575503
							lintwarn(
Packit 575503
						_("[s]printf: value %g is too big for %%c format"),
Packit 575503
									arg->numbr);
Packit 575503
Packit 575503
						goto out0;
Packit 575503
					}
Packit 575503
Packit 575503
					wc = uval;
Packit 575503
Packit 575503
					count = wcrtomb(buf, wc, & mbs);
Packit 575503
					if (count == 0
Packit 575503
					    || count == (size_t) -1) {
Packit 575503
						if (do_lint)
Packit 575503
							lintwarn(
Packit 575503
						_("[s]printf: value %g is not a valid wide character"),
Packit 575503
									arg->numbr);
Packit 575503
Packit 575503
						goto out0;
Packit 575503
					}
Packit 575503
Packit 575503
					memcpy(cpbuf, buf, count);
Packit 575503
					prec = count;
Packit 575503
					cp = cpbuf;
Packit 575503
					goto pr_tail;
Packit 575503
				}
Packit 575503
out0:
Packit 575503
				;
Packit 575503
				/* else,
Packit 575503
					fall through */
Packit 575503
Packit 575503
				cpbuf[0] = uval;
Packit 575503
				prec = 1;
Packit 575503
				cp = cpbuf;
Packit 575503
				goto pr_tail;
Packit 575503
			}
Packit 575503
			/*
Packit 575503
			 * As per POSIX, only output first character of a
Packit 575503
			 * string value.  Thus, we ignore any provided
Packit 575503
			 * precision, forcing it to 1.  (Didn't this
Packit 575503
			 * used to work? 6/2003.)
Packit 575503
			 */
Packit 575503
			cp = arg->stptr;
Packit 575503
			prec = 1;
Packit 575503
			/*
Packit 575503
			 * First character can be multiple bytes if
Packit 575503
			 * it's a multibyte character. Grr.
Packit 575503
			 */
Packit 575503
			if (gawk_mb_cur_max > 1) {
Packit 575503
				mbstate_t state;
Packit 575503
				size_t count;
Packit 575503
Packit 575503
				memset(& state, 0, sizeof(state));
Packit 575503
				count = mbrlen(cp, arg->stlen, & state);
Packit 575503
				if (count != (size_t) -1 && count != (size_t) -2 && count > 0) {
Packit 575503
					prec = count;
Packit 575503
					/* may need to increase fw so that padding happens, see pr_tail code */
Packit 575503
					if (fw > 0)
Packit 575503
						fw += count - 1;
Packit 575503
				}
Packit 575503
			}
Packit 575503
			goto pr_tail;
Packit 575503
		case 's':
Packit 575503
			need_format = false;
Packit 575503
			parse_next_arg();
Packit 575503
			arg = force_string(arg);
Packit 575503
			if (fw == 0 && ! have_prec)
Packit 575503
				prec = arg->stlen;
Packit 575503
			else {
Packit 575503
				char_count = mbc_char_count(arg->stptr, arg->stlen);
Packit 575503
				if (! have_prec || prec > char_count)
Packit 575503
					prec = char_count;
Packit 575503
			}
Packit 575503
			cp = arg->stptr;
Packit 575503
			goto pr_tail;
Packit 575503
		case 'd':
Packit 575503
		case 'i':
Packit 575503
			need_format = false;
Packit 575503
			parse_next_arg();
Packit 575503
			(void) force_number(arg);
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
			if (is_mpg_float(arg))
Packit 575503
				goto mpf0;
Packit 575503
			else if (is_mpg_integer(arg))
Packit 575503
				goto mpz0;
Packit 575503
			else
Packit 575503
#endif
Packit 575503
			tmpval = arg->numbr;
Packit 575503
Packit 575503
			/*
Packit 575503
			 * Check for Nan or Inf.
Packit 575503
			 */
Packit 575503
			if (isnan(tmpval) || isinf(tmpval))
Packit 575503
				goto out_of_range;
Packit 575503
			else
Packit 575503
				tmpval = double_to_int(tmpval);
Packit 575503
Packit 575503
			/*
Packit 575503
			 * ``The result of converting a zero value with a
Packit 575503
			 * precision of zero is no characters.''
Packit 575503
			 */
Packit 575503
			if (have_prec && prec == 0 && tmpval == 0)
Packit 575503
				goto pr_tail;
Packit 575503
Packit 575503
			if (tmpval < 0) {
Packit 575503
				tmpval = -tmpval;
Packit 575503
				sgn = true;
Packit 575503
			} else {
Packit 575503
				if (tmpval == -0.0)
Packit 575503
					/* avoid printing -0 */
Packit 575503
					tmpval = 0.0;
Packit 575503
				sgn = false;
Packit 575503
			}
Packit 575503
			/*
Packit 575503
			 * Use snprintf return value to tell if there
Packit 575503
			 * is enough room in the buffer or not.
Packit 575503
			 */
Packit 575503
			while ((i = snprintf(cpbufs[1].buf,
Packit 575503
					     cpbufs[1].bufsize, "%.0f",
Packit 575503
					     tmpval)) >=
Packit 575503
			       cpbufs[1].bufsize) {
Packit 575503
				if (cpbufs[1].buf == cpbufs[1].stackbuf)
Packit 575503
					cpbufs[1].buf = NULL;
Packit 575503
				if (i > 0) {
Packit 575503
					cpbufs[1].bufsize += ((i > cpbufs[1].bufsize) ?
Packit 575503
							      i : cpbufs[1].bufsize);
Packit 575503
				}
Packit 575503
				else
Packit 575503
					cpbufs[1].bufsize *= 2;
Packit 575503
				assert(cpbufs[1].bufsize > 0);
Packit 575503
				erealloc(cpbufs[1].buf, char *,
Packit 575503
					 cpbufs[1].bufsize, "format_tree");
Packit 575503
			}
Packit 575503
			if (i < 1)
Packit 575503
				goto out_of_range;
Packit 575503
#if defined(HAVE_LOCALE_H)
Packit 575503
			quote_flag = (quote_flag && loc.thousands_sep[0] != 0);
Packit 575503
#endif
Packit 575503
			chp = &cpbufs[1].buf[i-1];
Packit 575503
			ii = jj = 0;
Packit 575503
			do {
Packit 575503
				PREPEND(*chp);
Packit 575503
				chp--; i--;
Packit 575503
#if defined(HAVE_LOCALE_H)
Packit 575503
				if (quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) {
Packit 575503
					if (i) {	/* only add if more digits coming */
Packit 575503
						int k;
Packit 575503
						const char *ts = loc.thousands_sep;
Packit 575503
Packit 575503
						for (k = strlen(ts) - 1; k >= 0; k--) {
Packit 575503
							PREPEND(ts[k]);
Packit 575503
						}
Packit 575503
					}
Packit 575503
					if (loc.grouping[ii+1] == 0)
Packit 575503
						jj = 0;		/* keep using current val in loc.grouping[ii] */
Packit 575503
					else if (loc.grouping[ii+1] == CHAR_MAX)
Packit 575503
						quote_flag = false;
Packit 575503
					else {
Packit 575503
						ii++;
Packit 575503
						jj = 0;
Packit 575503
					}
Packit 575503
				}
Packit 575503
#endif
Packit 575503
			} while (i > 0);
Packit 575503
Packit 575503
			/* add more output digits to match the precision */
Packit 575503
			if (have_prec) {
Packit 575503
				while (cend - cp < prec)
Packit 575503
					PREPEND('0');
Packit 575503
			}
Packit 575503
Packit 575503
			if (sgn)
Packit 575503
				PREPEND('-');
Packit 575503
			else if (signchar)
Packit 575503
				PREPEND(signchar);
Packit 575503
			/*
Packit 575503
			 * When to fill with zeroes is of course not simple.
Packit 575503
			 * First: No zero fill if left-justifying.
Packit 575503
			 * Next: There seem to be two cases:
Packit 575503
			 * 	A '0' without a precision, e.g. %06d
Packit 575503
			 * 	A precision with no field width, e.g. %.10d
Packit 575503
			 * Any other case, we don't want to fill with zeroes.
Packit 575503
			 */
Packit 575503
			if (! lj
Packit 575503
			    && ((zero_flag && ! have_prec)
Packit 575503
				 || (fw == 0 && have_prec)))
Packit 575503
				fill = zero_string;
Packit 575503
			if (prec > fw)
Packit 575503
				fw = prec;
Packit 575503
			prec = cend - cp;
Packit 575503
			if (fw > prec && ! lj && fill != sp
Packit 575503
			    && (*cp == '-' || signchar)) {
Packit 575503
				bchunk_one(cp);
Packit 575503
				cp++;
Packit 575503
				prec--;
Packit 575503
				fw--;
Packit 575503
			}
Packit 575503
			goto pr_tail;
Packit 575503
		case 'X':
Packit 575503
			chbuf = Uchbuf;	/* FALL THROUGH */
Packit 575503
		case 'x':
Packit 575503
			base += 6;	/* FALL THROUGH */
Packit 575503
		case 'u':
Packit 575503
			base += 2;	/* FALL THROUGH */
Packit 575503
		case 'o':
Packit 575503
			base += 8;
Packit 575503
			need_format = false;
Packit 575503
			parse_next_arg();
Packit 575503
			(void) force_number(arg);
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
			if (is_mpg_integer(arg)) {
Packit 575503
mpz0:
Packit 575503
				zi = arg->mpg_i;
Packit 575503
Packit 575503
				if (cs1 != 'd' && cs1 != 'i') {
Packit 575503
					if (mpz_sgn(zi) <= 0) {
Packit 575503
						/*
Packit 575503
						 * Negative value or 0 requires special handling.
Packit 575503
						 * Unlike MPFR, GMP does not allow conversion
Packit 575503
						 * to (u)intmax_t. So we first convert GMP type to
Packit 575503
						 * a MPFR type.
Packit 575503
						 */
Packit 575503
						mf = mpz2mpfr(zi);
Packit 575503
						goto mpf1;
Packit 575503
					}
Packit 575503
					signchar = '\0';	/* Don't print '+' */
Packit 575503
				}
Packit 575503
Packit 575503
				/* See comments above about when to fill with zeros */
Packit 575503
				zero_flag = (! lj
Packit 575503
						    && ((zero_flag && ! have_prec)
Packit 575503
							 || (fw == 0 && have_prec)));
Packit 575503
Packit 575503
 				fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC;
Packit 575503
				goto fmt0;
Packit 575503
Packit 575503
			} else if (is_mpg_float(arg)) {
Packit 575503
mpf0:
Packit 575503
				mf = arg->mpg_numbr;
Packit 575503
				if (! mpfr_number_p(mf)) {
Packit 575503
					/* inf or NaN */
Packit 575503
					cs1 = 'g';
Packit 575503
					fmt_type = MP_FLOAT;
Packit 575503
					goto fmt1;
Packit 575503
				}
Packit 575503
Packit 575503
				if (cs1 != 'd' && cs1 != 'i') {
Packit 575503
mpf1:
Packit 575503
					/*
Packit 575503
					 * The output of printf("%#.0x", 0) is 0 instead of 0x, hence <= in
Packit 575503
					 * the comparison below.
Packit 575503
					 */
Packit 575503
					if (mpfr_sgn(mf) <= 0) {
Packit 575503
						if (! mpfr_fits_intmax_p(mf, ROUND_MODE)) {
Packit 575503
							/* -ve number is too large */
Packit 575503
							cs1 = 'g';
Packit 575503
							fmt_type = MP_FLOAT;
Packit 575503
							goto fmt1;
Packit 575503
						}
Packit 575503
Packit 575503
						tmpval = uval = (uintmax_t) mpfr_get_sj(mf, ROUND_MODE);
Packit 575503
						if (! alt && have_prec && prec == 0 && tmpval == 0)
Packit 575503
							goto pr_tail;	/* printf("%.0x", 0) is no characters */
Packit 575503
						goto int0;
Packit 575503
					}
Packit 575503
					signchar = '\0';	/* Don't print '+' */
Packit 575503
				}
Packit 575503
Packit 575503
				/* See comments above about when to fill with zeros */
Packit 575503
				zero_flag = (! lj
Packit 575503
						    && ((zero_flag && ! have_prec)
Packit 575503
							 || (fw == 0 && have_prec)));
Packit 575503
Packit 575503
				(void) mpfr_get_z(mpzval, mf, MPFR_RNDZ);	/* convert to GMP integer */
Packit 575503
 				fmt_type = have_prec ? MP_INT_WITH_PREC : MP_INT_WITHOUT_PREC;
Packit 575503
				zi = mpzval;
Packit 575503
				goto fmt0;
Packit 575503
			} else
Packit 575503
#endif
Packit 575503
				tmpval = arg->numbr;
Packit 575503
Packit 575503
			/*
Packit 575503
			 * ``The result of converting a zero value with a
Packit 575503
			 * precision of zero is no characters.''
Packit 575503
			 *
Packit 575503
			 * If I remember the ANSI C standard, though,
Packit 575503
			 * it says that for octal conversions
Packit 575503
			 * the precision is artificially increased
Packit 575503
			 * to add an extra 0 if # is supplied.
Packit 575503
			 * Indeed, in C,
Packit 575503
			 * 	printf("%#.0o\n", 0);
Packit 575503
			 * prints a single 0.
Packit 575503
			 */
Packit 575503
			if (! alt && have_prec && prec == 0 && tmpval == 0)
Packit 575503
				goto pr_tail;
Packit 575503
Packit 575503
			if (tmpval < 0) {
Packit 575503
				uval = (uintmax_t) (intmax_t) tmpval;
Packit 575503
				if ((AWKNUM)(intmax_t)uval != double_to_int(tmpval))
Packit 575503
					goto out_of_range;
Packit 575503
			} else {
Packit 575503
				uval = (uintmax_t) tmpval;
Packit 575503
				if ((AWKNUM)uval != double_to_int(tmpval))
Packit 575503
					goto out_of_range;
Packit 575503
			}
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
	int0:
Packit 575503
#endif
Packit 575503
#if defined(HAVE_LOCALE_H)
Packit 575503
			quote_flag = (quote_flag && loc.thousands_sep[0] != 0);
Packit 575503
#endif
Packit 575503
			/*
Packit 575503
			 * When to fill with zeroes is of course not simple.
Packit 575503
			 * First: No zero fill if left-justifying.
Packit 575503
			 * Next: There seem to be two cases:
Packit 575503
			 * 	A '0' without a precision, e.g. %06d
Packit 575503
			 * 	A precision with no field width, e.g. %.10d
Packit 575503
			 * Any other case, we don't want to fill with zeroes.
Packit 575503
			 */
Packit 575503
			if (! lj
Packit 575503
			    && ((zero_flag && ! have_prec)
Packit 575503
				 || (fw == 0 && have_prec)))
Packit 575503
				fill = zero_string;
Packit 575503
			ii = jj = 0;
Packit 575503
			do {
Packit 575503
				PREPEND(chbuf[uval % base]);
Packit 575503
				uval /= base;
Packit 575503
#if defined(HAVE_LOCALE_H)
Packit 575503
				if (base == 10 && quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) {
Packit 575503
					if (uval) {	/* only add if more digits coming */
Packit 575503
						int k;
Packit 575503
						const char *ts = loc.thousands_sep;
Packit 575503
Packit 575503
						for (k = strlen(ts) - 1; k >= 0; k--) {
Packit 575503
							PREPEND(ts[k]);
Packit 575503
						}
Packit 575503
					}
Packit 575503
					if (loc.grouping[ii+1] == 0)
Packit 575503
						jj = 0;     /* keep using current val in loc.grouping[ii] */
Packit 575503
					else if (loc.grouping[ii+1] == CHAR_MAX)
Packit 575503
						quote_flag = false;
Packit 575503
					else {
Packit 575503
						ii++;
Packit 575503
						jj = 0;
Packit 575503
					}
Packit 575503
				}
Packit 575503
#endif
Packit 575503
			} while (uval > 0);
Packit 575503
Packit 575503
			/* add more output digits to match the precision */
Packit 575503
			if (have_prec) {
Packit 575503
				while (cend - cp < prec)
Packit 575503
					PREPEND('0');
Packit 575503
			}
Packit 575503
Packit 575503
			if (alt && tmpval != 0) {
Packit 575503
				if (base == 16) {
Packit 575503
					PREPEND(cs1);
Packit 575503
					PREPEND('0');
Packit 575503
					if (fill != sp) {
Packit 575503
						bchunk(cp, 2);
Packit 575503
						cp += 2;
Packit 575503
						fw -= 2;
Packit 575503
					}
Packit 575503
				} else if (base == 8)
Packit 575503
					PREPEND('0');
Packit 575503
			}
Packit 575503
			base = 0;
Packit 575503
			if (prec > fw)
Packit 575503
				fw = prec;
Packit 575503
			prec = cend - cp;
Packit 575503
	pr_tail:
Packit 575503
			if (! lj) {
Packit 575503
				while (fw > prec) {
Packit 575503
			    		bchunk_one(fill);
Packit 575503
					fw--;
Packit 575503
				}
Packit 575503
			}
Packit 575503
			copy_count = prec;
Packit 575503
			if (fw == 0 && ! have_prec)
Packit 575503
				;
Packit 575503
			else if (gawk_mb_cur_max > 1) {
Packit 575503
				if (cs1 == 's') {
Packit 575503
					assert(cp == arg->stptr || cp == cpbuf);
Packit 575503
					copy_count = mbc_byte_count(arg->stptr, prec);
Packit 575503
				}
Packit 575503
				/* prec was set by code for %c */
Packit 575503
				/* else
Packit 575503
					copy_count = prec; */
Packit 575503
			}
Packit 575503
			bchunk(cp, copy_count);
Packit 575503
			while (fw > prec) {
Packit 575503
				bchunk_one(fill);
Packit 575503
				fw--;
Packit 575503
			}
Packit 575503
			s0 = s1;
Packit 575503
			break;
Packit 575503
Packit 575503
     out_of_range:
Packit 575503
			/* out of range - emergency use of %g format */
Packit 575503
			if (do_lint)
Packit 575503
				lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"),
Packit 575503
							(double) tmpval, cs1);
Packit 575503
			cs1 = 'g';
Packit 575503
			goto fmt1;
Packit 575503
Packit 575503
		case 'F':
Packit 575503
#if ! defined(PRINTF_HAS_F_FORMAT) || PRINTF_HAS_F_FORMAT != 1
Packit 575503
			cs1 = 'f';
Packit 575503
			/* FALL THROUGH */
Packit 575503
#endif
Packit 575503
		case 'g':
Packit 575503
		case 'G':
Packit 575503
		case 'e':
Packit 575503
		case 'f':
Packit 575503
		case 'E':
Packit 575503
			need_format = false;
Packit 575503
			parse_next_arg();
Packit 575503
			(void) force_number(arg);
Packit 575503
Packit 575503
			if (! is_mpg_number(arg))
Packit 575503
				tmpval = arg->numbr;
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
			else if (is_mpg_float(arg)) {
Packit 575503
				mf = arg->mpg_numbr;
Packit 575503
				fmt_type = MP_FLOAT;
Packit 575503
			} else {
Packit 575503
				/* arbitrary-precision integer, convert to MPFR float */
Packit 575503
				assert(mf == NULL);
Packit 575503
				mf = mpz2mpfr(arg->mpg_i);
Packit 575503
				fmt_type = MP_FLOAT;
Packit 575503
			}
Packit 575503
#endif
Packit 575503
     fmt1:
Packit 575503
			if (! have_prec)
Packit 575503
				prec = DEFAULT_G_PRECISION;
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
     fmt0:
Packit 575503
#endif
Packit 575503
			chksize(fw + prec + 11);	/* 11 == slop */
Packit 575503
			cp = cpbuf;
Packit 575503
			*cp++ = '%';
Packit 575503
			if (lj)
Packit 575503
				*cp++ = '-';
Packit 575503
			if (signchar)
Packit 575503
				*cp++ = signchar;
Packit 575503
			if (alt)
Packit 575503
				*cp++ = '#';
Packit 575503
			if (zero_flag)
Packit 575503
				*cp++ = '0';
Packit 575503
			if (quote_flag)
Packit 575503
				*cp++ = '\'';
Packit 575503
Packit 575503
#if defined(LC_NUMERIC)
Packit 575503
			if (quote_flag && ! use_lc_numeric)
Packit 575503
				setlocale(LC_NUMERIC, "");
Packit 575503
#endif
Packit 575503
Packit 575503
			switch (fmt_type) {
Packit 575503
#ifdef HAVE_MPFR
Packit 575503
			case MP_INT_WITH_PREC:
Packit 575503
				sprintf(cp, "*.*Z%c", cs1);
Packit 575503
				while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
Packit 575503
					     (int) fw, (int) prec, zi)) >= ofre)
Packit 575503
					chksize(nc)
Packit 575503
				break;
Packit 575503
			case MP_INT_WITHOUT_PREC:
Packit 575503
				sprintf(cp, "*Z%c", cs1);
Packit 575503
				while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
Packit 575503
					     (int) fw, zi)) >= ofre)
Packit 575503
					chksize(nc)
Packit 575503
				break;
Packit 575503
			case MP_FLOAT:
Packit 575503
				sprintf(cp, "*.*R*%c", cs1);
Packit 575503
				while ((nc = mpfr_snprintf(obufout, ofre, cpbuf,
Packit 575503
					     (int) fw, (int) prec, ROUND_MODE, mf)) >= ofre)
Packit 575503
					chksize(nc)
Packit 575503
				break;
Packit 575503
#endif
Packit 575503
			default:
Packit 575503
				sprintf(cp, "*.*%c", cs1);
Packit 575503
				while ((nc = snprintf(obufout, ofre, cpbuf,
Packit 575503
					     (int) fw, (int) prec,
Packit 575503
					     (double) tmpval)) >= ofre)
Packit 575503
					chksize(nc)
Packit 575503
			}
Packit 575503
Packit 575503
#if defined(LC_NUMERIC)
Packit 575503
			if (quote_flag && ! use_lc_numeric)
Packit 575503
				setlocale(LC_NUMERIC, "C");
Packit 575503
#endif
Packit 575503
Packit 575503
			len = strlen(obufout);
Packit 575503
			ofre -= len;
Packit 575503
			obufout += len;
Packit 575503
			s0 = s1;
Packit 575503
			break;
Packit 575503
		default:
Packit 575503
			if (do_lint && is_alpha(cs1))
Packit 575503
				lintwarn(_("ignoring unknown format specifier character `%c': no argument converted"), cs1);
Packit 575503
			break;
Packit 575503
		}
Packit 575503
		if (toofew) {
Packit 575503
			msg("%s\n\t`%s'\n\t%*s%s",
Packit 575503
			      _("fatal: not enough arguments to satisfy format string"),
Packit 575503
			      fmt_string, (int) (s1 - fmt_string - 1), "",
Packit 575503
			      _("^ ran out for this one"));
Packit 575503
			goto out;
Packit 575503
		}
Packit 575503
	}
Packit 575503
	if (do_lint) {
Packit 575503
		if (need_format)
Packit 575503
			lintwarn(
Packit 575503
			_("[s]printf: format specifier does not have control letter"));
Packit 575503
		if (cur_arg < num_args)
Packit 575503
			lintwarn(
Packit 575503
			_("too many arguments supplied for format string"));
Packit 575503
	}
Packit 575503
	bchunk(s0, s1 - s0);
Packit 575503
	olen_final = obufout - obuf;
Packit 575503
	if (ofre > 0)
Packit 575503
		erealloc(obuf, char *, olen_final + 1, "format_tree");
Packit 575503
	r = make_str_node(obuf, olen_final, ALREADY_MALLOCED);
Packit 575503
	obuf = NULL;
Packit 575503
out:
Packit 575503
	{
Packit 575503
		size_t k;
Packit 575503
		size_t count = sizeof(cpbufs)/sizeof(cpbufs[0]);
Packit 575503
		for (k = 0; k < count; k++) {
Packit 575503
			if (cpbufs[k].buf != cpbufs[k].stackbuf)
Packit 575503
				efree(cpbufs[k].buf);
Packit 575503
		}
Packit 575503
		if (obuf != NULL)
Packit 575503
			efree(obuf);
Packit 575503
	}
Packit 575503
Packit 575503
	if (r == NULL)
Packit 575503
		gawk_exit(EXIT_FATAL);
Packit 575503
	return r;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* printf_common --- common code for sprintf and printf */
Packit 575503
Packit 575503
static NODE *
Packit 575503
printf_common(int nargs)
Packit 575503
{
Packit 575503
	int i;
Packit 575503
	NODE *r, *tmp;
Packit 575503
Packit 575503
	assert(nargs > 0 && nargs <= max_args);
Packit 575503
	for (i = 1; i <= nargs; i++) {
Packit 575503
		tmp = args_array[nargs - i] = POP();
Packit 575503
		if (tmp->type == Node_var_array) {
Packit 575503
			while (--i > 0)
Packit 575503
				DEREF(args_array[nargs - i]);
Packit 575503
			fatal(_("attempt to use array `%s' in a scalar context"), array_vname(tmp));
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	args_array[0] = force_string(args_array[0]);
Packit 575503
	r = format_tree(args_array[0]->stptr, args_array[0]->stlen, args_array, nargs);
Packit 575503
	for (i = 0; i < nargs; i++)
Packit 575503
		DEREF(args_array[i]);
Packit 575503
	return r;
Packit 575503
}
Packit 575503
Packit 575503
/* do_sprintf --- perform sprintf */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_sprintf(int nargs)
Packit 575503
{
Packit 575503
	NODE *r;
Packit 575503
Packit 575503
	if (nargs == 0)
Packit 575503
		fatal(_("sprintf: no arguments"));
Packit 575503
Packit 575503
	r = printf_common(nargs);
Packit 575503
	if (r == NULL)
Packit 575503
		gawk_exit(EXIT_FATAL);
Packit 575503
	return r;
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* do_printf --- perform printf, including redirection */
Packit 575503
Packit 575503
void
Packit 575503
do_printf(int nargs, int redirtype)
Packit 575503
{
Packit 575503
	FILE *fp = NULL;
Packit 575503
	NODE *tmp;
Packit 575503
	struct redirect *rp = NULL;
Packit 575503
	int errflg = 0;
Packit 575503
	NODE *redir_exp = NULL;
Packit 575503
Packit 575503
	if (nargs == 0) {
Packit 575503
		if (do_traditional) {
Packit 575503
			if (do_lint)
Packit 575503
				lintwarn(_("printf: no arguments"));
Packit 575503
			if (redirtype != 0) {
Packit 575503
				redir_exp = TOP();
Packit 575503
				if (redir_exp->type != Node_val)
Packit 575503
					fatal(_("attempt to use array `%s' in a scalar context"), array_vname(redir_exp));
Packit 575503
				rp = redirect(redir_exp, redirtype, & errflg, true);
Packit 575503
				DEREF(redir_exp);
Packit 575503
				decr_sp();
Packit 575503
			}
Packit 575503
			return;	/* bwk accepts it silently */
Packit 575503
		}
Packit 575503
		fatal(_("printf: no arguments"));
Packit 575503
	}
Packit 575503
Packit 575503
	if (redirtype != 0) {
Packit 575503
		redir_exp = PEEK(nargs);
Packit 575503
		if (redir_exp->type != Node_val)
Packit 575503
			fatal(_("attempt to use array `%s' in a scalar context"), array_vname(redir_exp));
Packit 575503
		rp = redirect(redir_exp, redirtype, & errflg, true);
Packit 575503
		if (rp != NULL) {
Packit 575503
			if ((rp->flag & RED_TWOWAY) != 0 && rp->output.fp == NULL) {
Packit 575503
				if (is_non_fatal_redirect(redir_exp->stptr, redir_exp->stlen)) {
Packit 575503
					update_ERRNO_int(EBADF);
Packit 575503
					return;
Packit 575503
				}
Packit 575503
				(void) close_rp(rp, CLOSE_ALL);
Packit 575503
				fatal(_("printf: attempt to write to closed write end of two-way pipe"));
Packit 575503
			}
Packit 575503
			fp = rp->output.fp;
Packit 575503
		}
Packit 575503
		else if (errflg) {
Packit 575503
			update_ERRNO_int(errflg);
Packit 575503
			return;
Packit 575503
		}
Packit 575503
	} else if (do_debug)	/* only the debugger can change the default output */
Packit 575503
		fp = output_fp;
Packit 575503
	else
Packit 575503
		fp = stdout;
Packit 575503
Packit 575503
	tmp = printf_common(nargs);
Packit 575503
	if (redir_exp != NULL) {
Packit 575503
		DEREF(redir_exp);
Packit 575503
		decr_sp();
Packit 575503
	}
Packit 575503
	if (tmp != NULL) {
Packit 575503
		if (fp == NULL) {
Packit 575503
			DEREF(tmp);
Packit 575503
			return;
Packit 575503
		}
Packit 575503
		efwrite(tmp->stptr, sizeof(char), tmp->stlen, fp, "printf", rp, true);
Packit 575503
		if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
Packit 575503
			rp->output.gawk_fflush(rp->output.fp, rp->output.opaque);
Packit 575503
		DEREF(tmp);
Packit 575503
	} else
Packit 575503
		gawk_exit(EXIT_FATAL);
Packit 575503
}
Packit 575503
Packit 575503
/* do_sqrt --- do the sqrt function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_sqrt(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double arg;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("sqrt: received non-numeric argument"));
Packit 575503
	arg = (double) force_number(tmp)->numbr;
Packit 575503
	DEREF(tmp);
Packit 575503
	if (arg < 0.0)
Packit 575503
		warning(_("sqrt: called with negative argument %g"), arg);
Packit 575503
	return make_number((AWKNUM) sqrt(arg));
Packit 575503
}
Packit 575503
Packit 575503
/* do_substr --- do the substr function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_substr(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1;
Packit 575503
	NODE *r;
Packit 575503
	size_t indx;
Packit 575503
	size_t length = 0;
Packit 575503
	double d_index = 0, d_length = 0;
Packit 575503
	size_t src_len;
Packit 575503
Packit 575503
	if (nargs == 3) {
Packit 575503
		t1 = POP_NUMBER();
Packit 575503
		d_length = get_number_d(t1);
Packit 575503
		DEREF(t1);
Packit 575503
	}
Packit 575503
Packit 575503
	t1 = POP_NUMBER();
Packit 575503
	d_index = get_number_d(t1);
Packit 575503
	DEREF(t1);
Packit 575503
Packit 575503
	t1 = POP_STRING();
Packit 575503
Packit 575503
	if (nargs == 3) {
Packit 575503
		if (! (d_length >= 1)) {
Packit 575503
			if (do_lint == DO_LINT_ALL)
Packit 575503
				lintwarn(_("substr: length %g is not >= 1"), d_length);
Packit 575503
			else if (do_lint == DO_LINT_INVALID && ! (d_length >= 0))
Packit 575503
				lintwarn(_("substr: length %g is not >= 0"), d_length);
Packit 575503
			DEREF(t1);
Packit 575503
			/*
Packit 575503
			 * Return explicit null string instead of doing
Packit 575503
			 * dupnode(Nnull_string) so that if the result
Packit 575503
			 * is checked with the combination of length()
Packit 575503
			 * and lint, no error is reported about using
Packit 575503
			 * an uninitialized value. Same thing later, too.
Packit 575503
			 */
Packit 575503
			return make_string("", 0);
Packit 575503
		}
Packit 575503
		if (do_lint) {
Packit 575503
			if (double_to_int(d_length) != d_length)
Packit 575503
				lintwarn(
Packit 575503
			_("substr: non-integer length %g will be truncated"),
Packit 575503
					d_length);
Packit 575503
Packit 575503
			if (d_length > SIZE_MAX)
Packit 575503
				lintwarn(
Packit 575503
			_("substr: length %g too big for string indexing, truncating to %g"),
Packit 575503
					d_length, (double) SIZE_MAX);
Packit 575503
		}
Packit 575503
		if (d_length < SIZE_MAX)
Packit 575503
			length = d_length;
Packit 575503
		else
Packit 575503
			length = SIZE_MAX;
Packit 575503
	}
Packit 575503
Packit 575503
	/* the weird `! (foo)' tests help catch NaN values. */
Packit 575503
	if (! (d_index >= 1)) {
Packit 575503
		if (do_lint)
Packit 575503
			lintwarn(_("substr: start index %g is invalid, using 1"),
Packit 575503
				 d_index);
Packit 575503
		d_index = 1;
Packit 575503
	}
Packit 575503
	if (do_lint && double_to_int(d_index) != d_index)
Packit 575503
		lintwarn(_("substr: non-integer start index %g will be truncated"),
Packit 575503
			 d_index);
Packit 575503
Packit 575503
	/* awk indices are from 1, C's are from 0 */
Packit 575503
	if (d_index <= SIZE_MAX)
Packit 575503
		indx = d_index - 1;
Packit 575503
	else
Packit 575503
		indx = SIZE_MAX;
Packit 575503
Packit 575503
	if (nargs == 2) {	/* third arg. missing */
Packit 575503
		/* use remainder of string */
Packit 575503
		length = t1->stlen - indx;	/* default to bytes */
Packit 575503
		if (gawk_mb_cur_max > 1) {
Packit 575503
			t1 = force_wstring(t1);
Packit 575503
			if (t1->wstlen > 0)	/* use length of wide char string if we have one */
Packit 575503
				length = t1->wstlen - indx;
Packit 575503
		}
Packit 575503
		d_length = length;	/* set here in case used in diagnostics, below */
Packit 575503
	}
Packit 575503
Packit 575503
	if (t1->stlen == 0) {
Packit 575503
		/* substr("", 1, 0) produces a warning only if LINT_ALL */
Packit 575503
		if (do_lint && (do_lint == DO_LINT_ALL || ((indx | length) != 0)))
Packit 575503
			lintwarn(_("substr: source string is zero length"));
Packit 575503
		DEREF(t1);
Packit 575503
		return make_string("", 0);
Packit 575503
	}
Packit 575503
Packit 575503
	/* get total len of input string, for following checks */
Packit 575503
	if (gawk_mb_cur_max > 1) {
Packit 575503
		t1 = force_wstring(t1);
Packit 575503
		src_len = t1->wstlen;
Packit 575503
	} else
Packit 575503
		src_len = t1->stlen;
Packit 575503
Packit 575503
	if (indx >= src_len) {
Packit 575503
		if (do_lint)
Packit 575503
			lintwarn(_("substr: start index %g is past end of string"),
Packit 575503
				d_index);
Packit 575503
		DEREF(t1);
Packit 575503
		return make_string("", 0);
Packit 575503
	}
Packit 575503
	if (length > src_len - indx) {
Packit 575503
		if (do_lint)
Packit 575503
			lintwarn(
Packit 575503
	_("substr: length %g at start index %g exceeds length of first argument (%lu)"),
Packit 575503
			d_length, d_index, (unsigned long int) src_len);
Packit 575503
		length = src_len - indx;
Packit 575503
	}
Packit 575503
Packit 575503
	/* force_wstring() already called */
Packit 575503
	if (gawk_mb_cur_max == 1 || t1->wstlen == t1->stlen)
Packit 575503
		/* single byte case */
Packit 575503
		r = make_string(t1->stptr + indx, length);
Packit 575503
	else {
Packit 575503
		/* multibyte case, more work */
Packit 575503
		size_t result;
Packit 575503
		wchar_t *wp;
Packit 575503
		mbstate_t mbs;
Packit 575503
		char *substr, *cp;
Packit 575503
Packit 575503
		/*
Packit 575503
		 * Convert the wide chars in t1->wstptr back into m.b. chars.
Packit 575503
		 * This is pretty grotty, but it's the most straightforward
Packit 575503
		 * way to do things.
Packit 575503
		 */
Packit 575503
		memset(& mbs, 0, sizeof(mbs));
Packit 575503
		emalloc(substr, char *, (length * gawk_mb_cur_max) + 1, "do_substr");
Packit 575503
		wp = t1->wstptr + indx;
Packit 575503
		for (cp = substr; length > 0; length--) {
Packit 575503
			result = wcrtomb(cp, *wp, & mbs);
Packit 575503
			if (result == (size_t) -1)	/* what to do? break seems best */
Packit 575503
				break;
Packit 575503
			cp += result;
Packit 575503
			wp++;
Packit 575503
		}
Packit 575503
		*cp = '\0';
Packit 575503
		r = make_str_node(substr, cp - substr, ALREADY_MALLOCED);
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(t1);
Packit 575503
	return r;
Packit 575503
}
Packit 575503
Packit 575503
/* do_strftime --- format a time stamp */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_strftime(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2, *t3, *ret;
Packit 575503
	struct tm *tm;
Packit 575503
	time_t fclock;
Packit 575503
	double clock_val;
Packit 575503
	char *bufp;
Packit 575503
	size_t buflen, bufsize;
Packit 575503
	char buf[BUFSIZ];
Packit 575503
	const char *format;
Packit 575503
	int formatlen;
Packit 575503
	bool do_gmt;
Packit 575503
	NODE *val = NULL;
Packit 575503
	NODE *sub = NULL;
Packit 575503
	char save = '\0';	// initialize to avoid compiler warnings
Packit 575503
	static const time_t time_t_min = TYPE_MINIMUM(time_t);
Packit 575503
	static const time_t time_t_max = TYPE_MAXIMUM(time_t);
Packit 575503
Packit 575503
	/* set defaults first */
Packit 575503
	format = def_strftime_format;	/* traditional date format */
Packit 575503
	formatlen = strlen(format);
Packit 575503
	(void) time(& fclock);	/* current time of day */
Packit 575503
	do_gmt = false;
Packit 575503
Packit 575503
	if (PROCINFO_node != NULL) {
Packit 575503
		sub = make_string("strftime", 8);
Packit 575503
		val = in_array(PROCINFO_node, sub);
Packit 575503
		unref(sub);
Packit 575503
Packit 575503
		if (val != NULL) {
Packit 575503
			if (do_lint && (fixtype(val)->flags & STRING) == 0)
Packit 575503
				lintwarn(_("strftime: format value in PROCINFO[\"strftime\"] has numeric type"));
Packit 575503
			val = force_string(val);
Packit 575503
			format = val->stptr;
Packit 575503
			formatlen = val->stlen;
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	t1 = t2 = t3 = NULL;
Packit 575503
	if (nargs > 0) {	/* have args */
Packit 575503
		NODE *tmp;
Packit 575503
Packit 575503
		if (nargs == 3) {
Packit 575503
			t3 = POP_SCALAR();
Packit 575503
			do_gmt = boolval(t3);
Packit 575503
			DEREF(t3);
Packit 575503
		}
Packit 575503
Packit 575503
		if (nargs >= 2) {
Packit 575503
			t2 = POP_SCALAR();
Packit 575503
			if (do_lint && (fixtype(t2)->flags & NUMBER) == 0)
Packit 575503
				lintwarn(_("strftime: received non-numeric second argument"));
Packit 575503
			(void) force_number(t2);
Packit 575503
			clock_val = get_number_d(t2);
Packit 575503
			fclock = (time_t) clock_val;
Packit 575503
			/*
Packit 575503
			 * Protect against negative value being assigned
Packit 575503
			 * to unsigned time_t.
Packit 575503
			 */
Packit 575503
			if (clock_val < 0 && fclock > 0) {
Packit 575503
				if (do_lint)
Packit 575503
					lintwarn(_("strftime: second argument less than 0 or too big for time_t"));
Packit 575503
				return make_string("", 0);
Packit 575503
			}
Packit 575503
Packit 575503
			/* And check that the value is in range */
Packit 575503
			if (clock_val < time_t_min || clock_val > time_t_max) {
Packit 575503
				if (do_lint)
Packit 575503
					lintwarn(_("strftime: second argument out of range for time_t"));
Packit 575503
				return make_string("", 0);
Packit 575503
			}
Packit 575503
Packit 575503
			DEREF(t2);
Packit 575503
		}
Packit 575503
Packit 575503
		tmp = POP_SCALAR();
Packit 575503
		if (do_lint && (fixtype(tmp)->flags & STRING) == 0)
Packit 575503
			lintwarn(_("strftime: received non-string first argument"));
Packit 575503
Packit 575503
		t1 = force_string(tmp);
Packit 575503
		format = t1->stptr;
Packit 575503
		formatlen = t1->stlen;
Packit 575503
		if (formatlen == 0) {
Packit 575503
			if (do_lint)
Packit 575503
				lintwarn(_("strftime: received empty format string"));
Packit 575503
			DEREF(t1);
Packit 575503
			return make_string("", 0);
Packit 575503
		}
Packit 575503
		str_terminate(t1, save);
Packit 575503
	}
Packit 575503
Packit 575503
	if (do_gmt)
Packit 575503
		tm = gmtime(& fclock);
Packit 575503
	else
Packit 575503
		tm = localtime(& fclock);
Packit 575503
Packit 575503
	if (tm == NULL) {
Packit 575503
		ret = make_string("", 0);
Packit 575503
		goto done;
Packit 575503
	}
Packit 575503
Packit 575503
	bufp = buf;
Packit 575503
	bufsize = sizeof(buf);
Packit 575503
	for (;;) {
Packit 575503
		*bufp = '\0';
Packit 575503
		buflen = strftime(bufp, bufsize, format, tm);
Packit 575503
		/*
Packit 575503
		 * buflen can be zero EITHER because there's not enough
Packit 575503
		 * room in the string, or because the control command
Packit 575503
		 * goes to the empty string. Make a reasonable guess that
Packit 575503
		 * if the buffer is 1024 times bigger than the length of the
Packit 575503
		 * format string, it's not failing for lack of room.
Packit 575503
		 * Thanks to Paul Eggert for pointing out this issue.
Packit 575503
		 */
Packit 575503
		if (buflen > 0 || bufsize >= 1024 * formatlen)
Packit 575503
			break;
Packit 575503
		bufsize *= 2;
Packit 575503
		if (bufp == buf)
Packit 575503
			emalloc(bufp, char *, bufsize, "do_strftime");
Packit 575503
		else
Packit 575503
			erealloc(bufp, char *, bufsize, "do_strftime");
Packit 575503
	}
Packit 575503
	ret = make_string(bufp, buflen);
Packit 575503
	if (bufp != buf)
Packit 575503
		efree(bufp);
Packit 575503
done:
Packit 575503
	if (t1) {
Packit 575503
		str_restore(t1, save);
Packit 575503
		DEREF(t1);
Packit 575503
	}
Packit 575503
	return ret;
Packit 575503
}
Packit 575503
Packit 575503
/* do_systime --- get the time of day */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_systime(int nargs ATTRIBUTE_UNUSED)
Packit 575503
{
Packit 575503
	time_t lclock;
Packit 575503
Packit 575503
	(void) time(& lclock);
Packit 575503
	return make_number((AWKNUM) lclock);
Packit 575503
}
Packit 575503
Packit 575503
/* mktime_tz --- based on Linux timegm man page */
Packit 575503
Packit 575503
static time_t
Packit 575503
mktime_tz(struct tm *tm, const char *tzreq)
Packit 575503
{
Packit 575503
	time_t ret;
Packit 575503
	char *tz = getenv("TZ");
Packit 575503
Packit 575503
	if (tz)
Packit 575503
		tz = estrdup(tz, strlen(tz));
Packit 575503
	if (setenv("TZ", tzreq, 1) < 0) {
Packit 575503
		warning(_("setenv(TZ, %s) failed (%s)"), tzreq, strerror(errno));
Packit 575503
		return -1;
Packit 575503
	}
Packit 575503
	tzset();
Packit 575503
	ret = mktime(tm);
Packit 575503
	if (tz) {
Packit 575503
		if (setenv("TZ", tz, 1) < 0)
Packit 575503
			fatal(_("setenv(TZ, %s) restoration failed (%s)"), tz, strerror(errno));
Packit 575503
		free(tz);
Packit 575503
	} else {
Packit 575503
		if (unsetenv("TZ") < 0)
Packit 575503
			fatal(_("unsetenv(TZ) failed (%s)"), strerror(errno));
Packit 575503
	}
Packit 575503
	tzset();
Packit 575503
	return ret;
Packit 575503
}
Packit 575503
Packit 575503
/* do_mktime --- turn a time string into a timestamp */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_mktime(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2;
Packit 575503
	struct tm then;
Packit 575503
	long year;
Packit 575503
	int month, day, hour, minute, second, count;
Packit 575503
	int dst = -1; /* default is unknown */
Packit 575503
	time_t then_stamp;
Packit 575503
	char save;
Packit 575503
	bool do_gmt;
Packit 575503
Packit 575503
	if (nargs == 2) {
Packit 575503
		t2 = POP_SCALAR();
Packit 575503
		do_gmt = boolval(t2);
Packit 575503
		DEREF(t2);
Packit 575503
	}
Packit 575503
	else
Packit 575503
		do_gmt = false;
Packit 575503
	t1 = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(t1)->flags & STRING) == 0)
Packit 575503
		lintwarn(_("mktime: received non-string argument"));
Packit 575503
	t1 = force_string(t1);
Packit 575503
Packit 575503
	save = t1->stptr[t1->stlen];
Packit 575503
	t1->stptr[t1->stlen] = '\0';
Packit 575503
Packit 575503
	count = sscanf(t1->stptr, "%ld %d %d %d %d %d %d",
Packit 575503
		        & year, & month, & day,
Packit 575503
			& hour, & minute, & second,
Packit 575503
		        & dst);
Packit 575503
Packit 575503
	if (   do_lint /* Ready? Set! Go: */
Packit 575503
	    && (   (second < 0 || second > 60)
Packit 575503
		|| (minute < 0 || minute > 59)
Packit 575503
		|| (hour < 0 || hour > 23) /* FIXME ISO 8601 allows 24 ? */
Packit 575503
		|| (day < 1 || day > 31)
Packit 575503
		|| (month < 1 || month > 12) ))
Packit 575503
			lintwarn(_("mktime: at least one of the values is out of the default range"));
Packit 575503
Packit 575503
	t1->stptr[t1->stlen] = save;
Packit 575503
	DEREF(t1);
Packit 575503
Packit 575503
	if (count < 6
Packit 575503
	    || month == INT_MIN
Packit 575503
	    || year < INT_MIN + 1900
Packit 575503
	    || year - 1900 > INT_MAX)
Packit 575503
		return make_number((AWKNUM) -1);
Packit 575503
Packit 575503
	memset(& then, '\0', sizeof(then));
Packit 575503
	then.tm_sec = second;
Packit 575503
	then.tm_min = minute;
Packit 575503
	then.tm_hour = hour;
Packit 575503
	then.tm_mday = day;
Packit 575503
	then.tm_mon = month - 1;
Packit 575503
	then.tm_year = year - 1900;
Packit 575503
	then.tm_isdst = dst;
Packit 575503
Packit 575503
	then_stamp = (do_gmt ? mktime_tz(& then, "UTC+0") : mktime(& then));
Packit 575503
	return make_number((AWKNUM) then_stamp);
Packit 575503
}
Packit 575503
Packit 575503
/* do_system --- run an external command */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_system(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	AWKNUM ret = 0;		/* floating point on purpose, compat Unix awk */
Packit 575503
	char *cmd;
Packit 575503
	char save;
Packit 575503
	int status;
Packit 575503
Packit 575503
	if (do_sandbox)
Packit 575503
		fatal(_("'system' function not allowed in sandbox mode"));
Packit 575503
Packit 575503
	(void) flush_io();     /* so output is synchronous with gawk's */
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & STRING) == 0)
Packit 575503
		lintwarn(_("system: received non-string argument"));
Packit 575503
	cmd = force_string(tmp)->stptr;
Packit 575503
Packit 575503
	if (cmd && *cmd) {
Packit 575503
		/* insure arg to system is zero-terminated */
Packit 575503
		save = cmd[tmp->stlen];
Packit 575503
		cmd[tmp->stlen] = '\0';
Packit 575503
Packit 575503
		os_restore_mode(fileno(stdin));
Packit 575503
		set_sigpipe_to_default();
Packit 575503
Packit 575503
		status = system(cmd);
Packit 575503
		/*
Packit 575503
		 * 3/2016. What to do with ret? It's never simple.
Packit 575503
		 * POSIX says to use the full return value. BWK awk
Packit 575503
		 * divides the result by 256.  That normally gives the
Packit 575503
		 * exit status but gives a weird result for death-by-signal.
Packit 575503
		 * So we compromise as follows:
Packit 575503
		 */
Packit 575503
		ret = status;
Packit 575503
		if (status != -1) {
Packit 575503
			if (do_posix)
Packit 575503
				;	/* leave it alone, full 16 bits */
Packit 575503
			else if (do_traditional)
Packit 575503
#ifdef __MINGW32__
Packit 575503
				ret = (((unsigned)status) & ~0xC0000000);
Packit 575503
#else
Packit 575503
				ret = (status / 256.0);
Packit 575503
#endif
Packit 575503
			else
Packit 575503
				ret = sanitize_exit_status(status);
Packit 575503
		}
Packit 575503
Packit 575503
		if ((BINMODE & BINMODE_INPUT) != 0)
Packit 575503
			os_setbinmode(fileno(stdin), O_BINARY);
Packit 575503
		ignore_sigpipe();
Packit 575503
Packit 575503
		cmd[tmp->stlen] = save;
Packit 575503
	}
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) ret);
Packit 575503
}
Packit 575503
Packit 575503
/* do_print --- print items, separated by OFS, terminated with ORS */
Packit 575503
Packit 575503
void
Packit 575503
do_print(int nargs, int redirtype)
Packit 575503
{
Packit 575503
	struct redirect *rp = NULL;
Packit 575503
	int errflg = 0;
Packit 575503
	FILE *fp = NULL;
Packit 575503
	int i;
Packit 575503
	NODE *redir_exp = NULL;
Packit 575503
	NODE *tmp = NULL;
Packit 575503
Packit 575503
	assert(nargs <= max_args);
Packit 575503
Packit 575503
	if (redirtype != 0) {
Packit 575503
		redir_exp = PEEK(nargs);
Packit 575503
		if (redir_exp->type != Node_val)
Packit 575503
			fatal(_("attempt to use array `%s' in a scalar context"), array_vname(redir_exp));
Packit 575503
		rp = redirect(redir_exp, redirtype, & errflg, true);
Packit 575503
		if (rp != NULL) {
Packit 575503
			if ((rp->flag & RED_TWOWAY) != 0 && rp->output.fp == NULL) {
Packit 575503
				if (is_non_fatal_redirect(redir_exp->stptr, redir_exp->stlen)) {
Packit 575503
					update_ERRNO_int(EBADF);
Packit 575503
					return;
Packit 575503
				}
Packit 575503
				(void) close_rp(rp, CLOSE_ALL);
Packit 575503
				fatal(_("print: attempt to write to closed write end of two-way pipe"));
Packit 575503
			}
Packit 575503
			fp = rp->output.fp;
Packit 575503
		}
Packit 575503
		else if (errflg) {
Packit 575503
			update_ERRNO_int(errflg);
Packit 575503
			return;
Packit 575503
		}
Packit 575503
	} else if (do_debug)	/* only the debugger can change the default output */
Packit 575503
		fp = output_fp;
Packit 575503
	else
Packit 575503
		fp = stdout;
Packit 575503
Packit 575503
	for (i = 1; i <= nargs; i++) {
Packit 575503
		tmp = args_array[i] = POP();
Packit 575503
		if (tmp->type == Node_var_array) {
Packit 575503
			while (--i > 0)
Packit 575503
				DEREF(args_array[i]);
Packit 575503
			fatal(_("attempt to use array `%s' in a scalar context"), array_vname(tmp));
Packit 575503
		}
Packit 575503
		// Let force_string_ofmt handle checking if things
Packit 575503
		// are already valid.
Packit 575503
		args_array[i] = force_string_ofmt(tmp);
Packit 575503
	}
Packit 575503
Packit 575503
	if (redir_exp != NULL) {
Packit 575503
		DEREF(redir_exp);
Packit 575503
		decr_sp();
Packit 575503
	}
Packit 575503
Packit 575503
	if (fp == NULL) {
Packit 575503
		for (i = nargs; i > 0; i--)
Packit 575503
			DEREF(args_array[i]);
Packit 575503
		return;
Packit 575503
	}
Packit 575503
Packit 575503
	for (i = nargs; i > 0; i--) {
Packit 575503
		efwrite(args_array[i]->stptr, sizeof(char), args_array[i]->stlen, fp, "print", rp, false);
Packit 575503
		DEREF(args_array[i]);
Packit 575503
		if (i != 1 && OFSlen > 0)
Packit 575503
			efwrite(OFS, sizeof(char), (size_t) OFSlen,
Packit 575503
				fp, "print", rp, false);
Packit 575503
Packit 575503
	}
Packit 575503
	if (ORSlen > 0)
Packit 575503
		efwrite(ORS, sizeof(char), (size_t) ORSlen, fp, "print", rp, true);
Packit 575503
Packit 575503
	if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
Packit 575503
		rp->output.gawk_fflush(rp->output.fp, rp->output.opaque);
Packit 575503
}
Packit 575503
Packit 575503
/* do_print_rec --- special case printing of $0, for speed */
Packit 575503
Packit 575503
void
Packit 575503
do_print_rec(int nargs, int redirtype)
Packit 575503
{
Packit 575503
	FILE *fp = NULL;
Packit 575503
	NODE *f0;
Packit 575503
	struct redirect *rp = NULL;
Packit 575503
	int errflg = 0;
Packit 575503
	NODE *redir_exp = NULL;
Packit 575503
Packit 575503
	assert(nargs == 0);
Packit 575503
	if (redirtype != 0) {
Packit 575503
		redir_exp = TOP();
Packit 575503
		rp = redirect(redir_exp, redirtype, & errflg, true);
Packit 575503
		if (rp != NULL) {
Packit 575503
			if ((rp->flag & RED_TWOWAY) != 0 && rp->output.fp == NULL) {
Packit 575503
				if (is_non_fatal_redirect(redir_exp->stptr, redir_exp->stlen)) {
Packit 575503
					update_ERRNO_int(EBADF);
Packit 575503
					return;
Packit 575503
				}
Packit 575503
				(void) close_rp(rp, CLOSE_ALL);
Packit 575503
				fatal(_("print: attempt to write to closed write end of two-way pipe"));
Packit 575503
			}
Packit 575503
			fp = rp->output.fp;
Packit 575503
		}
Packit 575503
		DEREF(redir_exp);
Packit 575503
		decr_sp();
Packit 575503
	} else
Packit 575503
		fp = output_fp;
Packit 575503
Packit 575503
	if (errflg) {
Packit 575503
		update_ERRNO_int(errflg);
Packit 575503
		return;
Packit 575503
	}
Packit 575503
Packit 575503
	if (fp == NULL)
Packit 575503
		return;
Packit 575503
Packit 575503
	if (! field0_valid)
Packit 575503
		(void) get_field(0L, NULL);	/* rebuild record */
Packit 575503
Packit 575503
	f0 = fields_arr[0];
Packit 575503
Packit 575503
	if (do_lint && (f0->flags & NULL_FIELD) != 0)
Packit 575503
		lintwarn(_("reference to uninitialized field `$%d'"), 0);
Packit 575503
Packit 575503
	efwrite(f0->stptr, sizeof(char), f0->stlen, fp, "print", rp, false);
Packit 575503
Packit 575503
	if (ORSlen > 0)
Packit 575503
		efwrite(ORS, sizeof(char), (size_t) ORSlen, fp, "print", rp, true);
Packit 575503
Packit 575503
	if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
Packit 575503
		rp->output.gawk_fflush(rp->output.fp, rp->output.opaque);
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
/* is_wupper --- function version of iswupper for passing function pointers */
Packit 575503
Packit 575503
static int
Packit 575503
is_wupper(wchar_t c)
Packit 575503
{
Packit 575503
	return iswupper(c);
Packit 575503
}
Packit 575503
Packit 575503
/* is_wlower --- function version of iswlower for passing function pointers */
Packit 575503
Packit 575503
static int
Packit 575503
is_wlower(wchar_t c)
Packit 575503
{
Packit 575503
	return iswlower(c);
Packit 575503
}
Packit 575503
Packit 575503
/* to_wupper --- function version of towupper for passing function pointers */
Packit 575503
Packit 575503
static int
Packit 575503
to_wlower(wchar_t c)
Packit 575503
{
Packit 575503
	return towlower(c);
Packit 575503
}
Packit 575503
Packit 575503
/* to_wlower --- function version of towlower for passing function pointers */
Packit 575503
Packit 575503
static int
Packit 575503
to_wupper(wchar_t c)
Packit 575503
{
Packit 575503
	return towupper(c);
Packit 575503
}
Packit 575503
Packit 575503
/* wide_change_case --- generic case converter for wide characters */
Packit 575503
Packit 575503
static void
Packit 575503
wide_change_case(wchar_t *wstr,
Packit 575503
			size_t wlen,
Packit 575503
			int (*is_x)(wchar_t c),
Packit 575503
			int (*to_y)(wchar_t c))
Packit 575503
{
Packit 575503
	size_t i;
Packit 575503
	wchar_t *wcp;
Packit 575503
Packit 575503
	for (i = 0, wcp = wstr; i < wlen; i++, wcp++)
Packit 575503
		if (is_x(*wcp))
Packit 575503
			*wcp = to_y(*wcp);
Packit 575503
}
Packit 575503
Packit 575503
/* wide_toupper --- map a wide string to upper case */
Packit 575503
Packit 575503
static void
Packit 575503
wide_toupper(wchar_t *wstr, size_t wlen)
Packit 575503
{
Packit 575503
	wide_change_case(wstr, wlen, is_wlower, to_wupper);
Packit 575503
}
Packit 575503
Packit 575503
/* wide_tolower --- map a wide string to lower case */
Packit 575503
Packit 575503
static void
Packit 575503
wide_tolower(wchar_t *wstr, size_t wlen)
Packit 575503
{
Packit 575503
	wide_change_case(wstr, wlen, is_wupper, to_wlower);
Packit 575503
}
Packit 575503
Packit 575503
/* do_tolower --- lower case a string */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_tolower(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2;
Packit 575503
Packit 575503
	t1 = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(t1)->flags & STRING) == 0)
Packit 575503
		lintwarn(_("tolower: received non-string argument"));
Packit 575503
	t1 = force_string(t1);
Packit 575503
	t2 = make_string(t1->stptr, t1->stlen);
Packit 575503
Packit 575503
	if (gawk_mb_cur_max == 1) {
Packit 575503
		unsigned char *cp, *cp2;
Packit 575503
Packit 575503
		for (cp = (unsigned char *)t2->stptr,
Packit 575503
		     cp2 = (unsigned char *)(t2->stptr + t2->stlen);
Packit 575503
			cp < cp2; cp++)
Packit 575503
			if (isupper(*cp))
Packit 575503
				*cp = tolower(*cp);
Packit 575503
	} else {
Packit 575503
		force_wstring(t2);
Packit 575503
		wide_tolower(t2->wstptr, t2->wstlen);
Packit 575503
		wstr2str(t2);
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(t1);
Packit 575503
	return t2;
Packit 575503
}
Packit 575503
Packit 575503
/* do_toupper --- upper case a string */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_toupper(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2;
Packit 575503
Packit 575503
	t1 = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(t1)->flags & STRING) == 0)
Packit 575503
		lintwarn(_("toupper: received non-string argument"));
Packit 575503
	t1 = force_string(t1);
Packit 575503
	t2 = make_string(t1->stptr, t1->stlen);
Packit 575503
Packit 575503
	if (gawk_mb_cur_max == 1) {
Packit 575503
		unsigned char *cp, *cp2;
Packit 575503
Packit 575503
		for (cp = (unsigned char *)t2->stptr,
Packit 575503
		     cp2 = (unsigned char *)(t2->stptr + t2->stlen);
Packit 575503
			cp < cp2; cp++)
Packit 575503
			if (islower(*cp))
Packit 575503
				*cp = toupper(*cp);
Packit 575503
	} else {
Packit 575503
		force_wstring(t2);
Packit 575503
		wide_toupper(t2->wstptr, t2->wstlen);
Packit 575503
		wstr2str(t2);
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(t1);
Packit 575503
	return t2;
Packit 575503
}
Packit 575503
Packit 575503
/* do_atan2 --- do the atan2 function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_atan2(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2;
Packit 575503
	double d1, d2;
Packit 575503
Packit 575503
	POP_TWO_SCALARS(t1, t2);
Packit 575503
	if (do_lint) {
Packit 575503
		if ((fixtype(t1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("atan2: received non-numeric first argument"));
Packit 575503
		if ((fixtype(t2)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("atan2: received non-numeric second argument"));
Packit 575503
	}
Packit 575503
	d1 = force_number(t1)->numbr;
Packit 575503
	d2 = force_number(t2)->numbr;
Packit 575503
	DEREF(t1);
Packit 575503
	DEREF(t2);
Packit 575503
	return make_number((AWKNUM) atan2(d1, d2));
Packit 575503
}
Packit 575503
Packit 575503
/* do_sin --- do the sin function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_sin(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("sin: received non-numeric argument"));
Packit 575503
	d = sin((double) force_number(tmp)->numbr);
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) d);
Packit 575503
}
Packit 575503
Packit 575503
/* do_cos --- do the cos function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_cos(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("cos: received non-numeric argument"));
Packit 575503
	d = cos((double) force_number(tmp)->numbr);
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) d);
Packit 575503
}
Packit 575503
Packit 575503
/* do_rand --- do the rand function */
Packit 575503
Packit 575503
static bool firstrand = true;
Packit 575503
/* Some systems require this array to be integer aligned. Sigh. */
Packit 575503
#define SIZEOF_STATE 256
Packit 575503
static uint32_t istate[SIZEOF_STATE/sizeof(uint32_t)];
Packit 575503
static char *const state = (char *const) istate;
Packit 575503
Packit 575503
/* ARGSUSED */
Packit 575503
NODE *
Packit 575503
do_rand(int nargs ATTRIBUTE_UNUSED)
Packit 575503
{
Packit 575503
	double tmprand;
Packit 575503
#define RAND_DIVISOR ((double)GAWK_RANDOM_MAX+1.0)
Packit 575503
	if (firstrand) {
Packit 575503
		(void) initstate((unsigned) 1, state, SIZEOF_STATE);
Packit 575503
		/* don't need to srandom(1), initstate() does it for us. */
Packit 575503
		firstrand = false;
Packit 575503
		setstate(state);
Packit 575503
	}
Packit 575503
	/*
Packit 575503
	 * Per historical practice and POSIX, return value N is
Packit 575503
	 *
Packit 575503
	 * 	0 <= n < 1
Packit 575503
	 */
Packit 575503
 	/*
Packit 575503
	 * Date: Wed, 28 Aug 2013 17:52:46 -0700
Packit 575503
	 * From: Bob Jewett <jewett@bill.scs.agilent.com>
Packit 575503
	 *
Packit 575503
 	 * Call random() twice to fill in more bits in the value
Packit 575503
 	 * of the double.  Also, there is a bug in random() such
Packit 575503
 	 * that when the values of successive values are combined
Packit 575503
 	 * like (rand1*rand2)^2, (rand3*rand4)^2,  ...  the
Packit 575503
 	 * resulting time series is not white noise.  The
Packit 575503
 	 * following also seems to fix that bug.
Packit 575503
 	 *
Packit 575503
 	 * The add/subtract 0.5 keeps small bits from filling
Packit 575503
 	 * below 2^-53 in the double, not that anyone should be
Packit 575503
 	 * looking down there.
Packit 575503
	 *
Packit 575503
	 * Date: Wed, 25 Sep 2013 10:45:38 -0600 (MDT)
Packit 575503
	 * From: "Nelson H. F. Beebe" <beebe@math.utah.edu>
Packit 575503
	 * (4) The code is typical of many published fragments for converting
Packit 575503
	 *     from integer to floating-point, and I discuss the serious pitfalls
Packit 575503
	 *     in my book, because it leads to platform-dependent behavior at the
Packit 575503
	 *     end points of the interval [0,1]
Packit 575503
	 *
Packit 575503
	 * (5) the documentation in the gawk info node says
Packit 575503
	 *
Packit 575503
	 *     `rand()'
Packit 575503
	 * 	 Return a random number.  The values of `rand()' are uniformly
Packit 575503
	 * 	 distributed between zero and one.  The value could be zero but is
Packit 575503
	 * 	 never one.(1)
Packit 575503
	 *
Packit 575503
	 *     The division by RAND_DIVISOR may not guarantee that 1.0 is never
Packit 575503
	 *     returned: the programmer forgot the platform-dependent issue of
Packit 575503
	 *     rounding.
Packit 575503
	 *
Packit 575503
	 * For points 4 and 5, the safe way is a loop:
Packit 575503
	 *
Packit 575503
	 *         double
Packit 575503
	 * 	   rand(void)		// return value in [0.0, 1.0)
Packit 575503
	 *         {
Packit 575503
	 * 	    value = internal_rand();
Packit 575503
	 *
Packit 575503
	 * 	    while (value == 1.0)
Packit 575503
	 *                 value = internal_rand();
Packit 575503
	 *
Packit 575503
	 * 	    return (value);
Packit 575503
	 *         }
Packit 575503
 	 */
Packit 575503
Packit 575503
	do {
Packit 575503
		long d1, d2;
Packit 575503
		/*
Packit 575503
		 * Do the calls in predictable order to avoid
Packit 575503
		 * compiler differences in order of evaluation.
Packit 575503
		 */
Packit 575503
		d1 = random();
Packit 575503
		d2 = random();
Packit 575503
	 	tmprand = 0.5 + ( (d1/RAND_DIVISOR + d2) / RAND_DIVISOR );
Packit 575503
		tmprand -= 0.5;
Packit 575503
	} while (tmprand == 1.0);
Packit 575503
Packit 575503
 	return make_number((AWKNUM) tmprand);
Packit 575503
}
Packit 575503
Packit 575503
/* do_srand --- seed the random number generator */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_srand(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	static long save_seed = 1;
Packit 575503
	long ret = save_seed;	/* SVR4 awk srand returns previous seed */
Packit 575503
Packit 575503
	if (firstrand) {
Packit 575503
		(void) initstate((unsigned) 1, state, SIZEOF_STATE);
Packit 575503
		/* don't need to srandom(1), we're changing the seed below */
Packit 575503
		firstrand = false;
Packit 575503
		(void) setstate(state);
Packit 575503
	}
Packit 575503
Packit 575503
	if (nargs == 0)
Packit 575503
		srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
Packit 575503
	else {
Packit 575503
		tmp = POP_SCALAR();
Packit 575503
		if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("srand: received non-numeric argument"));
Packit 575503
		srandom((unsigned int) (save_seed = (long) force_number(tmp)->numbr));
Packit 575503
		DEREF(tmp);
Packit 575503
	}
Packit 575503
	return make_number((AWKNUM) ret);
Packit 575503
}
Packit 575503
Packit 575503
/* do_match --- match a regexp, set RSTART and RLENGTH,
Packit 575503
 * 	optional third arg is array filled with text of
Packit 575503
 * 	subpatterns enclosed in parens and start and len info.
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_match(int nargs)
Packit 575503
{
Packit 575503
	NODE *tre, *t1, *dest, *it;
Packit 575503
	int rstart, len, ii;
Packit 575503
	int rlength;
Packit 575503
	Regexp *rp;
Packit 575503
	regoff_t s;
Packit 575503
	char *start;
Packit 575503
	char *buf = NULL;
Packit 575503
	char buff[100];
Packit 575503
	size_t amt, oldamt = 0, ilen, slen;
Packit 575503
	char *subsepstr;
Packit 575503
	size_t subseplen;
Packit 575503
Packit 575503
	dest = NULL;
Packit 575503
	if (nargs == 3) {	/* 3rd optional arg for the subpatterns */
Packit 575503
		dest = POP_PARAM();
Packit 575503
		if (dest->type != Node_var_array)
Packit 575503
			fatal(_("match: third argument is not an array"));
Packit 575503
		assoc_clear(dest);
Packit 575503
	}
Packit 575503
	tre = POP();
Packit 575503
	rp = re_update(tre);
Packit 575503
	t1 = POP_STRING();
Packit 575503
Packit 575503
	rstart = research(rp, t1->stptr, 0, t1->stlen, RE_NEED_START);
Packit 575503
	if (rstart >= 0) {	/* match succeded */
Packit 575503
		size_t *wc_indices = NULL;
Packit 575503
Packit 575503
		rlength = REEND(rp, t1->stptr) - RESTART(rp, t1->stptr);	/* byte length */
Packit 575503
		if (rlength > 0 && gawk_mb_cur_max > 1) {
Packit 575503
			t1 = str2wstr(t1, & wc_indices);
Packit 575503
			rlength = wc_indices[rstart + rlength - 1] - wc_indices[rstart] + 1;
Packit 575503
			rstart = wc_indices[rstart];
Packit 575503
		}
Packit 575503
Packit 575503
		rstart++;	/* now it's 1-based indexing */
Packit 575503
Packit 575503
		/* Build the array only if the caller wants the optional subpatterns */
Packit 575503
		if (dest != NULL) {
Packit 575503
			subsepstr = SUBSEP_node->var_value->stptr;
Packit 575503
			subseplen = SUBSEP_node->var_value->stlen;
Packit 575503
Packit 575503
			for (ii = 0; ii < NUMSUBPATS(rp, t1->stptr); ii++) {
Packit 575503
				/*
Packit 575503
				 * Loop over all the subpats; some of them may have
Packit 575503
				 * matched even if all of them did not.
Packit 575503
				 */
Packit 575503
				if ((s = SUBPATSTART(rp, t1->stptr, ii)) != -1) {
Packit 575503
					size_t subpat_start;
Packit 575503
					size_t subpat_len;
Packit 575503
					NODE **lhs;
Packit 575503
					NODE *sub;
Packit 575503
Packit 575503
					start = t1->stptr + s;
Packit 575503
					subpat_start = s;
Packit 575503
					subpat_len = len = SUBPATEND(rp, t1->stptr, ii) - s;
Packit 575503
					if (len > 0 && gawk_mb_cur_max > 1) {
Packit 575503
						subpat_start = wc_indices[s];
Packit 575503
						subpat_len = wc_indices[s + len - 1] - subpat_start + 1;
Packit 575503
					}
Packit 575503
Packit 575503
					it = make_string(start, len);
Packit 575503
					it->flags |= USER_INPUT;
Packit 575503
Packit 575503
					sub = make_number((AWKNUM) (ii));
Packit 575503
					lhs = assoc_lookup(dest, sub);
Packit 575503
					unref(*lhs);
Packit 575503
					*lhs = it;
Packit 575503
					/* execute post-assignment routine if any */
Packit 575503
					if (dest->astore != NULL)
Packit 575503
						(*dest->astore)(dest, sub);
Packit 575503
					unref(sub);
Packit 575503
Packit 575503
					sprintf(buff, "%d", ii);
Packit 575503
					ilen = strlen(buff);
Packit 575503
					amt = ilen + subseplen + strlen("length") + 1;
Packit 575503
Packit 575503
					if (oldamt == 0) {
Packit 575503
						emalloc(buf, char *, amt, "do_match");
Packit 575503
					} else if (amt > oldamt) {
Packit 575503
						erealloc(buf, char *, amt, "do_match");
Packit 575503
					}
Packit 575503
					oldamt = amt;
Packit 575503
					memcpy(buf, buff, ilen);
Packit 575503
					memcpy(buf + ilen, subsepstr, subseplen);
Packit 575503
					memcpy(buf + ilen + subseplen, "start", 6);
Packit 575503
Packit 575503
					slen = ilen + subseplen + 5;
Packit 575503
Packit 575503
					it = make_number((AWKNUM) subpat_start + 1);
Packit 575503
					sub = make_string(buf, slen);
Packit 575503
					lhs = assoc_lookup(dest, sub);
Packit 575503
					unref(*lhs);
Packit 575503
					*lhs = it;
Packit 575503
					if (dest->astore != NULL)
Packit 575503
						(*dest->astore)(dest, sub);
Packit 575503
					unref(sub);
Packit 575503
Packit 575503
					memcpy(buf, buff, ilen);
Packit 575503
					memcpy(buf + ilen, subsepstr, subseplen);
Packit 575503
					memcpy(buf + ilen + subseplen, "length", 7);
Packit 575503
Packit 575503
					slen = ilen + subseplen + 6;
Packit 575503
Packit 575503
					it = make_number((AWKNUM) subpat_len);
Packit 575503
					sub = make_string(buf, slen);
Packit 575503
					lhs = assoc_lookup(dest, sub);
Packit 575503
					unref(*lhs);
Packit 575503
					*lhs = it;
Packit 575503
					if (dest->astore != NULL)
Packit 575503
						(*dest->astore)(dest, sub);
Packit 575503
					unref(sub);
Packit 575503
				}
Packit 575503
			}
Packit 575503
Packit 575503
			efree(buf);
Packit 575503
		}
Packit 575503
		if (wc_indices != NULL)
Packit 575503
			efree(wc_indices);
Packit 575503
	} else {		/* match failed */
Packit 575503
		rstart = 0;
Packit 575503
		rlength = -1;
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(t1);
Packit 575503
	unref(RSTART_node->var_value);
Packit 575503
	RSTART_node->var_value = make_number((AWKNUM) rstart);
Packit 575503
	unref(RLENGTH_node->var_value);
Packit 575503
	RLENGTH_node->var_value = make_number((AWKNUM) rlength);
Packit 575503
	return make_number((AWKNUM) rstart);
Packit 575503
}
Packit 575503
Packit 575503
/* do_sub --- do the work for sub, gsub, and gensub */
Packit 575503
Packit 575503
/*
Packit 575503
 * Gsub can be tricksy; particularly when handling the case of null strings.
Packit 575503
 * The following awk code was useful in debugging problems.  It is too bad
Packit 575503
 * that it does not readily translate directly into the C code, below.
Packit 575503
 *
Packit 575503
 * #! /usr/local/bin/mawk -f
Packit 575503
 *
Packit 575503
 * BEGIN {
Packit 575503
 * 	true = 1; false = 0
Packit 575503
 * 	print "--->", mygsub("abc", "b+", "FOO")
Packit 575503
 * 	print "--->", mygsub("abc", "x*", "X")
Packit 575503
 * 	print "--->", mygsub("abc", "b*", "X")
Packit 575503
 * 	print "--->", mygsub("abc", "c", "X")
Packit 575503
 * 	print "--->", mygsub("abc", "c+", "X")
Packit 575503
 * 	print "--->", mygsub("abc", "x*$", "X")
Packit 575503
 * }
Packit 575503
 *
Packit 575503
 * function mygsub(str, regex, replace,	origstr, newstr, eosflag, nonzeroflag)
Packit 575503
 * {
Packit 575503
 * 	origstr = str;
Packit 575503
 * 	eosflag = nonzeroflag = false
Packit 575503
 * 	while (match(str, regex)) {
Packit 575503
 * 		if (RLENGTH > 0) {	# easy case
Packit 575503
 * 			nonzeroflag = true
Packit 575503
 * 			if (RSTART == 1) {	# match at front of string
Packit 575503
 * 				newstr = newstr replace
Packit 575503
 * 			} else {
Packit 575503
 * 				newstr = newstr substr(str, 1, RSTART-1) replace
Packit 575503
 * 			}
Packit 575503
 * 			str = substr(str, RSTART+RLENGTH)
Packit 575503
 * 		} else if (nonzeroflag) {
Packit 575503
 * 			# last match was non-zero in length, and at the
Packit 575503
 * 			# current character, we get a zero length match,
Packit 575503
 * 			# which we don't really want, so skip over it
Packit 575503
 * 			newstr = newstr substr(str, 1, 1)
Packit 575503
 * 			str = substr(str, 2)
Packit 575503
 * 			nonzeroflag = false
Packit 575503
 * 		} else {
Packit 575503
 * 			# 0-length match
Packit 575503
 * 			if (RSTART == 1) {
Packit 575503
 * 				newstr = newstr replace substr(str, 1, 1)
Packit 575503
 * 				str = substr(str, 2)
Packit 575503
 * 			} else {
Packit 575503
 * 				return newstr str replace
Packit 575503
 * 			}
Packit 575503
 * 		}
Packit 575503
 * 		if (length(str) == 0)
Packit 575503
 * 			if (eosflag)
Packit 575503
 * 				break
Packit 575503
 * 			else
Packit 575503
 * 				eosflag = true
Packit 575503
 * 	}
Packit 575503
 * 	if (length(str) > 0)
Packit 575503
 * 		newstr = newstr str	# rest of string
Packit 575503
 *
Packit 575503
 * 	return newstr
Packit 575503
 * }
Packit 575503
 */
Packit 575503
Packit 575503
/*
Packit 575503
 * 1/2004:  The gawk sub/gsub behavior dates from 1996, when we proposed it
Packit 575503
 * for POSIX.  The proposal fell through the cracks, and the 2001 POSIX
Packit 575503
 * standard chose a more simple behavior.
Packit 575503
 *
Packit 575503
 * The relevant text is to be found on lines 6394-6407 (pages 166, 167) of the
Packit 575503
 * 2001 standard:
Packit 575503
 *
Packit 575503
 * sub(ere, repl[, in ])
Packit 575503
 *  Substitute the string repl in place of the first instance of the
Packit 575503
 *  extended regular expression ERE in string in and return the number of
Packit 575503
 *  substitutions. An ampersand ('&') appearing in the string repl shall
Packit 575503
 *  be replaced by the string from in that matches the ERE. An ampersand
Packit 575503
 *  preceded with a backslash ('\') shall be interpreted as the literal
Packit 575503
 *  ampersand character. An occurrence of two consecutive backslashes shall
Packit 575503
 *  be interpreted as just a single literal backslash character. Any other
Packit 575503
 *  occurrence of a backslash (for example, preceding any other character)
Packit 575503
 *  shall be treated as a literal backslash character. Note that if repl is a
Packit 575503
 *  string literal (the lexical token STRING; see Grammar (on page 170)), the
Packit 575503
 *  handling of the ampersand character occurs after any lexical processing,
Packit 575503
 *  including any lexical backslash escape sequence processing. If in is
Packit 575503
 *  specified and it is not an lvalue (see Expressions in awk (on page 156)),
Packit 575503
 *  the behavior is undefined. If in is omitted, awk shall use the current
Packit 575503
 *  record ($0) in its place.
Packit 575503
 *
Packit 575503
 * 11/2010: The text in the 2008 standard is the same as just quoted.
Packit 575503
 * However, POSIX behavior is now the default.  This can change the behavior
Packit 575503
 * of awk programs.  The old behavior is not available.
Packit 575503
 *
Packit 575503
 * 7/2011: Reverted backslash handling to what it used to be. It was in
Packit 575503
 * gawk for too long. Should have known better.
Packit 575503
 */
Packit 575503
Packit 575503
/*
Packit 575503
 * NB: `howmany' conflicts with a SunOS 4.x macro in <sys/param.h>.
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_sub(int nargs, unsigned int flags)
Packit 575503
{
Packit 575503
	char *scan;
Packit 575503
	char *bp, *cp;
Packit 575503
	char *buf = NULL;
Packit 575503
	size_t buflen;
Packit 575503
	char *matchend;
Packit 575503
	size_t len;
Packit 575503
	char *matchstart;
Packit 575503
	char *text;
Packit 575503
	size_t textlen = 0;
Packit 575503
	char *repl;
Packit 575503
	char *replend;
Packit 575503
	size_t repllen;
Packit 575503
	int sofar;
Packit 575503
	int ampersands;
Packit 575503
	int matches = 0;
Packit 575503
	Regexp *rp;
Packit 575503
	NODE *rep_node;		/* replacement text */
Packit 575503
	NODE *target;		/* string to make sub. in; $0 if none given */
Packit 575503
	NODE *tmp;
Packit 575503
	NODE **lhs = NULL;
Packit 575503
	long how_many = 1;	/* one substitution for sub, also gensub default */
Packit 575503
	bool global;
Packit 575503
	long current;
Packit 575503
	bool lastmatchnonzero;
Packit 575503
	char *mb_indices = NULL;
Packit 575503
Packit 575503
	if ((flags & GENSUB) != 0) {
Packit 575503
		double d;
Packit 575503
		NODE *glob_flag;
Packit 575503
Packit 575503
		tmp = PEEK(3);
Packit 575503
		rp = re_update(tmp);
Packit 575503
Packit 575503
		target = POP_STRING();	/* original string */
Packit 575503
Packit 575503
		glob_flag = POP_SCALAR();	/* value of global flag */
Packit 575503
		if (   (glob_flag->flags & STRING) != 0
Packit 575503
		    && glob_flag->stlen > 0
Packit 575503
		    && (glob_flag->stptr[0] == 'g' || glob_flag->stptr[0] == 'G'))
Packit 575503
			how_many = -1;
Packit 575503
		else {
Packit 575503
			(void) force_number(glob_flag);
Packit 575503
			d = get_number_d(glob_flag);
Packit 575503
			if (d < 1)
Packit 575503
				how_many = 1;
Packit 575503
			else if (d < LONG_MAX)
Packit 575503
				how_many = d;
Packit 575503
			else
Packit 575503
				how_many = LONG_MAX;
Packit 575503
			if (d <= 0) {
Packit 575503
				(void) force_string(glob_flag);
Packit 575503
				warning(_("gensub: third argument `%.*s' treated as 1"),
Packit 575503
						(int) glob_flag->stlen,
Packit 575503
						glob_flag->stptr);
Packit 575503
			}
Packit 575503
		}
Packit 575503
		DEREF(glob_flag);
Packit 575503
	} else {
Packit 575503
		/* take care of regexp early, in case re_update is fatal */
Packit 575503
Packit 575503
		tmp = PEEK(2);
Packit 575503
		rp = re_update(tmp);
Packit 575503
Packit 575503
		if ((flags & GSUB) != 0)
Packit 575503
			how_many = -1;
Packit 575503
Packit 575503
		/* original string */
Packit 575503
Packit 575503
		if ((flags & LITERAL) != 0)
Packit 575503
			target = POP_STRING();
Packit 575503
		else {
Packit 575503
			lhs = POP_ADDRESS();
Packit 575503
			target = force_string(*lhs);
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	global = (how_many == -1);
Packit 575503
Packit 575503
	rep_node = POP_STRING();	/* replacement text */
Packit 575503
	decr_sp();		/* regexp, already updated above */
Packit 575503
Packit 575503
	/* do the search early to avoid work on non-match */
Packit 575503
	if (research(rp, target->stptr, 0, target->stlen, RE_NEED_START) == -1 ||
Packit 575503
			RESTART(rp, target->stptr) > target->stlen)
Packit 575503
		goto done;
Packit 575503
Packit 575503
	target->flags |= STRING;
Packit 575503
Packit 575503
	text = target->stptr;
Packit 575503
	textlen = target->stlen;
Packit 575503
Packit 575503
	repl = rep_node->stptr;
Packit 575503
	replend = repl + rep_node->stlen;
Packit 575503
	repllen = replend - repl;
Packit 575503
Packit 575503
	ampersands = 0;
Packit 575503
Packit 575503
	/*
Packit 575503
	 * Some systems' malloc() can't handle being called with an
Packit 575503
	 * argument of zero.  Thus we have to have some special case
Packit 575503
	 * code to check for `repllen == 0'.  This can occur for
Packit 575503
	 * something like:
Packit 575503
	 * 	sub(/foo/, "", mystring)
Packit 575503
	 * for example.
Packit 575503
	 */
Packit 575503
	if (gawk_mb_cur_max > 1 && repllen > 0) {
Packit 575503
		emalloc(mb_indices, char *, repllen * sizeof(char), "do_sub");
Packit 575503
		index_multibyte_buffer(repl, mb_indices, repllen);
Packit 575503
	}
Packit 575503
Packit 575503
	/* compute length of replacement string, number of ampersands */
Packit 575503
	for (scan = repl; scan < replend; scan++) {
Packit 575503
		if ((gawk_mb_cur_max == 1 || (repllen > 0 && mb_indices[scan - repl] == 1))
Packit 575503
		    && (*scan == '&')) {
Packit 575503
			repllen--;
Packit 575503
			ampersands++;
Packit 575503
		} else if (*scan == '\\') {
Packit 575503
			if ((flags & GENSUB) != 0) {	/* gensub, behave sanely */
Packit 575503
				if (isdigit((unsigned char) scan[1])) {
Packit 575503
					ampersands++;
Packit 575503
					scan++;
Packit 575503
				} else {	/* \q for any q --> q */
Packit 575503
					repllen--;
Packit 575503
					scan++;
Packit 575503
				}
Packit 575503
			} else if (do_posix) {
Packit 575503
				/* \& --> &, \\ --> \ */
Packit 575503
				if (scan[1] == '&' || scan[1] == '\\') {
Packit 575503
					repllen--;
Packit 575503
					scan++;
Packit 575503
				} /* else
Packit 575503
					leave alone, it goes into the output */
Packit 575503
			} else {
Packit 575503
				/* gawk default behavior since 1996 */
Packit 575503
				if (strncmp(scan, "\\\\\\&", 4) == 0
Packit 575503
				    || strncmp(scan, "\\\\\\\\", 4) == 0) {	/* 2016: fixed */
Packit 575503
					/* \\\& --> \& */
Packit 575503
					/* \\\\ --> \\ */
Packit 575503
					repllen -= 2;
Packit 575503
					scan += 3;
Packit 575503
				} else if (strncmp(scan, "\\\\&", 3) == 0) {
Packit 575503
					/* \\& --> \<string> */
Packit 575503
					ampersands++;
Packit 575503
					repllen--;
Packit 575503
					scan += 2;
Packit 575503
				} else if (scan[1] == '&') {
Packit 575503
					/* \& --> & */
Packit 575503
					repllen--;
Packit 575503
					scan++;
Packit 575503
				} /* else
Packit 575503
					leave alone, it goes into the output */
Packit 575503
			}
Packit 575503
		}
Packit 575503
	}
Packit 575503
Packit 575503
	lastmatchnonzero = false;
Packit 575503
Packit 575503
	/* guesstimate how much room to allocate; +1 forces > 0 */
Packit 575503
	buflen = textlen + (ampersands + 1) * repllen + 1;
Packit 575503
	emalloc(buf, char *, buflen + 1, "do_sub");
Packit 575503
	buf[buflen] = '\0';
Packit 575503
Packit 575503
	bp = buf;
Packit 575503
	for (current = 1;; current++) {
Packit 575503
		matches++;
Packit 575503
		matchstart = target->stptr + RESTART(rp, target->stptr);
Packit 575503
		matchend = target->stptr + REEND(rp, target->stptr);
Packit 575503
Packit 575503
		/*
Packit 575503
		 * create the result, copying in parts of the original
Packit 575503
		 * string. note that length of replacement string can
Packit 575503
		 * vary since ampersand is actual text of regexp match.
Packit 575503
		 */
Packit 575503
Packit 575503
		/*
Packit 575503
		 * add 1 to len to handle "empty" case where
Packit 575503
		 * matchend == matchstart and we force a match on a single
Packit 575503
		 * char.  Use 'matchend - text' instead of 'matchstart - text'
Packit 575503
		 * because we may not actually make any substitution depending
Packit 575503
		 * on the 'global' and 'how_many' values.
Packit 575503
		 */
Packit 575503
		len = matchend - text + repllen
Packit 575503
		      + ampersands * (matchend - matchstart) + 1;
Packit 575503
		sofar = bp - buf;
Packit 575503
		while (buflen < (sofar + len + 1)) {
Packit 575503
			buflen *= 2;
Packit 575503
			erealloc(buf, char *, buflen, "sub_common");
Packit 575503
			bp = buf + sofar;
Packit 575503
		}
Packit 575503
		for (scan = text; scan < matchstart; scan++)
Packit 575503
			*bp++ = *scan;
Packit 575503
		if (global || current == how_many) {
Packit 575503
			/*
Packit 575503
			 * If the current match matched the null string,
Packit 575503
			 * and the last match didn't and did a replacement,
Packit 575503
			 * and the match of the null string is at the front of
Packit 575503
			 * the text (meaning right after end of the previous
Packit 575503
			 * replacement), then skip this one.
Packit 575503
			 */
Packit 575503
			if (matchstart == matchend
Packit 575503
			    && lastmatchnonzero
Packit 575503
			    && matchstart == text) {
Packit 575503
				lastmatchnonzero = false;
Packit 575503
				matches--;
Packit 575503
				goto empty;
Packit 575503
			}
Packit 575503
			/*
Packit 575503
			 * If replacing all occurrences, or this is the
Packit 575503
			 * match we want, copy in the replacement text,
Packit 575503
			 * making substitutions as we go.
Packit 575503
			 */
Packit 575503
			for (scan = repl; scan < replend; scan++)
Packit 575503
				if (*scan == '&'
Packit 575503
					/*
Packit 575503
					 * Don't test repllen here. A simple "&" could
Packit 575503
					 * end up with repllen == 0.
Packit 575503
					 */
Packit 575503
					&& (gawk_mb_cur_max == 1
Packit 575503
						|| mb_indices[scan - repl] == 1)
Packit 575503
				) {
Packit 575503
						for (cp = matchstart; cp < matchend; cp++)
Packit 575503
								*bp++ = *cp;
Packit 575503
				} else if (*scan == '\\'
Packit 575503
					&& (gawk_mb_cur_max == 1
Packit 575503
						|| (repllen > 0 && mb_indices[scan - repl] == 1))
Packit 575503
				) {
Packit 575503
					if (flags & GENSUB) {	/* gensub, behave sanely */
Packit 575503
						if (isdigit((unsigned char) scan[1])) {
Packit 575503
							int dig = scan[1] - '0';
Packit 575503
							if (dig < NUMSUBPATS(rp, target->stptr) && SUBPATSTART(rp, tp->stptr, dig) != -1) {
Packit 575503
								char *start, *end;
Packit 575503
Packit 575503
								start = target->stptr
Packit 575503
								      + SUBPATSTART(rp, target->stptr, dig);
Packit 575503
								end = target->stptr
Packit 575503
								      + SUBPATEND(rp, target->stptr, dig);
Packit 575503
Packit 575503
								for (cp = start; cp < end; cp++)
Packit 575503
									*bp++ = *cp;
Packit 575503
							}
Packit 575503
							scan++;
Packit 575503
						} else	/* \q for any q --> q */
Packit 575503
							*bp++ = *++scan;
Packit 575503
					} else if (do_posix) {
Packit 575503
						/* \& --> &, \\ --> \ */
Packit 575503
						if (scan[1] == '&' || scan[1] == '\\')
Packit 575503
							scan++;
Packit 575503
						*bp++ = *scan;
Packit 575503
					} else {
Packit 575503
						/* gawk default behavior since 1996 */
Packit 575503
						if (strncmp(scan, "\\\\\\&", 4) == 0
Packit 575503
						    || strncmp(scan, "\\\\\\\\", 4) == 0) {	/* 2016: fixed */
Packit 575503
							/* \\\& --> \& */
Packit 575503
							/* \\\\ --> \\ */
Packit 575503
							*bp++ = '\\';
Packit 575503
							*bp++ = scan[3];
Packit 575503
							scan += 3;
Packit 575503
						} else if (strncmp(scan, "\\\\&", 3) == 0) {
Packit 575503
							/* \\& --> \<string> */
Packit 575503
							*bp++ = '\\';
Packit 575503
							for (cp = matchstart; cp < matchend; cp++)
Packit 575503
								*bp++ = *cp;
Packit 575503
							scan += 2;
Packit 575503
						} else if (scan[1] == '&') {
Packit 575503
							/* \& --> & */
Packit 575503
							*bp++ = '&';
Packit 575503
							scan++;
Packit 575503
						} else
Packit 575503
							*bp++ = *scan;
Packit 575503
					}
Packit 575503
				} else
Packit 575503
					*bp++ = *scan;
Packit 575503
			if (matchstart != matchend)
Packit 575503
				lastmatchnonzero = true;
Packit 575503
		} else {
Packit 575503
			/*
Packit 575503
			 * don't want this match, skip over it by copying
Packit 575503
			 * in current text.
Packit 575503
			 */
Packit 575503
			for (cp = matchstart; cp < matchend; cp++)
Packit 575503
				*bp++ = *cp;
Packit 575503
		}
Packit 575503
	empty:
Packit 575503
		/* catch the case of gsub(//, "blah", whatever), i.e. empty regexp */
Packit 575503
		if (matchstart == matchend && matchend < text + textlen) {
Packit 575503
			*bp++ = *matchend;
Packit 575503
			matchend++;
Packit 575503
		}
Packit 575503
		textlen = text + textlen - matchend;
Packit 575503
		text = matchend;
Packit 575503
Packit 575503
#if 0
Packit 575503
		if (bp - buf > sofar + len)
Packit 575503
			fprintf(stderr, "debug: len = %zu, but used %ld\n", len, (long)((bp - buf) - (long)sofar));
Packit 575503
#endif
Packit 575503
Packit 575503
		if ((current >= how_many && ! global)
Packit 575503
		    || ((long) textlen <= 0 && matchstart == matchend)
Packit 575503
		    || research(rp, target->stptr, text - target->stptr, textlen, RE_NEED_START) == -1)
Packit 575503
			break;
Packit 575503
Packit 575503
	}
Packit 575503
	sofar = bp - buf;
Packit 575503
	if (buflen < (sofar + textlen + 1)) {
Packit 575503
		buflen = sofar + textlen + 1;
Packit 575503
		erealloc(buf, char *, buflen, "do_sub");
Packit 575503
		bp = buf + sofar;
Packit 575503
	}
Packit 575503
	/*
Packit 575503
	 * Note that text == matchend, since that assignment is made before
Packit 575503
	 * exiting the 'for' loop above. Thus we copy in the rest of the
Packit 575503
	 * original string.
Packit 575503
	 */
Packit 575503
	for (scan = text; scan < text + textlen; scan++)
Packit 575503
		*bp++ = *scan;
Packit 575503
	*bp = '\0';
Packit 575503
	textlen = bp - buf;
Packit 575503
Packit 575503
	if (mb_indices != NULL)
Packit 575503
		efree(mb_indices);
Packit 575503
Packit 575503
done:
Packit 575503
	DEREF(rep_node);
Packit 575503
Packit 575503
	if ((matches == 0 || (flags & LITERAL) != 0) && buf != NULL) {
Packit 575503
		efree(buf);
Packit 575503
		buf = NULL;
Packit 575503
	}
Packit 575503
Packit 575503
	if (flags & GENSUB) {
Packit 575503
		if (matches > 0) {
Packit 575503
			/* return the result string */
Packit 575503
			DEREF(target);
Packit 575503
			assert(buf != NULL);
Packit 575503
			return make_str_node(buf, textlen, ALREADY_MALLOCED);
Packit 575503
		}
Packit 575503
Packit 575503
		/* return the original string */
Packit 575503
		return target;
Packit 575503
	}
Packit 575503
Packit 575503
	/* For a string literal, must not change the original string. */
Packit 575503
	if ((flags & LITERAL) != 0)
Packit 575503
		DEREF(target);
Packit 575503
	else if (matches > 0) {
Packit 575503
		unref(*lhs);
Packit 575503
		*lhs = make_str_node(buf, textlen, ALREADY_MALLOCED);
Packit 575503
	}
Packit 575503
Packit 575503
	return make_number((AWKNUM) matches);
Packit 575503
}
Packit 575503
Packit 575503
/* call_sub --- call do_sub indirectly */
Packit 575503
Packit 575503
NODE *
Packit 575503
call_sub(const char *name, int nargs)
Packit 575503
{
Packit 575503
	unsigned int flags = 0;
Packit 575503
	NODE *regex, *replace, *glob_flag;
Packit 575503
	NODE **lhs, *rhs;
Packit 575503
	NODE *zero = make_number(0.0);
Packit 575503
	NODE *result;
Packit 575503
Packit 575503
	if (name[0] == 'g') {
Packit 575503
		if (name[1] == 'e')
Packit 575503
			flags = GENSUB;
Packit 575503
		else
Packit 575503
			flags = GSUB;
Packit 575503
	}
Packit 575503
Packit 575503
	if (flags == 0 || flags == GSUB) {
Packit 575503
		/* sub or gsub */
Packit 575503
		if (nargs != 2)
Packit 575503
			fatal(_("%s: can be called indirectly only with two arguments"), name);
Packit 575503
Packit 575503
		replace = POP_STRING();
Packit 575503
		regex = POP();	/* the regex */
Packit 575503
		/*
Packit 575503
		 * push regex
Packit 575503
		 * push replace
Packit 575503
		 * push $0
Packit 575503
		 */
Packit 575503
		if ((regex->flags & REGEX) != 0)
Packit 575503
			regex = regex->typed_re;
Packit 575503
		else
Packit 575503
			regex = make_regnode(Node_regex, regex);
Packit 575503
		PUSH(regex);
Packit 575503
		PUSH(replace);
Packit 575503
		lhs = r_get_field(zero, (Func_ptr *) 0, true);
Packit 575503
		nargs++;
Packit 575503
		PUSH_ADDRESS(lhs);
Packit 575503
	} else {
Packit 575503
		/* gensub */
Packit 575503
		if (nargs == 4)
Packit 575503
			rhs = POP();
Packit 575503
		else
Packit 575503
			rhs = NULL;
Packit 575503
		glob_flag = POP_STRING();
Packit 575503
		replace = POP_STRING();
Packit 575503
		regex = POP();	/* the regex */
Packit 575503
		/*
Packit 575503
		 * push regex
Packit 575503
		 * push replace
Packit 575503
		 * push glob_flag
Packit 575503
		 * if (nargs = 3) {
Packit 575503
		 *	 push $0
Packit 575503
		 *	 nargs++
Packit 575503
		 * }
Packit 575503
		 */
Packit 575503
		if ((regex->flags & REGEX) != 0)
Packit 575503
			regex = regex->typed_re;
Packit 575503
		else
Packit 575503
			regex = make_regnode(Node_regex, regex);
Packit 575503
		PUSH(regex);
Packit 575503
		PUSH(replace);
Packit 575503
		PUSH(glob_flag);
Packit 575503
		if (rhs == NULL) {
Packit 575503
			lhs = r_get_field(zero, (Func_ptr *) 0, true);
Packit 575503
			rhs = *lhs;
Packit 575503
			UPREF(rhs);
Packit 575503
			PUSH(rhs);
Packit 575503
			nargs++;
Packit 575503
		}
Packit 575503
		else
Packit 575503
			PUSH(rhs);
Packit 575503
	}
Packit 575503
Packit 575503
	unref(zero);
Packit 575503
	result = do_sub(nargs, flags);
Packit 575503
	if (flags != GENSUB)
Packit 575503
		reset_record();
Packit 575503
	return result;
Packit 575503
}
Packit 575503
Packit 575503
/* call_match --- call do_match indirectly */
Packit 575503
Packit 575503
NODE *
Packit 575503
call_match(int nargs)
Packit 575503
{
Packit 575503
	NODE *regex, *text, *array;
Packit 575503
	NODE *result;
Packit 575503
Packit 575503
	regex = text = array = NULL;
Packit 575503
	if (nargs == 3)
Packit 575503
		array = POP();
Packit 575503
	regex = POP();
Packit 575503
Packit 575503
	/* Don't need to pop the string just to push it back ... */
Packit 575503
Packit 575503
	if ((regex->flags & REGEX) != 0)
Packit 575503
		regex = regex->typed_re;
Packit 575503
	else
Packit 575503
		regex = make_regnode(Node_regex, regex);
Packit 575503
Packit 575503
	PUSH(regex);
Packit 575503
Packit 575503
	if (array)
Packit 575503
		PUSH(array);
Packit 575503
Packit 575503
	result = do_match(nargs);
Packit 575503
	return result;
Packit 575503
}
Packit 575503
Packit 575503
/* call_split_func --- call do_split or do_pat_split indirectly */
Packit 575503
Packit 575503
NODE *
Packit 575503
call_split_func(const char *name, int nargs)
Packit 575503
{
Packit 575503
	NODE *regex, *seps;
Packit 575503
	NODE *result;
Packit 575503
Packit 575503
	regex = seps = NULL;
Packit 575503
	if (nargs < 2)
Packit 575503
		fatal(_("indirect call to %s requires at least two arguments"),
Packit 575503
				name);
Packit 575503
Packit 575503
	if (nargs == 4)
Packit 575503
		seps = POP();
Packit 575503
Packit 575503
	if (nargs >= 3) {
Packit 575503
		regex = POP_STRING();
Packit 575503
		if ((regex->flags & REGEX) != 0)
Packit 575503
			regex = regex->typed_re;
Packit 575503
		else
Packit 575503
			regex = make_regnode(Node_regex, regex);
Packit 575503
	} else {
Packit 575503
		if (name[0] == 's') {
Packit 575503
			regex = make_regnode(Node_regex, FS_node->var_value);
Packit 575503
			regex->re_flags |= FS_DFLT;
Packit 575503
		} else
Packit 575503
			regex = make_regnode(Node_regex, FPAT_node->var_value);
Packit 575503
		nargs++;
Packit 575503
	}
Packit 575503
Packit 575503
	/* Don't need to pop the string or the data array */
Packit 575503
Packit 575503
	PUSH(regex);
Packit 575503
Packit 575503
	if (seps)
Packit 575503
		PUSH(seps);
Packit 575503
Packit 575503
	result = (name[0] == 's') ? do_split(nargs) : do_patsplit(nargs);
Packit 575503
Packit 575503
	return result;
Packit 575503
}
Packit 575503
Packit 575503
/* make_integer - Convert an integer to a number node.  */
Packit 575503
Packit 575503
static NODE *
Packit 575503
make_integer(uintmax_t n)
Packit 575503
{
Packit 575503
	n = adjust_uint(n);
Packit 575503
Packit 575503
	return make_number((AWKNUM) n);
Packit 575503
}
Packit 575503
Packit 575503
/* do_lshift --- perform a << operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_lshift(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1, *s2;
Packit 575503
	uintmax_t uval, ushift, res;
Packit 575503
	AWKNUM val, shift;
Packit 575503
Packit 575503
	POP_TWO_SCALARS(s1, s2);
Packit 575503
	if (do_lint) {
Packit 575503
		if ((fixtype(s1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("lshift: received non-numeric first argument"));
Packit 575503
		if ((fixtype(s2)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("lshift: received non-numeric second argument"));
Packit 575503
	}
Packit 575503
Packit 575503
	val = force_number(s1)->numbr;
Packit 575503
	shift = force_number(s2)->numbr;
Packit 575503
	if (val < 0 || shift < 0)
Packit 575503
		fatal(_("lshift(%f, %f): negative values are not allowed"), val, shift);
Packit 575503
Packit 575503
	if (do_lint) {
Packit 575503
		if (double_to_int(val) != val || double_to_int(shift) != shift)
Packit 575503
			lintwarn(_("lshift(%f, %f): fractional values will be truncated"), val, shift);
Packit 575503
		if (shift >= sizeof(uintmax_t) * CHAR_BIT)
Packit 575503
			lintwarn(_("lshift(%f, %f): too large shift value will give strange results"), val, shift);
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(s1);
Packit 575503
	DEREF(s2);
Packit 575503
Packit 575503
	uval = (uintmax_t) val;
Packit 575503
	ushift = (uintmax_t) shift;
Packit 575503
Packit 575503
	res = uval << ushift;
Packit 575503
	return make_integer(res);
Packit 575503
}
Packit 575503
Packit 575503
/* do_rshift --- perform a >> operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_rshift(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1, *s2;
Packit 575503
	uintmax_t uval, ushift, res;
Packit 575503
	AWKNUM val, shift;
Packit 575503
Packit 575503
	POP_TWO_SCALARS(s1, s2);
Packit 575503
	if (do_lint) {
Packit 575503
		if ((fixtype(s1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("rshift: received non-numeric first argument"));
Packit 575503
		if ((fixtype(s2)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("rshift: received non-numeric second argument"));
Packit 575503
	}
Packit 575503
Packit 575503
	val = force_number(s1)->numbr;
Packit 575503
	shift = force_number(s2)->numbr;
Packit 575503
	if (val < 0 || shift < 0)
Packit 575503
		fatal(_("rshift(%f, %f): negative values are not allowed"), val, shift);
Packit 575503
Packit 575503
	if (do_lint) {
Packit 575503
		if (double_to_int(val) != val || double_to_int(shift) != shift)
Packit 575503
			lintwarn(_("rshift(%f, %f): fractional values will be truncated"), val, shift);
Packit 575503
		if (shift >= sizeof(uintmax_t) * CHAR_BIT)
Packit 575503
			lintwarn(_("rshift(%f, %f): too large shift value will give strange results"), val, shift);
Packit 575503
	}
Packit 575503
Packit 575503
	DEREF(s1);
Packit 575503
	DEREF(s2);
Packit 575503
Packit 575503
	uval = (uintmax_t) val;
Packit 575503
	ushift = (uintmax_t) shift;
Packit 575503
Packit 575503
	res = uval >> ushift;
Packit 575503
	return make_integer(res);
Packit 575503
}
Packit 575503
Packit 575503
/* do_and --- perform an & operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_and(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1;
Packit 575503
	uintmax_t res, uval;
Packit 575503
	AWKNUM val;
Packit 575503
	int i;
Packit 575503
Packit 575503
	res = ~0;	/* start off with all ones */
Packit 575503
	if (nargs < 2)
Packit 575503
		fatal(_("and: called with less than two arguments"));
Packit 575503
Packit 575503
	for (i = 1; nargs > 0; nargs--, i++) {
Packit 575503
		s1 = POP_SCALAR();
Packit 575503
		if (do_lint && (fixtype(s1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("and: argument %d is non-numeric"), i);
Packit 575503
Packit 575503
		val = force_number(s1)->numbr;
Packit 575503
		if (val < 0)
Packit 575503
			fatal(_("and: argument %d negative value %g is not allowed"), i, val);
Packit 575503
Packit 575503
		uval = (uintmax_t) val;
Packit 575503
		res &= uval;
Packit 575503
Packit 575503
		DEREF(s1);
Packit 575503
	}
Packit 575503
Packit 575503
	return make_integer(res);
Packit 575503
}
Packit 575503
Packit 575503
/* do_or --- perform an | operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_or(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1;
Packit 575503
	uintmax_t res, uval;
Packit 575503
	AWKNUM val;
Packit 575503
	int i;
Packit 575503
Packit 575503
	res = 0;
Packit 575503
	if (nargs < 2)
Packit 575503
		fatal(_("or: called with less than two arguments"));
Packit 575503
Packit 575503
	for (i = 1; nargs > 0; nargs--, i++) {
Packit 575503
		s1 = POP_SCALAR();
Packit 575503
		if (do_lint && (fixtype(s1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("or: argument %d is non-numeric"), i);
Packit 575503
Packit 575503
		val = force_number(s1)->numbr;
Packit 575503
		if (val < 0)
Packit 575503
			fatal(_("or: argument %d negative value %g is not allowed"), i, val);
Packit 575503
Packit 575503
		uval = (uintmax_t) val;
Packit 575503
		res |= uval;
Packit 575503
Packit 575503
		DEREF(s1);
Packit 575503
	}
Packit 575503
Packit 575503
	return make_integer(res);
Packit 575503
}
Packit 575503
Packit 575503
/* do_xor --- perform an ^ operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_xor(int nargs)
Packit 575503
{
Packit 575503
	NODE *s1;
Packit 575503
	uintmax_t res, uval;
Packit 575503
	AWKNUM val;
Packit 575503
	int i;
Packit 575503
Packit 575503
	if (nargs < 2)
Packit 575503
		fatal(_("xor: called with less than two arguments"));
Packit 575503
Packit 575503
	res = 0;	/* silence compiler warning */
Packit 575503
	for (i = 1; nargs > 0; nargs--, i++) {
Packit 575503
		s1 = POP_SCALAR();
Packit 575503
		if (do_lint && (fixtype(s1)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("xor: argument %d is non-numeric"), i);
Packit 575503
Packit 575503
		val = force_number(s1)->numbr;
Packit 575503
		if (val < 0)
Packit 575503
			fatal(_("xor: argument %d negative value %g is not allowed"), i, val);
Packit 575503
Packit 575503
		uval = (uintmax_t) val;
Packit 575503
		if (i == 1)
Packit 575503
			res = uval;
Packit 575503
		else
Packit 575503
			res ^= uval;
Packit 575503
Packit 575503
		DEREF(s1);
Packit 575503
	}
Packit 575503
Packit 575503
	return make_integer(res);
Packit 575503
}
Packit 575503
Packit 575503
/* do_compl --- perform a ~ operation */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_compl(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	double d;
Packit 575503
	uintmax_t uval;
Packit 575503
Packit 575503
	tmp = POP_SCALAR();
Packit 575503
	if (do_lint && (fixtype(tmp)->flags & NUMBER) == 0)
Packit 575503
		lintwarn(_("compl: received non-numeric argument"));
Packit 575503
	d = force_number(tmp)->numbr;
Packit 575503
	DEREF(tmp);
Packit 575503
Packit 575503
	if (d < 0)
Packit 575503
		fatal(_("compl(%f): negative value is not allowed"), d);
Packit 575503
Packit 575503
	if (do_lint && double_to_int(d) != d)
Packit 575503
		lintwarn(_("compl(%f): fractional value will be truncated"), d);
Packit 575503
Packit 575503
	uval = (uintmax_t) d;
Packit 575503
	uval = ~ uval;
Packit 575503
	return make_integer(uval);
Packit 575503
}
Packit 575503
Packit 575503
/* do_strtonum --- the strtonum function */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_strtonum(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp;
Packit 575503
	AWKNUM d;
Packit 575503
Packit 575503
	tmp = fixtype(POP_SCALAR());
Packit 575503
	if ((tmp->flags & NUMBER) != 0)
Packit 575503
		d = (AWKNUM) tmp->numbr;
Packit 575503
	else if (get_numbase(tmp->stptr, tmp->stlen, use_lc_numeric) != 10)
Packit 575503
		d = nondec2awknum(tmp->stptr, tmp->stlen, NULL);
Packit 575503
	else
Packit 575503
		d = (AWKNUM) force_number(tmp)->numbr;
Packit 575503
Packit 575503
	DEREF(tmp);
Packit 575503
	return make_number((AWKNUM) d);
Packit 575503
}
Packit 575503
Packit 575503
/* nondec2awknum --- convert octal or hex value to double */
Packit 575503
Packit 575503
/*
Packit 575503
 * Because of awk's concatenation rules and the way awk.y:yylex()
Packit 575503
 * collects a number, this routine has to be willing to stop on the
Packit 575503
 * first invalid character.
Packit 575503
 */
Packit 575503
Packit 575503
AWKNUM
Packit 575503
nondec2awknum(char *str, size_t len, char **endptr)
Packit 575503
{
Packit 575503
	AWKNUM retval = 0.0;
Packit 575503
	char save;
Packit 575503
	short val;
Packit 575503
	char *start = str;
Packit 575503
Packit 575503
	if (len >= 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) {
Packit 575503
		/*
Packit 575503
		 * User called strtonum("0x") or some such,
Packit 575503
		 * so just quit early.
Packit 575503
		 */
Packit 575503
		if (len <= 2) {
Packit 575503
			if (endptr)
Packit 575503
				*endptr = start;
Packit 575503
			return (AWKNUM) 0.0;
Packit 575503
		}
Packit 575503
Packit 575503
		for (str += 2, len -= 2; len > 0; len--, str++) {
Packit 575503
			switch (*str) {
Packit 575503
			case '0':
Packit 575503
			case '1':
Packit 575503
			case '2':
Packit 575503
			case '3':
Packit 575503
			case '4':
Packit 575503
			case '5':
Packit 575503
			case '6':
Packit 575503
			case '7':
Packit 575503
			case '8':
Packit 575503
			case '9':
Packit 575503
				val = *str - '0';
Packit 575503
				break;
Packit 575503
			case 'a':
Packit 575503
			case 'b':
Packit 575503
			case 'c':
Packit 575503
			case 'd':
Packit 575503
			case 'e':
Packit 575503
			case 'f':
Packit 575503
				val = *str - 'a' + 10;
Packit 575503
				break;
Packit 575503
			case 'A':
Packit 575503
			case 'B':
Packit 575503
			case 'C':
Packit 575503
			case 'D':
Packit 575503
			case 'E':
Packit 575503
			case 'F':
Packit 575503
				val = *str - 'A' + 10;
Packit 575503
				break;
Packit 575503
			default:
Packit 575503
				if (endptr)
Packit 575503
					*endptr = str;
Packit 575503
				goto done;
Packit 575503
			}
Packit 575503
			retval = (retval * 16) + val;
Packit 575503
		}
Packit 575503
		if (endptr)
Packit 575503
			*endptr = str;
Packit 575503
	} else if (len >= 1 && *str == '0') {
Packit 575503
		for (; len > 0; len--) {
Packit 575503
			if (! isdigit((unsigned char) *str)) {
Packit 575503
				if (endptr)
Packit 575503
					*endptr = str;
Packit 575503
				goto done;
Packit 575503
			}
Packit 575503
			else if (*str == '8' || *str == '9') {
Packit 575503
				str = start;
Packit 575503
				goto decimal;
Packit 575503
			}
Packit 575503
			retval = (retval * 8) + (*str - '0');
Packit 575503
			str++;
Packit 575503
		}
Packit 575503
		if (endptr)
Packit 575503
			*endptr = str;
Packit 575503
	} else {
Packit 575503
decimal:
Packit 575503
		save = str[len];
Packit 575503
		str[len] = '\0';
Packit 575503
		retval = strtod(str, endptr);
Packit 575503
		str[len] = save;
Packit 575503
	}
Packit 575503
done:
Packit 575503
	return retval;
Packit 575503
}
Packit 575503
Packit 575503
/* do_dcgettext, do_dcngettext --- handle i18n translations */
Packit 575503
Packit 575503
#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
Packit 575503
Packit 575503
static int
Packit 575503
localecategory_from_argument(NODE *t)
Packit 575503
{
Packit 575503
	static const struct category_table {
Packit 575503
		int val;
Packit 575503
		const char *name;
Packit 575503
	} cat_tab[] = {
Packit 575503
#ifdef LC_ALL
Packit 575503
		{ LC_ALL,	"LC_ALL" },
Packit 575503
#endif /* LC_ALL */
Packit 575503
#ifdef LC_COLLATE
Packit 575503
		{ LC_COLLATE,	"LC_COLLATE" },
Packit 575503
#endif /* LC_COLLATE */
Packit 575503
#ifdef LC_CTYPE
Packit 575503
		{ LC_CTYPE,	"LC_CTYPE" },
Packit 575503
#endif /* LC_CTYPE */
Packit 575503
#ifdef LC_MESSAGES
Packit 575503
		{ LC_MESSAGES,	"LC_MESSAGES" },
Packit 575503
#endif /* LC_MESSAGES */
Packit 575503
#ifdef LC_MONETARY
Packit 575503
		{ LC_MONETARY,	"LC_MONETARY" },
Packit 575503
#endif /* LC_MONETARY */
Packit 575503
#ifdef LC_NUMERIC
Packit 575503
		{ LC_NUMERIC,	"LC_NUMERIC" },
Packit 575503
#endif /* LC_NUMERIC */
Packit 575503
#ifdef LC_RESPONSE
Packit 575503
		{ LC_RESPONSE,	"LC_RESPONSE" },
Packit 575503
#endif /* LC_RESPONSE */
Packit 575503
#ifdef LC_TIME
Packit 575503
		{ LC_TIME,	"LC_TIME" },
Packit 575503
#endif /* LC_TIME */
Packit 575503
	};
Packit 575503
Packit 575503
	if (t != NULL) {
Packit 575503
		int low, high, i, mid;
Packit 575503
		char *category;
Packit 575503
		int lc_cat = -1;
Packit 575503
Packit 575503
		char save = t->stptr[t->stlen];
Packit 575503
		t->stptr[t->stlen] = '\0';
Packit 575503
		category = t->stptr;
Packit 575503
Packit 575503
		/* binary search the table */
Packit 575503
		low = 0;
Packit 575503
		high = (sizeof(cat_tab) / sizeof(cat_tab[0])) - 1;
Packit 575503
		while (low <= high) {
Packit 575503
			mid = (low + high) / 2;
Packit 575503
			i = strcmp(category, cat_tab[mid].name);
Packit 575503
Packit 575503
			if (i < 0)		/* category < mid */
Packit 575503
				high = mid - 1;
Packit 575503
			else if (i > 0)		/* category > mid */
Packit 575503
				low = mid + 1;
Packit 575503
			else {
Packit 575503
				lc_cat = cat_tab[mid].val;
Packit 575503
				break;
Packit 575503
			}
Packit 575503
		}
Packit 575503
		t->stptr[t->stlen] = save;
Packit 575503
		if (lc_cat == -1)	/* not there */
Packit 575503
			fatal(_("dcgettext: `%s' is not a valid locale category"), category);
Packit 575503
Packit 575503
		return lc_cat;
Packit 575503
	} else
Packit 575503
		return LC_MESSAGES;
Packit 575503
}
Packit 575503
Packit 575503
#endif
Packit 575503
Packit 575503
/*
Packit 575503
 * awk usage is
Packit 575503
 *
Packit 575503
 * 	str = dcgettext(string [, domain [, category]])
Packit 575503
 * 	str = dcngettext(string1, string2, number [, domain [, category]])
Packit 575503
 *
Packit 575503
 * Default domain is TEXTDOMAIN, default category is LC_MESSAGES.
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_dcgettext(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp, *t1, *t2 = NULL;
Packit 575503
	char *string;
Packit 575503
	char *the_result;
Packit 575503
	size_t reslen;
Packit 575503
#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
Packit 575503
	int lc_cat;
Packit 575503
	char *domain;
Packit 575503
	char save1 = '\0', save2 = '\0';
Packit 575503
Packit 575503
	if (nargs == 3) {	/* third argument */
Packit 575503
		tmp = POP_STRING();
Packit 575503
		lc_cat = localecategory_from_argument(tmp);
Packit 575503
		DEREF(tmp);
Packit 575503
	} else
Packit 575503
		lc_cat = LC_MESSAGES;
Packit 575503
Packit 575503
	if (nargs >= 2) {  /* second argument */
Packit 575503
		t2 = POP_STRING();
Packit 575503
		domain = t2->stptr;
Packit 575503
		str_terminate(t2, save2);
Packit 575503
	} else
Packit 575503
		domain = TEXTDOMAIN;
Packit 575503
#else
Packit 575503
	if (nargs == 3) {
Packit 575503
		tmp = POP_STRING();
Packit 575503
		DEREF(tmp);
Packit 575503
	}
Packit 575503
	if (nargs >= 2) {
Packit 575503
		t2 = POP_STRING();
Packit 575503
		DEREF(t2);
Packit 575503
	}
Packit 575503
#endif
Packit 575503
Packit 575503
	t1 = POP_STRING();	/* first argument */
Packit 575503
	string = t1->stptr;
Packit 575503
Packit 575503
#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
Packit 575503
	str_terminate(t1, save1);
Packit 575503
	the_result = dcgettext(domain, string, lc_cat);
Packit 575503
	str_restore(t1, save1);
Packit 575503
	if (t2 != NULL) {
Packit 575503
		str_restore(t2, save2);
Packit 575503
		DEREF(t2);
Packit 575503
	}
Packit 575503
	reslen = strlen(the_result);
Packit 575503
#else
Packit 575503
	the_result = string;
Packit 575503
	reslen = t1->stlen;
Packit 575503
#endif
Packit 575503
	DEREF(t1);
Packit 575503
	return make_string(the_result, reslen);
Packit 575503
}
Packit 575503
Packit 575503
Packit 575503
NODE *
Packit 575503
do_dcngettext(int nargs)
Packit 575503
{
Packit 575503
	NODE *tmp, *t1, *t2, *t3;
Packit 575503
	char *string1, *string2;
Packit 575503
	unsigned long number;
Packit 575503
	AWKNUM d;
Packit 575503
	char *the_result;
Packit 575503
	size_t reslen;
Packit 575503
Packit 575503
#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
Packit 575503
	int lc_cat;
Packit 575503
	char *domain;
Packit 575503
	char save = '\0', save1 = '\0', save2 = '\0';
Packit 575503
	bool saved_end = false;
Packit 575503
Packit 575503
	if (nargs == 5) {	/* fifth argument */
Packit 575503
		tmp = POP_STRING();
Packit 575503
		lc_cat = localecategory_from_argument(tmp);
Packit 575503
		DEREF(tmp);
Packit 575503
	} else
Packit 575503
		lc_cat = LC_MESSAGES;
Packit 575503
Packit 575503
	t3 = NULL;
Packit 575503
	if (nargs >= 4) {	/* fourth argument */
Packit 575503
		t3 = POP_STRING();
Packit 575503
		domain = t3->stptr;
Packit 575503
		save = domain[t3->stlen];
Packit 575503
		domain[t3->stlen] = '\0';
Packit 575503
		saved_end = true;
Packit 575503
	} else
Packit 575503
		domain = TEXTDOMAIN;
Packit 575503
#else
Packit 575503
	if (nargs == 5) {
Packit 575503
		tmp = POP_STRING();
Packit 575503
		DEREF(tmp);
Packit 575503
	}
Packit 575503
	if (nargs >= 4) {
Packit 575503
		t3 = POP_STRING();
Packit 575503
		DEREF(t3);
Packit 575503
	}
Packit 575503
#endif
Packit 575503
Packit 575503
	t2 = POP_NUMBER();	/* third argument */
Packit 575503
	d = get_number_d(t2);
Packit 575503
	DEREF(t2);
Packit 575503
Packit 575503
	number = (unsigned long) double_to_int(d);
Packit 575503
	t2 = POP_STRING();	/* second argument */
Packit 575503
	string2 = t2->stptr;
Packit 575503
	t1 = POP_STRING();	/* first argument */
Packit 575503
	string1 = t1->stptr;
Packit 575503
Packit 575503
#if ENABLE_NLS && defined(LC_MESSAGES) && HAVE_DCGETTEXT
Packit 575503
Packit 575503
	str_terminate(t1, save1);
Packit 575503
	str_terminate(t2, save2);
Packit 575503
	the_result = dcngettext(domain, string1, string2, number, lc_cat);
Packit 575503
	reslen = strlen(the_result);
Packit 575503
	str_restore(t1, save1);
Packit 575503
	str_restore(t2, save2);
Packit 575503
	if (saved_end)
Packit 575503
		domain[t3->stlen] = save;
Packit 575503
	if (t3 != NULL)
Packit 575503
		DEREF(t3);
Packit 575503
#else
Packit 575503
	if (number == 1) {
Packit 575503
		the_result = string1;
Packit 575503
		reslen = t1->stlen;
Packit 575503
	} else {
Packit 575503
		the_result = string2;
Packit 575503
		reslen = t2->stlen;
Packit 575503
	}
Packit 575503
#endif
Packit 575503
	DEREF(t1);
Packit 575503
	DEREF(t2);
Packit 575503
	return make_string(the_result, reslen);
Packit 575503
}
Packit 575503
Packit 575503
/* do_bindtextdomain --- set the directory for a text domain */
Packit 575503
Packit 575503
/*
Packit 575503
 * awk usage is
Packit 575503
 *
Packit 575503
 * 	binding = bindtextdomain(dir [, domain])
Packit 575503
 *
Packit 575503
 * If dir is "", pass NULL to C version.
Packit 575503
 * Default domain is TEXTDOMAIN.
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_bindtextdomain(int nargs)
Packit 575503
{
Packit 575503
	NODE *t1, *t2;
Packit 575503
	const char *directory, *domain;
Packit 575503
	const char *the_result;
Packit 575503
Packit 575503
	t1 = t2 = NULL;
Packit 575503
	/* set defaults */
Packit 575503
	directory = NULL;
Packit 575503
	domain = TEXTDOMAIN;
Packit 575503
	char save = '\0', save1 = '\0';
Packit 575503
Packit 575503
	if (nargs == 2) {	/* second argument */
Packit 575503
		t2 = POP_STRING();
Packit 575503
		domain = (const char *) t2->stptr;
Packit 575503
		save = t2->stptr[t2->stlen];
Packit 575503
		t2->stptr[t2->stlen] = '\0';
Packit 575503
	}
Packit 575503
Packit 575503
	/* first argument */
Packit 575503
	t1 = POP_STRING();
Packit 575503
	if (t1->stlen > 0) {
Packit 575503
		directory = (const char *) t1->stptr;
Packit 575503
		str_terminate(t1, save1);
Packit 575503
	}
Packit 575503
Packit 575503
	the_result = bindtextdomain(domain, directory);
Packit 575503
	if (directory)
Packit 575503
		str_restore(t1, save1);
Packit 575503
Packit 575503
	DEREF(t1);
Packit 575503
	if (t2 != NULL) {
Packit 575503
		t2->stptr[t2->stlen] = save;
Packit 575503
		DEREF(t2);
Packit 575503
	}
Packit 575503
Packit 575503
	return make_string(the_result, strlen(the_result));
Packit 575503
}
Packit 575503
Packit 575503
#ifdef SUPPLY_INTDIV
Packit 575503
/* do_intdiv --- do integer division, return quotient and remainder in dest array */
Packit 575503
Packit 575503
/*
Packit 575503
 * We define the semantics as:
Packit 575503
 * 	numerator = int(numerator)
Packit 575503
 *	denominator = int(denonmator)
Packit 575503
 *	quotient = int(numerator / denomator)
Packit 575503
 *	remainder = int(numerator % denomator)
Packit 575503
 */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_intdiv(int nargs)
Packit 575503
{
Packit 575503
	NODE *numerator, *denominator, *result;
Packit 575503
	double num, denom, quotient, remainder;
Packit 575503
	NODE *sub, **lhs;
Packit 575503
Packit 575503
	result = POP_PARAM();
Packit 575503
	if (result->type != Node_var_array)
Packit 575503
		fatal(_("intdiv: third argument is not an array"));
Packit 575503
	assoc_clear(result);
Packit 575503
Packit 575503
	denominator = POP_SCALAR();
Packit 575503
	numerator = POP_SCALAR();
Packit 575503
Packit 575503
	if (do_lint) {
Packit 575503
		if ((fixtype(numerator)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("intdiv: received non-numeric first argument"));
Packit 575503
		if ((fixtype(denominator)->flags & NUMBER) == 0)
Packit 575503
			lintwarn(_("intdiv: received non-numeric second argument"));
Packit 575503
	}
Packit 575503
Packit 575503
	(void) force_number(numerator);
Packit 575503
	(void) force_number(denominator);
Packit 575503
	num = double_to_int(get_number_d(numerator));
Packit 575503
	denom = double_to_int(get_number_d(denominator));
Packit 575503
Packit 575503
	if (denom == 0.0)
Packit 575503
		fatal(_("intdiv: division by zero attempted"));
Packit 575503
Packit 575503
	quotient = double_to_int(num / denom);
Packit 575503
	/*
Packit 575503
	 * FIXME: This code is duplicated, factor it out to a
Packit 575503
	 * separate function.
Packit 575503
	 */
Packit 575503
#ifdef HAVE_FMOD
Packit 575503
	remainder = fmod(num, denom);
Packit 575503
#else	/* ! HAVE_FMOD */
Packit 575503
	(void) modf(num / denom, & remainder);
Packit 575503
	remainder = num - remainder * denom;
Packit 575503
#endif	/* ! HAVE_FMOD */
Packit 575503
	remainder = double_to_int(remainder);
Packit 575503
Packit 575503
	sub = make_string("quotient", 8);
Packit 575503
	lhs = assoc_lookup(result, sub);
Packit 575503
	unref(*lhs);
Packit 575503
	*lhs = make_number((AWKNUM) quotient);
Packit 575503
Packit 575503
	sub = make_string("remainder", 9);
Packit 575503
	lhs = assoc_lookup(result, sub);
Packit 575503
	unref(*lhs);
Packit 575503
	*lhs = make_number((AWKNUM) remainder);
Packit 575503
Packit 575503
	DEREF(denominator);
Packit 575503
	DEREF(numerator);
Packit 575503
Packit 575503
	return make_number((AWKNUM) 0.0);
Packit 575503
}
Packit 575503
#endif /* SUPPLY_INTDIV */
Packit 575503
Packit 575503
/* do_typeof --- return a string with the type of the arg */
Packit 575503
Packit 575503
NODE *
Packit 575503
do_typeof(int nargs)
Packit 575503
{
Packit 575503
	NODE *arg;
Packit 575503
	char *res = NULL;
Packit 575503
	bool deref = true;
Packit 575503
Packit 575503
	arg = POP();
Packit 575503
	switch (arg->type) {
Packit 575503
	case Node_var_array:
Packit 575503
		/* Node_var_array is never UPREF'ed */
Packit 575503
		res = "array";
Packit 575503
		deref = false;
Packit 575503
		break;
Packit 575503
	case Node_val:
Packit 575503
		switch (fixtype(arg)->flags & (STRING|NUMBER|USER_INPUT|REGEX)) {
Packit 575503
		case NUMBER:
Packit 575503
			res = "number";
Packit 575503
			break;
Packit 575503
		case NUMBER|USER_INPUT:
Packit 575503
			res = "strnum";
Packit 575503
			break;
Packit 575503
		case REGEX:
Packit 575503
			res = "regexp";
Packit 575503
			break;
Packit 575503
		case STRING:
Packit 575503
			res = "string";
Packit 575503
			// fall through
Packit 575503
		case NUMBER|STRING:
Packit 575503
			if (arg == Nnull_string || (arg->flags & NULL_FIELD) != 0) {
Packit 575503
				res = "unassigned";
Packit 575503
				break;
Packit 575503
			}
Packit 575503
			/* fall through */
Packit 575503
		default:
Packit 575503
			if (res == NULL) {
Packit 575503
				warning(_("typeof detected invalid flags combination `%s'; please file a bug report."), flags2str(arg->flags));
Packit 575503
				res = "unknown";
Packit 575503
			}
Packit 575503
			break;
Packit 575503
		}
Packit 575503
		break;
Packit 575503
	case Node_var_new:
Packit 575503
		res = "untyped";
Packit 575503
		deref = false;
Packit 575503
		break;
Packit 575503
	case Node_var:
Packit 575503
		/*
Packit 575503
		 * Note: this doesn't happen because the function calling code
Packit 575503
		 * in interpret.h pushes Node_var->var_value.
Packit 575503
		 */
Packit 575503
		fatal(_("typeof: invalid argument type `%s'"),
Packit 575503
				nodetype2str(arg->type));
Packit 575503
		break;
Packit 575503
	default:
Packit 575503
		fatal(_("typeof: unknown argument type `%s'"),
Packit 575503
				nodetype2str(arg->type));
Packit 575503
		break;
Packit 575503
	}
Packit 575503
Packit 575503
	if (deref)
Packit 575503
		DEREF(arg);
Packit 575503
	return make_string(res, strlen(res));
Packit 575503
}
Packit 575503
Packit 575503
/* mbc_byte_count --- return number of bytes for corresponding numchars multibyte characters */
Packit 575503
Packit 575503
static size_t
Packit 575503
mbc_byte_count(const char *ptr, size_t numchars)
Packit 575503
{
Packit 575503
	mbstate_t cur_state;
Packit 575503
	size_t sum = 0;
Packit 575503
	int mb_len;
Packit 575503
Packit 575503
	memset(& cur_state, 0, sizeof(cur_state));
Packit 575503
Packit 575503
	assert(gawk_mb_cur_max > 1);
Packit 575503
	mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state);
Packit 575503
	if (mb_len <= 0)
Packit 575503
		return numchars;	/* no valid m.b. char */
Packit 575503
Packit 575503
	for (; numchars > 0; numchars--) {
Packit 575503
		mb_len = mbrlen(ptr, numchars * gawk_mb_cur_max, &cur_state);
Packit 575503
		if (mb_len <= 0)
Packit 575503
			break;
Packit 575503
		sum += mb_len;
Packit 575503
		ptr += mb_len;
Packit 575503
	}
Packit 575503
Packit 575503
	return sum;
Packit 575503
}
Packit 575503
Packit 575503
/* mbc_char_count --- return number of m.b. chars in string, up to numbytes bytes */
Packit 575503
Packit 575503
static size_t
Packit 575503
mbc_char_count(const char *ptr, size_t numbytes)
Packit 575503
{
Packit 575503
	mbstate_t cur_state;
Packit 575503
	size_t sum = 0;
Packit 575503
	int mb_len;
Packit 575503
Packit 575503
	if (gawk_mb_cur_max == 1)
Packit 575503
		return numbytes;
Packit 575503
Packit 575503
	memset(& cur_state, 0, sizeof(cur_state));
Packit 575503
Packit 575503
	mb_len = mbrlen(ptr, numbytes, &cur_state);
Packit 575503
	if (mb_len <= 0)
Packit 575503
		return numbytes;	/* no valid m.b. char */
Packit 575503
Packit 575503
	while (numbytes > 0) {
Packit 575503
		mb_len = mbrlen(ptr, numbytes, &cur_state);
Packit 575503
		if (mb_len <= 0)
Packit 575503
			break;
Packit 575503
		sum++;
Packit 575503
		ptr += mb_len;
Packit 575503
		numbytes -= mb_len;
Packit 575503
	}
Packit 575503
Packit 575503
	return sum;
Packit 575503
}
Packit 575503
Packit 575503
/* sanitize_exit_status --- convert a 16 bit Unix exit status into something reasonable */
Packit 575503
Packit 575503
int sanitize_exit_status(int status)
Packit 575503
{
Packit 575503
	int ret = 0;
Packit 575503
Packit 575503
	if (WIFEXITED(status))
Packit 575503
		ret = WEXITSTATUS(status); /* normal exit */
Packit 575503
	else if (WIFSIGNALED(status)) {
Packit 575503
		bool coredumped = false;
Packit 575503
#ifdef WCOREDUMP
Packit 575503
		coredumped = WCOREDUMP(status);
Packit 575503
#endif
Packit 575503
		/* use 256 since exit values are 8 bits */
Packit 575503
		ret = WTERMSIG(status) + (coredumped ? 512 : 256);
Packit 575503
	} else
Packit 575503
		ret = 0;	/* shouldn't get here */
Packit 575503
Packit 575503
	return ret;
Packit 575503
}