zsh-workers
 help / color / mirror / code / Atom feed
* Re: globbing in conditional expressions
       [not found]                         ` <140530085542.ZM18304@torch.brasslantern.com>
@ 2014-05-30 18:57                           ` Peter Stephenson
  2014-05-30 19:19                             ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2014-05-30 18:57 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 30 May 2014 08:55:42 -0700
Bart Schaefer <schaefer@brasslantern.com> wrote:
> On May 30,  9:47am, Peter Stephenson wrote:
> } But this is specific to zsh --- why are we using [ rather than [[ anyway?
> 
> Because [[ ]] doesn't do globbing on its arguments, and this is a
> filename generation rather than a pattern.  We messed around a few weeks
> ago with trying to make [[ ]] do globbing and ran into tokenization
> conflicts.

(Moved this to workers.  No user serviceable parts here.)

Oh, er, right... you make it sound as if knowing what's going on is
somehow a positive thing.

One thing we could do is make [[ ... = ... ]] expand its right hand
argument the same way as normal command arguments when told to do so, so
there's no retokenization.  (There's no point for [ ] since the globbing
has already happened.)  Then we could simply join the arguments
resulting from the glob --- there may just be one, but we shouldn't
simply ignore multuple results.

We wouldn't want this always to happen, since that's confusing, but we
could signal it, for example, using the extended glob syntax (#q), which
explicitly indicates a glob pattern --- it has no meaning at all in
non-glob pattern matching.  This would limit it's use to when extended
globbing is on, but I think that's an entirely sensible limitation
anyway.

I think this patch does roughly the right thing.

So, for example, in the zsh Src directory

[[ "glob.c glob.o" = g*.?(#q) ]]

is true.  Of course you can have other qualifiers such as NY after the
#q.

Piecing together the various sorts of expansion from execmd() isn't
trivial --- it's quite likely I've missed some subtlety, possibly not
even very subtle, in the first hunk below.

diff --git a/Functions/Zle/expand-absolute-path b/Functions/Zle/expand-absolute-path
index b857576..a420d41 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -282,9 +282,20 @@ evalcond(Estate state, char *fromtest)
 
 		right = dupstring(opat = ecrawstr(state->prog, state->pc,
 						  &htok));
-		if (htok)
-		    singsub(&right);
+		if (htok) { 
+		    if (!fromtest &&
+			checkglobqual(right, strlen(right), 1, NULL)) {
+			LinkList args = newlinklist();
+			addlinknode(args, right);
+			prefork(args, 0);
+			while (!errflag && args && nonempty(args) &&
+			       has_token((char *)peekfirst(args)))
+			    zglob(args, firstnode(args), 0);
+			right = sepjoin(hlinklist2array(args, 0), NULL, 1);
+		    } else
+			singsub(&right);
+		}
 		save = (!(state->prog->flags & EF_HEAP) &&
    
    char *s = *sp; 			!strcmp(opat, right) && pprog != dummy_patprog2);
 
diff --git a/Src/glob.c b/Src/glob.c
index 07dd7c2..01f8a6e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1061,6 +1061,65 @@ insert_glob_match(LinkList list, LinkNode next, char *data)
     insertlinknode(list, next, data);
 }
 
+/*
+ * Return
+ *   1 if str ends in bare glob qualifiers
+ *   2 if str ends in non-bare glob qualifiers (#q)
+ *   0 otherwise.
+ *
+ * str is the string to check.
+ * sl is it's length (to avoid recalculation).
+ * nobareglob is 1 if bare glob qualifiers are not allowed.
+ * *sp, if sp is not null, wil be a pointer to the start parentheses.
+ */
+
+/**/
+int
+checkglobqual(char *str, int sl, int nobareglob, char **sp)
+{
+    char *s;
+    int paren, ret = 1;
+
+    if (str[sl - 1] != Outpar)
+	return 0;
+
+    /* Check these are really qualifiers, not a set of *
+     * alternatives or exclusions.  We can be more     *
+     * lenient with an explicit (#q) than with a bare  *
+     * set of qualifiers.                              */
+    paren = 0;
+    for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
+	switch (*s) {
+	case Outpar:
+	    paren++; /*FALLTHROUGH*/
+	case Bar:
+	    if (!zpc_disables[ZPC_BAR])
+		nobareglob = 1;
+	    break;
+	case Tilde:
+	    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
+		nobareglob = 1;
+	    break;
+	case Inpar:
+	    paren--;
+	    break;
+	}
+    }
+    if (*s != Inpar)
+	return 0;
+    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
+	if (s[2] != 'q')
+	    return 0;
+	ret = 2;
+    } else if (nobareglob)
+	return 0;
+
+    if (sp)
+	*sp = s;
+
+    return ret;
+}
+
 /* Main entry point to the globbing code for filename globbing. *
  * np points to a node in the list list which will be expanded  *
  * into a series of nodes.                                      */
@@ -1118,7 +1177,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	   (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) {
 	struct qual *newquals;
 	char *s;
-	int sense, paren;
+	int sense, qualsfound;
 	off_t data;
 	char *sdata, *newcolonmod;
 	int (*func) _((char *, Statptr, off_t, char *));
@@ -1148,40 +1207,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	newquals = qo = qn = ql = NULL;
 
 	sl = strlen(str);
-	if (str[sl - 1] != Outpar)
-	    break;
-
-	/* Check these are really qualifiers, not a set of *
-	 * alternatives or exclusions.  We can be more     *
-	 * lenient with an explicit (#q) than with a bare  *
-	 * set of qualifiers.                              */
-	paren = 0;
-	for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
-	    switch (*s) {
-	    case Outpar:
-		paren++; /*FALLTHROUGH*/
-	    case Bar:
-		if (!zpc_disables[ZPC_BAR])
-		    nobareglob = 1;
-		break;
-	    case Tilde:
-		if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
-		    nobareglob = 1;
-		break;
-	    case Inpar:
-		paren--;
-		break;
-	    }
-	}
-	if (*s != Inpar)
-	    break;
-	if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
-	    if (s[2] == 'q') {
-		*s = 0;
-		s += 2;
-	    } else
-		break;
-	} else if (nobareglob)
+	if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s)))
 	    break;
 
 	/* Real qualifiers found. */
