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

Re: globbing in conditional expressions



Bart Schaefer wrote on Tue, May 13, 2014 at 08:41:17 -0700:
> On May 8, 10:19pm, Roman Neuhauser wrote:
> }
> } maybe a crazy idea...  how about something like [[ -m pattern ]] which
> } would succeed iff pattern matched at least one path?  this could be
> } somewhat more amenable to shortcircuiting.
> 
> Returning to this after a bit of a detour through [[ ... ]] expression
> parsing:
> 
> You could define (via zmodload) an operator that applies filename
> generation to its argument, but changes to the internals of globbing
> would be needed to make a short-circuit happen.  Then those changes
> would have to be exposed somehow so that the operator could use them.
> 

I've taken a shot at making those changes, see attached.  This version
just adds a [[ -m $pattern ]] rather than a zmodload operator, but I'm
sure it could be converted to using zmodload.

(Aside: where is the documentation of internal functions in the C
source?  It isn't the *.c/*.h files where I looked for it.)

> As I mentioned before, in the case of the match failing this would be
> exactly as expensive as not short-circuiting.

Right, so short-circuiting would only be useful for callers that want to
know whether a pattern matches a large directory, but don't care about
having a list of matching filenames.  Does anyone have such a use-case?
From 95dfc45753601eeb1d406a640c03434fcbc61704 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Tue, 13 May 2014 10:18:56 +0000
Subject: [PATCH 3/3] Add [[ -m pattern ]] that short-circuits.

---
 Doc/Zsh/cond.yo      |    3 ++
 Src/Zle/compctl.c    |    8 ++---
 Src/Zle/zle_tricky.c |    2 +-
 Src/cond.c           |   13 +++++++++
 Src/exec.c           |    8 ++---
 Src/glob.c           |   79 +++++++++++++++++++++++++++++++-------------------
 Src/parse.c          |    4 +--
 Src/subst.c          |    8 +++--
 Test/C02cond.ztst    |    3 ++
 9 files changed, 84 insertions(+), 44 deletions(-)

diff --git a/Doc/Zsh/cond.yo b/Doc/Zsh/cond.yo
index 9f8a7d8..ac07cf4 100644
--- a/Doc/Zsh/cond.yo
+++ b/Doc/Zsh/cond.yo
@@ -38,6 +38,9 @@ true if var(file) exists and is a symbolic link.
 item(tt(-k) var(file))(
 true if var(file) exists and has its sticky bit set.
 )
+item(tt(-m) var(pattern))(
+true if var(pattern) has any matches.  Short-circuits.
+)
 item(tt(-n) var(string))(
 true if length of var(string) is non-zero.
 )
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 2563095..7565142 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -2228,7 +2228,7 @@ gen_matches_files(int dirs, int execs, int all)
 			    /* Do the globbing... */
 			    remnulargs(p);
 			    addlinknode(l, p);
-			    globlist(l, 0);
+			    globlist(l, 0, 0);
 			    /* And see if that produced a filename. */
 			    tt = nonempty(l);
 			    while (ugetnode(l));
@@ -3384,7 +3384,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 		tokenize(p);
 		remnulargs(p);
 		addlinknode(l, p);
-		globlist(l, 0);
+		globlist(l, 0, 0);
 
 		if (nonempty(l)) {
 		    /* And add the resulting words. */
@@ -3533,7 +3533,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 				/* Do the globbing. */
 				ng = opts[NULLGLOB];
 				opts[NULLGLOB] = 1;
-				globlist(l, 0);
+				globlist(l, 0, 0);
 				opts[NULLGLOB] = ng;
 				/* Get the results. */
 				if (nonempty(l) && peekfirst(l)) {
@@ -3730,7 +3730,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	/* Fine, now do full expansion. */
 	prefork(foo, 0);
 	if (!errflag) {
-	    globlist(foo, 0);
+	    globlist(foo, 0, 0);
 	    if (!errflag)
 		/* And add the resulting words as matches. */
 		for (n = firstnode(foo); n; incnode(n))
diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c
index 499c4ae..c4f97a8 100644
--- a/Src/Zle/zle_tricky.c
+++ b/Src/Zle/zle_tricky.c
@@ -2197,7 +2197,7 @@ doexpansion(char *s, int lst, int olst, int explincmd)
 	int ng = opts[NULLGLOB];
 
 	opts[NULLGLOB] = 1;
-	globlist(vl, 1);
+	globlist(vl, 0, 1);
 	opts[NULLGLOB] = ng;
     }
     if (errflag)
diff --git a/Src/cond.c b/Src/cond.c
index c673542..b5f162a 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -319,6 +319,19 @@ evalcond(Estate state, char *fromtest)
 	return (!(dostat(left) & S_ISGID));
     case 'k':
 	return (!(dostat(left) & S_ISVTX));
+    case 'm':
+	{
+	    LinkList globber = newsizedlist(2);
+	    tokenize(left);
+	    setsizednode(globber, 0, "");
+	    setsizednode(globber, 1, left);
+#if 1
+	    return (!globlist(globber, 1 /* short circuit */, 1 /* nountok */));
+#else
+	    globlist(globber, 0 /* short circuit */, 1 /* nountok */);
+	    return (!nextnode(firstnode(globber)));
+#endif
+	}
     case 'n':
 	return (!strlen(left));
     case 'o':
diff --git a/Src/exec.c b/Src/exec.c
index 8249def..aa381e9 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2237,7 +2237,7 @@ addvars(Estate state, Wordcode pc, int addflags)
 		return;
 	    }
 	    if (isset(GLOBASSIGN) || !isstr)
-		globlist(vl, 0);
+		globlist(vl, 0, 0);
 	    if (errflag) {
 		state->pc = opc;
 		return;
@@ -2348,7 +2348,7 @@ execsubst(LinkList strs)
 	prefork(strs, esprefork);
 	if (esglob && !errflag) {
 	    LinkList ostrs = strs;
-	    globlist(strs, 0);
+	    globlist(strs, 0, 0);
 	    strs = ostrs;
 	}
     }
@@ -2621,7 +2621,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 	    if (!(cflags & BINF_NOGLOB))
 		while (!checked && !errflag && args && nonempty(args) &&
 		       has_token((char *) peekfirst(args)))
-		    zglob(args, firstnode(args), 0);
+		    zglob(args, firstnode(args), 0, 0);
 	    else if (!unglobbed) {
 		for (node = firstnode(args); node; incnode(node))
 		    untokenize((char *) getdata(node));
@@ -2942,7 +2942,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
 
     if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
 	LinkList oargs = args;
-	globlist(args, 0);
+	globlist(args, 0, 0);
 	args = oargs;
     }
     if (errflag) {
diff --git a/Src/glob.c b/Src/glob.c
index 07dd7c2..1a521a5 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -452,8 +452,8 @@ insert(char *s, int checked)
  * tried all of it.                                */
 
 /**/
-static void
-scanner(Complist q)
+static int
+scanner(Complist q, int shortcircuit)
 {
     Patprog p;
     int closure;
@@ -463,14 +463,15 @@ scanner(Complist q)
 
     init_dirsav(&ds);
     if (!q)
-	return;
+	return -1;
 
     if ((closure = q->closure)) {
 	/* (foo/)# - match zero or more dirs */
 	if (q->closure == 2)	/* (foo/)## - match one or more dirs */
 	    q->closure = 1;
 	else
-	    scanner(q->next);
+	    if (scanner(q->next, shortcircuit) == 1)
+		return 1;
     }
     p = q->pat;
     /* Now the actual matching for the current path section. */
@@ -485,13 +486,13 @@ scanner(Complist q)
 	    int err;
 
 	    if (l >= PATH_MAX)
-		return;
+		return -1;
 	    err = lchdir(pathbuf + pathbufcwd, &ds, 0);
 	    if (err == -1)
-		return;
+		return -1;
 	    if (err) {
 		zerr("current directory lost during glob");
-		return;
+		return -1;
 	    }
 	    pathbufcwd = pathpos;
 	}
@@ -516,14 +517,18 @@ scanner(Complist q)
 		if (add) {
 		    addpath(str, l);
 		    if (!closure || !statfullpath("", NULL, 1))
-			scanner((q->closure) ? q : q->next);
+			if (scanner((q->closure) ? q : q->next, shortcircuit) == 1)
+			    return 1;
 		    pathbuf[pathpos = oppos] = '\0';
 		}
 	    }
 	} else {
 	    if (str[l])
 		str = dupstrpfx(str, l);
-	    insert(str, 0);
+	    if (!shortcircuit)
+		insert(str, 0);
+	    else
+		return 1;
 	}
     } else {
 	/* Do pattern matching on current path section. */
@@ -534,7 +539,7 @@ scanner(Complist q)
 	int subdirlen = 0;
 
 	if (lock == NULL)
-	    return;
+	    return -1;
 	while ((fn = zreaddir(lock, 1)) && !errflag) {
 	    /* prefix and suffix are zle trickery */
 	    if (!dirs && !colonmod &&
@@ -613,7 +618,10 @@ scanner(Complist q)
 		    subdirlen += sizeof(int);
 		} else
 		    /* if the last filename component, just add it */
-		    insert(fn, 1);
+		    if (!shortcircuit)
+			insert(fn, 1);
+		    else
+			return 1;
 	    }
 	}
 	closedir(lock);
