ubuntu-buildroot/output/build/host-gawk-5.2.0/node.c

1114 lines
25 KiB
C

/*
* node.c -- routines for node management
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991-2001, 2003-2015, 2017-2019, 2021, 2022,
* the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Programming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "awk.h"
static NODE *r_make_number(double x);
static AWKNUM get_ieee_magic_val(char *val);
extern NODE **fmt_list; /* declared in eval.c */
NODE *(*make_number)(double) = r_make_number;
NODE *(*str2number)(NODE *) = r_force_number;
NODE *(*format_val)(const char *, int, NODE *) = r_format_val;
int (*cmp_numbers)(const NODE *, const NODE *) = cmp_awknums;
/* is_hex --- return true if a string looks like a hex value */
static bool
is_hex(const char *str, const char *cpend)
{
/* on entry, we know the string length is >= 1 */
if (*str == '-' || *str == '+')
str++;
if (str + 1 < cpend && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
return true;
return false;
}
/* force_number --- force a value to be numeric */
NODE *
r_force_number(NODE *n)
{
char *cp;
char *cpend;
char save;
char *ptr;
if (n->type == Node_elem_new) {
n->type = Node_val;
n->flags &= ~STRING;
n->stptr[0] = '0'; // STRCUR is still set
n->stlen = 1;
return n;
}
if ((n->flags & NUMCUR) != 0)
return n;
/*
* We should always set NUMCUR. If USER_INPUT is set and it's a
* numeric string, we clear STRING and enable NUMBER, but if it's not
* numeric, we disable USER_INPUT.
*/
/* All the conditionals are an attempt to avoid the expensive strtod */
n->flags |= NUMCUR;
n->numbr = 0.0;
/* Trim leading white space, bailing out if there's nothing else */
for (cp = n->stptr, cpend = cp + n->stlen;
cp < cpend && isspace((unsigned char) *cp); cp++)
continue;
if (cp == cpend)
goto badnum;
/* At this point, we know the string is not entirely white space */
/* Trim trailing white space */
while (isspace((unsigned char) cpend[-1]))
cpend--;
/*
* 2/2007:
* POSIX, by way of severe language lawyering, seems to
* allow things like "inf" and "nan" to mean something.
* So if do_posix, the user gets what he deserves.
* This also allows hexadecimal floating point. Ugh.
*/
if (! do_posix) {
if (is_alpha((unsigned char) *cp))
goto badnum;
else if (is_ieee_magic_val(cp)) {
if (cpend == cp + 4) {
n->numbr = get_ieee_magic_val(cp);
goto goodnum;
} else
goto badnum;
}
/* else
fall through */
}
/* else POSIX, so
fall through */
if ( (! do_posix /* not POSIXLY paranoid and */
&& (is_alpha((unsigned char) *cp) /* letter, or */
/* CANNOT do non-decimal and saw 0x */
|| (! do_non_decimal_data && is_hex(cp, cpend))))) {
goto badnum;
}
if (cpend - cp == 1) { /* only one character */
if (isdigit((unsigned char) *cp)) { /* it's a digit! */
n->numbr = (AWKNUM)(*cp - '0');
if (n->stlen == 1) /* no white space */
n->flags |= NUMINT;
goto goodnum;
}
goto badnum;
}
errno = 0;
if (do_non_decimal_data /* main.c assures false if do_posix */
&& ! do_traditional && get_numbase(cp, cpend - cp, true) != 10) {
/* nondec2awknum() saves and restores the byte after the string itself */
n->numbr = nondec2awknum(cp, cpend - cp, &ptr);
} else {
save = *cpend;
*cpend = '\0';
n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
*cpend = save;
}
if (errno == 0 || errno == ERANGE) {
errno = 0; /* reset in case of ERANGE */
if (ptr == cpend)
goto goodnum;
/* else keep the leading numeric value without updating flags */
/* fall through to badnum */
} else {
errno = 0;
/*
* N.B. For subnormal values, strtod may return the
* floating-point representation while setting errno to ERANGE.
* We force the numeric value to 0 in such cases.
*/
n->numbr = 0;
/*
* Or should we accept it as a NUMBER even though strtod
* threw an error?
*/
/* fall through to badnum */
}
badnum:
n->flags &= ~USER_INPUT;
return n;
goodnum:
if (isnan(n->numbr) && *cp == '-' && signbit(n->numbr) == 0)
n->numbr = -(n->numbr);
if ((n->flags & USER_INPUT) != 0) {
/* leave USER_INPUT enabled to indicate that this is a strnum */
n->flags &= ~STRING;
n->flags |= NUMBER;
}
return n;
}
/*
* The following lookup table is used as an optimization in force_string;
* (more complicated) variations on this theme didn't seem to pay off, but
* systematic testing might be in order at some point.
*/
static const char *values[] = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
};
#define NVAL (sizeof(values)/sizeof(values[0]))
/* r_format_val --- format a numeric value based on format */
NODE *
r_format_val(const char *format, int index, NODE *s)
{
char buf[BUFSIZ];
char *sp = buf;
double val;
/*
* 2/2007: Simplify our lives here. Instead of worrying about
* whether or not the value will fit into a long just so we
* can use sprintf("%ld", val) on it, always format it ourselves.
* The only thing to worry about is that integral values always
* format as integers. %.0f does that very well.
*
* 6/2008: Would that things were so simple. Always using %.0f
* imposes a notable performance penalty for applications that
* do a lot of conversion of integers to strings. So, we reinstate
* the old code, but use %.0f for integral values that are outside
* the range of a long. This seems a reasonable compromise.
*
* 12/2009: Use <= and >= in the comparisons with LONG_xxx instead of
* < and > so that things work correctly on systems with 64 bit integers.
*/
if (out_of_range(s)) {
const char *result = format_nan_inf(s, 'g');
return make_string(result, strlen(result));
} else if ((val = double_to_int(s->numbr)) != s->numbr
|| val <= LONG_MIN || val >= LONG_MAX
) {
/* not an integral value, or out of integer range */
/*
* Once upon a time, we just blindly did this:
* sprintf(sp, format, s->numbr);
* s->stlen = strlen(sp);
* s->stfmt = index;
* but that's no good if, e.g., OFMT is %s. So we punt,
* and just always format the value ourselves.
*/
NODE *dummy[2], *r;
unsigned int oflags;
/* create dummy node for a sole use of format_tree */
dummy[1] = s;
oflags = s->flags;
if (val == s->numbr) {
/* integral value, but outside range of %ld, use %.0f */
r = format_tree("%.0f", 4, dummy, 2);
s->stfmt = STFMT_UNUSED;
} else {
r = format_tree(format, fmt_list[index]->stlen, dummy, 2);
assert(r != NULL);
s->stfmt = index;
}
s->flags = oflags;
s->stlen = r->stlen;
if ((s->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
efree(s->stptr);
s->stptr = r->stptr;
#ifdef HAVE_MPFR
s->strndmode = MPFR_round_mode;
#endif
freenode(r); /* Do not unref(r)! We want to keep s->stptr == r->stpr. */
goto no_malloc;
} else {
/*
* integral value; force conversion to long only once.
*/
long num = (long) val;
if (num < NVAL && num >= 0) {
sp = (char *) values[num];
s->stlen = 1;
} else {
(void) sprintf(sp, "%ld", num);
s->stlen = strlen(sp);
}
s->stfmt = STFMT_UNUSED;
if ((s->flags & INTIND) != 0) {
s->flags &= ~(INTIND|NUMBER);
s->flags |= STRING;
}
#ifdef HAVE_MPFR
s->strndmode = MPFR_round_mode;
#endif
}
if ((s->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
efree(s->stptr);
emalloc(s->stptr, char *, s->stlen + 1, "format_val");
memcpy(s->stptr, sp, s->stlen + 1);
no_malloc:
s->flags |= STRCUR;
free_wstr(s);
return s;
}
/* r_dupnode --- duplicate a node */
NODE *
r_dupnode(NODE *n)
{
NODE *r;
assert(n->type == Node_val);
#ifdef GAWKDEBUG
/* Do the same as in awk.h:dupnode(). */
if ((n->flags & MALLOC) != 0) {
n->valref++;
return n;
}
#endif
getnode(r);
*r = *n;
#ifdef HAVE_MPFR
if ((n->flags & MPZN) != 0) {
mpz_init(r->mpg_i);
mpz_set(r->mpg_i, n->mpg_i);
} else if ((n->flags & MPFN) != 0) {
mpfr_init(r->mpg_numbr);
int tval = mpfr_set(r->mpg_numbr, n->mpg_numbr, ROUND_MODE);
IEEE_FMT(r->mpg_numbr, tval);
}
#endif
r->flags |= MALLOC;
r->valref = 1;
/*
* DON'T call free_wstr(r) here!
* r->wstptr still points at n->wstptr's value, and we
* don't want to free it!
*/
r->wstptr = NULL;
r->wstlen = 0;
if ((n->flags & STRCUR) != 0) {
emalloc(r->stptr, char *, n->stlen + 1, "r_dupnode");
memcpy(r->stptr, n->stptr, n->stlen);
r->stptr[n->stlen] = '\0';
r->stlen = n->stlen;
if ((n->flags & WSTRCUR) != 0) {
r->wstlen = n->wstlen;
emalloc(r->wstptr, wchar_t *, sizeof(wchar_t) * (n->wstlen + 1), "r_dupnode");
memcpy(r->wstptr, n->wstptr, n->wstlen * sizeof(wchar_t));
r->wstptr[n->wstlen] = L'\0';
r->flags |= WSTRCUR;
}
}
return r;
}
/* r_make_number --- allocate a node with defined number */
static NODE *
r_make_number(double x)
{
NODE *r = make_number_node(0);
r->numbr = x;
return r;
}
/* cmp_awknums --- compare two AWKNUMs */
int
cmp_awknums(const NODE *t1, const NODE *t2)
{
/*
* This routine is used to sort numeric array indices or values.
* For the purposes of sorting, NaN is considered greater than
* any other value, and all NaN values are considered equivalent and equal.
* This isn't in compliance with IEEE standard, but compliance w.r.t. NaN
* comparison at the awk level is a different issue, and needs to be dealt
* with in the interpreter for each opcode seperately.
*/
if (isnan(t1->numbr))
return ! isnan(t2->numbr);
if (isnan(t2->numbr))
return -1;
/* don't subtract, in case one or both are infinite */
if (t1->numbr == t2->numbr)
return 0;
if (t1->numbr < t2->numbr)
return -1;
return 1;
}
/* make_str_node --- make a string node */
NODE *
make_str_node(const char *s, size_t len, int flags)
{
NODE *r;
getnode(r);
r->type = Node_val;
r->numbr = 0;
r->flags = (MALLOC|STRING|STRCUR);
r->valref = 1;
r->stfmt = STFMT_UNUSED;
#ifdef HAVE_MPFR
r->strndmode = MPFR_round_mode;
#endif
r->wstptr = NULL;
r->wstlen = 0;
if ((flags & ALREADY_MALLOCED) != 0)
r->stptr = (char *) s;
else {
emalloc(r->stptr, char *, len + 1, "make_str_node");
memcpy(r->stptr, s, len);
}
r->stptr[len] = '\0';
if ((flags & SCAN) != 0) { /* scan for escape sequences */
const char *pf;
char *ptm;
int c;
const char *end;
mbstate_t cur_state;
memset(& cur_state, 0, sizeof(cur_state));
end = &(r->stptr[len]);
for (pf = ptm = r->stptr; pf < end;) {
/*
* Keep multibyte characters together. This avoids
* problems if a subsequent byte of a multibyte
* character happens to be a backslash.
*/
if (gawk_mb_cur_max > 1) {
int mblen = mbrlen(pf, end-pf, &cur_state);
if (mblen > 1) {
int i;
for (i = 0; i < mblen; i++)
*ptm++ = *pf++;
continue;
}
}
c = *pf++;
if (c == '\\') {
c = parse_escape(&pf);
if (c < 0) {
if (do_lint)
lintwarn(_("backslash string continuation is not portable"));
if ((flags & ELIDE_BACK_NL) != 0)
continue;
c = '\\';
}
*ptm++ = c;
} else
*ptm++ = c;
}
len = ptm - r->stptr;
erealloc(r->stptr, char *, len + 1, "make_str_node");
r->stptr[len] = '\0';
}
r->stlen = len;
return r;
}
/* make_typed_regex --- make a typed regex node */
NODE *
make_typed_regex(const char *re, size_t len)
{
NODE *n, *exp, *n2;
exp = make_str_node(re, len, ALREADY_MALLOCED);
n = make_regnode(Node_regex, exp);
if (n == NULL)
fatal(_("could not make typed regex"));
n2 = make_string(re, len);
n2->typed_re = n;
#if HAVE_MPFR
if (do_mpfr)
mpg_zero(n2);
else
#endif
n2->numbr = 0;
n2->flags |= NUMCUR|STRCUR|REGEX;
n2->flags &= ~(STRING|NUMBER);
return n2;
}
/* unref --- remove reference to a particular node */
void
r_unref(NODE *tmp)
{
#ifdef GAWKDEBUG
/* Do the same as in awk.h:unref(). */
assert(tmp == NULL || tmp->valref > 0);
if (tmp == NULL || --tmp->valref > 0)
return;
#endif
if ((tmp->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR))
efree(tmp->stptr);
mpfr_unset(tmp);
free_wstr(tmp);
freenode(tmp);
}
/*
* parse_escape:
*
* Parse a C escape sequence. STRING_PTR points to a variable containing a
* pointer to the string to parse. That pointer is updated past the
* characters we use. The value of the escape sequence is returned.
*
* A negative value means the sequence \ newline was seen, which is supposed to
* be equivalent to nothing at all.
*
* If \ is followed by a null character, we return a negative value and leave
* the string pointer pointing at the null character.
*
* If \ is followed by 000, we return 0 and leave the string pointer after the
* zeros. A value of 0 does not mean end of string.
*
* POSIX doesn't allow \x.
*/
int
parse_escape(const char **string_ptr)
{
int c = *(*string_ptr)++;
int i;
int count;
int j;
const char *start;
if (do_lint_old) {
switch (c) {
case 'a':
case 'b':
case 'f':
case 'r':
lintwarn(_("old awk does not support the `\\%c' escape sequence"), c);
break;
}
}
switch (c) {
case 'a':
return '\a';
case 'b':
return '\b';
case 'f':
return '\f';
case 'n':
return '\n';
case 'r':
return '\r';
case 't':
return '\t';
case 'v':
return '\v';
case '\n':
return -2;
case 0:
(*string_ptr)--;
return -1;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
i = c - '0';
count = 0;
while (++count < 3) {
if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
i *= 8;
i += c - '0';
} else {
(*string_ptr)--;
break;
}
}
return i;
case 'x':
if (do_lint) {
static bool warned = false;
if (! warned) {
warned = true;
lintwarn(_("POSIX does not allow `\\x' escapes"));
}
}
if (do_posix)
return ('x');
if (! isxdigit((unsigned char) (*string_ptr)[0])) {
warning(_("no hex digits in `\\x' escape sequence"));
return ('x');
}
start = *string_ptr;
for (i = j = 0; j < 2; j++) {
/* do outside test to avoid multiple side effects */
c = (unsigned char) *(*string_ptr)++;
if (isxdigit(c)) {
i *= 16;
if (isdigit(c))
i += c - '0';
else if (isupper(c))
i += c - 'A' + 10;
else
i += c - 'a' + 10;
} else {
(*string_ptr)--;
break;
}
}
if (do_lint && j == 2 && isxdigit((unsigned char)*(*string_ptr)))
lintwarn(_("hex escape \\x%.*s of %d characters probably not interpreted the way you expect"), 3, start, 3);
return i;
case '\\':
case '"':
return c;
default:
{
static bool warned[256];
unsigned char uc = (unsigned char) c;
/* N.B.: use unsigned char here to avoid Latin-1 problems */
if (! warned[uc]) {
warned[uc] = true;
warning(_("escape sequence `\\%c' treated as plain `%c'"), uc, uc);
}
}
return c;
}
}
/* get_numbase --- return the base to use for the number in 's' */
int
get_numbase(const char *s, size_t len, bool use_locale)
{
int dec_point = '.';
const char *str = s;
#if defined(HAVE_LOCALE_H)
/*
* loc.decimal_point may not have been initialized yet,
* so double check it before using it.
*/
if (use_locale && loc.decimal_point != NULL && loc.decimal_point[0] != '\0')
dec_point = loc.decimal_point[0]; /* XXX --- assumes one char */
#endif
if (len < 2 || str[0] != '0')
return 10;
/* leading 0x or 0X */
if (str[1] == 'x' || str[1] == 'X')
return 16;
/*
* Numbers with '.', 'e', or 'E' are decimal.
* Have to check so that things like 00.34 are handled right.
*
* These beasts can have trailing whitespace. Deal with that too.
*/
for (; len > 0; len--, str++) {
if (*str == 'e' || *str == 'E' || *str == dec_point)
return 10;
else if (! isdigit((unsigned char) *str))
break;
}
if (! isdigit((unsigned char) s[1])
|| s[1] == '8' || s[1] == '9'
)
return 10;
return 8;
}
/* str2wstr --- convert a multibyte string to a wide string */
NODE *
str2wstr(NODE *n, size_t **ptr)
{
size_t i, count, src_count;
char *sp;
mbstate_t mbs;
wchar_t wc, *wsp;
static bool warned = false;
assert((n->flags & (STRING|STRCUR)) != 0);
/*
* Don't convert global null string or global null field
* variables to a wide string. They are both zero-length anyway.
* This also avoids future double-free errors while releasing
* shallow copies, eg. *tmp = *Null_field; free_wstr(tmp);
*/
if (n == Nnull_string || n == Null_field)
return n;
if ((n->flags & WSTRCUR) != 0) {
if (ptr == NULL)
return n;
/* otherwise
fall through and recompute to fill in the array */
free_wstr(n);
}
/*
* After consideration and consultation, this
* code trades space for time. We allocate
* an array of wchar_t that is n->stlen long.
* This is needed in the worst case anyway, where
* each input byte maps to one wchar_t. The
* advantage is that we only have to convert the string
* once, instead of twice, once to find out how many
* wide characters, and then again to actually fill in
* the info. If there's a lot left over, we can
* realloc the wide string down in size.
*/
emalloc(n->wstptr, wchar_t *, sizeof(wchar_t) * (n->stlen + 1), "str2wstr");
wsp = n->wstptr;
/*
* For use by do_match, create and fill in an array.
* For each byte `i' in n->stptr (the original string),
* a[i] is equal to `j', where `j' is the corresponding wchar_t
* in the converted wide string.
*
* Create the array.
*/
if (ptr != NULL) {
ezalloc(*ptr, size_t *, sizeof(size_t) * n->stlen, "str2wstr");
}
sp = n->stptr;
src_count = n->stlen;
memset(& mbs, 0, sizeof(mbs));
for (i = 0; src_count > 0; i++) {
/*
* 9/2010: Check the current byte; if it's a valid character,
* then it doesn't start a multibyte sequence. This brings a
* big speed up. Thanks to Ulrich Drepper for the tip.
* 11/2010: Thanks to Paolo Bonzini for some even faster code.
*/
if (is_valid_character(*sp)) {
count = 1;
wc = btowc_cache(*sp);
} else
count = mbrtowc(& wc, sp, src_count, & mbs);
switch (count) {
case (size_t) -2:
case (size_t) -1:
/*
* mbrtowc(3) says the state of mbs becomes undefined
* after a bad character, so reset it.
*/
memset(& mbs, 0, sizeof(mbs));
/* Warn the user something's wrong */
if (! warned) {
warned = true;
warning(_("Invalid multibyte data detected. There may be a mismatch between your data and your locale"));
}
/*
* 8/2015: If we're using UTF, then instead of just
* skipping the character, plug in the Unicode
* replacement character. In most cases this gives
* us "better" results, in that character counts
* and string lengths tend to make more sense.
*
* Otherwise, just skip the bad byte and keep going,
* so that we get a more-or-less full string, instead of
* stopping early. This is particularly important
* for match() where we need to build the indices.
*/
if (using_utf8()) {
count = 1;
wc = 0xFFFD; /* unicode replacement character */
goto set_wc;
} else {
/* skip it and keep going */
sp++;
src_count--;
}
break;
case 0:
count = 1;
/* fall through */
default:
set_wc:
*wsp++ = wc;
src_count -= count;
while (count--) {
if (ptr != NULL)
(*ptr)[sp - n->stptr] = i;
sp++;
}
break;
}
}
*wsp = L'\0';
n->wstlen = wsp - n->wstptr;
n->flags |= WSTRCUR;
#define ARBITRARY_AMOUNT_TO_GIVE_BACK 100
if (n->stlen - n->wstlen > ARBITRARY_AMOUNT_TO_GIVE_BACK)
erealloc(n->wstptr, wchar_t *, sizeof(wchar_t) * (n->wstlen + 1), "str2wstr");
return n;
}
/* wstr2str --- convert a wide string back into multibyte one */
NODE *
wstr2str(NODE *n)
{
size_t result;
size_t length;
wchar_t *wp;
mbstate_t mbs;
char *newval, *cp;
assert(n->valref == 1);
assert((n->flags & WSTRCUR) != 0);
/*
* Convert the wide chars in t1->wstptr back into m.b. chars.
* This is pretty grotty, but it's the most straightforward
* way to do things.
*/
memset(& mbs, 0, sizeof(mbs));
length = n->wstlen;
emalloc(newval, char *, (length * gawk_mb_cur_max) + 1, "wstr2str");
wp = n->wstptr;
for (cp = newval; length > 0; length--) {
result = wcrtomb(cp, *wp, & mbs);
if (result == (size_t) -1) /* what to do? break seems best */
break;
cp += result;
wp++;
}
*cp = '\0';
/* N.B. caller just created n with make_string, so this free is safe */
efree(n->stptr);
n->stptr = newval;
n->stlen = cp - newval;
return n;
}
/* free_wstr --- release the wide string part of a node */
void
r_free_wstr(NODE *n)
{
assert(n->type == Node_val);
if ((n->flags & WSTRCUR) != 0) {
assert(n->wstptr != NULL);
efree(n->wstptr);
}
n->wstptr = NULL;
n->wstlen = 0;
n->flags &= ~WSTRCUR;
}
static void __attribute__ ((unused))
dump_wstr(FILE *fp, const wchar_t *str, size_t len)
{
if (str == NULL || len == 0)
return;
for (; len--; str++)
putwc(*str, fp);
}
/* wstrstr --- walk haystack, looking for needle, wide char version */
const wchar_t *
wstrstr(const wchar_t *haystack, size_t hs_len,
const wchar_t *needle, size_t needle_len)
{
size_t i;
if (haystack == NULL || needle == NULL || needle_len > hs_len)
return NULL;
for (i = 0; i < hs_len; i++) {
if (haystack[i] == needle[0]
&& i+needle_len-1 < hs_len
&& haystack[i+needle_len-1] == needle[needle_len-1]) {
/* first & last chars match, check string */
if (memcmp(haystack+i, needle, sizeof(wchar_t) * needle_len) == 0) {
return haystack + i;
}
}
}
return NULL;
}
/* wcasestrstr --- walk haystack, nocase look for needle, wide char version */
const wchar_t *
wcasestrstr(const wchar_t *haystack, size_t hs_len,
const wchar_t *needle, size_t needle_len)
{
size_t i, j;
if (haystack == NULL || needle == NULL || needle_len > hs_len)
return NULL;
for (i = 0; i < hs_len; i++) {
if (towlower(haystack[i]) == towlower(needle[0])
&& i+needle_len-1 < hs_len
&& towlower(haystack[i+needle_len-1]) == towlower(needle[needle_len-1])) {
/* first & last chars match, check string */
const wchar_t *start;
start = haystack+i;
for (j = 0; j < needle_len; j++, start++) {
wchar_t h, n;
h = towlower(*start);
n = towlower(needle[j]);
if (h != n)
goto out;
}
return haystack + i;
}
out: ;
}
return NULL;
}
/* is_ieee_magic_val --- return true for +inf, -inf, +nan, -nan */
bool
is_ieee_magic_val(const char *val)
{
/*
* Avoid strncasecmp: it mishandles ASCII bytes in some locales.
* Assume the length is 4, as the caller checks this.
*/
return ( (val[0] == '+' || val[0] == '-')
&& ( ( (val[1] == 'i' || val[1] == 'I')
&& (val[2] == 'n' || val[2] == 'N')
&& (val[3] == 'f' || val[3] == 'F'))
|| ( (val[1] == 'n' || val[1] == 'N')
&& (val[2] == 'a' || val[2] == 'A')
&& (val[3] == 'n' || val[3] == 'N'))));
}
/* get_ieee_magic_val --- return magic value for string */
static AWKNUM
get_ieee_magic_val(char *val)
{
static bool first = true;
static AWKNUM inf;
static AWKNUM nan;
char save;
char *ptr;
save = val[4];
val[4] = '\0';
AWKNUM v = strtod(val, &ptr);
val[4] = save;
if (val == ptr) { /* Older strtod implementations don't support inf or nan. */
if (first) {
first = false;
nan = sqrt(-1.0);
inf = -log(0.0);
}
v = ((val[1] == 'i' || val[1] == 'I') ? inf : nan);
if (val[0] == '-')
v = -v;
}
return v;
}
wint_t btowc_cache[256];
/* init_btowc_cache --- initialize the cache */
void init_btowc_cache()
{
int i;
for (i = 0; i <= 255; i++) {
btowc_cache[i] = btowc(i);
}
}
#define BLOCKCHUNK 100
struct block_header nextfree[BLOCK_MAX] = {
{ NULL, sizeof(NODE), "node" },
{ NULL, sizeof(BUCKET), "bucket" },
};
#ifdef MEMDEBUG
void *
r_getblock(int id)
{
void *res;
emalloc(res, void *, nextfree[id].size, "getblock");
nextfree[id].active++;
if (nextfree[id].highwater < nextfree[id].active)
nextfree[id].highwater = nextfree[id].active;
return res;
}
void
r_freeblock(void *p, int id)
{
nextfree[id].active--;
free(p);
}
#else
/* more_blocks --- get more blocks of memory and add to the free list;
size of a block must be >= sizeof(struct block_item)
*/
void *
more_blocks(int id)
{
struct block_item *freep, *np, *next;
char *p, *endp;
size_t size;
size = nextfree[id].size;
assert(size >= sizeof(struct block_item));
emalloc(freep, struct block_item *, BLOCKCHUNK * size, "more_blocks");
p = (char *) freep;
endp = p + BLOCKCHUNK * size;
for (np = freep; ; np = next) {
next = (struct block_item *) (p += size);
if (p >= endp) {
np->freep = NULL;
break;
}
np->freep = next;
}
nextfree[id].freep = freep->freep;
nextfree[id].highwater += BLOCKCHUNK;
return freep;
}
#endif
/* make_bool_node --- make a boolean-valued node */
extern NODE *
make_bool_node(bool value)
{
NODE *val;
const char *sval;
AWKNUM nval;
sval = (value ? "1" : "0");
nval = (value ? 1.0 : 0.0);
val = make_number(nval);
val->stptr = estrdup(sval, strlen(sval));
val->stlen = strlen(sval);
val->flags |= NUMCUR|STRCUR|BOOLVAL;
val->stfmt = STFMT_UNUSED;
return val;
}