1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-28 05:00:59 +01:00

users/18857: add (Y) glob qualifier to generate only one match per pattern

This commit is contained in:
Daniel Shahaf 2014-06-01 14:18:21 -07:00 committed by Barton E. Schaefer
parent 501f2003a8
commit 10ae77c0cf
4 changed files with 44 additions and 11 deletions

View file

@ -1,3 +1,8 @@
2014-06-01 Barton E. Schaefer <schaefer@zsh.org>
* Daniel Shahaf: users/18857: Doc/Zsh/expn.yo, Src/glob.c,
Test/D02glob.ztst: add (Y) glob qualifier
2014-06-01 Peter Stephenson <p.w.stephenson@ntlworld.com> 2014-06-01 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 32640: Doc/Zsh/cond.yo, Doc/Zsh/expn.yo, NEWS, Src/cond.c, * 32640: Doc/Zsh/cond.yo, Doc/Zsh/expn.yo, NEWS, Src/cond.c,

View file

@ -2564,6 +2564,10 @@ item(tt(n))(
sets the tt(NUMERIC_GLOB_SORT) option for the current pattern sets the tt(NUMERIC_GLOB_SORT) option for the current pattern
pindex(NUMERIC_GLOB_SORT, setting in 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))( item(tt(o)var(c))(
specifies how the names of the files should be sorted. If var(c) is 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 tt(n) they are sorted by name (the default); if it is tt(L) they

View file

@ -452,8 +452,8 @@ insert(char *s, int checked)
* tried all of it. */ * tried all of it. */
/**/ /**/
static void static int
scanner(Complist q) scanner(Complist q, int shortcircuit)
{ {
Patprog p; Patprog p;
int closure; int closure;
@ -463,14 +463,15 @@ scanner(Complist q)
init_dirsav(&ds); init_dirsav(&ds);
if (!q) if (!q)
return; return -1;
if ((closure = q->closure)) { if ((closure = q->closure)) {
/* (foo/)# - match zero or more dirs */ /* (foo/)# - match zero or more dirs */
if (q->closure == 2) /* (foo/)## - match one or more dirs */ if (q->closure == 2) /* (foo/)## - match one or more dirs */
q->closure = 1; q->closure = 1;
else else
scanner(q->next); if (scanner(q->next, shortcircuit) == 1)
return 1;
} }
p = q->pat; p = q->pat;
/* Now the actual matching for the current path section. */ /* Now the actual matching for the current path section. */
@ -485,13 +486,13 @@ scanner(Complist q)
int err; int err;
if (l >= PATH_MAX) if (l >= PATH_MAX)
return; return -1;
err = lchdir(pathbuf + pathbufcwd, &ds, 0); err = lchdir(pathbuf + pathbufcwd, &ds, 0);
if (err == -1) if (err == -1)
return; return -1;
if (err) { if (err) {
zerr("current directory lost during glob"); zerr("current directory lost during glob");
return; return -1;
} }
pathbufcwd = pathpos; pathbufcwd = pathpos;
} }
@ -516,7 +517,8 @@ scanner(Complist q)
if (add) { if (add) {
addpath(str, l); addpath(str, l);
if (!closure || !statfullpath("", NULL, 1)) 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'; pathbuf[pathpos = oppos] = '\0';
} }
} }
@ -524,6 +526,8 @@ scanner(Complist q)
if (str[l]) if (str[l])
str = dupstrpfx(str, l); str = dupstrpfx(str, l);
insert(str, 0); insert(str, 0);
if (shortcircuit)
return 1;
} }
} else { } else {
/* Do pattern matching on current path section. */ /* Do pattern matching on current path section. */
@ -534,7 +538,7 @@ scanner(Complist q)
int subdirlen = 0; int subdirlen = 0;
if (lock == NULL) if (lock == NULL)
return; return -1;
while ((fn = zreaddir(lock, 1)) && !errflag) { while ((fn = zreaddir(lock, 1)) && !errflag) {
/* prefix and suffix are zle trickery */ /* prefix and suffix are zle trickery */
if (!dirs && !colonmod && if (!dirs && !colonmod &&
@ -614,6 +618,8 @@ scanner(Complist q)
} else } else
/* if the last filename component, just add it */ /* if the last filename component, just add it */
insert(fn, 1); insert(fn, 1);
if (shortcircuit)
return 1;
} }
} }
closedir(lock); closedir(lock);
@ -626,7 +632,8 @@ scanner(Complist q)
fn += l + 1; fn += l + 1;
memcpy((char *)&errsfound, fn, sizeof(int)); memcpy((char *)&errsfound, fn, sizeof(int));
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'; pathbuf[pathpos = oppos] = '\0';
} }
hrealloc(subdirs, subdirlen, 0); hrealloc(subdirs, subdirlen, 0);
@ -640,6 +647,7 @@ scanner(Complist q)
close(ds.dirfd); close(ds.dirfd);
pathbufcwd = pbcwdsav; pathbufcwd = pbcwdsav;
} }
return 0;
} }
/* This function tokenizes a zsh glob pattern */ /* This function tokenizes a zsh glob pattern */
@ -1141,6 +1149,7 @@ zglob(LinkList list, LinkNode np, int nountok)
/* and index+1 of the last match */ /* and index+1 of the last match */
struct globdata saved; /* saved glob state */ struct globdata saved; /* saved glob state */
int nobareglob = !isset(BAREGLOBQUAL); int nobareglob = !isset(BAREGLOBQUAL);
int shortcircuit = 0;
if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) { if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) {
if (!nountok) if (!nountok)
@ -1491,6 +1500,10 @@ zglob(LinkList list, LinkNode np, int nountok)
/* Numeric glob sort */ /* Numeric glob sort */
gf_numsort = !(sense & 1); gf_numsort = !(sense & 1);
break; break;
case 'Y':
/* Short circuit: just check if there are any matches */
shortcircuit = !(sense & 1);
break;
case 'a': case 'a':
/* Access time in given range */ /* Access time in given range */
g_amc = 0; g_amc = 0;
@ -1759,7 +1772,7 @@ zglob(LinkList list, LinkNode np, int nountok)
/* The actual processing takes place here: matches go into * /* The actual processing takes place here: matches go into *
* matchbuf. This is the only top-level call to scanner(). */ * matchbuf. This is the only top-level call to scanner(). */
scanner(q); scanner(q, shortcircuit);
/* Deal with failures to match depending on options */ /* Deal with failures to match depending on options */
if (matchct) if (matchct)

View file

@ -431,6 +431,7 @@
mkdir glob.tmp/dir5 mkdir glob.tmp/dir5
touch glob.tmp/dir5/N123 touch glob.tmp/dir5/N123
print glob.tmp/dir5/N<->(N) print glob.tmp/dir5/N<->(N)
rm -rf glob.tmp/dir5
0:Numeric glob is not usurped by process substitution. 0:Numeric glob is not usurped by process substitution.
>glob.tmp/dir5/N123 >glob.tmp/dir5/N123
@ -541,3 +542,13 @@
>No file beginning with z >No file beginning with z
>Multiple files matched >Multiple files matched
>Normal string if nullglob not set >Normal string if nullglob not set
(){ 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