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