Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [BUG] printf truncates large values



On Tue, 30 Jan 2018 17:40:21 +0100
Vincent Lefevre <vincent@xxxxxxxxxx> wrote:
> On a 64-bit x86_64 machine, with zsh 5.4.2:
> 
> cventin% printf "%x\n" 10865468317030705979
> zsh: number truncated after 19 digits: 10865468317030705979
> f1430962f7cd785

This is not because of the standard integer representation, it relates
to whatever type we picked for zlong (and the corresponding unsigned
type, zulong, which has the same length).

The problem is this goes through math evaluation (mathevali())
regardless of the fact that it's a constant, so becomes a zlong.  This
means we lose track of the fact it's actually unsigned.

The only easyish fix is to scan for a constant and avoid the mathevali()
in that case.  So I copied and adapted zstrtol_underscore() to do that.
Anything more requires propagating full-length unsigned values
separately through the integer code, which is a large undertaking not
entirely without its own problems.

pws

diff --git a/Src/builtin.c b/Src/builtin.c
index 0211f27..fb59738 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5243,7 +5243,10 @@ bin_print(char *name, char **args, Options ops, int func)
  		    	*d++ = 'l';
 #endif
 		    	*d++ = 'l', *d++ = *c, *d = '\0';
-			zulongval = (curarg) ? mathevali(curarg) : 0;
+			if (!curarg)
+			    zulongval = (zulong)0;
+			else if (!zstrtoul_underscore(curarg, &zulongval))
+			    zulongval = mathevali(curarg);
 			if (errflag) {
 			    zulongval = 0;
 			    errflag &= ~ERRFLAG_ERROR;
diff --git a/Src/utils.c b/Src/utils.c
index 74fdac3..3b589aa 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -2455,6 +2455,67 @@ zstrtol_underscore(const char *s, char **t, int base, int underscore)
     return neg ? -(zlong)calc : (zlong)calc;
 }
 
+/*
+ * If s represents a complete unsigned integer (and nothing else)
+ * return 1 and set retval to the value.  Otherwise return 0.
+ *
+ * Underscores are always allowed.
+ *
+ * Sensitive to OCTAL_ZEROES.
+ */
+
+/**/
+mod_export int
+zstrtoul_underscore(const char *s, zulong *retval)
+{
+    zulong calc = 0, newcalc = 0, base;
+
+    if (*s == '+')
+	s++;
+
+    if (*s != '0')
+	base = 10;
+    else if (*++s == 'x' || *s == 'X')
+	base = 16, s++;
+    else if (*s == 'b' || *s == 'B')
+	base = 2, s++;
+    else
+	base = isset(OCTALZEROES) ? 8 : 10;
+    if (base < 2 || base > 36) {
+	return 0;
+    } else if (base <= 10) {
+	for (; (*s >= '0' && *s < ('0' + base)) ||
+		 *s == '_'; s++) {
+	    if (*s == '_')
+		continue;
+	    newcalc = calc * base + *s - '0';
+	    if (newcalc < calc)
+	    {
+		return 0;
+	    }
+	    calc = newcalc;
+	}
+    } else {
+	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
+	     || (*s >= 'A' && *s < ('A' + base - 10))
+	     || *s == '_'; s++) {
+	    if (*s == '_')
+		continue;
+	    newcalc = calc*base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
+	    if (newcalc < calc)
+	    {
+		return 0;
+	    }
+	    calc = newcalc;
+	}
+    }
+
+    if (*s)
+	return 0;
+    *retval = calc;
+    return 1;
+}
+
 /**/
 mod_export int
 setblock_fd(int turnonblocking, int fd, long *modep)



Messages sorted by: Reverse Date, Date, Thread, Author