summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Completion/Core/_path_files65
-rw-r--r--Src/Zle/computil.c290
3 files changed, 314 insertions, 47 deletions
diff --git a/ChangeLog b/ChangeLog
index b249e1eb8..17ef304a6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2000-06-09 Sven Wischnowsky <wischnow@zsh.org>
+
+ * 11833: Completion/Core/_path_files, Src/Zle/computil.c: improve
+ _path_files, move some code into C, try to optimise glob patterns
+ and immediately accept exact directory matches
+
2000-06-08 Oliver Kiddle <opk@zsh.org>
* 11823: Doc/Makefile.in, Completion/User/_urls: avoid bug in
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 32a92302e..5673e7c4a 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -5,7 +5,7 @@
local linepath realpath donepath prepath testpath exppath skips skipped
local tmp1 tmp2 tmp3 tmp4 i orig eorig pre suf tpre tsuf opre osuf cpre
-local pats haspats ignore pfxsfx remt sopt gopt opt sdirs ignpar
+local pats haspats ignore pfxsfx sopt gopt opt sdirs ignpar
local nm=$compstate[nmatches] menu matcher mopts sort match mid
typeset -U prepaths exppaths
@@ -312,35 +312,14 @@ for prepath in "$prepaths[@]"; do
# Get the matching files by globbing.
- tmp2=( "$tmp1[@]" )
if [[ "$tpre$tsuf" = */* ]]; then
- if [[ ! -o globdots && "$PREFIX" = .* ]]; then
- tmp1=( ${^tmp1}${skipped}*(-/) ${^tmp1}${skipped}.*(-/) )
- else
- tmp1=( ${^tmp1}${skipped}*(-/) )
- fi
- if [[ -n "$sdirs" && ( -o globdots || "$PREFIX" = .* ) ]]; then
- if [[ "$sdirs" = yes ]]; then
- tmp1=( "$tmp1[@]" $^tmp2${skipped}{.,..} )
- elif [[ "$sdirs" = .. ]]; then
- tmp1=( "$tmp1[@]" $^tmp2${skipped}.. )
- fi
- fi
+ compfiles -P tmp1 "$skipped" "$_matcher" "$sdirs"
+ elif [[ "$sopt" = *[/f]* ]]; then
+ compfiles -p tmp1 "$skipped" "$_matcher" "$sdirs" "$pats"
else
- if [[ ! -o globdots && "$PREFIX" = .* ]]; then
- tmp1=( ${^tmp1}${skipped}${^~pats} ${^tmp1}${skipped}.${^~pats:#.*} )
- else
- tmp1=( ${^tmp1}${skipped}${^~pats} )
- fi
- if [[ -n "$sdirs" && "$sopt" = *[/f]* &&
- ( -o globdots || "$PREFIX" = .* ) ]]; then
- if [[ "$sdirs" = yes ]]; then
- tmp1=( "$tmp1[@]" $^tmp2${skipped}{.,..} )
- elif [[ "$sdirs" = .. ]]; then
- tmp1=( "$tmp1[@]" $^tmp2${skipped}.. )
- fi
- fi
+ compfiles -p tmp1 "$skipped" "$_matcher" '' "$pats"
fi
+ tmp1=( $~tmp1 )
if [[ -n "$PREFIX$SUFFIX" ]]; then
# See which of them match what's on the line.
@@ -354,9 +333,13 @@ for prepath in "$prepaths[@]"; do
compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}"
fi
else
- [[ "$tmp1[1]" = */* ]] && tmp2=( "$tmp1[@]" )
+ if [[ "$tmp1[1]" = */* ]]; then
+ tmp2=( "$tmp1[@]" )
+ else
+ tmp2=( '' )
+ fi
- builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
+ compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
fi
# If no file matches, save the expanded path and continue with
@@ -411,24 +394,12 @@ for prepath in "$prepaths[@]"; do
if [[ -n "$ignpar" && -z "$_comp_no_ignore" &&
"$tpre$tsuf" != */* && $#tmp1 -ne 0 &&
( "$ignpar" != *dir* || "$pats" = '*(-/)' ) &&
- ( "$ignpar" != *..* || "$tmp1" = *../* ) ]]; then
- if [[ "$ignpar" = *parent* ]]; then
- for i in ${(M)^tmp1:#*/*}(-/); do
- remt="${${i#$prepath$realpath$donepath}%/*}"
- while [[ "$remt" = */* &&
- ! "$prepath$realpath$donepath$remt" -ef "$i" ]]; do
- remt="${remt%/*}"
- done
- [[ "$remt" = */* || "$remt" -ef "$i" ]] &&
- _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" )
- done
- fi
- if [[ "$ignpar" = *pwd* ]]; then
- for i in ${^tmp1}(-/); do
- [[ "$i" -ef "$PWD" ]] && _comp_ignore=( "$_comp_ignore[@]" "${(q)i}" )
- done
- fi
- (( $#_comp_ignore && $mopts[(I)-F] )) || mopts=( "$mopts[@]" -F _comp_ignore )
+ ( "$ignpar" != *..* || "$tmp1[1]" = *../* ) ]]; then
+
+ compfiles -i tmp1 _comp_ignore "$ignpar" "$prepath$realpath$donepath"
+
+ (( $#_comp_ignore && $mopts[(I)-F] )) ||
+ mopts=( "$mopts[@]" -F _comp_ignore )
fi
# Step over to the next component, if any.
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index d6bc35a39..ecb7ca3e1 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -3023,6 +3023,295 @@ bin_compfmt(char *nam, char **args, char *ops, int func)
return 0;
}
+#define PATH_MAX2 (PATH_MAX * 2)
+
+static LinkList
+cfp_test_exact(LinkList names, char *skipped)
+{
+ char buf[PATH_MAX2 + 1], *suf, *p;
+ int l, sl, found = 0;
+ struct stat st;
+ LinkNode node;
+ LinkList ret = newlinklist();
+
+ if (!(compprefix && *compprefix) && !(compsuffix && *compsuffix))
+ return NULL;
+
+ sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) +
+ (compsuffix ? strlen(compsuffix) : 0);
+
+ if (sl > PATH_MAX2)
+ return NULL;
+
+ suf = dyncat(skipped, rembslash(dyncat(compprefix, compsuffix)));
+
+ for (node = firstnode(names); node; incnode(node)) {
+ if ((l = strlen(p = (char *) getdata(node))) && l + sl < PATH_MAX2) {
+ strcpy(buf, p);
+ strcpy(buf + l, suf);
+
+ if (!ztat(buf, &st, 0) && S_ISDIR(st.st_mode)) {
+ found = 1;
+ addlinknode(ret, dupstring(buf));
+ }
+ }
+ }
+ return (found ? ret : NULL);
+}
+
+static void
+cfp_opt_pats(char **pats, char *matcher)
+{
+ char *add, **p, *q, *t, *s;
+
+ /**** For now we don't try to improve the patterns if there are match
+ * specs. We should work on this some more...
+ *
+ * And another one: we can do this with comppatmatch if the word
+ * doesn't contain wildcards, unless the approximate matcher is
+ * active. Better: unless there is a compadd function. I.e., we
+ * need one more piece of information from the shell code, at least
+ * I'd prefer to get it from _path_files in case we find other
+ * conditions for not trying to improve patterns. */
+
+ if ((comppatmatch && *comppatmatch) || *matcher ||
+ !compprefix || !*compprefix)
+ return;
+
+ add = rembslash(compprefix);
+
+ for (p = pats; *add && (q = *p); p++) {
+ if (*q) {
+ q = dupstring(q);
+ t = q + strlen(q) - 1;
+ if (*t == ')') {
+ for (s = t--; t > q; t--)
+ if (*t == ')' || *t == '|' || *t == '~' || *t == '(')
+ break;
+ if (t != q && *t == '(')
+ *t = '\0';
+ }
+ for (; *q && *add; q++) {
+ if (*q == '\\' && q[1]) {
+ for (s = add, q++; *s && *s != *q; s++);
+ *s = '\0';
+ } else if (*q == '<') {
+ for (s = add; *s && !idigit(*s); s++);
+ *s = '\0';
+ } else if (*q == '[') {
+ int not, first = 1;
+ char *x = ++q;
+
+ if ((not = (*x == '!' || *x == '^')))
+ x++;
+ for (; *x && (first || *x != ']'); x++) {
+ if (x[1] == '-' && x[2]) {
+ char c1 = *x, c2 = x[2];
+
+ for (s = add; *s && (*x < c1 || *x > c2); s++);
+ *s = '\0';
+ } else {
+ for (s = add; *s && *s != *x; s++);
+ *s = '\0';
+ }
+ }
+ } else if (*q != '?' && *q != '*' && *q != '(' && *q != ')' &&
+ *q != '|' && *q != '~' && *q != '#') {
+ for (s = add; *s && *s != *q; s++);
+ *s = '\0';
+ }
+ }
+ }
+ }
+ if (*add) {
+ for (p = pats; *p; p++)
+ if (**p == '*')
+ *p = dyncat(add, *p);
+ }
+}
+
+static LinkList
+cfp_bld_pats(int dirs, LinkList names, char *skipped, char **pats)
+{
+ LinkList ret = newlinklist();
+ LinkNode node;
+ int ol, sl = strlen(skipped), pl, dot;
+ char **p, *o, *str;
+
+ dot = (unset(GLOBDOTS) && compprefix && *compprefix == '.');
+ for (node = firstnode(names); node; incnode(node)) {
+ ol = strlen(o = (char *) getdata(node));
+ for (p = pats; *p; p++) {
+ pl = strlen(*p);
+ str = (char *) zhalloc(ol + sl + pl + 1);
+ strcpy(str, o);
+ strcpy(str + ol, skipped);
+ strcpy(str + ol + sl, *p);
+ addlinknode(ret, str);
+ if (dot && **p != '.') {
+ str = (char *) zhalloc(ol + sl + pl + 2);
+ strcpy(str, o);
+ strcpy(str + ol, skipped);
+ str[ol + sl] = '.';
+ strcpy(str + ol + sl + 1, *p);
+ addlinknode(ret, str);
+ }
+ }
+ }
+ return ret;
+}
+
+static LinkList
+cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs)
+{
+ int add = 0;
+
+ if (*sdirs) {
+ if (!strcmp(sdirs, "yes"))
+ add = 2;
+ else if (!strcmp(sdirs, ".."))
+ add = 1;
+ }
+ if (add) {
+ LinkNode node;
+ char *s1 = dyncat(skipped, "..");
+ char *s2 = (add == 2 ? dyncat(skipped, ".") : NULL), *m;
+
+ for (node = firstnode(orig); node; incnode(node)) {
+ if (*(m = (char *) getdata(node))) {
+ addlinknode(final, dyncat((char *) getdata(node), s1));
+ if (s2)
+ addlinknode(final, dyncat((char *) getdata(node), s2));
+ }
+ }
+ }
+ return final;
+}
+
+static LinkList
+cf_pats(int dirs, LinkList names, char *skipped, char *matcher, char *sdirs,
+ char **pats)
+{
+ LinkList ret;
+ char *dpats[2];
+
+ if (dirs && (ret = cfp_test_exact(names, skipped)))
+ return cfp_add_sdirs(ret, names, skipped, sdirs);
+
+ if (dirs) {
+ dpats[0] = "*(-/)";
+ dpats[1] = NULL;
+ pats = dpats;
+ }
+ cfp_opt_pats(pats, matcher);
+
+ return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats),
+ names, skipped, sdirs);
+}
+
+static void
+cf_ignore(char **names, LinkList ign, char *style, char *path)
+{
+ int pl = strlen(path), tpar, tpwd, found;
+ struct stat nst, est, st;
+ char *n, *c, *e;
+
+ tpar = !!strstr(style, "parent");
+ if ((tpwd = !!strstr(style, "pwd")) && stat(pwd, &est))
+ tpwd = 0;
+
+ if (!tpar && !tpwd)
+ return;
+
+ for (; (n = *names); names++) {
+ if (!ztat(n, &nst, 0) && S_ISDIR(nst.st_mode)) {
+ if (tpwd && nst.st_dev == est.st_dev && nst.st_ino == est.st_ino) {
+ addlinknode(ign, bslashquote(n, NULL, 0));
+ continue;
+ }
+ if (tpar && !strncmp((c = dupstring(n)), path, pl)) {
+ found = 0;
+ while ((e = strrchr(c, '/')) && e > c + pl) {
+ *e = '\0';
+ if (!ztat(c, &st, 0) &&
+ st.st_dev == nst.st_dev && st.st_ino == nst.st_ino) {
+ found = 1;
+ break;
+ }
+ }
+ if (found || ((e = strrchr(c, '/')) && e > c + pl &&
+ !ztat(c, &st, 0) && st.st_dev == nst.st_dev &&
+ st.st_ino == nst.st_ino))
+ addlinknode(ign, bslashquote(n, NULL, 0));
+ }
+ }
+ }
+}
+
+static int
+bin_compfiles(char *nam, char **args, char *ops, int func)
+{
+ if (**args != '-') {
+ zwarnnam(nam, "missing option: %s", *args, 0);
+ return 1;
+ }
+ if (args[0][2]) {
+ zwarnnam(nam, "invalid option: %s", *args, 0);
+ return 1;
+ }
+ switch (args[0][1]) {
+ case 'p':
+ case 'P':
+ {
+ char **tmp;
+ LinkList l;
+
+ if (!args[1] || !args[2] || !args[3] || !args[4] ||
+ (args[0][1] == 'p' && !args[5])) {
+ zwarnnam(nam, "too few arguments", NULL, 0);
+ return 1;
+ }
+ if (!(tmp = getaparam(args[1]))) {
+ zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+ return 0;
+ }
+ for (l = newlinklist(); *tmp; tmp++)
+ addlinknode(l, *tmp);
+ set_list_array(args[1], cf_pats((args[0][1] == 'P'), l, args[2],
+ args[3], args[4], args + 5));
+ return 0;
+ }
+ case 'i':
+ {
+ char **tmp;
+ LinkList l;
+
+ if (!args[1] || !args[2] || !args[3] || !args[4]) {
+ zwarnnam(nam, "too few arguments", NULL, 0);
+ return 1;
+ }
+ if (args[5]) {
+ zwarnnam(nam, "too many arguments", NULL, 0);
+ return 1;
+ }
+ tmp = getaparam(args[2]);
+ l = newlinklist();
+ if (tmp)
+ for (; *tmp; tmp++)
+ addlinknode(l, *tmp);
+ if (!(tmp = getaparam(args[1]))) {
+ zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+ return 0;
+ }
+ cf_ignore(tmp, l, args[3], args[4]);
+ set_list_array(args[2], l);
+ return 0;
+ }
+ }
+ zwarnnam(nam, "invalid option: %s", *args, 0);
+ return 1;
+}
+
static struct builtin bintab[] = {
BUILTIN("compdescribe", 0, bin_compdescribe, 3, -1, 0, NULL, NULL),
BUILTIN("comparguments", 0, bin_comparguments, 1, -1, 0, NULL, NULL),
@@ -3031,6 +3320,7 @@ static struct builtin bintab[] = {
BUILTIN("comptags", 0, bin_comptags, 1, -1, 0, NULL, NULL),
BUILTIN("comptry", 0, bin_comptry, 0, -1, 0, NULL, NULL),
BUILTIN("compfmt", 0, bin_compfmt, 2, -1, 0, NULL, NULL),
+ BUILTIN("compfiles", 0, bin_compfiles, 1, -1, 0, NULL, NULL),
};