@@ -1194,6 +1220,8 @@ zglob(LinkList list, LinkNode np, int nountok)
 
 	str[sl-1] = 0;
 	*s++ = 0;
+	if (qualsfound == 2)
+	    s += 2;
 	while (*s && !newcolonmod) {
 	    func = (int (*) _((char *, Statptr, off_t, char *)))0;
 	    if (idigit(*s)) {

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: globbing in conditional expressions
  2014-05-30 18:57                           ` globbing in conditional expressions Peter Stephenson
@ 2014-05-30 19:19                             ` Peter Stephenson
  2014-05-31 19:38                               ` Peter Stephenson
  0 siblings, 1 reply; 6+ messages in thread
From: Peter Stephenson @ 2014-05-30 19:19 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 30 May 2014 19:57:34 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> One thing we could do is make [[ ... = ... ]] expand its right hand
> argument the same way as normal command arguments when told to do so, so
> there's no retokenization.

No reason not to do this for all string arguments, I suppose --- there's
nothig here that's specific to pattern matching.  The function
cond_match() appears not to be used.

[[ -z nonexistent*(#qN) ]] 
[[ -n glob*(#qN) ]] 


diff --git a/Src/cond.c b/Src/cond.c
index c673542..6e9b558 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -37,6 +37,21 @@ static char *condstr[COND_MOD] = {
     "-ne", "-lt", "-gt", "-le", "-ge", "=~"
 };
 
+static void cond_subst(char **strp, int glob_ok)
+{
+    if (glob_ok &&
+	checkglobqual(*strp, strlen(*strp), 1, NULL)) {
+	LinkList args = newlinklist();
+	addlinknode(args, *strp);
+	prefork(args, 0);
+	while (!errflag && args && nonempty(args) &&
+	       has_token((char *)peekfirst(args)))
+	    zglob(args, firstnode(args), 0);
+	*strp = sepjoin(hlinklist2array(args, 0), NULL, 1);
+    } else
+	singsub(strp);
+}
+
 /*
  * Evaluate a conditional expression given the arguments.
  * If fromtest is set, the caller is the test or [ builtin;
@@ -177,13 +192,13 @@ evalcond(Estate state, char *fromtest)
     }
     left = ecgetstr(state, EC_DUPTOK, &htok);
     if (htok) {
-	singsub(&left);
+	cond_subst(&left, !fromtest);
 	untokenize(left);
     }
     if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
 	right = ecgetstr(state, EC_DUPTOK, &htok);
 	if (htok) {
-	    singsub(&right);
+	    cond_subst(&right, !fromtest);
 	    untokenize(right);
 	}
     }
@@ -194,7 +209,7 @@ evalcond(Estate state, char *fromtest)
 	    fprintf(xtrerr, " %s ", condstr[ctype]);
 	    if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
 		char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
-		singsub(&rt);
+		cond_subst(&rt, !fromtest);
 		quote_tokenized_output(rt, xtrerr);
 	    }
 	    else
@@ -283,7 +298,7 @@ evalcond(Estate state, char *fromtest)
 		right = dupstring(opat = ecrawstr(state->prog, state->pc,
 						  &htok));
 		if (htok)
-		    singsub(&right);
+		    cond_subst(&right, !fromtest);
 		save = (!(state->prog->flags & EF_HEAP) &&
 			!strcmp(opat, right) && pprog != dummy_patprog2);
 
@@ -517,17 +532,6 @@ cond_val(char **args, int num)
 }
 
 /**/
-mod_export int
-cond_match(char **args, int num, char *str)
-{
-    char *s = args[num];
-
-    singsub(&s);
-
-    return matchpat(str, s);
-}
-
-/**/
 static void
 tracemodcond(char *name, char **args, int inf)
 {
diff --git a/Src/glob.c b/Src/glob.c
index 07dd7c2..01f8a6e 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1061,6 +1061,65 @@ insert_glob_match(LinkList list, LinkNode next, char *data)
     insertlinknode(list, next, data);
 }
 
+/*
+ * Return
+ *   1 if str ends in bare glob qualifiers
+ *   2 if str ends in non-bare glob qualifiers (#q)
+ *   0 otherwise.
+ *
+ * str is the string to check.
+ * sl is it's length (to avoid recalculation).
+ * nobareglob is 1 if bare glob qualifiers are not allowed.
+ * *sp, if sp is not null, wil be a pointer to the start parentheses.
+ */
+
+/**/
+int
+checkglobqual(char *str, int sl, int nobareglob, char **sp)
+{
+    char *s;
+    int paren, ret = 1;
+
+    if (str[sl - 1] != Outpar)
+	return 0;
+
+    /* Check these are really qualifiers, not a set of *
+     * alternatives or exclusions.  We can be more     *
+     * lenient with an explicit (#q) than with a bare  *
+     * set of qualifiers.                              */
+    paren = 0;
+    for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
+	switch (*s) {
+	case Outpar:
+	    paren++; /*FALLTHROUGH*/
+	case Bar:
+	    if (!zpc_disables[ZPC_BAR])
+		nobareglob = 1;
+	    break;
+	case Tilde:
+	    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
+		nobareglob = 1;
+	    break;
+	case Inpar:
+	    paren--;
+	    break;
+	}
+    }
+    if (*s != Inpar)
+	return 0;
+    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
+	if (s[2] != 'q')
+	    return 0;
+	ret = 2;
+    } else if (nobareglob)
+	return 0;
+
+    if (sp)
+	*sp = s;
+
+    return ret;
+}
+
 /* Main entry point to the globbing code for filename globbing. *
  * np points to a node in the list list which will be expanded  *
  * into a series of nodes.                                      */
@@ -1118,7 +1177,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	   (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) {
 	struct qual *newquals;
 	char *s;
-	int sense, paren;
+	int sense, qualsfound;
 	off_t data;
 	char *sdata, *newcolonmod;
 	int (*func) _((char *, Statptr, off_t, char *));
@@ -1148,40 +1207,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	newquals = qo = qn = ql = NULL;
 
 	sl = strlen(str);
-	if (str[sl - 1] != Outpar)
-	    break;
-
-	/* Check these are really qualifiers, not a set of *
-	 * alternatives or exclusions.  We can be more     *
-	 * lenient with an explicit (#q) than with a bare  *
-	 * set of qualifiers.                              */
-	paren = 0;
-	for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
-	    switch (*s) {
-	    case Outpar:
-		paren++; /*FALLTHROUGH*/
-	    case Bar:
-		if (!zpc_disables[ZPC_BAR])
-		    nobareglob = 1;
-		break;
-	    case Tilde:
-		if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
-		    nobareglob = 1;
-		break;
-	    case Inpar:
-		paren--;
-		break;
-	    }
-	}
-	if (*s != Inpar)
-	    break;
-	if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
-	    if (s[2] == 'q') {
-		*s = 0;
-		s += 2;
-	    } else
-		break;
-	} else if (nobareglob)
+	if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s)))
 	    break;
 
 	/* Real qualifiers found. */
@@ -1194,6 +1220,8 @@ zglob(LinkList list, LinkNode np, int nountok)
 
 	str[sl-1] = 0;
 	*s++ = 0;
+	if (qualsfound == 2)
+	    s += 2;
 	while (*s && !newcolonmod) {
 	    func = (int (*) _((char *, Statptr, off_t, char *)))0;
 	    if (idigit(*s)) {


-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: globbing in conditional expressions
  2014-05-30 19:19                             ` Peter Stephenson
@ 2014-05-31 19:38                               ` Peter Stephenson
  2014-06-01 18:20                                 ` Bart Schaefer
  2014-08-04 17:16                                 ` Mikael Magnusson
  0 siblings, 2 replies; 6+ messages in thread
From: Peter Stephenson @ 2014-05-31 19:38 UTC (permalink / raw)
  To: Zsh Hackers' List

On Fri, 30 May 2014 20:19:42 +0100
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> On Fri, 30 May 2014 19:57:34 +0100
> Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> > One thing we could do is make [[ ... = ... ]] expand its right hand
> > argument the same way as normal command arguments when told to do so, so
> > there's no retokenization.
> 
> No reason not to do this for all string arguments, I suppose --- there's
> nothing here that's specific to pattern matching.

Here's a finalish patch with test and documentation, if anyone wants to
comment.  Haven't thought of a good reason not to do this, myself.

diff --git a/Doc/Zsh/cond.yo b/Doc/Zsh/cond.yo
index 26c0eaa..d04ceb2 100644
--- a/Doc/Zsh/cond.yo
+++ b/Doc/Zsh/cond.yo
@@ -196,8 +196,34 @@ where possible.
 Normal shell expansion is performed on the var(file), var(string) and
 var(pattern) arguments, but the result of each expansion is constrained to
 be a single word, similar to the effect of double quotes.
-Filename generation is not performed on any form of argument to conditions.
-However, pattern metacharacters are active for the var(pattern) arguments;
+
+Filename generation is not performed on any form of argument to
+conditions.  However, it can be forced in any case where normal shell
+expansion is valid and when the option tt(EXTENDED_GLOB) is in effect by
+using an explicit glob qualifier of the form tt(LPAR()#q+RPAR()) at the
+end of the string.  A normal glob qualifier expression may appear
+between the `tt(q)' and the closing parenthesis; if none appears the
+expression has no effect beyond causing filename generation.  The
+results of filename generation are joined together to form a single
+word, as with the results of other forms of expansion.
+
+This special use of filename generation is only available with the
+tt([[) syntax.  If the condition occurs within the tt([) or tt(test)
+builtin commands then globbing occurs instead as part of normal command
+line expansion before the condition is evaluated.  In this case it may
+generate multiple words which are likely to confuse the syntax of the
+test command.
+
+For example,
+
+tt([[ -n file*(#qN) ]])
+
+produces status zero if and only if there is at least one file in the
+current directory beginning with the string `tt(file)'.  The globbing
+qualifier tt(N) ensures that the expression is empty if there is
+no matching file.
+
+Pattern metacharacters are active for the var(pattern) arguments;
 the patterns are the same as those used for filename generation, see
 ifzman(\
 zmanref(zshexpn)\
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index de0f454..6de73ea 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2305,7 +2305,11 @@ contained within it are balanced; appearance of `tt(|)', `tt(LPAR())' or
 recognised in this form even if a bare glob qualifier exists at the end of
 the pattern, for example `tt(*(#q*)(.))' will recognise executable regular
 files if both options are set; however, mixed syntax should probably be
-avoided for the sake of clarity.
+avoided for the sake of clarity.  Note that within conditions using the
+`tt([[)' form the presence of a parenthesised expression
+tt(LPAR()#q...+RPAR()) at the end of a string indicates that globbing
+should be performed; the expression may include glob qualifiers, but
+it is also valid if it is simply tt(LPAR()#q+RPAR()).
 
 A qualifier may be any one of the following:
 
diff --git a/NEWS b/NEWS
index e4d747e..87e67fd 100644
--- a/NEWS
+++ b/NEWS
@@ -58,6 +58,13 @@ between the right hand side of the screen (this causes problems with
 some terminals).  It is not special and is not set by default; the
 effect in that case is as if it was 1, as in previous versions.
 
+If the option EXTENDED_GLOB is in effect, it is possible to force
+globbing within conditional code using the [[ ... ]] syntax by flagging
+that a certain string is a glob using the (#q) glob qualifier syntax.
+The resulting glob is treated as a single argument.  For example,
+[[ -n *.c(#qN) ]] tests whether there are any .c files in the current
+directory.
+
 
 Changes between 4.2 and 5.0.0
 -----------------------------
diff --git a/Src/cond.c b/Src/cond.c
index c673542..6e9b558 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -37,6 +37,21 @@ static char *condstr[COND_MOD] = {
     "-ne", "-lt", "-gt", "-le", "-ge", "=~"
 };
 
+static void cond_subst(char **strp, int glob_ok)
+{
+    if (glob_ok &&
+	checkglobqual(*strp, strlen(*strp), 1, NULL)) {
+	LinkList args = newlinklist();
+	addlinknode(args, *strp);
+	prefork(args, 0);
+	while (!errflag && args && nonempty(args) &&
+	       has_token((char *)peekfirst(args)))
+	    zglob(args, firstnode(args), 0);
+	*strp = sepjoin(hlinklist2array(args, 0), NULL, 1);
+    } else
+	singsub(strp);
+}
+
 /*
  * Evaluate a conditional expression given the arguments.
  * If fromtest is set, the caller is the test or [ builtin;
@@ -177,13 +192,13 @@ evalcond(Estate state, char *fromtest)
     }
     left = ecgetstr(state, EC_DUPTOK, &htok);
     if (htok) {
-	singsub(&left);
+	cond_subst(&left, !fromtest);
 	untokenize(left);
     }
     if (ctype <= COND_GE && ctype != COND_STREQ && ctype != COND_STRNEQ) {
 	right = ecgetstr(state, EC_DUPTOK, &htok);
 	if (htok) {
-	    singsub(&right);
+	    cond_subst(&right, !fromtest);
 	    untokenize(right);
 	}
     }
@@ -194,7 +209,7 @@ evalcond(Estate state, char *fromtest)
 	    fprintf(xtrerr, " %s ", condstr[ctype]);
 	    if (ctype == COND_STREQ || ctype == COND_STRNEQ) {
 		char *rt = dupstring(ecrawstr(state->prog, state->pc, NULL));
-		singsub(&rt);
+		cond_subst(&rt, !fromtest);
 		quote_tokenized_output(rt, xtrerr);
 	    }
 	    else
@@ -283,7 +298,7 @@ evalcond(Estate state, char *fromtest)
 		right = dupstring(opat = ecrawstr(state->prog, state->pc,
 						  &htok));
 		if (htok)
-		    singsub(&right);
+		    cond_subst(&right, !fromtest);
 		save = (!(state->prog->flags & EF_HEAP) &&
 			!strcmp(opat, right) && pprog != dummy_patprog2);
 
@@ -517,17 +532,6 @@ cond_val(char **args, int num)
 }
 
 /**/
-mod_export int
-cond_match(char **args, int num, char *str)
-{
-    char *s = args[num];
-
-    singsub(&s);
-
-    return matchpat(str, s);
-}
-
-/**/
 static void
 tracemodcond(char *name, char **args, int inf)
 {
diff --git a/Src/glob.c b/Src/glob.c
index 07dd7c2..57d7f99 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1061,6 +1061,65 @@ insert_glob_match(LinkList list, LinkNode next, char *data)
     insertlinknode(list, next, data);
 }
 
+/*
+ * Return
+ *   1 if str ends in bare glob qualifiers
+ *   2 if str ends in non-bare glob qualifiers (#q)
+ *   0 otherwise.
+ *
+ * str is the string to check.
+ * sl is it's length (to avoid recalculation).
+ * nobareglob is 1 if bare glob qualifiers are not allowed.
+ * *sp, if sp is not null, will be a pointer to the opening parenthesis.
+ */
+
+/**/
+int
+checkglobqual(char *str, int sl, int nobareglob, char **sp)
+{
+    char *s;
+    int paren, ret = 1;
+
+    if (str[sl - 1] != Outpar)
+	return 0;
+
+    /* Check these are really qualifiers, not a set of *
+     * alternatives or exclusions.  We can be more     *
+     * lenient with an explicit (#q) than with a bare  *
+     * set of qualifiers.                              */
+    paren = 0;
+    for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
+	switch (*s) {
+	case Outpar:
+	    paren++; /*FALLTHROUGH*/
+	case Bar:
+	    if (!zpc_disables[ZPC_BAR])
+		nobareglob = 1;
+	    break;
+	case Tilde:
+	    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
+		nobareglob = 1;
+	    break;
+	case Inpar:
+	    paren--;
+	    break;
+	}
+    }
+    if (*s != Inpar)
+	return 0;
+    if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
+	if (s[2] != 'q')
+	    return 0;
+	ret = 2;
+    } else if (nobareglob)
+	return 0;
+
+    if (sp)
+	*sp = s;
+
+    return ret;
+}
+
 /* Main entry point to the globbing code for filename globbing. *
  * np points to a node in the list list which will be expanded  *
  * into a series of nodes.                                      */
@@ -1118,7 +1177,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	   (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH])) {
 	struct qual *newquals;
 	char *s;
-	int sense, paren;
+	int sense, qualsfound;
 	off_t data;
 	char *sdata, *newcolonmod;
 	int (*func) _((char *, Statptr, off_t, char *));
@@ -1148,40 +1207,7 @@ zglob(LinkList list, LinkNode np, int nountok)
 	newquals = qo = qn = ql = NULL;
 
 	sl = strlen(str);
-	if (str[sl - 1] != Outpar)
-	    break;
-
-	/* Check these are really qualifiers, not a set of *
-	 * alternatives or exclusions.  We can be more     *
-	 * lenient with an explicit (#q) than with a bare  *
-	 * set of qualifiers.                              */
-	paren = 0;
-	for (s = str + sl - 2; *s && (*s != Inpar || paren); s--) {
-	    switch (*s) {
-	    case Outpar:
-		paren++; /*FALLTHROUGH*/
-	    case Bar:
-		if (!zpc_disables[ZPC_BAR])
-		    nobareglob = 1;
-		break;
-	    case Tilde:
-		if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_TILDE])
-		    nobareglob = 1;
-		break;
-	    case Inpar:
-		paren--;
-		break;
-	    }
-	}
-	if (*s != Inpar)
-	    break;
-	if (isset(EXTENDEDGLOB) && !zpc_disables[ZPC_HASH] && s[1] == Pound) {
-	    if (s[2] == 'q') {
-		*s = 0;
-		s += 2;
-	    } else
-		break;
-	} else if (nobareglob)
+	if (!(qualsfound = checkglobqual(str, sl, nobareglob, &s)))
 	    break;
 
 	/* Real qualifiers found. */
@@ -1194,6 +1220,8 @@ zglob(LinkList list, LinkNode np, int nountok)
 
 	str[sl-1] = 0;
 	*s++ = 0;
+	if (qualsfound == 2)
+	    s += 2;
 	while (*s && !newcolonmod) {
 	    func = (int (*) _((char *, Statptr, off_t, char *)))0;
 	    if (idigit(*s)) {
diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst
index 1f8f652..3e1ea82 100644
--- a/Test/D02glob.ztst
+++ b/Test/D02glob.ztst
@@ -526,3 +526,18 @@
 >+bus+bus matches +(+bus|-car)
 >@sinhats matches @(@sinhats|wrensinfens)
 >!kerror matches !(!somethingelse)
+
+  (
+    setopt extendedglob
+    cd glob.tmp
+    [[ -n a*(#qN) ]] && print File beginning with a
+    [[ -z z*(#qN) ]] && print No file beginning with z
+    [[ "a b c" = ?(#q) ]] && print Multiple files matched
+    setopt nonomatch
+    [[ -n z*(#q) ]] && print Normal string if nullglob not set
+  )
+0:Force glob expansion in conditions using (#q)
+>File beginning with a
+>No file beginning with z
+>Multiple files matched
+>Normal string if nullglob not set


-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: globbing in conditional expressions
  2014-05-31 19:38                               ` Peter Stephenson
@ 2014-06-01 18:20                                 ` Bart Schaefer
  2014-08-04 17:16                                 ` Mikael Magnusson
  1 sibling, 0 replies; 6+ messages in thread
From: Bart Schaefer @ 2014-06-01 18:20 UTC (permalink / raw)
  To: Zsh Hackers' List

On May 31,  8:38pm, Peter Stephenson wrote:
}
} Here's a finalish patch with test and documentation, if anyone wants to
} comment.  Haven't thought of a good reason not to do this, myself.

I tried a few oddball things I thought might go wrong and none of them
did.  I'm pleased that this is independent of users/18857, too.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: globbing in conditional expressions
  2014-05-31 19:38                               ` Peter Stephenson
  2014-06-01 18:20                                 ` Bart Schaefer
@ 2014-08-04 17:16                                 ` Mikael Magnusson
  2014-08-04 18:35                                   ` Peter Stephenson
  1 sibling, 1 reply; 6+ messages in thread
From: Mikael Magnusson @ 2014-08-04 17:16 UTC (permalink / raw)
  To: Zsh Hackers' List

On 31 May 2014 21:38, Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> On Fri, 30 May 2014 20:19:42 +0100
> Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
>> On Fri, 30 May 2014 19:57:34 +0100
>> Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
>> > One thing we could do is make [[ ... = ... ]] expand its right hand
>> > argument the same way as normal command arguments when told to do so, so
>> > there's no retokenization.
>>
>> No reason not to do this for all string arguments, I suppose --- there's
>> nothing here that's specific to pattern matching.
>
> Here's a finalish patch with test and documentation, if anyone wants to
> comment.  Haven't thought of a good reason not to do this, myself.
>
[...]
> diff --git a/NEWS b/NEWS
> index e4d747e..87e67fd 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -58,6 +58,13 @@ between the right hand side of the screen (this causes problems with
>  some terminals).  It is not special and is not set by default; the
>  effect in that case is as if it was 1, as in previous versions.
>
> +If the option EXTENDED_GLOB is in effect, it is possible to force
> +globbing within conditional code using the [[ ... ]] syntax by flagging
> +that a certain string is a glob using the (#q) glob qualifier syntax.
> +The resulting glob is treated as a single argument.  For example,
> +[[ -n *.c(#qN) ]] tests whether there are any .c files in the current
> +directory.

I was looking at the NEWS file since I figured I should maybe add :^
there, when I noticed this section added in this patch. I suddenly
remembered this example in the manpage which for some reason did not
come to mind while this patch was being discussed;

       q      A `q' and everything up to the closing parenthesis of the globbing
              flags are ignored by the pattern matching code.  This is
intended to
              support the use of glob qualifiers, see below.  The
result is that the
              pattern `(#b)(*).c(#q.)' can be used both for globbing
and for match‐
              ing against a string.  In the former case, the `(#q.)'
will be treated
              as a glob qualifier and the `(#b)' will not be useful,
while in the
              latter case the `(#b)' is useful for backreferences and
the `(#q.)'
              will be ignored.  Note that colon modifiers in the glob
qualifiers are
              also not applied in ordinary pattern matching.

However,
% [[ alpha.c = (#b)(*).c(#q.) ]]
zsh: no matches found: (#b)(*).c(#q.)

This example no longer works.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: globbing in conditional expressions
  2014-08-04 17:16                                 ` Mikael Magnusson
@ 2014-08-04 18:35                                   ` Peter Stephenson
  0 siblings, 0 replies; 6+ messages in thread
From: Peter Stephenson @ 2014-08-04 18:35 UTC (permalink / raw)
  To: Zsh Hackers' List

On Mon, 4 Aug 2014 19:16:01 +0200
Mikael Magnusson <mikachu@gmail.com> wrote:
> However,
> % [[ alpha.c = (#b)(*).c(#q.) ]]
> zsh: no matches found: (#b)(*).c(#q.)
> 
> This example no longer works.

Oh, you mean if alpha.c doesn't exist, since it's doing now globbing
rather than pattern matching.  Probably we oughtn't to be doing the
special glob thing if the argument is already being treated as a
pattern.  This could probably do with a test.

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 8394ffc..9bff013 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2338,7 +2338,9 @@ avoided for the sake of clarity.  Note that within conditions using the
 `tt([[)' form the presence of a parenthesised expression
 tt(LPAR()#q...+RPAR()) at the end of a string indicates that globbing
 should be performed; the expression may include glob qualifiers, but
-it is also valid if it is simply tt(LPAR()#q+RPAR()).
+it is also valid if it is simply tt(LPAR()#q+RPAR()).  This does
+not apply to the right hand side of pattern match operators as the
+syntax already has special significance.
 
 A qualifier may be any one of the following:
 
diff --git a/Src/cond.c b/Src/cond.c
index 3807409..df90656 100644
--- a/Src/cond.c
+++ b/Src/cond.c
@@ -297,8 +297,7 @@ evalcond(Estate state, char *fromtest)
 
 		right = dupstring(opat = ecrawstr(state->prog, state->pc,
 						  &htok));
-		if (htok)
-		    cond_subst(&right, !fromtest);
+		singsub(&right);
 		save = (!(state->prog->flags & EF_HEAP) &&
 			!strcmp(opat, right) && pprog != dummy_patprog2);
 
diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst
index 358c934..d7a0fae 100644
--- a/Test/D02glob.ztst
+++ b/Test/D02glob.ztst
@@ -533,14 +533,12 @@
     cd glob.tmp
     [[ -n a*(#qN) ]] && print File beginning with a
     [[ -z z*(#qN) ]] && print No file beginning with z
-    [[ "a b c" = ?(#q) ]] && print Multiple files matched
     setopt nonomatch
     [[ -n z*(#q) ]] && print Normal string if nullglob not set
   )
 0:Force glob expansion in conditions using (#q)
 >File beginning with a
 >No file beginning with z
->Multiple files matched
 >Normal string if nullglob not set
 
  (){ print $#@ } glob.tmp/dir*(Y1)

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2014-08-04 18:42 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20140507154407.660eb500@pwslap01u.europe.root.pri>
     [not found] ` <20140508105522.GE2052@tarsus.local2>
     [not found]   ` <20140508122045.3c68c3fa@pwslap01u.europe.root.pri>
     [not found]     ` <140508083418.ZM14713@torch.brasslantern.com>
     [not found]       ` <20140508201936.GB53652@isis.sigpipe.cz>
     [not found]         ` <140513084117.ZM22925@torch.brasslantern.com>
     [not found]           ` <20140514041908.GF2471@tarsus.local2>
     [not found]             ` <140514001819.ZM23478@torch.brasslantern.com>
     [not found]               ` <20140515092901.GC2174@tarsus.local2>
     [not found]                 ` <140515075003.ZM28035@torch.brasslantern.com>
     [not found]                   ` <20140526235216.GC1920@tarsus.local2>
     [not found]                     ` <140529205956.ZM17410@torch.brasslantern.com>
     [not found]                       ` <20140530094752.4a116629@pwslap01u.europe.root.pri>
     [not found]                         ` <140530085542.ZM18304@torch.brasslantern.com>
2014-05-30 18:57                           ` globbing in conditional expressions Peter Stephenson
2014-05-30 19:19                             ` Peter Stephenson
2014-05-31 19:38                               ` Peter Stephenson
2014-06-01 18:20                                 ` Bart Schaefer
2014-08-04 17:16                                 ` Mikael Magnusson
2014-08-04 18:35                                   ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).