about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOliver Kiddle <opk@zsh.org>2021-11-12 20:33:52 +0100
committerOliver Kiddle <opk@zsh.org>2021-11-12 20:33:52 +0100
commitdfb7ac94bb4c28472d759a61ea3c2dab30cf9cd3 (patch)
tree1894d81725e4825c1b1948c0ccf1236b439e3710
parent631576de0f7b4e52487175e3e017e5136424b626 (diff)
downloadzsh-dfb7ac94bb4c28472d759a61ea3c2dab30cf9cd3.tar.gz
zsh-dfb7ac94bb4c28472d759a61ea3c2dab30cf9cd3.tar.xz
zsh-dfb7ac94bb4c28472d759a61ea3c2dab30cf9cd3.zip
49561: add zformat -F option, similar to -f but ternary expressions check for existence instead of doing math evaluation
-rw-r--r--ChangeLog8
-rw-r--r--Completion/Base/Core/_description2
-rw-r--r--Completion/Base/Core/_message2
-rw-r--r--Doc/Zsh/mod_zutil.yo11
-rw-r--r--Src/Modules/zutil.c48
-rw-r--r--Test/V13zformat.ztst24
6 files changed, 75 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index 5865cb727..65c0dfa8f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2021-11-12  Oliver Kiddle  <opk@zsh.org>
+
+	* 49561: Src/Modules/zutil.c, Doc/Zsh/mod_zutil.yo,
+	Completion/Base/Core/_description, Completion/Base/Core/_message,
+	Test/V13zformat.ztst: Add zformat -F option, similar to -f but
+	ternary expressions check for existence instead of doing math
+	evaluation. Make use it with the format style.
+
 2021-11-07  Oliver Kiddle  <opk@zsh.org>
 
 	* 49544: Src/Modules/watch.c: only tie watch/WATCH if both come
diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description
index bdb4007a6..5b54484c7 100644
--- a/Completion/Base/Core/_description
+++ b/Completion/Base/Core/_description
@@ -78,7 +78,7 @@ shift 2
 if [[ -z "$1" && $# -eq 1 ]]; then
   format=
 elif [[ -n "$format" ]]; then
-  zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}"
+  zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
 fi
 
 if [[ -n "$gname" ]]; then
diff --git a/Completion/Base/Core/_message b/Completion/Base/Core/_message
index 4d5645eaf..dbeed4a88 100644
--- a/Completion/Base/Core/_message
+++ b/Completion/Base/Core/_message
@@ -39,7 +39,7 @@ else
 fi
 
 if [[ -n "$format$raw" ]]; then
-  [[ -z "$raw" ]] && zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}"
+  [[ -z "$raw" ]] && zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}"
   builtin compadd "$gopt[@]" -x "$format"
   _comp_mesg=yes
 fi
diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo
index 41d3dfdb0..3cf9e5028 100644
--- a/Doc/Zsh/mod_zutil.yo
+++ b/Doc/Zsh/mod_zutil.yo
@@ -150,8 +150,9 @@ enditem()
 )
 findex(zformat)
 xitem(tt(zformat -f) var(param) var(format) var(spec) ...)
