From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11414 invoked by alias); 4 Jun 2014 02:17:33 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 32694 Received: (qmail 10040 invoked from network); 4 Jun 2014 02:17:27 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= daniel.shahaf.name; h=date:from:to:cc:subject:message-id :references:mime-version:content-type:in-reply-to; s=mesmtp; bh= ruraa1ph/XkKlhwUIFmZcV39Ihs=; b=ULo86NH9gtFFfK9NhQnB+YNIpzDSbrNM M9tqoUsbPhlIrsFVwzwxlEPs5G6YadftROrkuxmavxvvMWtGihRNKxJ3kfBFlG8s gXuJ+RCWxF2ut2wi7o7b+fjqRkGcLjgVqwTedlv0HqA9KqHxlZd0I0i+bkd5nPyI qsNSqhO4XQk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=date:from:to:cc:subject:message-id :references:mime-version:content-type:in-reply-to; s=smtpout; bh=ruraa1ph/XkKlhwUIFmZcV39Ihs=; b=rDZN3QdBwBLkFmRG7HLc7k82rAk8 729tWAdC+fQt8KNHTD4kpcu9SWz1+A5kroPGXMQ+LJKfSPDkcaBlVEIXtlc0uzS3 SP2aoXKO07Yw048z52mr9FE2KgOoleVw93f43I7ZenyKm3LHjXpWjfO5H0PIc/f4 of9krIM7cRMgFSc= X-Sasl-enc: mLtzfkCTm95PBkAiDCSEWZshw6QP3VPEskzrrr4SfpzY 1401847691 Date: Wed, 4 Jun 2014 02:08:04 +0000 From: Daniel Shahaf To: Bart Schaefer Cc: zsh-workers@zsh.org Subject: [PATCH] Re: (Y) modifier: up to N matches? Message-ID: <20140604020804.GA2032@tarsus.local2> References: <20140602182346.GB1858@tarsus.local2> <140602204603.ZM26905@torch.brasslantern.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="azLHFNyN32YCQGCU" Content-Disposition: inline In-Reply-To: <140602204603.ZM26905@torch.brasslantern.com> User-Agent: Mutt/1.5.21 (2010-09-15) --azLHFNyN32YCQGCU Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Bart Schaefer wrote on Mon, Jun 02, 2014 at 20:46:03 -0700: > On Jun 2, 6:23pm, Daniel Shahaf wrote: > } > } Would it make sense to have (Y) take a numeric argument specifying the > } maximal number of files to match? e.g., *(NY5) would expand to between > } 0 and 5 filenames (but never more than 5). > > My concern is that people are going to expect the (o)/(O) qualifiers to > take effect before (Y) does, and will be confused about the "skipped" > files when it takes effect after. If (Y) can't return more than one > result, there's nothing to sort. The expectations about sorting are just as much of a problem with (Y) as they would be with (Y42): in both cases there might be "skipped" files. For example, in the source dir, *(Y/on) [or *(Y1/on)] might result in "Etc" even though "Doc" exists. Let's clarify that in the documentation of (Y). > However, I can't come up with any other objection. Two patches attached. The first patch implements (Y42) and adds completion and the above-mentioned docs clarification. (The two latter parts should be useful even if we don't add an argument to (Y).) The second patch isn't strictly required, but it cleans up the return type changes that are no longer needed after the first patch. FWIW, I'm intentionally making (Y) without argument an error; we can settle on its semantics later after (Y42) has seen some "in the field" use. The spelling (Y1) can be used instead. Cheers, Daniel --azLHFNyN32YCQGCU Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0001-Teach-Y-to-take-an-argument.patch" >>From d4bd736da686b5927a3c655d3e25f9d0b2fc7f99 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 2 Jun 2014 19:14:10 +0000 Subject: [PATCH 1/2] Teach (Y) to take an argument --- Completion/Zsh/Type/_globquals | 1 + Doc/Zsh/expn.yo | 7 ++++--- Src/glob.c | 34 +++++++++++++++++++++++++++------- Test/D02glob.ztst | 23 ++++++++++++++--------- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Completion/Zsh/Type/_globquals b/Completion/Zsh/Type/_globquals index c98bd0c..37db161 100644 --- a/Completion/Zsh/Type/_globquals +++ b/Completion/Zsh/Type/_globquals @@ -251,6 +251,7 @@ case $state in "o:+ sort order, up" "O:+ sort order, down" "P:prepend word" + "Y:+ at most ARG matches" "[:+ range of files" "):end of qualifiers" "\::modifier" diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 25247f9..2f91fec 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -2564,9 +2564,10 @@ item(tt(n))( sets the tt(NUMERIC_GLOB_SORT) option for the current pattern pindex(NUMERIC_GLOB_SORT, setting in pattern) ) -item(tt(Y))( -enables short-circuit mode: the pattern will expand to just the first -matching filename, if any. +item(tt(Y)var(n))( +enables short-circuit mode: the pattern will expand to at most var(n) +filenames. If more than var(n) matches exist, only the first var(n) +matches in directory traversal order will be considered. ) item(tt(o)var(c))( specifies how the names of the files should be sorted. If var(c) is diff --git a/Src/glob.c b/Src/glob.c index 0ca63fc..33b49f1 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -473,7 +473,8 @@ scanner(Complist q, int shortcircuit) if (q->closure == 2) /* (foo/)## - match one or more dirs */ q->closure = 1; else - if (scanner(q->next, shortcircuit) == 1) + scanner(q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) return 1; } p = q->pat; @@ -520,7 +521,8 @@ scanner(Complist q, int shortcircuit) if (add) { addpath(str, l); if (!closure || !statfullpath("", NULL, 1)) - if (scanner((q->closure) ? q : q->next, shortcircuit) == 1) + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) return 1; pathbuf[pathpos = oppos] = '\0'; } @@ -528,7 +530,8 @@ scanner(Complist q, int shortcircuit) } else { if (str[l]) str = dupstrpfx(str, l); - if (insert(str, 0) == 1 && shortcircuit) + insert(str, 0); + if (shortcircuit && shortcircuit == matchct) return 1; } } else { @@ -619,7 +622,8 @@ scanner(Complist q, int shortcircuit) subdirlen += sizeof(int); } else /* if the last filename component, just add it */ - if (insert(fn, 1) == 1 && shortcircuit) + insert(fn, 1); + if (shortcircuit && shortcircuit == matchct) return 1; } } @@ -633,7 +637,9 @@ scanner(Complist q, int shortcircuit) fn += l + 1; memcpy((char *)&errsfound, fn, sizeof(int)); fn += sizeof(int); - if (scanner((q->closure) ? q : q->next, shortcircuit) == 1) /* scan next level */ + /* scan next level */ + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) return 1; pathbuf[pathpos = oppos] = '\0'; } @@ -1150,7 +1156,8 @@ zglob(LinkList list, LinkNode np, int nountok) /* and index+1 of the last match */ struct globdata saved; /* saved glob state */ int nobareglob = !isset(BAREGLOBQUAL); - int shortcircuit = 0; + int shortcircuit = 0; /* How many files to match; */ + /* 0 means no limit */ if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) { if (!nountok) @@ -1502,9 +1509,22 @@ zglob(LinkList list, LinkNode np, int nountok) gf_numsort = !(sense & 1); break; case 'Y': - /* Short circuit: just check if there are any matches */ + { + /* Short circuit: limit number of matches */ + const char *s_saved = s; shortcircuit = !(sense & 1); + if (shortcircuit) { + /* Parse the argument. */ + data = qgetnum(&s); + if ((shortcircuit = data) != data) { + /* Integer overflow */ + zerr("value too big: Y%s", s_saved); + restore_globstate(saved); + return; + } + } break; + } case 'a': /* Access time in given range */ g_amc = 0; diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index 9e29de2..c00bbe3 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -543,17 +543,22 @@ >Multiple files matched >Normal string if nullglob not set - (){ print $#@ } glob.tmp/dir*(Y) - (){ print $#@ } glob.tmp/file*(NY) - (){ [[ $1 = glob.tmp/dir? ]] && echo "(Y) returns a matching filename" } glob.tmp/dir*(Y) - # Can be negated - (){ print $@:t } glob.tmp/dir*(Y^Y) - (){ [[ $#@ -eq 1 ]] && print Globs before last path component } glob.tmp/dir?/subdir(NY) - (){ [[ $#@ -eq 0 ]] && print Respects qualifiers } glob.tmp/dir?/subdir(NY.) + (){ print $#@ } glob.tmp/dir*(Y1) + (){ print $#@ } glob.tmp/file*(NY1) + (){ [[ "$*" == */dir?\ */dir? ]] && print Returns matching filenames } glob.tmp/dir*(Y2) + (){ print "Limit is upper bound:" $@:t } glob.tmp/dir*(Y5) + (){ print "Negated:" $@:t } glob.tmp/dir*(Y1^Y) + (){ print "Sorting:" $@:t } glob.tmp/dir*(Y4On) + (){ [[ $#@ -eq 1 ]] && print Globs before last path component } glob.tmp/dir?/subdir(NY1) + (){ [[ $#@ -eq 0 ]] && print Respects qualifiers } glob.tmp/dir*(NY1.) + (print -- *(Y)) 2>/dev/null || print "Argument required" 0:short-circuit modifier >1 >0 ->(Y) returns a matching filename ->dir1 dir2 dir3 dir4 +>Returns matching filenames +>Limit is upper bound: dir1 dir2 dir3 dir4 +>Negated: dir1 dir2 dir3 dir4 +>Sorting: dir4 dir3 dir2 dir1 >Globs before last path component >Respects qualifiers +>Argument required -- 1.7.10.4 --azLHFNyN32YCQGCU Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0002-glob.c-Undo-now-unneeded-return-type-changes.patch" >>From b3ad6fe33263861671e19329d3a773128b220358 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 2 Jun 2014 23:11:37 +0000 Subject: [PATCH 2/2] glob.c: Undo now-unneeded return type changes --- Src/glob.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Src/glob.c b/Src/glob.c index 33b49f1..c74a560 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -297,16 +297,15 @@ statfullpath(const char *s, struct stat *st, int l) char **inserts; -/* add a match to the list. Return 1 if it was inserted, 0 otherwise. */ +/* add a match to the list */ /**/ -static int +static void insert(char *s, int checked) { struct stat buf, buf2, *bp; char *news = s; int statted = 0; - int inserted = 0; queue_signals(); inserts = NULL; @@ -317,7 +316,7 @@ insert(char *s, int checked) checked = statted = 1; if (statfullpath(s, &buf, 1)) { unqueue_signals(); - return inserted; + return; } mode = buf.st_mode; if (gf_follow) { @@ -341,7 +340,7 @@ insert(char *s, int checked) if (!statted && statfullpath(s, &buf, 1)) { unqueue_signals(); - return inserted; + return; } news = dyncat(pathbuf, news); @@ -366,7 +365,7 @@ insert(char *s, int checked) /* Try next alternative, or return if there are no more */ if (!(qo = qo->or)) { unqueue_signals(); - return inserted; + return; } qn = qo; continue; @@ -376,7 +375,7 @@ insert(char *s, int checked) } else if (!checked) { if (statfullpath(s, NULL, 1)) { unqueue_signals(); - return inserted; + return; } statted = 1; news = dyncat(pathbuf, news); @@ -436,7 +435,6 @@ insert(char *s, int checked) } matchptr++; - inserted = 1; if (++matchct == matchsz) { matchbuf = (Gmatch )realloc((char *)matchbuf, sizeof(struct gmatch) * (matchsz *= 2)); @@ -447,7 +445,7 @@ insert(char *s, int checked) break; } unqueue_signals(); - return inserted; + return; } /* Do the globbing: scanner is called recursively * @@ -455,7 +453,7 @@ insert(char *s, int checked) * tried all of it. */ /**/ -static int +static void scanner(Complist q, int shortcircuit) { Patprog p; @@ -466,7 +464,7 @@ scanner(Complist q, int shortcircuit) init_dirsav(&ds); if (!q) - return -1; + return; if ((closure = q->closure)) { /* (foo/)# - match zero or more dirs */ @@ -475,7 +473,7 @@ scanner(Complist q, int shortcircuit) else scanner(q->next, shortcircuit); if (shortcircuit && shortcircuit == matchct) - return 1; + return; } p = q->pat; /* Now the actual matching for the current path section. */ @@ -490,13 +488,13 @@ scanner(Complist q, int shortcircuit) int err; if (l >= PATH_MAX) - return -1; + return; err = lchdir(pathbuf + pathbufcwd, &ds, 0); if (err == -1) - return -1; + return; if (err) { zerr("current directory lost during glob"); - return -1; + return; } pathbufcwd = pathpos; } @@ -523,7 +521,7 @@ scanner(Complist q, int shortcircuit) if (!closure || !statfullpath("", NULL, 1)) scanner((q->closure) ? q : q->next, shortcircuit); if (shortcircuit && shortcircuit == matchct) - return 1; + return; pathbuf[pathpos = oppos] = '\0'; } } @@ -532,7 +530,7 @@ scanner(Complist q, int shortcircuit) str = dupstrpfx(str, l); insert(str, 0); if (shortcircuit && shortcircuit == matchct) - return 1; + return; } } else { /* Do pattern matching on current path section. */ @@ -543,7 +541,7 @@ scanner(Complist q, int shortcircuit) int subdirlen = 0; if (lock == NULL) - return -1; + return; while ((fn = zreaddir(lock, 1)) && !errflag) { /* prefix and suffix are zle trickery */ if (!dirs && !colonmod && @@ -624,7 +622,7 @@ scanner(Complist q, int shortcircuit) /* if the last filename component, just add it */ insert(fn, 1); if (shortcircuit && shortcircuit == matchct) - return 1; + return; } } closedir(lock); @@ -640,7 +638,7 @@ scanner(Complist q, int shortcircuit) /* scan next level */ scanner((q->closure) ? q : q->next, shortcircuit); if (shortcircuit && shortcircuit == matchct) - return 1; + return; pathbuf[pathpos = oppos] = '\0'; } hrealloc(subdirs, subdirlen, 0); @@ -654,7 +652,7 @@ scanner(Complist q, int shortcircuit) close(ds.dirfd); pathbufcwd = pbcwdsav; } - return 0; + return; } /* This function tokenizes a zsh glob pattern */ -- 1.7.10.4 --azLHFNyN32YCQGCU--