awk: improve nadling of negative numbers in bitwise ops;
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 2 Sep 2008 09:00:23 +0000 (09:00 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 2 Sep 2008 09:00:23 +0000 (09:00 -0000)
 fix handling of octal costants. add test for it

function                                             old     new   delta
getvar_i_int                                           -      93     +93
my_strtod                                              -      84     +84
qrealloc                                              33      36      +3
getvar_i                                              99      95      -4
next_token                                           979     919     -60
evaluate                                            6359    5627    -732
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 1/3 up/down: 180/-796)         Total: -616 bytes

editors/awk.c
testsuite/awk.tests

index 72eca245f0d098ae8291004b81bc0ce60bbd3761..74e3adce52f1018203762f17c6e2fc5281db5570 100644 (file)
@@ -681,6 +681,18 @@ static ALWAYS_INLINE int isalnum_(int c)
        return (isalnum(c) || c == '_');
 }
 
+static double my_strtod(char **pp)
+{
+#if ENABLE_DESKTOP
+       if ((*pp)[0] == '0'
+        && ((((*pp)[1] | 0x20) == 'x') || isdigit((*pp)[1]))
+       ) {
+               return strtoull(*pp, pp, 0);
+       }
+#endif
+       return strtod(*pp, pp);
+}
+
 /* -------- working with variables (set/get/copy/etc) -------- */
 
 static xhash *iamarray(var *v)
@@ -790,7 +802,7 @@ static double getvar_i(var *v)
                v->number = 0;
                s = v->string;
                if (s && *s) {
-                       v->number = strtod(s, &s);
+                       v->number = my_strtod(&s);
                        if (v->type & VF_USER) {
                                skip_spaces(&s);
                                if (*s != '\0')
@@ -804,6 +816,18 @@ static double getvar_i(var *v)
        return v->number;
 }
 
+/* Used for operands of bitwise ops */
+static unsigned long getvar_i_int(var *v)
+{
+       double d = getvar_i(v);
+
+       /* Casting doubles to longs is undefined for values outside
+        * of target type range. Try to widen it as much as possible */
+       if (d >= 0)
+               return (unsigned long)d;
+       return - (long) (unsigned long) (-d);
+}
+
 static var *copyvar(var *dest, const var *src)
 {
        if (dest != src) {
@@ -973,12 +997,7 @@ static uint32_t next_token(uint32_t expected)
 
                } else if (*p == '.' || isdigit(*p)) {
                        /* it's a number */
-#if ENABLE_DESKTOP
-                       if (p[0] == '0' && (p[1] | 0x20) == 'x')
-                               t_double = strtoll(p, &p, 0);
-                       else
-#endif
-                               t_double = strtod(p, &p);
+                       t_double = my_strtod(&p);
                        if (*p == '.')
                                syntax_error(EMSG_UNEXP_TOKEN);
                        tc = TC_NUMBER;
@@ -2022,7 +2041,7 @@ static var *exec_builtin(node *op, var *res)
                n = awk_split(as[0], spl, &s);
                s1 = s;
                clear_array(iamarray(av[1]));
-               for (i=1; i<=n; i++)
+               for (i = 1; i <= n; i++)
                        setari_u(av[1], i, nextword(&s1));
                free(s);
                setvar_i(res, n);
@@ -2042,27 +2061,27 @@ static var *exec_builtin(node *op, var *res)
        /* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5:
         * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */
        case B_an:
-               setvar_i(res, (unsigned long)getvar_i(av[0]) & (unsigned long)getvar_i(av[1]));
+               setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1]));
                break;
 
        case B_co:
-               setvar_i(res, ~(unsigned long)getvar_i(av[0]));
+               setvar_i(res, ~getvar_i_int(av[0]));
                break;
 
        case B_ls:
-               setvar_i(res, (unsigned long)getvar_i(av[0]) << (unsigned long)getvar_i(av[1]));
+               setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1]));
                break;
 
        case B_or:
-               setvar_i(res, (unsigned long)getvar_i(av[0]) | (unsigned long)getvar_i(av[1]));
+               setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1]));
                break;
 
        case B_rs:
-               setvar_i(res, (unsigned long)getvar_i(av[0]) >> (unsigned long)getvar_i(av[1]));
+               setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1]));
                break;
 
        case B_xo:
-               setvar_i(res, (unsigned long)getvar_i(av[0]) ^ (unsigned long)getvar_i(av[1]));
+               setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1]));
                break;
 
        case B_lo:
index b3c076e4b6f003d198bffbee32f2d94fbac20c9e..aa386361f141101dcdc9e970e3b479cd4d18f744 100755 (executable)
@@ -20,6 +20,7 @@ testing "awk -F case 7" "awk -F '[#]' '{ print NF }'" "5\n" "" "z##abc##zz\n"
 testing "awk bitwise op"  "awk '{ print or(4294967295,1) }'" "4.29497e+09\n" "" "\n"
 testing "awk hex const 1" "awk '{ print or(0xffffffff,1) }'" "4.29497e+09\n" "" "\n"
 testing "awk hex const 2" "awk '{ print or(0x80000000,1) }'" "2.14748e+09\n" "" "\n"
+testing "awk oct const"   "awk '{ print or(01234,1) }'"      "669\n"         "" "\n"
 
 tar xjf awk_t1.tar.bz2
 testing "awk 'gcc build bug'" \