@@ -626,7 +634,8 @@ scanner(Complist q)
 		fn += l + 1;
 		memcpy((char *)&errsfound, fn, sizeof(int));
 		fn += sizeof(int);
-		scanner((q->closure) ? q : q->next);  /* scan next level */
+		if (scanner((q->closure) ? q : q->next, shortcircuit) == 1) /* scan next level */
+		    return 1;
 		pathbuf[pathpos = oppos] = '\0';
 	    }
 	    hrealloc(subdirs, subdirlen, 0);
@@ -640,6 +649,7 @@ scanner(Complist q)
 	    close(ds.dirfd);
 	pathbufcwd = pbcwdsav;
     }
+    return 0;
 }
 
 /* This function tokenizes a zsh glob pattern */
@@ -1066,8 +1076,8 @@ insert_glob_match(LinkList list, LinkNode next, char *data)
  * into a series of nodes.                                      */
 
 /**/
-void
-zglob(LinkList list, LinkNode np, int nountok)
+int
+zglob(LinkList list, LinkNode np, int shortcircuit, int nountok)
 {
     struct qual *qo, *qn, *ql;
     LinkNode node = prevnode(np);
@@ -1084,7 +1094,7 @@ zglob(LinkList list, LinkNode np, int nountok)
     if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) {
 	if (!nountok)
 	    untokenize(ostr);
-	return;
+	return -1;
     }
     save_globstate(saved);
 
