summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/expn.yo33
-rw-r--r--Src/glob.c974
-rw-r--r--Src/pattern.c179
4 files changed, 699 insertions, 493 deletions
diff --git a/ChangeLog b/ChangeLog
index c635f4c22..45ef93034 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2002-01-22 Peter Stephenson <pws@csr.com>
+
+ * 16486: Doc/Zsh/expn.yo, Src/glob.c, Src/pattern.c: support
+ (#q...) EXTENDED_GLOB syntax for qualifiers. May be chained,
+ ignored by pattern matching code.
+
2002-01-22 Sven Wischnowsky <wischnow@zsh.org>
* 16483: Completion/Base/Completer/_complete,
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index f58c61145..3d40da10b 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1442,6 +1442,17 @@ anywhere except at the start of the string, although this actually means
need to use `tt((""~(#s)))' to match a zero-length portion of the string
not at the start.
)
+item(tt(q))(
+A `tt(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 `tt((#b)(*).c(#q.))' can be used both for globbing and for
+matching against a string. In the former case, the `tt((#q.))' will be
+treated as a glob qualifier and the `tt((#b))' will not be useful, while in
+the latter case the `tt((#b))' is useful for backreferences and the
+`tt((#q.))' will be ignored. Note that colon modifiers in the glob
+qualifiers are also not applied in ordinary pattern matching.
+)
enditem()
For example, the test string tt(fooxx) can be matched by the pattern
@@ -1564,6 +1575,20 @@ qualifiers, for example `tt((^x))', can be forced to be treated as part of
the glob pattern by doubling the parentheses, in this case producing
`tt(((^x)))'.
+If the option tt(EXTENDED_GLOB) is set, a different syntax for glob
+qualifiers is available, namely `tt((#qx))' where tt(x) is any of the same
+glob qualifiers used in the other format. The qualifiers must still appear
+at the end of the pattern. However, with this syntax multiple glob
+qualifiers may be chained together. They are treated as a logical AND of
+the individual sets of flags. Also, as the syntax is unambiguous, the
+expression will be treated as glob qualifiers just as long any parentheses
+contained within it are balanced; appearance of `tt(|)', `tt(LPAR())' or
+`tt(~)' does not negate the effect. Note that qualifiers will be
+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.
+
A qualifier may be any one of the following:
startitem()
@@ -1847,3 +1872,11 @@ example(ls *.*~(lex|parse).[ch](^D^l1))
lists all files having a link count of one whose names contain a dot
(but not those starting with a dot, since tt(GLOB_DOTS) is explicitly
switched off) except for tt(lex.c), tt(lex.h), tt(parse.c) and tt(parse.h).
+
+example(print b*.pro(#q:s/pro/shmo/)(#q.:s/builtin/shmiltin/))
+
+demonstrates how colon modifiers and other qualifiers may be chained
+together. The ordinary qualifier `tt(.)' is applied first, then the colon
+modifiers in order from left to right. So if tt(EXTENDED_GLOB) is set and
+the base battern matches the regular file tt(builtin.pro), the shell will
+print `tt(shmiltin.shmo)'.
diff --git a/Src/glob.c b/Src/glob.c
index 91b71dc77..76f23cdb6 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -38,7 +38,7 @@
/* flag for CSHNULLGLOB */
-typedef struct gmatch *Gmatch;
+typedef struct gmatch *Gmatch;
struct gmatch {
char *name;
@@ -76,7 +76,7 @@ struct gmatch {
/**/
int badcshglob;
-
+
/**/
int pathpos; /* position in pathbuf (needed by pattern code) */
@@ -707,6 +707,7 @@ static Complist
parsepat(char *str)
{
long assert;
+ int ignore;
patcompstart();
/*
@@ -717,7 +718,7 @@ parsepat(char *str)
(isset(KSHGLOB) && *str == '@' && str[1] == Inpar &&
str[2] == Pound)) {
str += (*str == Inpar) ? 2 : 3;
- if (!patgetglobflags(&str, &assert))
+ if (!patgetglobflags(&str, &assert, &ignore))
return NULL;
}
@@ -914,6 +915,35 @@ gmatchcmp(Gmatch a, Gmatch b)
return 0;
}
+/*
+ * Duplicate a list of qualifiers using the `next' linkage (not the
+ * `or' linkage). Return the head element and set *last (if last non-NULL)
+ * to point to the last element of the new list. All allocation is on the
+ * heap (or off the heap?)
+ */
+static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp)
+{
+ struct qual *qfirst = NULL, *qlast = NULL;
+
+ while (orig) {
+ struct qual *qnew = (struct qual *)zhalloc(sizeof(struct qual));
+ *qnew = *orig;
+ qnew->next = qnew->or = NULL;
+
+ if (!qfirst)
+ qfirst = qnew;
+ if (qlast)
+ qlast->next = qnew;
+ qlast = qnew;
+
+ orig = orig->next;
+ }
+
+ if (lastp)
+ *lastp = qlast;
+ return qfirst;
+}
+
/* 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. */
@@ -932,6 +962,7 @@ zglob(LinkList list, LinkNode np, int nountok)
int first = 0, end = -1; /* index of first match to return */
/* and index+1 of the last match */
struct globdata saved; /* saved glob state */
+ int nobareglob = !isset(BAREGLOBQUAL);
if (unset(GLOBOPT) || !haswilds(ostr)) {
if (!nountok)
@@ -941,13 +972,22 @@ zglob(LinkList list, LinkNode np, int nountok)
save_globstate(saved);
str = dupstring(ostr);
- sl = strlen(str);
uremnode(list, np);
- /* Initialise state variables for current file pattern */
- qo = qn = quals = ql = NULL;
+ /* quals will hold the complete list of qualifiers (file static). */
+ quals = NULL;
+ /*
+ * qualct and qualorct indicate we have qualifiers in the last
+ * alternative, or a set of alternatives, respectively. They
+ * are not necessarily an accurate count, however.
+ */
qualct = qualorct = 0;
+ /*
+ * colonmod is a concatenated list of all colon modifiers found in
+ * all sets of qualifiers.
+ */
colonmod = NULL;
+ /* The gf_* flags are qualifiers which are applied globally. */
gf_nullglob = isset(NULLGLOB);
gf_markdirs = isset(MARKDIRS);
gf_listtypes = gf_follow = 0;
@@ -956,433 +996,549 @@ zglob(LinkList list, LinkNode np, int nountok)
gf_sorts = gf_nsorts = 0;
/* Check for qualifiers */
- if (isset(BAREGLOBQUAL) && str[sl - 1] == Outpar) {
+ while (!nobareglob || isset(EXTENDEDGLOB)) {
+ struct qual *newquals;
char *s;
+ int sense, paren;
+ off_t data;
+ char *sdata, *newcolonmod;
+ int (*func) _((char *, Statptr, off_t, char *));
+
+ /*
+ * Initialise state variables for current file pattern.
+ * newquals is the root for the linked list of all qualifiers.
+ * qo is the root of the current list of alternatives.
+ * ql is the end of the current alternative where the `next' will go.
+ * qn is the current qualifier node to be added.
+ *
+ * Here is an attempt at a diagram. An `or' is added horizontally
+ * to the top line, a `next' at the bottom of the right hand line.
+ * `qn' is usually NULL unless a new `or' has just been added.
+ *
+ * quals -> x -> x -> qo
+ * | | |
+ * x x x
+ * | |
+ * x ql
+ *
+ * In fact, after each loop the complete set is in the file static
+ * `quals'. Then, if we have a second set of qualifiers, we merge
+ * the lists together. This is only tricky if one or both have an
+ * `or' in them; then we need to distribute over all alternatives.
+ */
+ 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 */
- for (s = str + sl - 2; *s != Inpar; s--)
- if (*s == Bar || *s == Outpar ||
- (isset(EXTENDEDGLOB) && *s == Tilde))
+ * 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:
+ nobareglob = 1;
+ break;
+ case Tilde:
+ if (isset(EXTENDEDGLOB))
+ nobareglob = 1;
break;
- if (*s == Inpar && (!isset(EXTENDEDGLOB) || s[1] != Pound)) {
- /* Real qualifiers found. */
- int sense = 0; /* bit 0 for match (0)/don't match (1) */
- /* bit 1 for follow links (2), don't (0) */
- off_t data = 0; /* Any numerical argument required */
- char *sdata = NULL; /* Any list argument required */
- int (*func) _((char *, Statptr, off_t, char *));
-
- str[sl-1] = 0;
- *s++ = 0;
- while (*s && !colonmod) {
- func = (int (*) _((char *, Statptr, off_t, char *)))0;
- if (idigit(*s)) {
- /* Store numeric argument for qualifier */
+ case Inpar:
+ paren--;
+ break;
+ }
+ }
+ if (*s != Inpar)
+ break;
+ if (isset(EXTENDEDGLOB) && s[1] == Pound) {
+ if (s[2] == 'q') {
+ *s = 0;
+ s += 2;
+ } else
+ break;
+ } else if (nobareglob)
+ break;
+
+ /* Real qualifiers found. */
+ nobareglob = 1;
+ sense = 0; /* bit 0 for match (0)/don't match (1) */
+ /* bit 1 for follow links (2), don't (0) */
+ data = 0; /* Any numerical argument required */
+ sdata = NULL; /* Any list argument required */
+ newcolonmod = NULL; /* Contains trailing colon modifiers */
+
+ str[sl-1] = 0;
+ *s++ = 0;
+ while (*s && !newcolonmod) {
+ func = (int (*) _((char *, Statptr, off_t, char *)))0;
+ if (idigit(*s)) {
+ /* Store numeric argument for qualifier */
+ func = qualflags;
+ data = 0;
+ sdata = NULL;
+ while (idigit(*s))
+ data = data * 010 + (*s++ - '0');
+ } else if (*s == ',') {
+ /* A comma separates alternative sets of qualifiers */
+ s++;
+ sense = 0;
+ if (qualct) {
+ qn = (struct qual *)hcalloc(sizeof *qn);
+ qo->or = qn;
+ qo = qn;
+ qualorct++;
+ qualct = 0;
+ ql = NULL;
+ }
+ } else {
+ switch (*s++) {
+ case ':':
+ /* Remaining arguments are history-type *
+ * colon substitutions, handled separately. */
+ newcolonmod = s - 1;
+ untokenize(newcolonmod);
+ if (colonmod) {
+ /* remember we're searching backwards */
+ colonmod = dyncat(newcolonmod, colonmod);
+ } else
+ colonmod = newcolonmod;
+ break;
+ case Hat:
+ case '^':
+ /* Toggle sense: go from positive to *
+ * negative match and vice versa. */
+ sense ^= 1;
+ break;
+ case '-':
+ /* Toggle matching of symbolic links */
+ sense ^= 2;
+ break;
+ case '@':
+ /* Match symbolic links */
+ func = qualislnk;
+ break;
+ case Equals:
+ case '=':
+ /* Match sockets */
+ func = qualissock;
+ break;
+ case 'p':
+ /* Match named pipes */
+ func = qualisfifo;
+ break;
+ case '/':
+ /* Match directories */
+ func = qualisdir;
+ break;
+ case '.':
+ /* Match regular files */
+ func = qualisreg;
+ break;
+ case '%':
+ /* Match special files: block, *
+ * character or any device */
+ if (*s == 'b')
+ s++, func = qualisblk;
+ else if (*s == 'c')
+ s++, func = qualischr;
+ else
+ func = qualisdev;
+ break;
+ case Star:
+ /* Match executable plain files */
+ func = qualiscom;
+ break;
+ case 'R':
+ /* Match world-readable files */
func = qualflags;
- data = 0;
- sdata = NULL;
- while (idigit(*s))
- data = data * 010 + (*s++ - '0');
- } else if (*s == ',') {
- /* A comma separates alternative sets of qualifiers */
- s++;
- sense = 0;
- if (qualct) {
- qn = (struct qual *)hcalloc(sizeof *qn);
- qo->or = qn;
- qo = qn;
- qualorct++;
- qualct = 0;
- ql = NULL;
- }
- } else
- switch (*s++) {
- case ':':
- /* Remaining arguments are history-type *
- * colon substitutions, handled separately. */
- colonmod = s - 1;
- untokenize(colonmod);
- break;
- case Hat:
- case '^':
- /* Toggle sense: go from positive to *
- * negative match and vice versa. */
- sense ^= 1;
- break;
- case '-':
- /* Toggle matching of symbolic links */
- sense ^= 2;
- break;
- case '@':
- /* Match symbolic links */
- func = qualislnk;
- break;
- case Equals:
- case '=':
- /* Match sockets */
- func = qualissock;
- break;
- case 'p':
- /* Match named pipes */
- func = qualisfifo;
- break;
- case '/':
- /* Match directories */
- func = qualisdir;
- break;
- case '.':
- /* Match regular files */
- func = qualisreg;
- break;
- case '%':
- /* Match special files: block, *
- * character or any device */
- if (*s == 'b')
- s++, func = qualisblk;
- else if (*s == 'c')
- s++, func = qualischr;
- else
- func = qualisdev;
- break;
- case Star:
- /* Match executable plain files */
- func = qualiscom;
- break;
- case 'R':
- /* Match world-readable files */
- func = qualflags;
- data = 0004;
- break;
- case 'W':
- /* Match world-writeable files */
- func = qualflags;
- data = 0002;
- break;
- case 'X':
- /* Match world-executable files */
- func = qualflags;
- data = 0001;
- break;
- case 'A':
- func = qualflags;
- data = 0040;
- break;
- case 'I':
- func = qualflags;
- data = 0020;
- break;
- case 'E':
- func = qualflags;
- data = 0010;
- break;
- case 'r':
- /* Match files readable by current process */
- func = qualflags;
- data = 0400;
- break;
- case 'w':
- /* Match files writeable by current process */
- func = qualflags;
- data = 0200;
- break;
- case 'x':
- /* Match files executable by current process */
- func = qualflags;
- data = 0100;
- break;
- case 's':
- /* Match setuid files */
- func = qualflags;
- data = 04000;
- break;
- case 'S':
- /* Match setgid files */
- func = qualflags;
- data = 02000;
- break;
- case 't':
- func = qualflags;
- data = 01000;
- break;
- case 'd':
- /* Match device files by device number *
- * (as given by stat's st_dev element). */
- func = qualdev;
+ data = 0004;
+ break;
+ case 'W':
+ /* Match world-writeable files */
+ func = qualflags;
+ data = 0002;
+ break;
+ case 'X':
+ /* Match world-executable files */
+ func = qualflags;
+ data = 0001;
+ break;
+ case 'A':
+ func = qualflags;
+ data = 0040;
+ break;
+ case 'I':
+ func = qualflags;
+ data = 0020;
+ break;
+ case 'E':
+ func = qualflags;
+ data = 0010;
+ break;
+ case 'r':
+ /* Match files readable by current process */
+ func = qualflags;
+ data = 0400;
+ break;
+ case 'w':
+ /* Match files writeable by current process */
+ func = qualflags;
+ data = 0200;
+ break;
+ case 'x':
+ /* Match files executable by current process */
+ func = qualflags;
+ data = 0100;
+ break;
+ case 's':
+ /* Match setuid files */
+ func = qualflags;
+ data = 04000;
+ break;
+ case 'S':
+ /* Match setgid files */
+ func = qualflags;
+ data = 02000;
+ break;
+ case 't':
+ func = qualflags;
+ data = 01000;
+ break;
+ case 'd':
+ /* Match device files by device number *
+ * (as given by stat's st_dev element). */
+ func = qualdev;
+ data = qgetnum(&s);
+ break;
+ case 'l':
+ /* Match files with the given no. of hard links */
+ func = qualnlink;
+ g_amc = -1;
+ goto getrange;
+ case 'U':
+ /* Match files owned by effective user ID */
+ func = qualuid;
+ data = geteuid();
+ break;
+ case 'G':
+ /* Match files owned by effective group ID */
+ func = qualgid;
+ data = getegid();
+ break;
+ case 'u':
+ /* Match files owned by given user id */
+ func = qualuid;
+ /* either the actual uid... */
+ if (idigit(*s))
data = qgetnum(&s);
- break;
- case 'l':
- /* Match files with the given no. of hard links */
- func = qualnlink;
- g_amc = -1;
- goto getrange;
- case 'U':
- /* Match files owned by effective user ID */
- func = qualuid;
- data = geteuid();
- break;
- case 'G':
- /* Match files owned by effective group ID */
- func = qualgid;
- data = getegid();
- break;
- case 'u':
- /* Match files owned by given user id */
- func = qualuid;
- /* either the actual uid... */
- if (idigit(*s))
- data = qgetnum(&s);
- else {
- /* ... or a user name */
- char sav, *tt;
-
- /* Find matching delimiters */
- tt = get_strarg(s);
- if (!*tt) {
- zerr("missing end of name",
- NULL, 0);
- data = 0;
- } else {
+ else {
+ /* ... or a user name */
+ char sav, *tt;
+
+ /* Find matching delimiters */
+ tt = get_strarg(s);
+ if (!*tt) {
+ zerr("missing end of name",
+ NULL, 0);
+ data = 0;
+ } else {
#ifdef HAVE_GETPWNAM
- struct passwd *pw;
- sav = *tt;
- *tt = '\0';
-
- if ((pw = getpwnam(s + 1)))
- data = pw->pw_uid;
- else {
- zerr("unknown user", NULL, 0);
- data = 0;
- }
- *tt = sav;
-#else /* !HAVE_GETPWNAM */
- sav = *tt;
+ struct passwd *pw;
+ sav = *tt;
+ *tt = '\0';
+
+ if ((pw = getpwnam(s + 1)))
+ data = pw->pw_uid;
+ else {
zerr("unknown user", NULL, 0);
data = 0;
-#endif /* !HAVE_GETPWNAM */
- if (sav)
- s = tt + 1;
- else
- s = tt;
}
+ *tt = sav;
+#else /* !HAVE_GETPWNAM */
+ sav = *tt;
+ zerr("unknown user", NULL, 0);
+ data = 0;
+#endif /* !HAVE_GETPWNAM */
+ if (sav)
+ s = tt + 1;
+ else
+ s = tt;
}
- break;
- case 'g':
- /* Given gid or group id... works like `u' */
- func = qualgid;
- /* either the actual gid... */
- if (idigit(*s))
- data = qgetnum(&s);
- else {
- /* ...or a delimited group name. */
- char sav, *tt;
-
- tt = get_strarg(s);
- if (!*tt) {
- zerr("missing end of name",
- NULL, 0);
- data = 0;
- } else {
+ }
+ break;
+ case 'g':
+ /* Given gid or group id... works like `u' */
+ func = qualgid;
+ /* either the actual gid... */
+ if (idigit(*s))
+ data = qgetnum(&s);
+ else {
+ /* ...or a delimited group name. */
+ char sav, *tt;
+
+ tt = get_strarg(s);
+ if (!*tt) {
+ zerr("missing end of name",
+ NULL, 0);
+ data = 0;
+ } else {
#ifdef HAVE_GETGRNAM
- struct group *gr;
- sav = *tt;
- *tt = '\0';
-
- if ((gr = getgrnam(s + 1)))
- data = gr->gr_gid;
- else {
- zerr("unknown group", NULL, 0);
- data = 0;
- }
- *tt = sav;
-#else /* !HAVE_GETGRNAM */
- sav = *tt;
+ struct group *gr;
+ sav = *tt;
+ *tt = '\0';
+
+ if ((gr = getgrnam(s + 1)))
+ data = gr->gr_gid;
+ else {
zerr("unknown group", NULL, 0);
data = 0;
-#endif /* !HAVE_GETGRNAM */
- if (sav)
- s = tt + 1;
- else
- s = tt;
- }
- }
- break;
- case 'f':
- /* Match modes with chmod-spec. */
- func = qualmodeflags;
- data = qgetmodespec(&s);
- break;
- case 'M':
- /* Mark directories with a / */
- if ((gf_markdirs = !(sense & 1)))
- gf_follow = sense & 2;
- break;
- case 'T':
- /* Mark types in a `ls -F' type fashion */
- if ((gf_listtypes = !(sense & 1)))
- gf_follow = sense & 2;
- break;
- case 'N':
- /* Nullglob: remove unmatched patterns. */
- gf_nullglob = !(sense & 1);
- break;
- case 'D':
- /* Glob dots: match leading dots implicitly */
- gf_noglobdots = sense & 1;
- break;
- case 'n':
- /* Numeric glob sort */
- gf_numsort = !(sense & 1);
- break;
- case 'a':
- /* Access time in given range */
- g_amc = 0;
- func = qualtime;
- goto getrange;
- case 'm':
- /* Modification time in given range */
- g_amc = 1;
- func = qualtime;
- goto getrange;
- case 'c':
- /* Inode creation time in given range */
- g_amc = 2;
- func = qualtime;
- goto getrange;
- case 'L':
- /* File size (Length) in given range */
- func = qualsize;
- g_amc = -1;
- /* Get size multiplier */
- g_units = TT_BYTES;
- if (*s == 'p' || *s == 'P')
- g_units = TT_POSIX_BLOCKS, ++s;
- else if (*s == 'k' || *s == 'K')
- g_units = TT_KILOBYTES, ++s;
- else if (*s == 'm' || *s == 'M')
- g_units = TT_MEGABYTES, ++s;
- getrange:
- /* Get time multiplier */
- if (g_amc >= 0) {
- g_units = TT_DAYS;
- if (*s == 'h')
- g_units = TT_HOURS, ++s;
- else if (*s == 'm')
- g_units = TT_MINS, ++s;
- else if (*s == 'w')
- g_units = TT_WEEKS, ++s;
- else if (*s == 'M')
- g_units = TT_MONTHS, ++s;
- else if (*s == 's')
- g_units = TT_SECONDS, ++s;
- }
- /* See if it's greater than, equal to, or less than */
- if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0))
- ++s;
- data = qgetnum(&s);
- break;
-
- case 'o':
- case 'O':
- {
- int t;
-
- switch (*s) {
- case 'n': t = GS_NAME; break;
- case 'L': t = GS_SIZE; break;
- case 'l': t = GS_LINKS; break;
- case 'a': t = GS_ATIME; break;
- case 'm': t = GS_MTIME; break;
- case 'c': t = GS_CTIME; break;
- case 'd': t = GS_DEPTH; break;
- default:
- zerr("unknown sort specifier", NULL, 0);
- restore_globstate(saved);
- return;
- }
- if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
- t <<= GS_SHIFT;
- if (gf_sorts & t) {
- zerr("doubled sort specifier", NULL, 0);
- restore_globstate(saved);
- return;
}
- gf_sorts |= t;
- gf_sortlist[gf_nsorts++] = t |
- (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0);
- s++;
- break;
+ *tt = sav;
+#else /* !HAVE_GETGRNAM */
+ sav = *tt;
+ zerr("unknown group", NULL, 0);
+ data = 0;
+#endif /* !HAVE_GETGRNAM */
+ if (sav)
+ s = tt + 1;
+ else
+ s = tt;
}
- case 'e':
- {
- char sav, *tt = get_strarg(s);
+ }
+ break;
+ case 'f':
+ /* Match modes with chmod-spec. */
+ func = qualmodeflags;
+ data = qgetmodespec(&s);
+ break;
+ case 'M':
+ /* Mark directories with a / */
+ if ((gf_markdirs = !(sense & 1)))
+ gf_follow = sense & 2;
+ break;
+ case 'T':
+ /* Mark types in a `ls -F' type fashion */
+ if ((gf_listtypes = !(sense & 1)))
+ gf_follow = sense & 2;
+ break;
+ case 'N':
+ /* Nullglob: remove unmatched patterns. */
+ gf_nullglob = !(sense & 1);
+ break;
+ case 'D':
+ /* Glob dots: match leading dots implicitly */
+ gf_noglobdots = sense & 1;
+ break;
+ case 'n':
+ /* Numeric glob sort */
+ gf_numsort = !(sense & 1);
+ break;
+ case 'a':
+ /* Access time in given range */
+ g_amc = 0;
+ func = qualtime;
+ goto getrange;
+ case 'm':
+ /* Modification time in given range */
+ g_amc = 1;
+ func = qualtime;
+ goto getrange;
+ case 'c':
+ /* Inode creation time in given range */
+ g_amc = 2;
+ func = qualtime;
+ goto getrange;
+ case 'L':
+ /* File size (Length) in given range */
+ func = qualsize;
+ g_amc = -1;
+ /* Get size multiplier */
+ g_units = TT_BYTES;
+ if (*s == 'p' || *s == 'P')
+ g_units = TT_POSIX_BLOCKS, ++s;
+ else if (*s == 'k' || *s == 'K')
+ g_units = TT_KILOBYTES, ++s;
+ else if (*s == 'm' || *s == 'M')
+ g_units = TT_MEGABYTES, ++s;
+ getrange:
+ /* Get time multiplier */
+ if (g_amc >= 0) {
+ g_units = TT_DAYS;
+ if (*s == 'h')
+ g_units = TT_HOURS, ++s;
+ else if (*s == 'm')
+ g_units = TT_MINS, ++s;
+ else if (*s == 'w')
+ g_units = TT_WEEKS, ++s;
+ else if (*s == 'M')
+ g_units = TT_MONTHS, ++s;
+ else if (*s == 's')
+ g_units = TT_SECONDS, ++s;
+ }
+ /* See if it's greater than, equal to, or less than */
+ if ((g_range = *s == '+' ? 1 : *s == '-' ? -1 : 0))
+ ++s;
+ data = qgetnum(&s);
+ break;
- if (!*tt) {
- zerr("missing end of string", NULL, 0);
- data = 0;
- } else {
- sav = *tt;
- *tt = '\0';
- func = qualsheval;
- sdata = dupstring(s + 1);
- untokenize(sdata);
- *tt = sav;
- if (sav)
- s = tt + 1;
- else
- s = tt;
- }
- break;
- }
- case '[':
- case Inbrack:
- {
- char *os = --s;
- struct value v;
-
- v.isarr = SCANPM_WANTVALS;
- v.pm = NULL;
- v.end = -1;
- v.inv = 0;
- if (getindex(&s, &v, 0) || s == os) {
- zerr("invalid subscript", NULL, 0);
- restore_globstate(saved);
- return;
- }
- first = v.start;
- end = v.end;
- break;
- }
+ case 'o':
+ case 'O':
+ {
+ int t;
+
+ switch (*s) {
+ case 'n': t = GS_NAME; break;
+ case 'L': t = GS_SIZE; break;
+ case 'l': t = GS_LINKS; break;
+ case 'a': t = GS_ATIME; break;
+ case 'm': t = GS_MTIME; break;
+ case 'c': t = GS_CTIME; break;
+ case 'd': t = GS_DEPTH; break;
default:
- zerr("unknown file attribute", NULL, 0);
+ zerr("unknown sort specifier", NULL, 0);
restore_globstate(saved);
return;
}
- if (func) {
- /* Requested test is performed by function func */
- if (!qn)
- qn = (struct qual *)hcalloc(sizeof *qn);
- if (ql)
- ql->next = qn;
- ql = qn;
- if (!quals)
- quals = qo = qn;
- qn->func = func;
- qn->sense = sense;
- qn->data = data;
- qn->sdata = sdata;
- qn->range = g_range;
- qn->units = g_units;
- qn->amc = g_amc;
- qn = NULL;
- qualct++;
+ if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
+ t <<= GS_SHIFT;
+ if (gf_sorts & t) {
+ zerr("doubled sort specifier", NULL, 0);
+ restore_globstate(saved);
+ return;
+ }
+ gf_sorts |= t;
+ gf_sortlist[gf_nsorts++] = t |
+ (((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0);
+ s++;
+ break;
+ }
+ case 'e':
+ {
+ char sav, *tt = get_strarg(s);
+
+ if (!*tt) {
+ zerr("missing end of string", NULL, 0);
+ data = 0;
+ } else {
+ sav = *tt;
+ *tt = '\0';
+ func = qualsheval;
+ sdata = dupstring(s + 1);
+ untokenize(sdata);
+ *tt = sav;
+ if (sav)
+ s = tt + 1;
+ else
+ s = tt;
+ }
+ break;
}
- if (errflag) {
+ case '[':
+ case Inbrack:
+ {
+ char *os = --s;
+ struct value v;
+
+ v.isarr = SCANPM_WANTVALS;
+ v.pm = NULL;
+ v.end = -1;
+ v.inv = 0;
+ if (getindex(&s, &v, 0) || s == os) {
+ zerr("invalid subscript", NULL, 0);
+ restore_globstate(saved);
+ return;
+ }
+ first = v.start;
+ end = v.end;
+ break;
+ }
+ default:
+ zerr("unknown file attribute", NULL, 0);
restore_globstate(saved);
return;
}
}
+ if (func) {
+ /* Requested test is performed by function func */
+ if (!qn)
+ qn = (struct qual *)hcalloc(sizeof *qn);
+ if (ql)
+ ql->next = qn;
+ ql = qn;
+ if (!newquals)
+ newquals = qo = qn;
+ qn->func = func;
+ qn->sense = sense;
+ qn->data = data;
+ qn->sdata = sdata;
+ qn->range = g_range;
+ qn->units = g_units;
+ qn->amc = g_amc;
+
+ qn = NULL;
+ qualct++;
+ }
+ if (errflag) {
+ restore_globstate(saved);
+ return;
+ }
}
+
+ if (quals && newquals) {
+ /* Merge previous group of qualifiers with new set. */
+ if (quals->or || newquals->or) {
+ /* The hard case. */
+ struct qual *qorhead = NULL, *qortail = NULL;
+ /*
+ * Distribute in the most trivial way, by creating
+ * all possible combinations of the two sets and chaining
+ * these into one long set of alternatives given
+ * by qorhead and qortail.
+ */
+ for (qn = newquals; qn; qn = qn->or) {
+ for (qo = quals; qo; qo = qo->or) {
+ struct qual *qfirst, *qlast;
+ int islast = !qn->or && !qo->or;
+ /* Generate first set of qualifiers... */
+ if (islast) {
+ /* Last time round: don't bother copying. */
+ qfirst = qn;
+ for (qlast = qfirst; qlast->next;
+ qlast = qlast->next)
+ ;
+ } else
+ qfirst = dup_qual_list(qn, &qlast);
+ /* ... link into new `or' chain ... */
+ if (!qorhead)
+ qorhead = qfirst;
+ if (qortail)
+ qortail->or = qfirst;
+ qortail = qfirst;
+ /* ... and concatenate second set. */
+ qlast->next = islast ? qo : dup_qual_list(qo, NULL);
+ }
+ }
+ quals = qorhead;
+ } else {
+ /*
+ * Easy: we can just chain the qualifiers together.
+ * This is an optimisation; the code above will work, too.
+ * We retain the original left to right ordering --- remember
+ * we are searching for sets of qualifiers from the right.
+ */
+ qn = newquals;
+ for ( ; newquals->next; newquals = newquals->next)
+ ;
+ newquals->next = quals;
+ quals = qn;
+ }
+ } else
+ quals = newquals;
}
q = parsepat(str);
if (!q || errflag) { /* if parsing failed */
@@ -1638,7 +1794,7 @@ xpandredir(struct redir *fn, LinkList tab)
if (fn->type == REDIR_MERGEIN || fn->type == REDIR_MERGEOUT) {
if (s[0] == '-' && !s[1])
fn->type = REDIR_CLOSE;
- else if (s[0] == 'p' && !s[1])
+ else if (s[0] == 'p' && !s[1])
fn->fd2 = -2;
else {
while (idigit(*s))
@@ -1703,7 +1859,7 @@ xpandbraces(LinkList list, LinkNode *np)
int rstart = zstrtol(str+1,&dots,10), rend = 0, err = 0, rev = 0;
int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2;
int strp = str - str3;
-
+
if (dots == str + 1 || *dots != '.' || dots[1] != '.')
err++;
else {
@@ -1846,7 +2002,7 @@ struct repldata {
};
typedef struct repldata *Repldata;
-/*
+/*
* List of bits of matches to concatenate with replacement string.
* The data is a struct repldata. It is not used in cases like
* ${...//#foo/bar} even though SUB_GLOBAL is set, since the match
diff --git a/Src/pattern.c b/Src/pattern.c
index f75698adc..cf78a1138 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -631,7 +631,7 @@ patcompswitch(int paren, int *flagp)
static long
patcompbranch(int *flagp)
{
- long chain, latest, starter;
+ long chain, latest = 0, starter;
int flags = 0;
*flagp = P_PURESTR;
@@ -647,44 +647,46 @@ patcompbranch(int *flagp)
patparse[2] == Pound))) {
/* Globbing flags. */
char *pp1 = patparse;
- int oldglobflags = patglobflags;
+ int oldglobflags = patglobflags, ignore;
long assert;
patparse += (*patparse == '@') ? 3 : 2;
- if (!patgetglobflags(&patparse, &assert))
+ if (!patgetglobflags(&patparse, &assert, &ignore))
return 0;
- if (assert) {
- /*
- * Start/end assertion looking like flags, but
- * actually handled as a normal node
- */
- latest = patnode(assert);
- flags = 0;
- } else {
- if (pp1 == patstart) {
- /* Right at start of pattern, the simplest case.
- * Put them into the flags and don't emit anything.
- */
- ((Patprog)patout)->globflags = patglobflags;
- continue;
- } else if (!*patparse) {
- /* Right at the end, so just leave the flags for
- * the next Patprog in the chain to pick up.
+ if (!ignore) {
+ if (assert) {
+ /*
+ * Start/end assertion looking like flags, but
+ * actually handled as a normal node
*/
- break;
- }
- /*
- * Otherwise, we have to stick them in as a pattern
- * matching nothing.
- */
- if (oldglobflags != patglobflags) {
- /* Flags changed */
- union upat up;
- latest = patnode(P_GFLAGS);
- up.l = patglobflags;
- patadd((char *)&up, 0, sizeof(union upat), 0);
+ latest = patnode(assert);
+ flags = 0;
} else {
- /* No effect. */
- continue;
+ if (pp1 == patstart) {
+ /* Right at start of pattern, the simplest case.
+ * Put them into the flags and don't emit anything.
+ */
+ ((Patprog)patout)->globflags = patglobflags;
+ continue;
+ } else if (!*patparse) {
+ /* Right at the end, so just leave the flags for
+ * the next Patprog in the chain to pick up.
+ */
+ break;
+ }
+ /*
+ * Otherwise, we have to stick them in as a pattern
+ * matching nothing.
+ */
+ if (oldglobflags != patglobflags) {
+ /* Flags changed */
+ union upat up;
+ latest = patnode(P_GFLAGS);
+ up.l = patglobflags;
+ patadd((char *)&up, 0, sizeof(union upat), 0);
+ } else {
+ /* No effect. */
+ continue;
+ }
}
}
} else if (isset(EXTENDEDGLOB) && *patparse == Hat) {
@@ -720,74 +722,83 @@ patcompbranch(int *flagp)
/**/
int
-patgetglobflags(char **strp, long *assertp)
+patgetglobflags(char **strp, long *assertp, int *ignore)
{
char *nptr, *ptr = *strp;
zlong ret;
*assertp = 0;
+ *ignore = 1;
/* (#X): assumes we are still positioned on the first X */
for (; *ptr && *ptr != Outpar; ptr++) {
- switch (*ptr) {
- case 'a':
- /* Approximate matching, max no. of errors follows */
- ret = zstrtol(++ptr, &nptr, 10);
- /*
- * We can't have more than 254, because we need 255 to
- * mark 254 errors in wbranch and exclude sync strings
- * (hypothetically --- hope no-one tries it).
- */
- if (ret < 0 || ret > 254 || ptr == nptr)
- return 0;
- patglobflags = (patglobflags & ~0xff) | (ret & 0xff);
- ptr = nptr-1;
+ if (*ptr == 'q') {
+ /* Glob qualifiers, ignored in pattern code */
+ while (*ptr && *ptr != Outpar)
+ ptr++;
break;
+ } else {
+ *ignore = 0;
+ switch (*ptr) {
+ case 'a':
+ /* Approximate matching, max no. of errors follows */
+ ret = zstrtol(++ptr, &nptr, 10);
+ /*
+ * We can't have more than 254, because we need 255 to
+ * mark 254 errors in wbranch and exclude sync strings
+ * (hypothetically --- hope no-one tries it).
+ */
+ if (ret < 0 || ret > 254 || ptr == nptr)
+ return 0;
+ patglobflags = (patglobflags & ~0xff) | (ret & 0xff);
+ ptr = nptr-1;
+ break;
- case 'l':
- /* Lowercase in pattern matches lower or upper in target */
- patglobflags = (patglobflags & ~GF_IGNCASE) | GF_LCMATCHUC;
- break;
+ case 'l':
+ /* Lowercase in pattern matches lower or upper in target */
+ patglobflags = (patglobflags & ~GF_IGNCASE) | GF_LCMATCHUC;
+ break;
- case 'i':
- /* Fully case insensitive */
- patglobflags = (patglobflags & ~GF_LCMATCHUC) | GF_IGNCASE;
- break;
+ case 'i':
+ /* Fully case insensitive */
+ patglobflags = (patglobflags & ~GF_LCMATCHUC) | GF_IGNCASE;
+ break;
- case 'I':
- /* Restore case sensitivity */
- patglobflags &= ~(GF_LCMATCHUC|GF_IGNCASE);
- break;
+ case 'I':
+ /* Restore case sensitivity */
+ patglobflags &= ~(GF_LCMATCHUC|GF_IGNCASE);
+ break;
- case 'b':
- /* Make backreferences */
- patglobflags |= GF_BACKREF;
- break;
+ case 'b':
+ /* Make backreferences */
+ patglobflags |= GF_BACKREF;
+ break;
- case 'B':
- /* Don't make backreferences */
- patglobflags &= ~GF_BACKREF;
- break;
+ case 'B':
+ /* Don't make backreferences */
+ patglobflags &= ~GF_BACKREF;
+ break;
- case 'm':
- /* Make references to complete match */
- patglobflags |= GF_MATCHREF;
- break;
+ case 'm':
+ /* Make references to complete match */
+ patglobflags |= GF_MATCHREF;
+ break;
- case 'M':
- /* Don't */
- patglobflags &= ~GF_MATCHREF;
- break;
+ case 'M':
+ /* Don't */
+ patglobflags &= ~GF_MATCHREF;
+ break;
- case 's':
- *assertp = P_ISSTART;
- break;
+ case 's':
+ *assertp = P_ISSTART;
+ break;
- case 'e':
- *assertp = P_ISEND;
- break;
+ case 'e':
+ *assertp = P_ISEND;
+ break;
- default:
- return 0;
+ default:
+ return 0;
+ }
}
}
if (*ptr != Outpar)