about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--Doc/Zsh/expn.yo74
-rw-r--r--Src/Modules/zftp.c10
-rw-r--r--Src/Zle/compcore.c2
-rw-r--r--Src/Zle/compctl.c4
-rw-r--r--Src/Zle/zle_main.c2
-rw-r--r--Src/Zle/zle_misc.c2
-rw-r--r--Src/exec.c9
-rw-r--r--Src/math.c2
-rw-r--r--Src/signals.c2
-rw-r--r--Src/subst.c19
-rw-r--r--Src/utils.c49
-rw-r--r--Test/D01prompt.ztst43
13 files changed, 204 insertions, 24 deletions
diff --git a/ChangeLog b/ChangeLog
index 4fddfe8f6..cbf91653e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2008-09-26  Peter Stephenson  <pws@csr.com>
+
+	* 25744: Doc/Zsh/expn.yo, Src/exec.c, Src/math.c, Src/signals.c,
+	Src/subst.c, Src/utils.c, Src/Modules/zftp.c, Src/Zle/compcore.c,
+	Src/Zle/compctl.c, Src/Zle/zle_main.c, Src/Zle/zle_misc.c,
+	Test/D01prompt.ztst: Add dynamic named directories using
+	~[<stuff>] which calls zsh_directory_name n <stuff> and
+	reverse call to look up names.  Also further simplify doshfunc()
+	interface to use flags directly from the Shfunc structure.
+
 2008-09-25  Peter Stephenson  <pws@csr.com>
 
 	* 25684: Src/prompt.c: make %x and %I consistent with
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 35f07d47f..ec528313f 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -1308,8 +1308,73 @@ The tt(PUSHD_MINUS)
 option exchanges the effects of `tt(~PLUS())' and `tt(~-)' where they are
 followed by a number.
 
