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