/*XXX - if no magic, if dest given, copy to dst return ? - if magic && (no globbing || syntax error) debunk to dst return ? - return ? */ int has_globbing(const char *xp, const char *xpe) { const unsigned char *p = (const unsigned char *) xp; const unsigned char *pe = (const unsigned char *) xpe; int c; int nest = 0, bnest = 0; int saw_glob = 0; int in_bracket = 0; /* inside [...] */ for (; p < pe; p++) { if (!ISMAGIC(*p)) continue; if ((c = *++p) == '*' || c == '?') saw_glob = 1; else if (c == '[') { if (!in_bracket) { saw_glob = 1; in_bracket = 1; if (ISMAGIC(p[1]) && p[2] == NOT) p += 2; if (ISMAGIC(p[1]) && p[2] == ']') p += 2; } /* XXX Do we need to check ranges here? POSIX Q */ } else if (c == ']') { if (in_bracket) { if (bnest) /* [a*(b]) */ return 0; in_bracket = 0; } } else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) { saw_glob = 1; if (in_bracket) bnest++; else nest++; } else if (c == '|') { if (in_bracket && !bnest) /* *(a[foo|bar]) */ return 0; } else if (c == /*(*/ ')') { if (in_bracket) { if (!bnest--) /* *(a[b)c] */ return 0; } else if (nest) nest--; } /* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-] MAGIC-{, MAGIC-,, MAGIC-} */ } return saw_glob && !in_bracket && !nest; }
static const unsigned char * cclass(const unsigned char *p, int sub) { int c, d, rv, not, found = 0; const unsigned char *orig_p = p; if ((not = (ISMAGIC(*p) && *++p == NOT))) p++; do { /* check for POSIX character class (e.g. [[:alpha:]]) */ if ((p[0] == MAGIC && p[1] == '[' && p[2] == ':') || (p[0] == '[' && p[1] == ':')) { do { const char *pp = p + (*p == MAGIC) + 2; rv = posix_cclass(pp, sub, &p); switch (rv) { case 1: found = 1; break; case -2: return NULL; } } while (rv != -1 && p[0] == MAGIC && p[1] == '[' && p[2] == ':'); if (p[0] == MAGIC && p[1] == ']') break; } c = *p++; if (ISMAGIC(c)) { c = *p++; if ((c & 0x80) && !ISMAGIC(c)) { c &= 0x7f;/* extended pattern matching: *+?@! */ /* XXX the ( char isn't handled as part of [] */ if (c == ' ') /* simile for @: plain (..) */ c = '(' /*)*/; } } if (c == '\0') /* No closing ] - act as if the opening [ was quoted */ return sub == '[' ? orig_p : NULL; if (ISMAGIC(p[0]) && p[1] == '-' && (!ISMAGIC(p[2]) || p[3] != ']')) { p += 2; /* MAGIC- */ d = *p++; if (ISMAGIC(d)) { d = *p++; if ((d & 0x80) && !ISMAGIC(d)) d &= 0x7f; } /* POSIX says this is an invalid expression */ if (c > d) return NULL; } else d = c; if (c == sub || (c <= sub && sub <= d)) found = 1; } while (!(ISMAGIC(p[0]) && p[1] == ']')); return (found != not) ? p+2 : NULL; }
static const unsigned char * gmatch_cclass(const unsigned char *p, unsigned char sub) { unsigned char c, d; bool notp, found = false; const unsigned char *orig_p = p; if ((notp = tobool(ISMAGIC(*p) && *++p == '!'))) p++; do { c = *p++; if (ISMAGIC(c)) { c = *p++; if ((c & 0x80) && !ISMAGIC(c)) { /* extended pattern matching: *+?@! */ c &= 0x7F; /* XXX the ( char isn't handled as part of [] */ if (c == ' ') /* simile for @: plain (..) */ c = '(' /*)*/; } } if (c == '\0') /* No closing ] - act as if the opening [ was quoted */ return (sub == '[' ? orig_p : NULL); if (ISMAGIC(p[0]) && p[1] == '-' && (!ISMAGIC(p[2]) || p[3] != ']')) { /* MAGIC- */ p += 2; d = *p++; if (ISMAGIC(d)) { d = *p++; if ((d & 0x80) && !ISMAGIC(d)) d &= 0x7f; } /* POSIX says this is an invalid expression */ if (c > d) return (NULL); } else d = c; if (c == sub || (c <= sub && sub <= d)) found = true; } while (!(ISMAGIC(p[0]) && p[1] == ']')); return ((found != notp) ? p+2 : NULL); }
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */ const unsigned char * pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep) { int nest = 0; for (; p < pe; p++) { if (!ISMAGIC(*p)) continue; if ((*++p == /*(*/ ')' && nest-- == 0) || (*p == '|' && match_sep && nest == 0)) return ++p; if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f)) nest++; } return (const unsigned char *) 0; }
static const unsigned char * cclass(const unsigned char *p, int sub) { int c, d, not, found = 0; const unsigned char *orig_p = p; if ((not = (ISMAGIC(*p) && *++p == NOT))) p++; do { c = *p++; if (ISMAGIC(c)) { c = *p++; if ((c & 0x80) && !ISMAGIC(c)) { c &= 0x7f;/* extended pattern matching: *+?@! */ /* XXX the ( char isn't handled as part of [] */ if (c == ' ') /* simile for @: plain (..) */ c = '(' /*)*/; } } if (c == '\0') /* No closing ] - act as if the opening [ was quoted */ return sub == '[' ? orig_p : NULL; if (ISMAGIC(p[0]) && p[1] == '-' && (!ISMAGIC(p[2]) || p[3] != ']')) { p += 2; /* MAGIC- */ d = *p++; if (ISMAGIC(d)) { d = *p++; if ((d & 0x80) && !ISMAGIC(d)) d &= 0x7f; } /* POSIX says this is an invalid expression */ if (c > d) return NULL; } else d = c; if (c == sub || (c <= sub && sub <= d)) found = 1; } while (!(ISMAGIC(p[0]) && p[1] == ']')); return (found != not) ? p+2 : NULL; }
/* variant of fputs for ptreef and wdstrip */ static const char * wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) { int c; const char *cs; /*- * problems: * `...` -> $(...) * 'foo' -> "foo" * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) */ while (/* CONSTCOND */ 1) switch (*wp++) { case EOS: return (--wp); case ADELIM: case CHAR: c = *wp++; if ((opmode & WDS_MAGIC) && (ISMAGIC(c) || c == '[' || c == '!' || c == '-' || c == ']' || c == '*' || c == '?')) shf_putc(MAGIC, shf); shf_putc(c, shf); break; case QCHAR: { bool doq; c = *wp++; doq = (c == '"' || c == '`' || c == '$' || c == '\\'); if (opmode & WDS_TPUTS) { if (quotelevel == 0) doq = true; } else { if (!(opmode & WDS_KEEPQ)) doq = false; } if (doq) shf_putc('\\', shf); shf_putc(c, shf); break; } case COMSUB: shf_puts("$(", shf); cs = ")"; pSUB: while ((c = *wp++) != 0) shf_putc(c, shf); shf_puts(cs, shf); break; case FUNSUB: c = ' '; if (0) /* FALLTHROUGH */ case VALSUB: c = '|'; shf_putc('$', shf); shf_putc('{', shf); shf_putc(c, shf); cs = ";}"; goto pSUB; case EXPRSUB: shf_puts("$((", shf); cs = "))"; goto pSUB; case OQUOTE: if (opmode & WDS_TPUTS) { quotelevel++; shf_putc('"', shf); } break; case CQUOTE: if (opmode & WDS_TPUTS) { if (quotelevel) quotelevel--; shf_putc('"', shf); } break; case OSUBST: shf_putc('$', shf); if (*wp++ == '{') shf_putc('{', shf); while ((c = *wp++) != 0) shf_putc(c, shf); wp = wdvarput(shf, wp, 0, opmode); break; case CSUBST: if (*wp++ == '}') shf_putc('}', shf); return (wp); case OPAT: if (opmode & WDS_MAGIC) { shf_putc(MAGIC, shf); shf_putchar(*wp++ | 0x80, shf); } else { shf_putchar(*wp++, shf); shf_putc('(', shf); } break; case SPAT: c = '|'; if (0) case CPAT: c = /*(*/ ')'; if (opmode & WDS_MAGIC) shf_putc(MAGIC, shf); shf_putc(c, shf); break; } }
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */ static int do_gmatch(const unsigned char *s, const unsigned char *se, const unsigned char *p, const unsigned char *pe) { int sc, pc; const unsigned char *prest, *psub, *pnext; const unsigned char *srest; if (s == NULL || p == NULL) return 0; while (p < pe) { pc = *p++; sc = s < se ? *s : '\0'; s++; if (!ISMAGIC(pc)) { if (sc != pc) return 0; continue; } switch (*p++) { case '[': if (sc == 0 || (p = cclass(p, sc)) == NULL) return 0; break; case '?': if (sc == 0) return 0; break; case '*': if (p == pe) return 1; s--; do { if (do_gmatch(s, se, p, pe)) return 1; } while (s++ < se); return 0; /* * [*+?@!](pattern|pattern|..) * * Not ifdef'd KSH as this is needed for ${..%..}, etc. */ case 0x80|'+': /* matches one or more times */ case 0x80|'*': /* matches zero or more times */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; /* take care of zero matches */ if (p[-1] == (0x80 | '*') && do_gmatch(s, se, prest, pe)) return 1; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); for (srest = s; srest <= se; srest++) { if (do_gmatch(s, srest, psub, pnext - 2) && (do_gmatch(srest, se, prest, pe) || (s != srest && do_gmatch(srest, se, p - 2, pe)))) return 1; } if (pnext == prest) break; } return 0; case 0x80|'?': /* matches zero or once */ case 0x80|'@': /* matches one of the patterns */ case 0x80|' ': /* simile for @ */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; /* Take care of zero matches */ if (p[-1] == (0x80 | '?') && do_gmatch(s, se, prest, pe)) return 1; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); srest = prest == pe ? se : s; for (; srest <= se; srest++) { if (do_gmatch(s, srest, psub, pnext - 2) && do_gmatch(srest, se, prest, pe)) return 1; } if (pnext == prest) break; } return 0; case 0x80|'!': /* matches none of the patterns */ if (!(prest = pat_scan(p, pe, 0))) return 0; s--; for (srest = s; srest <= se; srest++) { int matched = 0; for (psub = p; ; psub = pnext) { pnext = pat_scan(psub, pe, 1); if (do_gmatch(s, srest, psub, pnext - 2)) { matched = 1; break; } if (pnext == prest) break; } if (!matched && do_gmatch(srest, se, prest, pe)) return 1; } return 0; default: if (sc != p[-1]) return 0; break; } } return s == se; }
/** * pattern simplifications: * - @(x) -> x (not @(x|y) though) * - ** -> * */ static void * simplify_gmatch_pattern(const unsigned char *sp) { uint8_t c; unsigned char *cp, *dp; const unsigned char *ps, *se; cp = alloc(strlen((const void *)sp) + 1, ATEMP); goto simplify_gmatch_pat1a; /* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */ simplify_gmatch_pat1: sp = cp; simplify_gmatch_pat1a: dp = cp; se = sp + strlen((const void *)sp); while ((c = *sp++)) { if (!ISMAGIC(c)) { *dp++ = c; continue; } switch ((c = *sp++)) { case 0x80|'@': /* simile for @ */ case 0x80|' ': /* check whether it has only one clause */ ps = pat_scan(sp, se, true); if (!ps || ps[-1] != /*(*/ ')') /* nope */ break; /* copy inner clause until matching close */ ps -= 2; while ((const unsigned char *)sp < ps) *dp++ = *sp++; /* skip MAGIC and closing parenthesis */ sp += 2; /* copy the rest of the pattern */ memmove(dp, sp, strlen((const void *)sp) + 1); /* redo from start */ goto simplify_gmatch_pat1; } *dp++ = MAGIC; *dp++ = c; } *dp = '\0'; /* collapse adjacent asterisk wildcards */ sp = dp = cp; while ((c = *sp++)) { if (!ISMAGIC(c)) { *dp++ = c; continue; } switch ((c = *sp++)) { case '*': while (ISMAGIC(sp[0]) && sp[1] == c) sp += 2; break; } *dp++ = MAGIC; *dp++ = c; } *dp = '\0'; /* return the result, allocated from ATEMP */ return (cp); }
static void glob_path(int flags, const char *pat, XPtrV *wp, const char *path) { const char *sp, *p; char *xp; int staterr; int pathlen; int patlen; int oldsize, newsize, i, j; char **words; XString xs; patlen = strlen(pat) + 1; sp = path; Xinit(xs, xp, patlen + 128, ATEMP); while (sp) { xp = Xstring(xs, xp); if (!(p = strchr(sp, ':'))) p = sp + strlen(sp); pathlen = p - sp; if (pathlen) { /* Copy sp into xp, stuffing any MAGIC characters * on the way */ const char *s = sp; XcheckN(xs, xp, pathlen * 2); while (s < p) { if (ISMAGIC(*s)) *xp++ = MAGIC; *xp++ = *s++; } *xp++ = '/'; pathlen++; } sp = p; XcheckN(xs, xp, patlen); memcpy(xp, pat, patlen); oldsize = XPsize(*wp); glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */ newsize = XPsize(*wp); /* Check that each match is executable... */ words = (char **) XPptrv(*wp); for (i = j = oldsize; i < newsize; i++) { staterr = 0; if ((search_access(words[i], X_OK, &staterr) >= 0) || (staterr == EISDIR)) { words[j] = words[i]; if (!(flags & XCF_FULLPATH)) memmove(words[j], words[j] + pathlen, strlen(words[j] + pathlen) + 1); j++; } else afree(words[i], ATEMP); } wp->cur = (void **) &words[j]; if (!*sp++) break; } Xfree(xs, xp); }