>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