diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | Doc/Zsh/expn.yo | 74 | ||||
-rw-r--r-- | Src/Modules/zftp.c | 10 | ||||
-rw-r--r-- | Src/Zle/compcore.c | 2 | ||||
-rw-r--r-- | Src/Zle/compctl.c | 4 | ||||
-rw-r--r-- | Src/Zle/zle_main.c | 2 | ||||
-rw-r--r-- | Src/Zle/zle_misc.c | 2 | ||||
-rw-r--r-- | Src/exec.c | 9 | ||||
-rw-r--r-- | Src/math.c | 2 | ||||
-rw-r--r-- | Src/signals.c | 2 | ||||
-rw-r--r-- | Src/subst.c | 19 | ||||
-rw-r--r-- | Src/utils.c | 49 | ||||
-rw-r--r-- | Test/D01prompt.ztst | 43 |
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] |