summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2009-01-27 09:55:22 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2009-01-27 09:55:22 +0000
commit7c43fb69bf81dd37c0743a761c6b43519ea9a0d0 (patch)
tree4d060057081de5ffc051dc551901a9e71894f115
parent6f16f59b9f0f1835f303eeeffcea3f981fcd8d92 (diff)
downloadzsh-7c43fb69bf81dd37c0743a761c6b43519ea9a0d0.tar.gz
zsh-7c43fb69bf81dd37c0743a761c6b43519ea9a0d0.zip
26448: add glob sort operators oe and o+
-rw-r--r--ChangeLog7
-rw-r--r--Doc/Zsh/expn.yo12
-rw-r--r--Src/glob.c217
3 files changed, 191 insertions, 45 deletions
diff --git a/ChangeLog b/ChangeLog
index b30aae481..9ddc81860 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-01-27 Peter Stephenson <pws@csr.com>
+
+ * 26448: Doc/Zsh/expn.yo, Src/glob.c: glob sort operators
+ oe and o+ allow arbitrary code to pick name for sorting.
+
2009-01-26 Peter Stephenson <pws@csr.com>
* Greg Klanderman: 26439: Doc/Zsh/mod_system.yo: convert sections
@@ -11007,5 +11012,5 @@
*****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.4527 $
+* $Revision: 1.4528 $
*****************************************************
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index ec92a34b9..1468a3a36 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2229,7 +2229,7 @@ pindex(NUMERIC_GLOB_SORT, setting in pattern)
item(tt(o)var(c))(
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
-are sorted depending on the size (length) of the files; if tt(l)
+are sorted depending on the size (length) of the files; if tt(l)
they are sorted by the number of links; if tt(a), tt(m), or tt(c)
they are sorted by the time of the last access, modification, or
inode change respectively; if tt(d), files in subdirectories appear before
@@ -2242,6 +2242,16 @@ youngest file. Also note that the modifiers tt(^) and tt(-) are used,
so `tt(*(^-oL))' gives a list of all files sorted by file size in descending
order, following any symbolic links. Unless tt(oN) is used, multiple order
specifiers may occur to resolve ties.
+
+tt(oe) and tt(o+) are special cases; they are each followed by shell code,
+delimited as for the tt(e) glob qualifier and the tt(+) glob qualifier
+respectively (see above). The code is executed for each matched file with
+the parameter tt(REPLY) set to the name of the file on entry. The code
+should modify the parameter tt(REPLY) in some fashion. On return, the value
+of the parameter is used instead of the file name as the string on which to
+sort. Unlike other sort operators, tt(oe) and tt(o+) may be repeated, but
+note that the maximum number of sort operators of any kind that may appear
+in any glob expression is 12.
)
item(tt(O)var(c))(
like `tt(o)', but sorts in descending order; i.e. `tt(*(^oc))' is the
diff --git a/Src/glob.c b/Src/glob.c
index f8d7b5ad0..5000ff457 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -42,6 +42,11 @@ typedef struct gmatch *Gmatch;
struct gmatch {
char *name;
+ /*
+ * Array of sort strings: one for each GS_EXEC sort type in
+ * the glob qualifiers.
+ */
+ char **sortstrs;
off_t size ALIGN64;
long atime;
long mtime;
@@ -68,8 +73,9 @@ struct gmatch {
#define GS_NAME 1
#define GS_DEPTH 2
+#define GS_EXEC 4
-#define GS_SHIFT_BASE 4
+#define GS_SHIFT_BASE 8
#define GS_SIZE (GS_SHIFT_BASE)
#define GS_ATIME (GS_SHIFT_BASE << 1)
@@ -135,6 +141,17 @@ struct qual {
/**/
mod_export char *glob_pre, *glob_suf;
+/* Element of a glob sort */
+struct globsort {
+ /* Sort type */
+ int tp;
+ /* Sort code to eval, if type is GS_EXEC */
+ char *exec;
+};
+
+/* Maximum entries in sort array */
+#define MAX_SORTS (12)
+
/* struct to easily save/restore current state */
struct globdata {
@@ -157,7 +174,8 @@ struct globdata {
int gd_range, gd_amc, gd_units;
int gd_gf_nullglob, gd_gf_markdirs, gd_gf_noglobdots, gd_gf_listtypes;
int gd_gf_numsort;
- int gd_gf_follow, gd_gf_sorts, gd_gf_nsorts, gd_gf_sortlist[11];
+ int gd_gf_follow, gd_gf_sorts, gd_gf_nsorts;
+ struct globsort gd_gf_sortlist[MAX_SORTS];
char *gd_glob_pre, *gd_glob_suf;
};
@@ -880,11 +898,13 @@ qgetmodespec(char **s)
static int
gmatchcmp(Gmatch a, Gmatch b)
{
- int i, *s;
+ int i;
off_t r = 0L;
+ struct globsort *s;
+ char **asortstrp = NULL, **bsortstrp = NULL;
for (i = gf_nsorts, s = gf_sortlist; i; i--, s++) {
- switch (*s & ~GS_DESC) {
+ switch (s->tp & ~GS_DESC) {
case GS_NAME:
r = zstrcmp(b->name, a->name, gf_numsort ? SORTIT_NUMERICALLY : 0);
break;
@@ -910,6 +930,17 @@ gmatchcmp(Gmatch a, Gmatch b)
r = slasha - slashb;
}
break;
+ case GS_EXEC:
+ if (!asortstrp) {
+ asortstrp = a->sortstrs;
+ bsortstrp = b->sortstrs;
+ } else {
+ asortstrp++;
+ bsortstrp++;
+ }
+ r = zstrcmp(*bsortstrp, *asortstrp,
+ gf_numsort ? SORTIT_NUMERICALLY : 0);
+ break;
case GS_SIZE:
r = b->size - a->size;
break;
@@ -966,7 +997,7 @@ gmatchcmp(Gmatch a, Gmatch b)
break;
}
if (r)
- return (int) ((*s & GS_DESC) ? -r : r);
+ return (int) ((s->tp & GS_DESC) ? -r : r);
}
return 0;
}
@@ -1000,6 +1031,49 @@ static struct qual *dup_qual_list(struct qual *orig, struct qual **lastp)
return qfirst;
}
+
+/*
+ * Get a glob string for execution, following e or + qualifiers.
+ * Pointer is character after the e or +.
+ */
+
+/**/
+static char *
+glob_exec_string(char **sp)
+{
+ char sav, *tt, *sdata, *s = *sp;
+ int plus;
+
+ if (s[-1] == '+') {
+ plus = 0;
+ tt = itype_end(s, IIDENT, 0);
+ if (tt == s)
+ {
+ zerr("missing identifier after `+'");
+ return NULL;
+ }
+ } else {
+ tt = get_strarg(s, &plus);
+ if (!*tt)
+ {
+ zerr("missing end of string");
+ return NULL;
+ }
+ }
+
+ sav = *tt;
+ *tt = '\0';
+ sdata = dupstring(s + plus);
+ untokenize(sdata);
+ *tt = sav;
+ if (sav)
+ *sp = tt + plus;
+ else
+ *sp = tt;
+
+ return sdata;
+}
+
/* 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. */
@@ -1449,7 +1523,16 @@ zglob(LinkList list, LinkNode np, int nountok)
case 'O':
{
int t;
+ char *send;
+ if (gf_nsorts == MAX_SORTS) {
+ zerr("too many glob sort specifiers");
+ restore_globstate(saved);
+ return;
+ }
+
+ /* usually just one character */
+ send = s+1;
switch (*s) {
case 'n': t = GS_NAME; break;
case 'L': t = GS_SIZE; break;
@@ -1459,60 +1542,50 @@ zglob(LinkList list, LinkNode np, int nountok)
case 'c': t = GS_CTIME; break;
case 'd': t = GS_DEPTH; break;
case 'N': t = GS_NONE; break;
+ case 'e':
+ case '+':
+ {
+ t = GS_EXEC;
+ if ((gf_sortlist[gf_nsorts].exec =
+ glob_exec_string(&send)) == NULL)
+ {
+ restore_globstate(saved);
+ return;
+ }
+ break;
+ }
default:
zerr("unknown sort specifier");
restore_globstate(saved);
return;
}
- if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
- t <<= GS_SHIFT;
- if (gf_sorts & t) {
- zerr("doubled sort specifier");
- restore_globstate(saved);
- return;
+ if (t != GS_EXEC) {
+ if ((sense & 2) && !(t & (GS_NAME|GS_DEPTH)))
+ t <<= GS_SHIFT; /* HERE: GS_EXEC? */
+ if (gf_sorts & t) {
+ zerr("doubled sort specifier");
+ restore_globstate(saved);
+ return;
+ }
}
gf_sorts |= t;
- gf_sortlist[gf_nsorts++] = t |
+ gf_sortlist[gf_nsorts++].tp = t |
(((sense & 1) ^ (s[-1] == 'O')) ? GS_DESC : 0);
- s++;
+ s = send;
break;
}
case '+':
case 'e':
{
- char sav, *tt;
- int plus;
+ char *tt;
- if (s[-1] == '+') {
- plus = 0;
- tt = itype_end(s, IIDENT, 0);
- if (tt == s)
- {
- zerr("missing identifier after `+'");
- tt = NULL;
- }
- } else {
- tt = get_strarg(s, &plus);
- if (!*tt)
- {
- zerr("missing end of string");
- tt = NULL;
- }
- }
+ tt = glob_exec_string(&s);
if (tt == NULL) {
data = 0;
} else {
- sav = *tt;
- *tt = '\0';
func = qualsheval;
- sdata = dupstring(s + plus);
- untokenize(sdata);
- *tt = sav;
- if (sav)
- s = tt + plus;
- else
- s = tt;
+ sdata = tt;
}
break;
}
@@ -1632,7 +1705,7 @@ zglob(LinkList list, LinkNode np, int nountok)
return;
}
if (!gf_nsorts) {
- gf_sortlist[0] = gf_sorts = GS_NAME;
+ gf_sortlist[0].tp = gf_sorts = GS_NAME;
gf_nsorts = 1;
}
/* Initialise receptacle for matched files, *
@@ -1665,7 +1738,65 @@ zglob(LinkList list, LinkNode np, int nountok)
}
}
- if (!(gf_sortlist[0] & GS_NONE)) {
+ if (!(gf_sortlist[0].tp & GS_NONE)) {
+ /*
+ * Get the strings to use for sorting by executing
+ * the code chunk. We allow more than one of these.
+ */
+ int nexecs = 0;
+ struct globsort *sortp;
+ struct globsort *lastsortp = gf_sortlist + gf_nsorts;
+
+ /* First find out if there are any GS_EXECs, counting them. */
+ for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
+ {
+ if (sortp->tp & GS_EXEC)
+ nexecs++;
+ }
+
+ if (nexecs) {
+ Gmatch tmpptr;
+ int iexec = 0;
+
+ /* Yes; allocate enough space for strings for each */
+ for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++)
+ tmpptr->sortstrs = (char **)zhalloc(nexecs*sizeof(char*));
+
+ /* Loop over each one, incrementing iexec */
+ for (sortp = gf_sortlist; sortp < lastsortp; sortp++)
+ {
+ /* Ignore unless this is a GS_EXEC */
+ if (sortp->tp & GS_EXEC) {
+ Eprog prog;
+
+ if ((prog = parse_string(sortp->exec, 0))) {
+ int ef = errflag, lv = lastval, ret;
+
+ /* Parsed OK, execute for each name */
+ for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++) {
+ setsparam("REPLY", ztrdup(tmpptr->name));
+ execode(prog, 1, 0);
+ if (!errflag)
+ tmpptr->sortstrs[iexec] =
+ dupstring(getsparam("REPLY"));
+ else
+ tmpptr->sortstrs[iexec] = tmpptr->name;
+ }
+
+ ret = lastval;
+ errflag = ef;
+ lastval = lv;
+ } else {
+ /* Failed, let's be safe */
+ for (tmpptr = matchbuf; tmpptr < matchptr; tmpptr++)
+ tmpptr->sortstrs[iexec] = tmpptr->name;
+ }
+
+ iexec++;
+ }
+ }
+ }
+
/* Sort arguments in to lexical (and possibly numeric) order. *
* This is reversed to facilitate insertion into the list. */
qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch),
@@ -1682,7 +1813,7 @@ zglob(LinkList list, LinkNode np, int nountok)
else if (end > matchct)
end = matchct;
if ((end -= first) > 0) {
- if (gf_sortlist[0] & GS_NONE) {
+ if (gf_sortlist[0].tp & GS_NONE) {
/* Match list was never reversed, so insert back to front. */
matchptr = matchbuf + matchct - first - 1;
while (end-- > 0) {