summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Completion/Core/_path_files58
-rw-r--r--Doc/Zsh/compsys.yo18
-rw-r--r--Src/Zle/compcore.c4
-rw-r--r--Src/Zle/computil.c203
5 files changed, 233 insertions, 54 deletions
diff --git a/ChangeLog b/ChangeLog
index 7d5eb22b2..18c8fe9c2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,10 @@
 
 2000-06-19  Sven Wischnowsky  <wischnow@zsh.org>
 
+	* 11971: Completion/Core/_path_files, Doc/Zsh/compsys.yo,
+ 	Src/Zle/compcore.c, Src/Zle/computil.c: improve filename
+ 	completion; use accept-exact for in-path completion; new fake style
+	
 	* users/3188: Completion/Core/_description, Completion/Core/_setup,
  	Doc/Zsh/compsys.yo: restore ZLS_COLORS when possible; better
  	group-name handling in ZLS_COLORS
diff --git a/Completion/Core/_path_files b/Completion/Core/_path_files
index 7f6f83184..250f6c750 100644
--- a/Completion/Core/_path_files
+++ b/Completion/Core/_path_files
@@ -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 @@ else
   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 @@ for prepath in "$prepaths[@]"; do
     # 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 @@ for prepath in "$prepaths[@]"; do
   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'.
+    # The return value is non-zero if the component is ambiguous.
 
-    if [[ "$tmp3" = */* ]]; then
-      tmp4=( "${(@M)tmp1:#${tmp3%%/*}/*}" )
-      (( $#tmp4 )) && tmp1=( "$tmp4[@]" )
-    fi
-
-    # 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%%/*}"
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 82a9fd5fe..2afd505fc 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -780,6 +780,13 @@ matches.  If it is set to `true' for at least one match which is the
 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
@@ -968,6 +975,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))(
 In most places where filenames are completed, the function tt(_files)
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 7497d03b4..62d63000a 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -1914,8 +1914,8 @@ addmatches(Cadata dat, char **argv)
 	    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);
diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c
index 51a1f61aa..3955ed69d 100644
--- a/Src/Zle/computil.c
+++ b/Src/Zle/computil.c
@@ -3058,17 +3058,37 @@ bin_compfmt(char *nam, char **args, char *ops, int func)
 #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 @@ cfp_test_exact(LinkList names, char *skipped)
     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) && S_ISDIR(st.st_mode)) {
+	    if (!ztat(buf, &st, 0)) {
+		if (alist) {
+		    LinkNode anode;
+
+		    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 @@ cfp_bld_pats(int dirs, LinkList names, char *skipped, char **pats)
 }
 
 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,10 +3383,62 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs)
 	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;
+			}
+		    }
+		}
 	    }
 	}
     }
@@ -3361,14 +3446,14 @@ cfp_add_sdirs(LinkList final, LinkList orig, char *skipped, char *sdirs)
 }
 
 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 @@ cf_pats(int dirs, int noopt, LinkList names, char *skipped, char *matcher,
 	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 @@ cf_ignore(char **names, LinkList ign, char *style, char *path)
     }
 }
 
+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 @@ bin_compfiles(char *nam, char **args, char *ops, int func)
 	    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 @@ bin_compfiles(char *nam, char **args, char *ops, int func)
 	    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':
@@ -3483,6 +3624,28 @@ bin_compfiles(char *nam, char **args, char *ops, int func)
 	    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);
     return 1;