diff options
Diffstat (limited to 'Src/glob.c')
-rw-r--r-- | Src/glob.c | 305 |
1 files changed, 251 insertions, 54 deletions
diff --git a/Src/glob.c b/Src/glob.c index e0d0cf68e..627166c7a 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -120,6 +120,8 @@ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figu #define TT_POSIX_BLOCKS 1 #define TT_KILOBYTES 2 #define TT_MEGABYTES 3 +#define TT_GIGABYTES 4 +#define TT_TERABYTES 5 typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *)); @@ -443,6 +445,7 @@ insert(char *s, int checked) break; } unqueue_signals(); + return; } /* Do the globbing: scanner is called recursively * @@ -451,7 +454,7 @@ insert(char *s, int checked) /**/ static void -scanner(Complist q) +scanner(Complist q, int shortcircuit) { Patprog p; int closure; @@ -459,16 +462,19 @@ scanner(Complist q) int errssofar = errsfound; struct dirsav ds; - init_dirsav(&ds); if (!q) return; + init_dirsav(&ds); if ((closure = q->closure)) { /* (foo/)# - match zero or more dirs */ if (q->closure == 2) /* (foo/)## - match one or more dirs */ q->closure = 1; - else - scanner(q->next); + else { + scanner(q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; + } } p = q->pat; /* Now the actual matching for the current path section. */ @@ -513,8 +519,11 @@ scanner(Complist q) } if (add) { addpath(str, l); - if (!closure || !statfullpath("", NULL, 1)) - scanner((q->closure) ? q : q->next); + if (!closure || !statfullpath("", NULL, 1)) { + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; + } pathbuf[pathpos = oppos] = '\0'; } } @@ -522,6 +531,8 @@ scanner(Complist q) if (str[l]) str = dupstrpfx(str, l); insert(str, 0); + if (shortcircuit && shortcircuit == matchct) + return; } } else { /* Do pattern matching on current path section. */ @@ -609,9 +620,12 @@ scanner(Complist q) memcpy(subdirs + subdirlen, (char *)&errsfound, sizeof(int)); subdirlen += sizeof(int); - } else + } else { /* if the last filename component, just add it */ insert(fn, 1); + if (shortcircuit && shortcircuit == matchct) + return; + } } } closedir(lock); @@ -624,7 +638,10 @@ scanner(Complist q) fn += l + 1; memcpy((char *)&errsfound, fn, sizeof(int)); fn += sizeof(int); - scanner((q->closure) ? q : q->next); /* scan next level */ + /* scan next level */ + scanner((q->closure) ? q : q->next, shortcircuit); + if (shortcircuit && shortcircuit == matchct) + return; pathbuf[pathpos = oppos] = '\0'; } hrealloc(subdirs, subdirlen, 0); @@ -638,6 +655,7 @@ scanner(Complist q) close(ds.dirfd); pathbufcwd = pbcwdsav; } + return; } /* This function tokenizes a zsh glob pattern */ @@ -1059,6 +1077,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. */ @@ -1078,6 +1157,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; /* How many files to match; */ + /* 0 means no limit */ if (unset(GLOBOPT) || !haswilds(ostr) || unset(EXECOPT)) { if (!nountok) @@ -1116,7 +1197,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 *)); @@ -1146,40 +1227,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. */ @@ -1192,6 +1240,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)) { @@ -1362,7 +1412,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* Find matching delimiters */ tt = get_strarg(s, &arglen); if (!*tt) { - zerr("missing end of name"); + zerr("missing delimiter for 'u' glob qualifier"); data = 0; } else { #ifdef USE_GETPWNAM @@ -1402,7 +1452,7 @@ zglob(LinkList list, LinkNode np, int nountok) tt = get_strarg(s, &arglen); if (!*tt) { - zerr("missing end of name"); + zerr("missing delimiter for 'g' glob qualifier"); data = 0; } else { #ifdef USE_GETGRNAM @@ -1459,6 +1509,23 @@ zglob(LinkList list, LinkNode np, int nountok) /* Numeric glob sort */ gf_numsort = !(sense & 1); break; + case 'Y': + { + /* 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; @@ -1486,6 +1553,12 @@ zglob(LinkList list, LinkNode np, int nountok) g_units = TT_KILOBYTES, ++s; else if (*s == 'm' || *s == 'M') g_units = TT_MEGABYTES, ++s; +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) + else if (*s == 'g' || *s == 'G') + g_units = TT_GIGABYTES, ++s; + else if (*s == 't' || *s == 'T') + g_units = TT_TERABYTES, ++s; +#endif getrange: /* Get time multiplier */ if (g_amc >= 0) { @@ -1549,9 +1622,10 @@ zglob(LinkList list, LinkNode np, int nountok) restore_globstate(saved); return; } + if ((sense & 2) && + (t & (GS_SIZE|GS_ATIME|GS_MTIME|GS_CTIME|GS_LINKS))) + t <<= GS_SHIFT; /* HERE: GS_EXEC? */ 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); @@ -1709,7 +1783,7 @@ zglob(LinkList list, LinkNode np, int nountok) return; } if (!gf_nsorts) { - gf_sortlist[0].tp = gf_sorts = GS_NAME; + gf_sortlist[0].tp = gf_sorts = (shortcircuit ? GS_NONE : GS_NAME); gf_nsorts = 1; } /* Initialise receptacle for matched files, * @@ -1721,7 +1795,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* The actual processing takes place here: matches go into * * matchbuf. This is the only top-level call to scanner(). */ - scanner(q); + scanner(q, shortcircuit); /* Deal with failures to match depending on options */ if (matchct) @@ -1832,6 +1906,8 @@ zglob(LinkList list, LinkNode np, int nountok) matchptr++; } } + } else if (!badcshglob && !isset(NOMATCH) && matchct == 1) { + insert_glob_match(list, node, (--matchptr)->name); } free(matchbuf); @@ -1895,6 +1971,8 @@ hasbraces(char *str) switch (*str++) { case Inbrace: if (!lbr) { + if (bracechardots(str-1, NULL, NULL)) + return 1; lbr = str - 1; if (*str == '-') str++; @@ -2027,6 +2105,68 @@ xpandredir(struct redir *fn, LinkList redirtab) return ret; } +/* + * Check for a brace expansion of the form {<char>..<char>}. + * On input str must be positioned at an Inbrace, but the sequence + * of characters beyond that has not necessarily been checked. + * Return 1 if found else 0. + * + * The other parameters are optionaland if the function returns 1 are + * used to return: + * - *c1p: the first character in the expansion. + * - *c2p: the final character in the expansion. + */ + +/**/ +static int +bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) +{ + convchar_t cstart, cend; + char *pnext = str + 1, *pconv, convstr[2]; + if (itok(*pnext)) { + if (*pnext == Inbrace) + return 0; + convstr[0] = ztokens[*pnext - Pound]; + convstr[1] = '\0'; + pconv = convstr; + } else + pconv = pnext; + MB_METACHARINIT(); + pnext += MB_METACHARLENCONV(pconv, &cstart); + if ( +#ifdef MULTIBYTE_SUPPORT + cstart == WEOF || +#else + !cstart || +#endif + pnext[0] != '.' || pnext[1] != '.') + return 0; + pnext += 2; + if (itok(*pnext)) { + if (*pnext == Inbrace) + return 0; + convstr[0] = ztokens[*pnext - Pound]; + convstr[1] = '\0'; + pconv = convstr; + } else + pconv = pnext; + MB_METACHARINIT(); + pnext += MB_METACHARLENCONV(pconv, &cend); + if ( +#ifdef MULTIBYTE_SUPPORT + cend == WEOF || +#else + !cend || +#endif + *pnext != Outbrace) + return 0; + if (c1p) + *c1p = cstart; + if (c2p) + *c2p = cend; + return 1; +} + /* brace expansion */ /**/ @@ -2060,10 +2200,57 @@ xpandbraces(LinkList list, LinkNode *np) char *dots, *p, *dots2 = NULL; LinkNode olast = last; /* Get the first number of the range */ - zlong rstart = zstrtol(str+1,&dots,10), rend = 0; + zlong rstart, rend; int err = 0, rev = 0, rincr = 1; - int wid1 = (dots - str) - 1, wid2 = (str2 - dots) - 2, wid3 = 0; - int strp = str - str3; + int wid1, wid2, wid3, strp; + convchar_t cstart, cend; + + if (bracechardots(str, &cstart, &cend)) { + int lenalloc; + /* + * This is a character range. + */ + if (cend < cstart) { + convchar_t ctmp = cend; + cend = cstart; + cstart = ctmp; + rev = 1; + } + uremnode(list, node); + strp = str - str3; + lenalloc = strp + strlen(str2+1) + 1; + for (; cend >= cstart; cend--) { +#ifdef MULTIBYTE_SUPPORT + char *ncptr; + int nclen; + mb_metacharinit(); + ncptr = wcs_nicechar(cend, NULL, NULL); + nclen = strlen(ncptr); + p = zhalloc(lenalloc + nclen); + memcpy(p, str3, strp); + memcpy(p + strp, ncptr, nclen); + strcpy(p + strp + nclen, str2 + 1); +#else + p = zhalloc(lenalloc + 1); + memcpy(p, str3, strp); + sprintf(p + strp, "%c", cend); + strcat(p + strp, str2 + 1); +#endif + insertlinknode(list, last, p); + if (rev) /* decreasing: add in reverse order. */ + last = nextnode(last); + } + *np = nextnode(olast); + return; + } + + /* Get the first number of the range */ + rstart = zstrtol(str+1,&dots,10); + rend = 0; + wid1 = (dots - str) - 1; + wid2 = (str2 - dots) - 2; + wid3 = 0; + strp = str - str3; if (dots == str + 1 || *dots != '.' || dots[1] != '.') err++; @@ -3443,9 +3630,9 @@ qualiscom(UNUSED(char *name), struct stat *buf, UNUSED(off_t mod), UNUSED(char * static int qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy)) { -#if defined(LONG_IS_64_BIT) || defined(OFF_T_IS_64_BIT) +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) # define QS_CAST_SIZE() - off_t scaled = buf->st_size; + zlong scaled = buf->st_size; #else # define QS_CAST_SIZE() (unsigned long) unsigned long scaled = (unsigned long)buf->st_size; @@ -3464,6 +3651,16 @@ qualsize(UNUSED(char *name), struct stat *buf, off_t size, UNUSED(char *dummy)) scaled += 1048575l; scaled /= 1048576l; break; +#if defined(ZSH_64_BIT_TYPE) || defined(LONG_IS_64_BIT) + case TT_GIGABYTES: + scaled += ZLONG_CONST(1073741823); + scaled /= ZLONG_CONST(1073741824); + break; + case TT_TERABYTES: + scaled += ZLONG_CONST(1099511627775); + scaled /= ZLONG_CONST(1099511627776); + break; +#endif } return (g_range < 0 ? scaled < QS_CAST_SIZE() size : |