Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: files and paths and...
- X-seq: zsh-workers 11971
- From: Sven Wischnowsky <wischnow@xxxxxxxxxxxxxxxxxxxxxxx>
- To: zsh-workers@xxxxxxxxxxxxxx
- Subject: PATCH: files and paths and...
- Date: Mon, 19 Jun 2000 11:29:10 +0200 (MET DST)
- Mailing-list: contact zsh-workers-help@xxxxxxxxxxxxxx; run by ezmlm
I'm trying to group my patches... this one's for _path_files and
friends.
Tanaka Akira wrote:
> Accidentaly, I found that zsh dumps core as follows.
>
> Z(2):akr@flux% Src/zsh -f
> flux% bindkey -e; autoload -U compinit; compinit -D; compdef _tst tst
> flux% _tst() { _files -g -/ }
> flux% zstyle ':completion:*' ignored-patterns '*'
> flux% tst -<TAB>
> zsh: segmentation fault (core dumped) Src/zsh -f
> Z(2):akr@flux%
>
> Note that I couldn't get proper backtrace.
Missing initialisation for a string buffer when adding zero-length
matches.
The other things we were discussing:
Peter Stephenson wrote:
> Andrej wrote:
> > Even if
> > we cannot find out all drives (is it possible?) _path_files still has to
> > treat /cygdrive/?/ specially, and not try to glob it. It can also always
> > offer ``cygdrive'' for the first component (of course, if it matches
> > current prefix/suffix).
>
> Sven will have to answer for the feasibility of all of this, but...
>
> One thing that might fit our needs here and elsewhere (e.g. speedups of
> path completion when you know you don't want initial path components
> re-jigged) is a style along the lines of fix-path-prefix, only maybe with a
> better name. A number would fix that many components (`2', here), while
> something else e.g. `all' would turn off completion of earlier bits of the
> path altogether. A more generic `fix-prefix' might possibly be useful in
> certain other completions.
I decided that this is really an accept-exact thingy, applied to
in-path completion. Good idea? I mean... it really *is* about
accepting prefix paths if they exist, i.e. if there are exact matches.
So, the patch enhances the accept-exact style to take not only boolean
values, but also patterns, BUT ONLY when looked up with the `files'
tag. When one of the patterns matches the path typed so far and there
are such directories, _path_files will not try to glob them. The
change below this is, of course, that _path_files now looks up the
accept-exact style at all. One thing I couldn't decide is if
_path_files should use the setting of REC_EXACT, too. Currently it
doesn't, because if the style isn't set, but REC_EXACT is, that would
mean to behave as if the style were set to `true' and then _path_files
would skip all fully typed paths, so that if there is `foobar/yyy',
and `foo/xxx', completing `foo/y<TAB>' would not complete to
`foobar/yyy'. It's easy to make it use REC_EXACT, though. Should we?
The foo/foobar thing is the example I used to say that we can't always
accept path prefixes, so the patch takes it back, or, more precisely,
replaces it with that accep-exact thing.
Then there is the other thing:
> While this may be useful, I meant something different. I'd prefer if
> /c/d/t would still complete to /cygdrive/d/temp. I meant, that
> _path_files (BTW I agree that it already deserves to be converted to
> shell code. It may even give old compctl the ability to complete paths)
I suggested adding a way to `fake' matches and the patch adds the
`fake' style (is that a good name? I used a rather generic name
because there may be other places where something like this might
become useful). It's values are of the form `dir:names...', which
means that when completing inside directory `dir', completion will
also complete to the `names'. So, if I got that cygwin stuff right,
one could do:
zstyle '*:files' fake '/:cygdrive' '/cygdrive:a b c...'
Does that work?
I haven't yet tried to use this mechanism to automatically complete
automounted directories, but I think this would be a nice use. This
should probably be detected and done automatically, I think.
And then the patch also contains the optimisations I already talked
about but had forgotten to include in the patches I committed.
Bye
Sven
Index: Completion/Core/_path_files
===================================================================
RCS file: /cvsroot/zsh/zsh/Completion/Core/_path_files,v
retrieving revision 1.21
diff -u -r1.21 _path_files
--- Completion/Core/_path_files 2000/06/13 09:05:37 1.21
+++ Completion/Core/_path_files 2000/06/19 09:28:41
@@ -6,7 +6,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 sopt gopt opt sdirs ignpar cfopt
-local nm=$compstate[nmatches] menu matcher mopts sort match mid
+local nm=$compstate[nmatches] menu matcher mopts sort match mid accex fake
typeset -U prepaths exppaths
@@ -139,12 +139,14 @@
skips='((.|..)/)##'
fi
-zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs &&
- [[ "$sdirs" = (yes|true|on|1) ]] && sdirs=yes
+zstyle -s ":completion:${curcontext}:paths" special-dirs sdirs
[[ "$pats" = ((|*[[:blank:]])\*(|[[:blank:]]*)|*\([^[:blank:]]#/[^[:blank:]]#\)*) ]] &&
sopt=$sopt/
+zstyle -a ":completion:${curcontext}:files" accept-exact accex
+zstyle -a ":completion:${curcontext}:files" fake fake
+
zstyle -s ":completion:${curcontext}:files" ignore-parents ignpar
if [[ -n "$compstate[pattern_match]" &&
@@ -314,33 +316,33 @@
# Get the matching files by globbing.
if [[ "$tpre$tsuf" = */* ]]; then
- compfiles -P$cfopt tmp1 "$skipped" "$_matcher" "$sdirs"
+ compfiles -P$cfopt tmp1 accex "$skipped" "$_matcher" "$sdirs" fake
elif [[ "$sopt" = *[/f]* ]]; then
- compfiles -p$cfopt tmp1 "$skipped" "$_matcher" "$sdirs" "$pats[@]"
+ compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher" "$sdirs" fake "$pats[@]"
else
- compfiles -p$cfopt tmp1 "$skipped" "$_matcher" '' "$pats[@]"
+ compfiles -p$cfopt tmp1 accex "$skipped" "$_matcher" '' fake "$pats[@]"
fi
tmp1=( $~tmp1 )
if [[ -n "$PREFIX$SUFFIX" ]]; then
# See which of them match what's on the line.
- if [[ -n "$_comp_correct" ]]; then
- tmp2=( "$tmp1[@]" )
- builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
-
- if [[ $#tmp1 -eq 0 ]]; then
- tmp1=( "$tmp2[@]" )
- compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}"
- fi
- else
- if [[ "$tmp1[1]" = */* ]]; then
+ if [[ "$tmp1[1]" = */* ]]; then
+ if [[ -n "$_comp_correct" ]]; then
tmp2=( "$tmp1[@]" )
+ builtin compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
+
+ if [[ $#tmp1 -eq 0 ]]; then
+ tmp1=( "$tmp2[@]" )
+ compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp2:t}"
+ fi
else
- tmp2=( '' )
+ tmp2=( "$tmp1[@]" )
+ compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
fi
-
- compadd -D tmp1 -F _comp_ignore "$matcher[@]" - "${(@)tmp1:t}"
+ else
+ tmp2=( '' )
+ compadd -D tmp1 -F _comp_ignore "$matcher[@]" -a tmp1
fi
# If no file matches, save the expanded path and continue with
@@ -431,26 +433,18 @@
tmp3="$pre$suf"
tpre="$pre"
tsuf="$suf"
- tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" )
+ [[ -n "${prepath}${realpath}${testpath}" ]] &&
+ tmp1=( "${(@)tmp1#${prepath}${realpath}${testpath}}" )
while true; do
# First we check if some of the files match the original string
# for this component. If there are some we remove all other
# names. This avoids having `foo' complete to `foo' and `foobar'.
-
- if [[ "$tmp3" = */* ]]; then
- tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" )
- (( $#tmp4 )) && tmp1=( "$tmp4[@]" )
- fi
+ # The return value is non-zero if the component is ambiguous.
- # Next we see if this component is ambiguous.
-
- if [[ "$tmp3" = */* ]]; then
- tmp4=$tmp1[(I)^${${tmp1[1]%%/*}//(#b)([][\\<>(|)^#~*?])/\\$match[1]}/*]
- else
- tmp4=$tmp1[(I)^${tmp1[1]//(#b)([][\\<>(|)^#~*?])/\\$match[1]}]
- fi
+ compfiles -r tmp1 "$tmp3"
+ tmp4=$?
if [[ "$tpre" = */* ]]; then
tmp2="${cpre}${tpre%%/*}"
Index: Doc/Zsh/compsys.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/compsys.yo,v
retrieving revision 1.68
diff -u -r1.68 compsys.yo
--- Doc/Zsh/compsys.yo 2000/06/19 08:47:44 1.68
+++ Doc/Zsh/compsys.yo 2000/06/19 09:28:43
@@ -780,6 +780,13 @@
same as the string on the line, this match will immediately be
accepted.
+When completing filenames (where it is looked up for the tt(files)
+tag), this style also accepts any number of patterns as the value. If
+this is used, pathnames matching one of these patterns will be
+accepted immediately even if the command line contains some more
+partially typed pathname components and these match no file under the
+directory accepted.
+
Note that this is also used by the tt(_expand) completer to decide if
words beginning with a tilde or parameter expansion should be
expanded. This means that if, for example, there are parameters
@@ -967,6 +974,17 @@
generated this way (e.g. due to the option tt(AUTO_MENU) being set),
this will also cycle through the names of the files in pathname
components after the first ambiguous one.
+)
+kindex(fake, completion style)
+item(tt(fake))(
+Currently, this style is only used when completing files and lookup up
+with the tag tt(files). Its values are of the form
+`var(dir)tt(:)var(names...)'. This will add the var(names) as
+possible matches when completing in the directory var(dir), even if no
+such files really exist.
+
+This can be useful on systems that support special filesystems whose
+top-level pathnames can not be listed or generated with glob patterns.
)
kindex(file-patterns, completion style)
item(tt(file-patterns))(
Index: Src/Zle/compcore.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compcore.c,v
retrieving revision 1.31
diff -u -r1.31 compcore.c
--- Src/Zle/compcore.c 2000/06/09 11:14:34 1.31
+++ Src/Zle/compcore.c 2000/06/19 09:28:44
@@ -1914,8 +1914,8 @@
if (aign || pign) {
int il = ppl + sl + psl, addit = 1;
- if (il > ilen)
- ibuf = (char *) zhalloc((ilen = il) + 1);
+ if (il + 1> ilen)
+ ibuf = (char *) zhalloc((ilen = il) + 2);
if (ppl)
memcpy(ibuf, dat->ppre, ppl);
Index: Src/Zle/computil.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/computil.c,v
retrieving revision 1.30
diff -u -r1.30 computil.c
--- Src/Zle/computil.c 2000/06/15 08:09:09 1.30
+++ Src/Zle/computil.c 2000/06/19 09:28:45
@@ -3058,17 +3058,37 @@
#define PATH_MAX2 (PATH_MAX * 2)
static LinkList
-cfp_test_exact(LinkList names, char *skipped)
+cfp_test_exact(LinkList names, char **accept, char *skipped)
{
char buf[PATH_MAX2 + 1], *suf, *p;
int l, sl, found = 0;
struct stat st;
LinkNode node;
- LinkList ret = newlinklist();
+ LinkList ret = newlinklist(), alist = NULL;
- if (!(compprefix && *compprefix) && !(compsuffix && *compsuffix))
+ if ((!(compprefix && *compprefix) && !(compsuffix && *compsuffix)) ||
+ (!accept || !*accept ||
+ ((!strcmp(*accept, "false") || !strcmp(*accept, "no") ||
+ !strcmp(*accept, "off") || !strcmp(*accept, "0")) && !accept[1])))
return NULL;
+ if (accept[1] ||
+ (strcmp(*accept, "true") && strcmp(*accept, "yes") &&
+ strcmp(*accept, "on") && strcmp(*accept, "1"))) {
+ Patprog prog;
+
+ alist = newlinklist();
+
+ for (; (p = *accept); accept++) {
+ if (*p == '*' && !p[1]) {
+ alist = NULL;
+ break;
+ }
+ tokenize(p = dupstring(p));
+ if ((prog = patcompile(p, 0, NULL)))
+ addlinknode(alist, prog);
+ }
+ }
sl = strlen(skipped) + (compprefix ? strlen(compprefix) : 0) +
(compsuffix ? strlen(compsuffix) : 0);
@@ -3078,11 +3098,22 @@
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) {
+ l = strlen(p = (char *) getdata(node));
+ if (l + sl < PATH_MAX2) {
strcpy(buf, p);
strcpy(buf + l, suf);
+
+ if (!ztat(buf, &st, 0)) {
+ if (alist) {
+ LinkNode anode;
- if (!ztat(buf, &st, 0) && S_ISDIR(st.st_mode)) {
+ for (anode = firstnode(alist); anode; incnode(anode))
+ if (pattry((Patprog) getdata(anode), buf))
+ break;
+
+ if (!anode)
+ continue;
+ }
found = 1;
addlinknode(ret, dupstring(buf));
}
@@ -3334,12 +3365,14 @@
}
static LinkList
-cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs)
+cfp_add_sdirs(LinkList final, LinkList orig, char *skipped,
+ char *sdirs, char **fake)
{
int add = 0;
- if (*sdirs) {
- if (!strcmp(sdirs, "yes"))
+ if (*sdirs && (isset(GLOBDOTS) || (compprefix && *compprefix == '.'))) {
+ if (!strcmp(sdirs, "yes") || !strcmp(sdirs, "true") ||
+ !strcmp(sdirs, "on") || !strcmp(sdirs, "1"))
add = 2;
else if (!strcmp(sdirs, ".."))
add = 1;
@@ -3350,25 +3383,77 @@
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 ((m = (char *) getdata(node))) {
+ addlinknode(final, dyncat(m, s1));
if (s2)
- addlinknode(final, dyncat((char *) getdata(node), s2));
+ addlinknode(final, dyncat(m, s2));
}
}
}
+ if (fake && *fake) {
+ LinkNode node;
+ char *m, *f, *p, *t, *a, c;
+ int sl = strlen(skipped) + 1;
+ struct stat st1, st2;
+
+ for (; (f = *fake); fake++) {
+ f = dupstring(f);
+ for (p = t = f; *p; p++) {
+ if (*p == ':')
+ break;
+ else if (*p == '\\' && p[1])
+ p++;
+ *t++ = *p;
+ }
+ if (*p) {
+ *t = *p++ = '\0';
+ if (!*p)
+ continue;
+
+ for (node = firstnode(orig); node; incnode(node)) {
+ if ((m = (char *) getdata(node)) &&
+ (!strcmp(f, m) ||
+ (!stat(f, &st1) && !stat((*m ? m : "."), &st2) &&
+ st1.st_dev == st2.st_dev &&
+ st1.st_ino == st2.st_ino))) {
+ while (*p) {
+ while (*p && inblank(*p))
+ p++;
+ if (!*p)
+ break;
+ for (f = t = p; *p; p++) {
+ if (inblank(*p))
+ break;
+ else if (*p == '\\' && p[1])
+ p++;
+ *t++ = *p;
+ }
+ c = *t;
+ *t = '\0';
+ a = (char *) zhalloc(strlen(m) + sl + strlen(f));
+ strcpy(a, m);
+ strcat(a, skipped);
+ strcat(a, f);
+ addlinknode(final, a);
+ *t = c;
+ }
+ }
+ }
+ }
+ }
+ }
return final;
}
static LinkList
-cf_pats(int dirs, int noopt, LinkList names, char *skipped, char *matcher,
- char *sdirs, char **pats)
+cf_pats(int dirs, int noopt, LinkList names, char **accept, char *skipped,
+ char *matcher, char *sdirs, char **fake, char **pats)
{
LinkList ret;
char *dpats[2];
- if (dirs && (ret = cfp_test_exact(names, skipped)))
- return cfp_add_sdirs(ret, names, skipped, sdirs);
+ if ((ret = cfp_test_exact(names, accept, skipped)))
+ return cfp_add_sdirs(ret, names, skipped, sdirs, fake);
if (dirs) {
dpats[0] = "*(-/)";
@@ -3379,7 +3464,7 @@
cfp_opt_pats(pats, matcher);
return cfp_add_sdirs(cfp_bld_pats(dirs, names, skipped, pats),
- names, skipped, sdirs);
+ names, skipped, sdirs, fake);
}
static void
@@ -3421,6 +3506,61 @@
}
}
+static LinkList
+cf_remove_other(char **names, char *pre, int *amb)
+{
+ char *p;
+
+ if ((p = strchr(pre, '/'))) {
+ char **n;
+
+ *p = '\0';
+ pre = dyncat(pre, "/");
+ *p = '/';
+
+ for (n = names; *n; n++)
+ if (strpfx(pre, *n))
+ break;
+
+ if (*n) {
+ LinkList ret = newlinklist();
+
+ for (; *names; names++)
+ if (strpfx(pre, *names))
+ addlinknode(ret, dupstring(*names));
+
+ *amb = 0;
+
+ return ret;
+ } else {
+ if (!(p = *names++))
+ *amb = 0;
+ else {
+ char *q;
+
+ if ((q = strchr((p = dupstring(p)), '/')))
+ *q = '\0';
+
+ for (; *names; names++)
+ if (!strpfx(p, *names)) {
+ *amb = 1;
+ return NULL;
+ }
+ }
+ }
+ } else {
+ if (!(p = *names++))
+ *amb = 0;
+ else
+ for (; *names; names++)
+ if (strcmp(p, *names)) {
+ *amb = 1;
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
static int
bin_compfiles(char *nam, char **args, char *ops, int func)
{
@@ -3438,8 +3578,8 @@
char **tmp;
LinkList l;
- if (!args[1] || !args[2] || !args[3] || !args[4] ||
- (args[0][1] == 'p' && !args[5])) {
+ if (!args[1] || !args[2] || !args[3] || !args[4] || !args[5] ||
+ !args[6] || (args[0][1] == 'p' && !args[7])) {
zwarnnam(nam, "too few arguments", NULL, 0);
return 1;
}
@@ -3450,8 +3590,9 @@
for (l = newlinklist(); *tmp; tmp++)
addlinknode(l, *tmp);
set_list_array(args[1], cf_pats((args[0][1] == 'P'), !!args[0][2],
- l, args[2], args[3], args[4],
- args + 5));
+ l, getaparam(args[2]), args[3],
+ args[4], args[5],
+ getaparam(args[6]), args + 7));
return 0;
}
case 'i':
@@ -3482,6 +3623,28 @@
cf_ignore(tmp, l, args[3], args[4]);
set_list_array(args[2], l);
return 0;
+ }
+ case 'r':
+ {
+ char **tmp;
+ LinkList l;
+ int ret = 0;
+
+ if (!args[1] || !args[2]) {
+ zwarnnam(nam, "too few arguments", NULL, 0);
+ return 1;
+ }
+ if (args[3]) {
+ zwarnnam(nam, "too many arguments", NULL, 0);
+ return 1;
+ }
+ if (!(tmp = getaparam(args[1]))) {
+ zwarnnam(nam, "unknown parameter: %s", args[1], 0);
+ return 0;
+ }
+ if ((l = cf_remove_other(tmp, args[2], &ret)))
+ set_list_array(args[1], l);
+ return ret;
}
}
zwarnnam(nam, "invalid option: %s", *args, 0);
--
Sven Wischnowsky wischnow@xxxxxxxxxxxxxxxxxxxxxxx
Messages sorted by:
Reverse Date,
Date,
Thread,
Author