diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | Completion/Core/_path_files | 65 | ||||
-rw-r--r-- | Src/Zle/computil.c | 290 |
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), }; |