From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 2717 invoked by alias); 26 May 2014 23:52:38 -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: 18857 Received: (qmail 22802 invoked from network); 26 May 2014 23:52:24 -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:subject:message-id:references :mime-version:content-type:in-reply-to; s=mesmtp; bh=Upierh/37r8 o+qWc63c+TuGR9FU=; b=L4DZuEV5+TsZla/IeMtzLfHqCiQL7PinZ4SlKrjOpeN LXw8YKan104QI7vGaQUtknswNIe6DbExs6MMCkE/oh4EXb/V76osAbUKxShuvRwL 4hyeQTdjTBnf99Yc0ka2pqDGDbx12h/5f1ghmkbVv4d3v+zSjZk4V1n0xBJjVSMM = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=date:from:to:subject:message-id :references:mime-version:content-type:in-reply-to; s=smtpout; bh=Upierh/37r8o+qWc63c+TuGR9FU=; b=sEqAonZLvHt0G7dU6NboY3Pol7X/ m5J3a4djKrm0NodP0pvYGlPUAuCaKQU20p4kEKE6HOaNmTNWK3R0KLEue4vZPcQO +0PuzWV1rK5CQwovxJSGr7aaDPAguzEdy7qCluuaz0+gvbuItsUMb06pRjnd4vh3 r6B84489EH17Oys= X-Sasl-enc: DkDSLmIOFG37lhNfE/C7yN9Ev3zmGpA/veIfsafpx9qQ 1401148339 Date: Mon, 26 May 2014 23:52:16 +0000 From: Daniel Shahaf To: zsh-users@zsh.org Subject: Re: globbing in conditional expressions Message-ID: <20140526235216.GC1920@tarsus.local2> References: <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> <20140514041908.GF2471@tarsus.local2> <140514001819.ZM23478@torch.brasslantern.com> <20140515092901.GC2174@tarsus.local2> <140515075003.ZM28035@torch.brasslantern.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="EeQfGwPcQSOJBaQU" Content-Disposition: inline In-Reply-To: <140515075003.ZM28035@torch.brasslantern.com> User-Agent: Mutt/1.5.21 (2010-09-15) --EeQfGwPcQSOJBaQU Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Bart Schaefer wrote on Thu, May 15, 2014 at 07:50:03 -0700: > On May 15, 9:29am, Daniel Shahaf wrote: > } Subject: Re: globbing in conditional expressions > } > } Bart Schaefer wrote on Wed, May 14, 2014 at 00:18:19 -0700: > } > Do I read correctly that "shortcircuit" also means "don't return any > } > file names, just return an indication of whether there is such a file" > } > ?? (Since you don't allocate the matchbuf array when shortcircuit.) > } > } Of course. As soon as you find a single matching filename you return to > } the caller, so there are two options: either return just a boolean, or > } return the first matching filename in readdir() order (via $REPLY, as in > } Roman's post). I'm not sure how idiomatic it would be for a [[ -x ]] > } condition to set $REPLY. > > I would not suggest that [[ -m ... ]] set $REPLY, but why does -m have > to be the only place where short-circuiting occurs? I could imagine > adding a globbing flag (#X) [where I'm using X as a placeholder rather > than a suggestion for the actual flag] which means that the glob returns > only the first matching file it stumbles upon. > > In fact if you had that flag you wouldn't need -m as a condition op, it > would suffice to do [ ! -z pat(NX) ]. The implementation also would not > need to change the call signatures of any of the C functions ... That turned out to be much less code to implement. It's attached, if anyone finds it useful. I couldn't get it to work via [ -z ], but this works: % Src/zsh -fc '() { (( $#@ )) } *(NY); echo $?' 0 % Src/zsh -fc '() { (( $#@ )) } *foobar(NY); echo $?' 1 --EeQfGwPcQSOJBaQU Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0001-Add-Y-glob-qualifier-that-short-circuits.patch" >>From 23eafe9265691c0ea6d705100c1ee9e84850efa5 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 13 May 2014 10:18:56 +0000 Subject: [PATCH] Add (Y) glob qualifier that short-circuits. --- Doc/Zsh/expn.yo | 4 ++++ Src/glob.c | 35 ++++++++++++++++++++++++----------- Test/D02glob.ztst | 11 +++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index de0f454..65374e8 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -2560,6 +2560,10 @@ item(tt(n))( sets the tt(NUMERIC_GLOB_SORT) option for the current pattern pindex(NUMERIC_GLOB_SORT, setting in pattern) ) +item(tt(Y))( +enables short-circuit mode: the pattern will expand to just the first +matching filename, if any. +) item(tt(o)var(c))( specifies how the names of the files should be sorted. If var(c) is tt(n) they are sorted by name (the default); if it is tt(L) they diff --git a/Src/glob.c b/Src/glob.c index 07dd7c2..eafb702 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,7 +517,8 @@ 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'; } } @@ -524,6 +526,8 @@ scanner(Complist q) if (str[l]) str = dupstrpfx(str, l); insert(str, 0); + if (shortcircuit) + return 1; } } else { /* Do pattern matching on current path section. */ @@ -534,7 +538,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 && @@ -614,6 +618,8 @@ scanner(Complist q) } else /* if the last filename component, just add it */ insert(fn, 1); + if (shortcircuit) + return 1; } } closedir(lock); @@ -626,7 +632,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 +647,7 @@ scanner(Complist q) close(ds.dirfd); pathbufcwd = pbcwdsav; } + return 0; } /* This function tokenizes a zsh glob pattern */ @@ -1080,6 +1088,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* and index+1 of the last match */ struct globdata saved; /* saved glob state */ int nobareglob = !isset(BAREGLOBQUAL); + int shortcircuit = 0; if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) { if (!nountok) @@ -1461,6 +1470,10 @@ zglob(LinkList list, LinkNode np, int nountok) /* Numeric glob sort */ gf_numsort = !(sense & 1); break; + case 'Y': + /* Short circuit: just check if there are any matches */ + shortcircuit = !(sense & 1); + break; case 'a': /* Access time in given range */ g_amc = 0; @@ -1729,7 +1742,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* The actual processing takes place here: matches go into * * matchbuf. This is the only top-level call to scanner(). */ - scanner(q); + scanner(q, shortcircuit); /* Deal with failures to match depending on options */ if (matchct) diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index 1f8f652..0ff088a 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -431,6 +431,7 @@ mkdir glob.tmp/dir5 touch glob.tmp/dir5/N123 print glob.tmp/dir5/N<->(N) + rm -rf glob.tmp/dir5 0:Numeric glob is not usurped by process substitution. >glob.tmp/dir5/N123 @@ -526,3 +527,13 @@ >+bus+bus matches +(+bus|-car) >@sinhats matches @(@sinhats|wrensinfens) >!kerror matches !(!somethingelse) + + (){ print $#@ } glob.tmp/dir*(Y) + (){ print $#@ } glob.tmp/file*(NY) + (){ [[ $1 = glob.tmp/dir? ]] && echo "(Y) returns a matching filename" } glob.tmp/dir*(Y) + (){ print $@:t } glob.tmp/dir*(Y^Y) +0:short-circuit modifier +>1 +>0 +>(Y) returns a matching filename +>dir1 dir2 dir3 dir4 -- 1.7.10.4 --EeQfGwPcQSOJBaQU--