-cindex(directories, named)
-cindex(named directories)
+subsect(Dynamic named directories)
+cindex(directories, named, dynamic)
+cindex(named directories, dynamicic)
+cindex(dynamic named directories)
+
+The feature described here is only available if the shell function
+tt(zsh_directory_name) exists.
+
+A `tt(~)' followed by a string var(namstr) in unquoted square brackets is
+treated specially as a dynamic directory name.  Note that the first
+unquoted closing square bracket always terminates var(namstr).  The shell
+function is passed two arguments: the string tt(n) (for name) and
+var(namstr).  It should either set the array tt(reply) to a single element
+which is the directory corresponding to the name and return status zero
+(executing an assignment as the last statement is usually sufficient), or
+it should return status non-zero.  In the former case the element of reply
+is used as the directory; in the latter case the substitution is deemed to
+have failed and tt(NOMATCH) handling is applied if the option is set.
+
+The function tt(zsh_directory_name) is also used to see if a directory can
+be turned into a name, for example when printing the directory stack or
+when expanding tt(%~) in prompts.  In this case the function is passed two
+arguments: the string tt(d) (for directory) and the candidate for dynamic
+naming.  The function should either return non-zero status, if the
+directory cannot be named by the function, or it should set the array reply
+to consist of two elements: the first is the dynamic name for the directory
+(as would appear within `tt(~[)var(...)tt(])'), and the second is the
+prefix length of the directory to be replaced.  For example, if the trial
+directory is tt(/home/myname/src/zsh) and the dynamic name for
+tt(/home/myname/src) (which has 16 characters) is tt(s), then the function
+sets
+
+example(reply=(s 16))
+
+The directory name so returned is compared with possible static names for
+parts of the directory path, as described below; it is used if the prefix
+length matched (16 in the example) is longer than that matched by any
+static name.
+
+As a working example, here is a function that expands any dynamic names
+beginning with the string tt(p:) to directories below
+tt(/home/pws/perforce).  In this simple case a static name for the
+directory would be just as effective.
+
+example(zsh_directory_name() {
+  emulate -L zsh
+  setopt extendedglob
+  local -a match mbegin mend
+  if [[ $1 = d ]]; then
+    if [[ $2 = (#b)(/home/pws/perforce/)([^/]##)* ]]; then
+      typeset -ga reply
+      reply=(p:$match[2] $(( ${#match[1]} + ${#match[2]} )) )
+    else
+      return 1
+    fi
+  else
+    [[ $2 != (#b)p:(?*) ]] && return 1
+    typeset -ga reply
+    reply=(/home/pws/perforce/$match[1])
+  fi
+  return 0
+})
+
+subsect(Static named directories)
+cindex(directories, named, static)
+cindex(named directories, static)
+cindex(static named directories)
 A `tt(~)' followed by anything not already covered is looked up as a
 named directory, and replaced by the value of that named directory if found.
 Named directories are typically home directories for users on the system.
@@ -1329,6 +1394,8 @@ with ties broken in favour of using a named directory,
 except when the directory is tt(/) itself.  The parameters tt($PWD) and
 tt($OLDPWD) are never abbreviated in this fashion.
 
+subsect(`=' expansion)
+
 If a word begins with an unquoted `tt(=)'
 and the tt(EQUALS) option is set,
 the remainder of the word is taken as the
@@ -1336,6 +1403,8 @@ name of a command.  If a command
 exists by that name, the word is replaced
 by the full pathname of the command.
 
+subsect(Notes)
+
 Filename expansion is performed on the right hand side of a parameter
 assignment, including those appearing after commands of the
 tt(typeset) family.  In this case, the right hand side will be treated
@@ -1349,6 +1418,7 @@ If the option tt(MAGIC_EQUAL_SUBST) is set, any unquoted shell
 argument in the form `var(identifier)tt(=)var(expression)' becomes eligible
 for file expansion as described in the previous paragraph.  Quoting the
 first `tt(=)' also inhibits this.
+
 texinode(Filename Generation)()(Filename Expansion)(Expansion)
 sect(Filename Generation)
 cindex(filename generation)
diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c
index 0d5e58e56..12a9f0de2 100644
--- a/Src/Modules/zftp.c
+++ b/Src/Modules/zftp.c
@@ -1480,7 +1480,7 @@ zfsenddata(char *name, int recv, int progress, off_t startat)
 	int osc = sfcontext;
 
 	sfcontext = SFC_HOOK;
-	doshfunc(shfunc, NULL, 0, 1);
+	doshfunc(shfunc, NULL, 1);
 	sfcontext = osc;
 	/* Now add in the bit of the file we've got/sent already */
 	sofar = last_sofar = startat;
@@ -1613,7 +1613,7 @@ zfsenddata(char *name, int recv, int progress, off_t startat)
 
 	    zfsetparam("ZFTP_COUNT", &sofar, ZFPM_READONLY|ZFPM_INTEGER);
 	    sfcontext = SFC_HOOK;
-	    doshfunc(shfunc, NULL, 0, 1);
+	    doshfunc(shfunc, NULL, 1);
 	    sfcontext = osc;
 	    last_sofar = sofar;
 	}
@@ -2395,7 +2395,7 @@ zfgetcwd(void)
 	int osc = sfcontext;
 
 	sfcontext = SFC_HOOK;
-	doshfunc(shfunc, NULL, 0, 1);
+	doshfunc(shfunc, NULL, 1);
 	sfcontext = osc;
     }
     return 0;
@@ -2615,7 +2615,7 @@ zftp_getput(char *name, char **args, int flags)
 	    zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"),
 		       ZFPM_READONLY);
 	    sfcontext = SFC_HOOK;
-	    doshfunc(shfunc, NULL, 0, 1);
+	    doshfunc(shfunc, NULL, 1);
 	    sfcontext = osc;
 	}
 	if (rest) {
@@ -2770,7 +2770,7 @@ zfclose(int leaveparams)
 	    int osc = sfcontext;
 
 	    sfcontext = SFC_HOOK;
-	    doshfunc(shfunc, NULL, 0, 1);
+	    doshfunc(shfunc, NULL, 1);
 	    sfcontext = osc;
 	}
     }
diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c
index 9f97779ff..529537d37 100644
--- a/Src/Zle/compcore.c
+++ b/Src/Zle/compcore.c
@@ -814,7 +814,7 @@ callcompfunc(char *s, char *fn)
 		while (*p)
 		    addlinknode(largs, dupstring(*p++));
 	    }
-	    doshfunc(shfunc, largs, 0, 0);
+	    doshfunc(shfunc, largs, 0);
 	    cfret = lastval;
 	    lastval = olv;
 	} OLDHEAPS;
diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c
index 9d03635ff..58c81c2c8 100644
--- a/Src/Zle/compctl.c
+++ b/Src/Zle/compctl.c
@@ -3664,7 +3664,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
 	    /* Call the function. */
-	    doshfunc(shfunc, args, 0, 1);
+	    doshfunc(shfunc, args, 1);
 	    sfcontext = osc;
 	    incompctlfunc = 0;
 	    /* And get the result from the reply parameter. */
@@ -3839,7 +3839,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
 	    if (incompfunc != 1)
 		incompctlfunc = 1;
 	    sfcontext = SFC_COMPLETE;
-	    doshfunc(shfunc, args, 0, 1);
+	    doshfunc(shfunc, args, 1);
 	    sfcontext = osc;
 	    incompctlfunc = 0;
 	    uv = "reply";
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 9e2edb956..093160808 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1330,7 +1330,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
 	    makezleparams(0);
 	    sfcontext = SFC_WIDGET;
 	    opts[XTRACE] = 0;
-	    ret = doshfunc(shf, largs, shf->node.flags, 1);
+	    ret = doshfunc(shf, largs, 1);
 	    opts[XTRACE] = oxt;
 	    sfcontext = osc;
 	    endparamscope();
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index c34db2970..738e7b8c4 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -1384,7 +1384,7 @@ iremovesuffix(ZLE_INT_T c, int keep)
 	    startparamscope();
 	    makezleparams(0);
 	    sfcontext = SFC_COMPLETE;
-	    doshfunc(shfunc, args, 0, 1);
+	    doshfunc(shfunc, args, 1);
 	    sfcontext = osc;
 	    endparamscope();
 
diff --git a/Src/exec.c b/Src/exec.c
index ad6f9955d..36e381eaa 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -518,7 +518,7 @@ commandnotfound(char *arg0, LinkList args)
 	return 127;
 
     pushnode(args, arg0);
-    return doshfunc(shf, args, shf->node.flags, 1);
+    return doshfunc(shf, args, 1);
 }
 
 /* execute an external command */
@@ -4064,7 +4064,7 @@ execshfunc(Shfunc shf, LinkList args)
     cmdsp = 0;
     if ((osfc = sfcontext) == SFC_NONE)
 	sfcontext = SFC_DIRECT;
-    doshfunc(shf, args, shf->node.flags, 0);
+    doshfunc(shf, args, 0);
     sfcontext = osfc;
     free(cmdstack);
     cmdstack = ocs;
@@ -4191,8 +4191,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
  * in which the first element is the function name (even if
  * FUNCTIONARGZERO is set as this is handled inside this function).
  *
- * flags are a set of the PM_ flags associated with the function.
- *
  * If noreturnval is nonzero, then reset the current return
  * value (lastval) to its value before the shell function
  * was executed.  However, in any case return the status value
@@ -4202,13 +4200,14 @@ loadautofn(Shfunc shf, int fksh, int autol)
 
 /**/
 mod_export int
-doshfunc(Shfunc shfunc, LinkList doshargs, int flags, int noreturnval)
+doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
 {
     char **tab, **x, *oargv0;
     int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
     int *oldpipestats = NULL;
     char saveopts[OPT_SIZE], *oldscriptname = scriptname;
     char *name = shfunc->node.nam;
+    int flags = shfunc->node.flags;
     char *fname = dupstring(name);
     int obreaks, saveemulation ;
     Eprog prog;
diff --git a/Src/math.c b/Src/math.c
index 78f7572a5..241445979 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -872,7 +872,7 @@ callmathfunc(char *o)
 			if (!shfunc)
 			    zerr("no such function: %s", shfnam);
 			else {
-			    doshfunc(shfunc, l, 0, 1);
+			    doshfunc(shfunc, l, 1);
 			    return lastmathval;
 			}
 		    } else {
diff --git a/Src/signals.c b/Src/signals.c
index ba5777524..2cedeb2b9 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -1160,7 +1160,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
 	trapisfunc = isfunc = 1;
 
 	sfcontext = SFC_SIGNAL;
-	doshfunc((Shfunc)sigfn, args, 0, 1);
+	doshfunc((Shfunc)sigfn, args, 1);
 	sfcontext = osc;
 	freelinklist(args, (FreeFunc) NULL);
 	zsfree(name);
diff --git a/Src/subst.c b/Src/subst.c
index 6c3487e9a..caa2ecfda 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -528,7 +528,8 @@ filesubstr(char **namptr, int assign)
     char *str = *namptr;
 
     if (*str == Tilde && str[1] != '=' && str[1] != Equals) {
-	char *ptr;
+	Shfunc dirfunc;
+	char *ptr, *tmp, *res;
 	int val;
 
 	val = zstrtol(str + 1, &ptr, 10);
@@ -539,9 +540,23 @@ filesubstr(char **namptr, int assign)
 	    *namptr = dyncat(pwd, str + 2);
 	    return 1;
 	} else if (str[1] == '-' && isend(str[2])) {   /* ~- */
-	    char *tmp;
 	    *namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2);
 	    return 1;
+	} else if (str[1] == Inbrack &&
+		   (dirfunc = getshfunc("zsh_directory_name")) &&
+		   (ptr = strchr(str+2, Outbrack))) {
+	    char **arr;
+	    untokenize(tmp = dupstrpfx(str+2, ptr - (str+2)));
+	    remnulargs(tmp);
+	    arr = subst_string_by_func(dirfunc, "n", tmp);
+	    res = arr ? *arr : NULL;
+	    if (res) {
+		*namptr = dyncat(res, ptr+1);
+		return 1;
+	    }
+	    if (isset(NOMATCH))
+		zerr("no directory expansion: ~[%s]", tmp);
+	    return 0;
 	} else if (!inblank(str[1]) && isend(*ptr) &&
 		   (!idigit(str[1]) || (ptr - str < 4))) {
 	    char *ds;
diff --git a/Src/utils.c b/Src/utils.c
index 748c62920..24a643ef2 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -826,6 +826,7 @@ finddir(char *s)
 {
     static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 };
     static int ffsz;
+    Shfunc func = getshfunc("zsh_directory_name");
 
     /* Invalidate directory cache if argument is NULL.  This is called *
      * whenever a node is added to or removed from the hash table, and *
@@ -841,7 +842,8 @@ finddir(char *s)
 	return finddir_last = NULL;
     }
 
-    if(!strcmp(s, finddir_full) && *finddir_full)
+    /* It's not safe to use the cache while we have function transformations.*/
+    if(!func && !strcmp(s, finddir_full) && *finddir_full)
 	return finddir_last;
 
     if ((int)strlen(s) >= ffsz) {
@@ -853,6 +855,21 @@ finddir(char *s)
     finddir_last=NULL;
     finddir_scan(&homenode.node, 0);
     scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 0);
+
+    if (func) {
+	char **ares = subst_string_by_func(func, "d", finddir_full);
+	int len;
+	if (ares && arrlen(ares) >= 2 &&
+	    (len = (int)zstrtol(ares[1], NULL, 10)) > finddir_best) {
+	    /* better duplicate this string since it's come from REPLY */
+	    finddir_last = (Nameddir)hcalloc(sizeof(struct nameddir));
+	    finddir_last->node.nam = tricat("[", dupstring(ares[0]), "]");
+	    finddir_last->dir = dupstrpfx(finddir_full, len);
+	    finddir_last->diff = len - strlen(finddir_last->node.nam);
+	    finddir_best = len;
+	}
+    }
+
     return finddir_last;
 }
 
@@ -1146,7 +1163,7 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
     sfcontext = SFC_HOOK;
 
     if ((shfunc = getshfunc(name))) {
-	ret = doshfunc(shfunc, lnklst, 0, 1);
+	ret = doshfunc(shfunc, lnklst, 1);
 	stat = 0;
     }
 
@@ -1162,7 +1179,7 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
 	if ((arrptr = getaparam(arrnam))) {
 	    for (; *arrptr; arrptr++) {
 		if ((shfunc = getshfunc(*arrptr))) {
-		    int newret = doshfunc(shfunc, lnklst, 0, 1);
+		    int newret = doshfunc(shfunc, lnklst, 1);
 		    if (!ret)
 			ret = newret;
 		    stat = 0;
@@ -2901,6 +2918,32 @@ getshfunc(char *nam)
     return (Shfunc) shfunctab->getnode(shfunctab, nam);
 }
 
+/*
+ * Call the function func to substitute string orig by setting
+ * the parameter reply.
+ * Return the array from reply, or NULL if the function returned
+ * non-zero status.
+ * The returned value comes directly from the parameter and
+ * so should be used before there is any chance of that
+ * being changed or unset.
+ * If arg1 is not NULL, it is used as an initial argument to
+ * the function, with the original string as the second argument.
+ */
+
+/**/
+char **
+subst_string_by_func(Shfunc func, char *arg1, char *orig)
+{
+    LinkList l = newlinklist();
+    addlinknode(l, func->node.nam);
+    if (arg1)
+	addlinknode(l, arg1);
+    addlinknode(l, orig);
+    if (doshfunc(func, l, 1))
+	return NULL;
+    return getaparam("reply");
+}
+
 /**/
 mod_export char **
 mkarray(char *s)
diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 361847aa1..84f9c7cbe 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -1,5 +1,7 @@
 %prep
 
+  mkdir prompt.tmp
+  cd prompt.tmp
   mydir=$PWD
   SHLVL=2
   setopt extendedglob
@@ -104,3 +106,44 @@
     print "Years do not agree in $date2, $date3"
   fi
 0:Dates produced by prompt escapes
+
+  mkdir foo
+  mkdir foo/bar
+  mkdir foo/bar/rod
+  (zsh_directory_name() {
+    emulate -L zsh
+    setopt extendedglob
+    local -a match mbegin mend
+    if [[ $1 = d ]]; then
+      if [[ $2 = (#b)(*bar)/rod ]]; then
+        reply=(barmy ${#match[1]})
+      else
+        return 1
+      fi
+    else
+      if [[ $2 = barmy ]]; then
+        reply=($mydir/foo/bar)
+      else
+        return 1
+      fi
+    fi
+  }
+  # success
+  print ~[barmy]/anything
+  cd foo/bar/rod
+  print -P %~
+  # failure
+  setopt nonomatch
+  print ~[scuzzy]/rubbish
+  cd ../..
+  print -P %~
+  # catastrophic failure
+  unsetopt nonomatch
+  print ~[scuzzy]/rubbish
+  )
+1q:Dynamic named directories
+>$mydir/foo/bar/anything
+>~[barmy]/rod
+>~[scuzzy]/rubbish
+>~mydir/foo
+?(eval):33: no directory expansion: ~[scuzzy]