@@ -1526,7 +1536,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 		    if (gf_nsorts == MAX_SORTS) {
 			zerr("too many glob sort specifiers");
 			restore_globstate(saved);
-			return;
+			return -1;
 		    }
 
 		    /* usually just one character */
@@ -1548,14 +1558,14 @@ zglob(LinkList list, LinkNode np, int nountok)
 			     glob_exec_string(&send)) == NULL)
 			{
 			    restore_globstate(saved);
-			    return;
+			    return -1;
 			}
 			break;
 		    }
 		    default:
 			zerr("unknown sort specifier");
 			restore_globstate(saved);
-			return;
+			return -1;
 		    }
 		    if (t != GS_EXEC) {
 			if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
@@ -1563,7 +1573,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 			if (gf_sorts & t) {
 			    zerr("doubled sort specifier");
 			    restore_globstate(saved);
-			    return;
+			    return -1;
 			}
 		    }
 		    gf_sorts |= t;
@@ -1600,7 +1610,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 		    if (getindex(&s, &v, 0) || s == os) {
 			zerr("invalid subscript");
 			restore_globstate(saved);
-			return;
+			return -1;
 		    }
 		    first = v.start;
 		    end = v.end;
@@ -1623,7 +1633,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 		    untokenize(--s);
 		    zerr("unknown file attribute: %c", *s);
 		    restore_globstate(saved);
-		    return;
+		    return -1;
 		}
 	    }
 	    if (func) {
@@ -1648,7 +1658,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	    }
 	    if (errflag) {
 		restore_globstate(saved);
-		return;
+		return -1;
 	    }
 	}
 
@@ -1710,11 +1720,11 @@ zglob(LinkList list, LinkNode np, int nountok)
 	    if (!nountok)
 		untokenize(ostr);
 	    insertlinknode(list, node, ostr);
-	    return;
+	    return -1;
 	}
 	errflag = 0;
 	zerr("bad pattern: %s", ostr);
