diff options
Diffstat (limited to 'node.c')
-rw-r--r-- | node.c | 93 |
1 files changed, 70 insertions, 23 deletions
@@ -26,15 +26,21 @@ #include "awk.h" #include "math.h" +#include "floatmagic.h" /* definition of isnan */ static int is_ieee_magic_val(const char *val); +static NODE *r_make_number(double x); static AWKNUM get_ieee_magic_val(const 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; /* force_number --- force a value to be numeric */ -AWKNUM +NODE * r_force_number(NODE *n) { char *cp; @@ -45,7 +51,7 @@ r_force_number(NODE *n) extern double strtod(); if (n->flags & NUMCUR) - return n->numbr; + return n; /* all the conditionals are an attempt to avoid the expensive strtod */ @@ -54,7 +60,7 @@ r_force_number(NODE *n) n->numbr = 0.0; if (n->stlen == 0) { - return 0.0; + return n; } cp = n->stptr; @@ -67,14 +73,14 @@ r_force_number(NODE *n) */ if (! do_posix) { if (isalpha((unsigned char) *cp)) { - return 0.0; + return n; } else if (n->stlen == 4 && is_ieee_magic_val(n->stptr)) { if (n->flags & MAYBE_NUM) n->flags &= ~MAYBE_NUM; n->flags |= NUMBER|NUMCUR; n->numbr = get_ieee_magic_val(n->stptr); - return n->numbr; + return n; } /* else fall through */ @@ -92,7 +98,7 @@ r_force_number(NODE *n) /* CANNOT do non-decimal and saw 0x */ || (! do_non_decimal_data && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X'))))) { - return 0.0; + return n; } if (n->flags & MAYBE_NUM) { @@ -109,12 +115,12 @@ r_force_number(NODE *n) if (cp == n->stptr) /* no leading spaces */ n->flags |= NUMINT; } - return n->numbr; + return n; } if (do_non_decimal_data) { /* main.c assures false if do_posix */ errno = 0; - if (! do_traditional && isnondecimal(cp, TRUE)) { + if (! do_traditional && get_numbase(cp, TRUE) != 10) { n->numbr = nondec2awknum(cp, cpend - cp); n->flags |= NUMCUR; ptr = cpend; @@ -139,7 +145,7 @@ finish: errno = 0; } - return n->numbr; + return n; } @@ -162,10 +168,10 @@ static const char *values[] = { }; #define NVAL (sizeof(values)/sizeof(values[0])) -/* format_val --- format a numeric value based on format */ +/* r_format_val --- format a numeric value based on format */ NODE * -format_val(const char *format, int index, NODE *s) +r_format_val(const char *format, int index, NODE *s) { char buf[BUFSIZ]; char *sp = buf; @@ -190,7 +196,8 @@ format_val(const char *format, int index, NODE *s) /* not an integral value, or out of range */ if ((val = double_to_int(s->numbr)) != s->numbr - || val <= LONG_MIN || val >= LONG_MAX) { + || val <= LONG_MIN || val >= LONG_MAX + ) { /* * Once upon a time, we just blindly did this: * sprintf(sp, format, s->numbr); @@ -201,11 +208,12 @@ format_val(const char *format, int index, NODE *s) */ NODE *dummy[2], *r; - unsigned short oflags; + 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); @@ -316,17 +324,17 @@ r_dupnode(NODE *n) return r; } -/* make_number --- allocate a node with defined number */ +/* r_make_number --- allocate a node with defined number */ -NODE * -make_number(AWKNUM x) +static NODE * +r_make_number(double x) { NODE *r; getnode(r); r->type = Node_val; r->numbr = x; - r->valref = 1; r->flags = MALLOC|NUMBER|NUMCUR; + r->valref = 1; r->stptr = NULL; r->stlen = 0; #if MBS_SUPPORT @@ -336,6 +344,32 @@ make_number(AWKNUM x) return r; } +/* cmp_awknums --- compare two AWKNUMs */ + +int +cmp_awknums(const NODE *t1, const NODE *t2) +{ + /* + * This routine is also 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; +} + /* r_make_str_node --- make a string node */ @@ -437,6 +471,13 @@ r_unref(NODE *tmp) efree(tmp->stptr); #endif +#ifdef HAVE_MPFR + if (is_mpg_float(tmp)) + mpfr_clear(tmp->mpg_numbr); + else if (is_mpg_integer(tmp)) + mpz_clear(tmp->mpg_i); +#endif + free_wstr(tmp); freenode(tmp); } @@ -577,12 +618,14 @@ parse_escape(const char **string_ptr) } } -/* isnondecimal --- return true if number is not a decimal number */ +/* get_numbase --- return the base to use for the number in 's' */ int -isnondecimal(const char *str, int use_locale) +get_numbase(const char *s, int use_locale) { int dec_point = '.'; + const char *str = s; + #if defined(HAVE_LOCALE_H) /* * loc.decimal_point may not have been initialized yet, @@ -593,11 +636,11 @@ isnondecimal(const char *str, int use_locale) #endif if (str[0] != '0') - return FALSE; + return 10; /* leading 0x or 0X */ if (str[1] == 'x' || str[1] == 'X') - return TRUE; + return 16; /* * Numbers with '.', 'e', or 'E' are decimal. @@ -607,12 +650,16 @@ isnondecimal(const char *str, int use_locale) */ for (; *str != '\0'; str++) { if (*str == 'e' || *str == 'E' || *str == dec_point) - return FALSE; + return 10; else if (! isdigit((unsigned char) *str)) break; } - return TRUE; + if (! isdigit((unsigned char) s[1]) + || s[1] == '8' || s[1] == '9' + ) + return 10; + return 8; } #if MBS_SUPPORT |