+xitem(tt(zformat -F) var(param) var(format) var(spec) ...)
 item(tt(zformat -a) var(array) var(sep) var(spec) ...)(
-This builtin provides two different forms of formatting. The first form 
+This builtin provides different forms of formatting. The first form
 is selected with the tt(-f) option. In this case the var(format)
 string will be modified by replacing sequences starting with a percent 
 sign in it with strings from the var(spec)s.  Each var(spec) should be
@@ -195,7 +196,13 @@ outputs "The answer is 'yes'." to tt(REPLY) since the value for the format
 specifier tt(c) is 3, agreeing with the digit argument to the ternary
 expression.
 
-The second form, using the tt(-a) option, can be used for aligning
+With tt(-F) instead of tt(-f), ternary expressions choose between the
+`true' or `false' text on the basis of whether the format specifier is
+present and non-empty.  A test number indicates a minimum width for the
+value given in the format specifier. Negative numbers reverse this,
+so the test is for whether the value exceeds a maximum width.
+
+The form, using the tt(-a) option, can be used for aligning
 strings.  Here, the var(spec)s are of the form
 `var(left)tt(:)var(right)' where `var(left)' and `var(right)' are
 arbitrary strings.  These strings are modified by replacing the colons
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 691ba6c2f..2f17c03f1 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -776,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
  *   ousedp	(*outp)[*ousedp] is where to write next
  *   olenp	*olenp is the size allocated for *outp
  *   endchar    Terminator character in addition to `\0' (may be '\0')
+ *   presence   -F: Ternary expressions test emptyness instead
  *   skip	If 1, don't output, just parse.
  */
 static char *zformat_substring(char* instr, char **specs, char **outp,
-			       int *ousedp, int *olenp, int endchar, int skip)
+			       int *ousedp, int *olenp, int endchar,
+			       int presence, int skip)
 {
     char *s;
 
@@ -813,20 +815,29 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 	    if (testit && STOUC(*s)) {
 		int actval, testval, endcharl;
 
-		/*
-		 * One one number is useful for ternary expressions.
-		 * Remember to put the sign back.
-		 */
+		/* Only one number is useful for ternary expressions. */
 		testval = (min >= 0) ? min : (max >= 0) ? max : 0;
-		if (right)
-		    testval *= -1;
 
-		if (specs[STOUC(*s)])
-		    actval = (int)mathevali(specs[STOUC(*s)]);
-		else
-		    actval = 0;
-		/* zero means values are equal, i.e. true */
-		actval -= testval;
+		if (specs[STOUC(*s)] && *specs[STOUC(*s)]) {
+		    if (presence) {
+			if (testval)
+#ifdef MULTIBYTE_SUPPORT
+			    if (isset(MULTIBYTE))
+				actval = MB_METASTRWIDTH(specs[STOUC(*s)]);
+			    else
+#endif
+				actval = strlen(specs[STOUC(*s)]);
+		        else
+			    actval = 1;
+			actval = right ? (testval < actval) : (testval >= actval);
+		    } else {
+			if (right) /* put the sign back */
+			    testval *= -1;
+			/* zero means values are equal, i.e. true */
+			actval = (int)mathevali(specs[STOUC(*s)]) - testval;
+		    }
+		} else
+		    actval = presence ? !right : testval;
 
 		/* careful about premature end of string */
 		if (!(endcharl = *++s))
@@ -837,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp,
 		 * vice versa... unless we are already skipping.
 		 */
 		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
-					    olenp, endcharl, skip || actval)))
+			    olenp, endcharl, presence, skip || actval)))
 		    return NULL;
 		if (!(s = zformat_substring(s+1, specs, outp, ousedp,
-					    olenp, ')', skip || !actval)))
+			    olenp, ')', presence, skip || !actval)))
 		    return NULL;
 	    } else if (skip) {
 		continue;
@@ -912,6 +923,7 @@ static int
 bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 {
     char opt;
+    int presence = 0;
 
     if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
 	zwarnnam(nam, "invalid argument: %s", args[0]);
@@ -920,6 +932,9 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
     args++;
 
     switch (opt) {
+    case 'F':
+	presence = 1;
+	/* fall-through */
     case 'f':
 	{
 	    char **ap, *specs[256] = {0}, *out;
@@ -939,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
 	    }
 	    out = (char *) zhalloc(olen = 128);
 
-	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0);
+	    zformat_substring(args[1], specs, &out, &oused, &olen, '\0',
+		    presence, 0);
 	    out[oused] = '\0';
 
 	    setsparam(args[0], ztrdup(out));
diff --git a/Test/V13zformat.ztst b/Test/V13zformat.ztst
index 982866e13..035a0a495 100644
--- a/Test/V13zformat.ztst
+++ b/Test/V13zformat.ztst
@@ -58,6 +58,30 @@
 0:nested conditionals test
 >yes
 
+ () {
+   zformat -f 1 '%(w.zero.fail) %(x.fail.present) %(y.empty.fail) %(z.missing.fail)' w:0 x:1 y:
+   zformat -F 2 '%(w.zero.fail) %(x.present.fail) %(y.fail.empty) %(z.fail.missing)' w:0 x:1 y:
+   echo $1
+   echo $2
+ }
+0:conditionals with empty and missing values
+>zero present empty missing
+>zero present empty missing
+
+ () {
+   local l
+   for l in 0 1 2 3; do
+     zformat -F 1 "%$l(a.a.A)%$l(b.b.B)%$l(c.c.C)%$l(d.d.D)" a: b:1 c:12 d:123
+     zformat -F 2 "%-$l(a.a.A)%-$l(b.b.B)%-$l(c.c.C)%-$l(d.d.D)" a: b:1 c:12 d:123
+     print - $1 $2
+   done
+ }
+0:minimum and maximum widths
+>Abcd aBCD
+>ABcd abCD
+>ABCd abcD
+>ABCD abcd
+
  zformat -a argv . foo:lorem ipsum:bar bazbaz '\\esc\:ape'
  print -rl -- "$@"
 0:basic -a test