>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