From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26128 invoked by alias); 14 May 2014 04:28:56 -0000 Mailing-List: contact zsh-users-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Users List List-Post: List-Help: X-Seq: 18796 Received: (qmail 12200 invoked from network); 14 May 2014 04:28:38 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= daniel.shahaf.name; h=date:from:to:cc:subject:message-id :references:mime-version:content-type:in-reply-to; s=mesmtp; bh= YXBlzbAeXC5qkgg/Q/KSYtujJmw=; b=YmHhT0wvlSmztF24xcpRNco+71J6cqX9 FVPsXgIU31e0zuMlZ3OX1bBmqt2T1p5ZBOhX2J/j5MFl1LQzAuDcDgKCJZuKu1r/ H+PRqIVcGmxnmrOR7nOa4mlynTkUCDCAgXMWxkBDgGDg4eNsUoDmX/S4JhWHd8XI wvGYX/2RXJU= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=date:from:to:cc:subject:message-id :references:mime-version:content-type:in-reply-to; s=smtpout; bh=YXBlzbAeXC5qkgg/Q/KSYtujJmw=; b=rzpJJBFK4zSnrEVmZ0M2IJW281Cp FYpXsLMXgi+V3WufrMw6v4ixvucCfzp5gMJHk0Cj8l+kLq0HZddSio8TyylwGv2N vmrVAe0mSCT1FeHnUl01C68Qo+YpuUjEzBp8wI8EAmrXG3sRGzBc57eswbf7eJ8I ZYj4KnGiKaoa35c= X-Sasl-enc: kAqx5xKtEWuQd4YKPiB/PZ0psbUleFqyDAJ8RSNXtovw 1400041151 Date: Wed, 14 May 2014 04:19:08 +0000 From: Daniel Shahaf To: Bart Schaefer Cc: zsh-users@zsh.org Subject: Re: globbing in conditional expressions Message-ID: <20140514041908.GF2471@tarsus.local2> References: <20140507124101.GA53652@isis.sigpipe.cz> <20140507154407.660eb500@pwslap01u.europe.root.pri> <20140508105522.GE2052@tarsus.local2> <20140508122045.3c68c3fa@pwslap01u.europe.root.pri> <140508083418.ZM14713@torch.brasslantern.com> <20140508201936.GB53652@isis.sigpipe.cz> <140513084117.ZM22925@torch.brasslantern.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="yrj/dFKFPuw6o+aM" Content-Disposition: inline In-Reply-To: <140513084117.ZM22925@torch.brasslantern.com> User-Agent: Mutt/1.5.21 (2010-09-15) --yrj/dFKFPuw6o+aM Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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? --yrj/dFKFPuw6o+aM Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0003-Add-m-pattern-that-short-circuits.patch" >>From 95dfc45753601eeb1d406a640c03434fcbc61704 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf 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 --yrj/dFKFPuw6o+aM--