summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <p.w.stephenson@ntlworld.com>2014-06-01 20:55:39 +0100
committerPeter Stephenson <p.w.stephenson@ntlworld.com>2014-06-01 20:55:39 +0100
commit501f2003a89673cebc956ec5aa5f4f401b3a8f5f (patch)
tree22b93c8461fbf6a9de5ff234bfd8f3292a8ab896
parent880020ca2ed8207fe39aa95169a6f9226ff7b8dc (diff)
downloadzsh-501f2003a89673cebc956ec5aa5f4f401b3a8f5f.tar.gz
zsh-501f2003a89673cebc956ec5aa5f4f401b3a8f5f.zip
32640: (#q) in [[ ... ]] forces globbing
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/cond.yo30
-rw-r--r--Doc/Zsh/expn.yo6
-rw-r--r--NEWS7
-rw-r--r--Src/cond.c34
-rw-r--r--Src/glob.c100
-rw-r--r--Test/D02glob.ztst15
7 files changed, 145 insertions, 53 deletions
diff --git a/ChangeLog b/ChangeLog
index 5a752b17f..6e2fa78fe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2014-06-01 Peter Stephenson <p.w.stephenson@ntlworld.com>
+
+ * 32640: Doc/Zsh/cond.yo, Doc/Zsh/expn.yo, NEWS, Src/cond.c,
+ Src/glob.c, Test/D02glob.ztst: (#q) with EXTENDED_GLOB forces
+ globbing in [[ ... ]].
+
2014-05-29 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 32624: Src/builtin.c, Src/jobs.c: use correct scaling factor
diff --git a/Doc/Zsh/cond.yo b/Doc/Zsh/cond.yo
index 26c0eaa58..d04ceb258 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 de0f454c4..6de73ea93 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 e4d747e01..87e67fd27 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 c67354297..6e9b55806 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 07dd7c2d4..278c147f9 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -1061,6 +1061,67 @@ 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 its 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 == str)
+ 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 +1179,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 +1209,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 +1222,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 1f8f65286..3e1ea8210 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