-	return;
+	return -1;
     }
     if (!gf_nsorts) {
 	gf_sortlist[0].tp = gf_sorts = GS_NAME;
@@ -1722,14 +1732,22 @@ zglob(LinkList list, LinkNode np, int nountok)
     }
     /* Initialise receptacle for matched files, *
      * expanded by insert() where necessary.    */
-    matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
-					 sizeof(struct gmatch));
-    matchct = 0;
+    if (!shortcircuit) {
+	matchptr = matchbuf = (Gmatch)zalloc((matchsz = 16) *
+					     sizeof(struct gmatch));
+	matchct = 0;
+    }
     pattrystart();
 
     /* The actual processing takes place here: matches go into  *
      * matchbuf.  This is the only top-level call to scanner(). */
-    scanner(q);
+    if (scanner(q, shortcircuit) == 1)
+	return 1;
+    else if (shortcircuit) {
+	/* ### TODO: this returns 0 even if scanner() returned -1 */
+	restore_globstate(saved);
+	return 0;
+    }
 
     /* Deal with failures to match depending on options */
     if (matchct)
@@ -1741,7 +1759,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	    zerr("no matches found: %s", ostr);
 	    free(matchbuf);
 	    restore_globstate(saved);
-	    return;
+	    return -1;
 	} else {
 	    /* treat as an ordinary string */
 	    untokenize(matchptr->name = dupstring(ostr));
@@ -1844,6 +1862,7 @@ zglob(LinkList list, LinkNode np, int nountok)
     free(matchbuf);
 
     restore_globstate(saved);
+    return 0;
 }
 
 /* Return the trailing character for marking file types */
@@ -1995,7 +2014,7 @@ xpandredir(struct redir *fn, LinkList redirtab)
     prefork(&fake, isset(MULTIOS) ? 0 : PREFORK_SINGLE);
     /* Globbing is only done for multios. */
     if (!errflag && isset(MULTIOS))
-	globlist(&fake, 0);
+	globlist(&fake, 0, 0);
     if (errflag)
 	return 0;
     if (nonempty(&fake) && !nextnode(firstnode(&fake))) {
diff --git a/Src/parse.c b/Src/parse.c
index 530a070..52b3218 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -2124,7 +2124,7 @@ par_cond_2(void)
     }
     s1 = tokstr;
     if (condlex == testlex)
-	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
+	dble = (*s1 == '-' && strspn(s1+1, "abcdefghkmnoprstuwxzLONGS") == 1
 		  && !s1[2]);
     condlex();
     if (tok == INANG || tok == OUTANG) {
@@ -2178,7 +2178,7 @@ par_cond_double(char *a, char *b)
 {
     if (a[0] != '-' || !a[1])
 	COND_ERROR("parse error: condition expected: %s", a);
-    else if (!a[2] && strspn(a+1, "abcdefgknoprstuwxzhLONGS") == 1) {
+    else if (!a[2] && strspn(a+1, "abcdefghkmnoprstuwxzLONGS") == 1) {
 	ecadd(WCB_COND(a[1], 0));
 	ecstr(b);
     } else {
diff --git a/Src/subst.c b/Src/subst.c
index 4713502..9ab04b3 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -370,18 +370,20 @@ quotesubst(char *str)
 }
 
 /**/
-mod_export void
-globlist(LinkList list, int nountok)
+mod_export int
+globlist(LinkList list, int shortcircuit, int nountok)
 {
     LinkNode node, next;
 
     badcshglob = 0;
     for (node = firstnode(list); !errflag && node; node = next) {
 	next = nextnode(node);
-	zglob(list, node, nountok);
+	if (zglob(list, node, shortcircuit, nountok) == 1)
+	    return 1;
     }
     if (badcshglob == 1)
 	zerr("no match");
+    return 0;
 }
 
 /* perform substitution on a single word */
diff --git a/Test/C02cond.ztst b/Test/C02cond.ztst
index 94fca8b..0c6cfbf 100644
--- a/Test/C02cond.ztst
+++ b/Test/C02cond.ztst
@@ -70,6 +70,9 @@
   [[ -k modish && ! -k zerolength ]]
 0:-k cond
 
+  [[ -m zero* ]] && [[ ! -m *nonexistent* ]]
+0:-m cond
+
   foo=foo
   bar=
   [[ -n $foo && ! -n $bar && ! -n '' ]]
-- 
1.7.10.4



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