about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2014-01-23 10:32:59 +0000
committerPeter Stephenson <pws@zsh.org>2014-01-23 10:32:59 +0000
commit22b8fd6da931657ec930ba2625179c704de3e830 (patch)
tree2266493aa799af7431af9308fd6cb7e088fb668e /Src
parent6c603a412751c810ba04bcd463cd3595091ca391 (diff)
downloadzsh-22b8fd6da931657ec930ba2625179c704de3e830.tar.gz
zsh-22b8fd6da931657ec930ba2625179c704de3e830.tar.xz
zsh-22b8fd6da931657ec930ba2625179c704de3e830.zip
32299: add use of underscores on arithmetic output for spacing
Diffstat (limited to 'Src')
-rw-r--r--Src/math.c35
-rw-r--r--Src/params.c151
-rw-r--r--Src/subst.c8
3 files changed, 176 insertions, 18 deletions
diff --git a/Src/math.c b/Src/math.c
index 42355f885..266569827 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -556,6 +556,9 @@ lexconstant(void)
 int outputradix;
 
 /**/
+int outputunderscore;
+
+/**/
 static int
 zzlex(void)
 {
@@ -713,7 +716,7 @@ zzlex(void)
 	    return EOI;
 	case '[':
 	    {
-		int n;
+		int n, checkradix = 0;
 
 		if (idigit(*ptr)) {
 		    n = zstrtol(ptr, &ptr, 10);
@@ -730,9 +733,19 @@ zzlex(void)
 			n = -1;
 			ptr++;
 		    }
-		    if (!idigit(*ptr))
+		    if (!idigit(*ptr) && *ptr != '_')
 			goto bofs;
-		    outputradix = n * zstrtol(ptr, &ptr, 10);
+		    if (idigit(*ptr)) {
+			outputradix = n * zstrtol(ptr, &ptr, 10);
+			checkradix = 1;
+		    }
+		    if (*ptr == '_') {
+			ptr++;
+			if (idigit(*ptr))
+			    outputunderscore = zstrtol(ptr, &ptr, 10);
+			else
+			    outputunderscore = 3;
+		    }
 		} else {
 		    bofs:
 		    zerr("bad output format specification");
@@ -740,11 +753,13 @@ zzlex(void)
 		}
 		if(*ptr != ']')
 			goto bofs;
-		n = (outputradix < 0) ? -outputradix : outputradix;
-		if (n < 2 || n > 36) {
-		    zerr("invalid base (must be 2 to 36 inclusive): %d",
-			 outputradix);
-		    return EOI;
+		if (checkradix) {
+		    n = (outputradix < 0) ? -outputradix : outputradix;
+		    if (n < 2 || n > 36) {
+			zerr("invalid base (must be 2 to 36 inclusive): %d",
+			     outputradix);
+			return EOI;
+		    }
 		}
 		ptr++;
 		break;
@@ -1337,9 +1352,9 @@ matheval(char *s)
     char *junk;
     mnumber x;
     int xmtok = mtok;
-    /* maintain outputradix across levels of evaluation */
+    /* maintain outputradix and outputunderscore across levels of evaluation */
     if (!mlevel)
-	outputradix = 0;
+	outputradix = outputunderscore = 0;
 
     if (!*s) {
 	x.type = MN_INTEGER;
diff --git a/Src/params.c b/Src/params.c
index ad9e3470b..dc41c6c92 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2416,9 +2416,10 @@ setnumvalue(Value v, mnumber val)
 	if ((val.type & MN_INTEGER) || outputradix) {
 	    if (!(val.type & MN_INTEGER))
 		val.u.l = (zlong) val.u.d;
-	    convbase(p = buf, val.u.l, outputradix);
+	    p = convbase_underscore(buf, val.u.l, outputradix,
+				    outputunderscore);
 	} else
-	    p = convfloat(val.u.d, 0, 0, NULL);
+	    p = convfloat_underscore(val.u.d, outputunderscore);
 	setstrvalue(v, ztrdup(p));
 	break;
     case PM_INTEGER:
@@ -4555,9 +4556,14 @@ delenv(Param pm)
      */
 }
 
+/*
+ * Guts of convbase: this version can return the number of digits
+ * sans any base discriminator.
+ */
+
 /**/
-mod_export void
-convbase(char *s, zlong v, int base)
+void
+convbase_ptr(char *s, zlong v, int base, int *ndigits)
 {
     int digs = 0;
     zulong x;
@@ -4583,6 +4589,8 @@ convbase(char *s, zlong v, int base)
 	x /= base;
     if (!digs)
 	digs = 1;
+    if (ndigits)
+	*ndigits = digs;
     s[digs--] = '\0';
     x = v;
     while (digs >= 0) {
@@ -4594,6 +4602,64 @@ convbase(char *s, zlong v, int base)
 }
 
 /*
+ * Basic conversion of integer to a string given a base.
+ * If 0 base is 10.
+ * If negative no base discriminator is output.
+ */
+
+/**/
+mod_export void
+convbase(char *s, zlong v, int base)
+{
+    convbase_ptr(s, v, base, NULL);
+}
+
+/*
+ * Add underscores to converted integer for readability with given spacing.
+ * s is as for convbase: at least BDIGBUFSIZE.
+ * If underscores were added, returned value with underscores comes from
+ * heap, else the returned value is s.
+ */
+
+/**/
+char *
+convbase_underscore(char *s, zlong v, int base, int underscore)
+{
+    char *retptr, *sptr, *dptr;
+    int ndigits, nunderscore, mod, len;
+
+    convbase_ptr(s, v, base, &ndigits);
+
+    if (underscore <= 0)
+	return s;
+
+    nunderscore = (ndigits - 1) / underscore;
+    if (!nunderscore)
+	return s;
+    len = strlen(s);
+    retptr = zhalloc(len + nunderscore + 1);
+    mod = 0;
+    memcpy(retptr, s, len - ndigits);
+    sptr = s + len;
+    dptr = retptr + len + nunderscore;
+    /* copy the null */
+    *dptr-- = *sptr--;
+    for (;;) {
+	*dptr = *sptr;
+	if (!--ndigits)
+	    break;
+	dptr--;
+	sptr--;
+	if (++mod == underscore) {
+	    mod = 0;
+	    *dptr-- = '_';
+	}
+    }
+
+    return retptr;
+}
+
+/*
  * Convert a floating point value for output.
  * Unlike convbase(), this has its own internal storage and returns
  * a value from the heap.
@@ -4659,6 +4725,83 @@ convfloat(double dval, int digits, int flags, FILE *fout)
     return ret;
 }
 
+/*
+ * convert float to string with basic options but inserting underscores
+ * for readability.
+ */
+
+/**/
+char *convfloat_underscore(double dval, int underscore)
+{
+    int ndigits_int = 0, ndigits_frac = 0, nunderscore, len;
+    char *s, *retptr, *sptr, *dptr;
+
+    s = convfloat(dval, 0, 0, NULL);
+    if (underscore <= 0)
+	return s;
+
+    /*
+     * Count the number of digits before and after the decimal point, if any.
+     */
+    sptr = s;
+    if (*sptr == '-')
+	sptr++;
+    while (idigit(*sptr)) {
+	ndigits_int++;
+	sptr++;
+    }
+    if (*sptr == '.') {
+	sptr++;
+	while (idigit(*sptr)) {
+	    ndigits_frac++;
+	    sptr++;
+	}
+    }
+
+    /*
+     * Work out how many underscores to insert --- remember we
+     * put them in integer and fractional parts separately.
+     */
+    nunderscore = (ndigits_int-1) / underscore + (ndigits_frac-1) / underscore;
+    if (!nunderscore)
+	return s;
+    len = strlen(s);
+    dptr = retptr = zhalloc(len + nunderscore + 1);
+
+    /*
+     * Insert underscores in integer part.
+     * Grouping starts from the point in both directions.
+     */
+    sptr = s;
+    if (*sptr == '-')
+	*dptr++ = *sptr++;
+    while (ndigits_int) {
+	*dptr++ = *sptr++;
+	if (--ndigits_int && !(ndigits_int % underscore))
+	    *dptr++ = '_';
+    }
+    if (ndigits_frac) {
+	/*
+	 * Insert underscores in the fractional part.
+	 */
+	int mod = 0;
+	/* decimal point, we already checked */
+	*dptr++ = *sptr++;
+	while (ndigits_frac) {
+	    *dptr++ = *sptr++;
+	    mod++;
+	    if (--ndigits_frac && mod == underscore) {
+		*dptr++ = '_';
+		mod = 0;
+	    }
+	}
+    }
+    /* Copy exponent and anything else up to null */
+    while ((*dptr++ = *sptr++))
+	;
+    return retptr;
+}
+
 /* Start a parameter scope */
 
 /**/
diff --git a/Src/subst.c b/Src/subst.c
index 1059508ef..cc5df3fba 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -3754,19 +3754,19 @@ static char *
 arithsubst(char *a, char **bptr, char *rest)
 {
     char *s = *bptr, *t;
-    char buf[BDIGBUFSIZE], *b = buf;
+    char buf[BDIGBUFSIZE], *b;
     mnumber v;
 
     singsub(&a);
     v = matheval(a);
     if ((v.type & MN_FLOAT) && !outputradix)
-	b = convfloat(v.u.d, 0, 0, NULL);
+	b = convfloat_underscore(v.u.d, outputunderscore);
     else {
 	if (v.type & MN_FLOAT)
 	    v.u.l = (zlong) v.u.d;
-	convbase(buf, v.u.l, outputradix);
+	b = convbase_underscore(buf, v.u.l, outputradix, outputunderscore);
     }
-    t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) + 
+    t = *bptr = (char *) hcalloc(strlen(*bptr) + strlen(b) +
 				 strlen(rest) + 1);
     t--;
     while ((*++t = *s++));