about summary refs log tree commit diff
path: root/Src/utils.c
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2018-01-31 09:14:40 +0000
committerPeter Stephenson <pws@zsh.org>2018-01-31 09:14:40 +0000
commit2cbf6b6a19716cd4f03c820929710499576aa809 (patch)
tree00e4ebe379b3d670bea164665f9b0af1c3bc92ae /Src/utils.c
parentf62af4a7efd223d7dd5459ca0969fa94470cf39c (diff)
downloadzsh-2cbf6b6a19716cd4f03c820929710499576aa809.tar.gz
zsh-2cbf6b6a19716cd4f03c820929710499576aa809.tar.xz
zsh-2cbf6b6a19716cd4f03c820929710499576aa809.zip
42332: Special case unsigned printf formats.
For constants we can avoid a conversion to signed by examining
the expression before passing to math eval.
Diffstat (limited to 'Src/utils.c')
-rw-r--r--Src/utils.c61
1 files changed, 61 insertions, 0 deletions
diff --git a/Src/utils.c b/Src/utils.c
index 74fdac31f..3b589aa35 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)