summaryrefslogtreecommitdiff
path: root/editors/awk.c
diff options
context:
space:
mode:
Diffstat (limited to 'editors/awk.c')
-rw-r--r--editors/awk.c47
1 files changed, 33 insertions, 14 deletions
diff --git a/editors/awk.c b/editors/awk.c
index 72eca24..74e3adc 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -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: