1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-10-30 17:50:58 +01:00

25744: dynamic named directories and further doshfunc() simplification

This commit is contained in:
Peter Stephenson 2008-09-26 09:11:27 +00:00
parent 84584ea58b
commit b2d08a2155
13 changed files with 204 additions and 24 deletions

View file

@ -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> 2008-09-25 Peter Stephenson <pws@csr.com>
* 25684: Src/prompt.c: make %x and %I consistent with * 25684: Src/prompt.c: make %x and %I consistent with

View file

@ -1308,8 +1308,73 @@ The tt(PUSHD_MINUS)
option exchanges the effects of `tt(~PLUS())' and `tt(~-)' where they are option exchanges the effects of `tt(~PLUS())' and `tt(~-)' where they are
followed by a number. followed by a number.
cindex(directories, named) subsect(Dynamic named directories)
cindex(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 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 directory, and replaced by the value of that named directory if found.
Named directories are typically home directories for users on the system. 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 except when the directory is tt(/) itself. The parameters tt($PWD) and
tt($OLDPWD) are never abbreviated in this fashion. tt($OLDPWD) are never abbreviated in this fashion.
subsect(`=' expansion)
If a word begins with an unquoted `tt(=)' If a word begins with an unquoted `tt(=)'
and the tt(EQUALS) option is set, and the tt(EQUALS) option is set,
the remainder of the word is taken as the 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 exists by that name, the word is replaced
by the full pathname of the command. by the full pathname of the command.
subsect(Notes)
Filename expansion is performed on the right hand side of a parameter Filename expansion is performed on the right hand side of a parameter
assignment, including those appearing after commands of the assignment, including those appearing after commands of the
tt(typeset) family. In this case, the right hand side will be treated 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 argument in the form `var(identifier)tt(=)var(expression)' becomes eligible
for file expansion as described in the previous paragraph. Quoting the for file expansion as described in the previous paragraph. Quoting the
first `tt(=)' also inhibits this. first `tt(=)' also inhibits this.
texinode(Filename Generation)()(Filename Expansion)(Expansion) texinode(Filename Generation)()(Filename Expansion)(Expansion)
sect(Filename Generation) sect(Filename Generation)
cindex(filename generation) cindex(filename generation)

View file

@ -1480,7 +1480,7 @@ zfsenddata(char *name, int recv, int progress, off_t startat)
int osc = sfcontext; int osc = sfcontext;
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
doshfunc(shfunc, NULL, 0, 1); doshfunc(shfunc, NULL, 1);
sfcontext = osc; sfcontext = osc;
/* Now add in the bit of the file we've got/sent already */ /* Now add in the bit of the file we've got/sent already */
sofar = last_sofar = startat; 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); zfsetparam("ZFTP_COUNT", &sofar, ZFPM_READONLY|ZFPM_INTEGER);
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
doshfunc(shfunc, NULL, 0, 1); doshfunc(shfunc, NULL, 1);
sfcontext = osc; sfcontext = osc;
last_sofar = sofar; last_sofar = sofar;
} }
@ -2395,7 +2395,7 @@ zfgetcwd(void)
int osc = sfcontext; int osc = sfcontext;
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
doshfunc(shfunc, NULL, 0, 1); doshfunc(shfunc, NULL, 1);
sfcontext = osc; sfcontext = osc;
} }
return 0; return 0;
@ -2615,7 +2615,7 @@ zftp_getput(char *name, char **args, int flags)
zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"), zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"),
ZFPM_READONLY); ZFPM_READONLY);
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
doshfunc(shfunc, NULL, 0, 1); doshfunc(shfunc, NULL, 1);
sfcontext = osc; sfcontext = osc;
} }
if (rest) { if (rest) {
@ -2770,7 +2770,7 @@ zfclose(int leaveparams)
int osc = sfcontext; int osc = sfcontext;
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
doshfunc(shfunc, NULL, 0, 1); doshfunc(shfunc, NULL, 1);
sfcontext = osc; sfcontext = osc;
} }
} }

View file

@ -814,7 +814,7 @@ callcompfunc(char *s, char *fn)
while (*p) while (*p)
addlinknode(largs, dupstring(*p++)); addlinknode(largs, dupstring(*p++));
} }
doshfunc(shfunc, largs, 0, 0); doshfunc(shfunc, largs, 0);
cfret = lastval; cfret = lastval;
lastval = olv; lastval = olv;
} OLDHEAPS; } OLDHEAPS;

View file

@ -3664,7 +3664,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
incompctlfunc = 1; incompctlfunc = 1;
sfcontext = SFC_COMPLETE; sfcontext = SFC_COMPLETE;
/* Call the function. */ /* Call the function. */
doshfunc(shfunc, args, 0, 1); doshfunc(shfunc, args, 1);
sfcontext = osc; sfcontext = osc;
incompctlfunc = 0; incompctlfunc = 0;
/* And get the result from the reply parameter. */ /* And get the result from the reply parameter. */
@ -3839,7 +3839,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd)
if (incompfunc != 1) if (incompfunc != 1)
incompctlfunc = 1; incompctlfunc = 1;
sfcontext = SFC_COMPLETE; sfcontext = SFC_COMPLETE;
doshfunc(shfunc, args, 0, 1); doshfunc(shfunc, args, 1);
sfcontext = osc; sfcontext = osc;
incompctlfunc = 0; incompctlfunc = 0;
uv = "reply"; uv = "reply";

View file

@ -1330,7 +1330,7 @@ execzlefunc(Thingy func, char **args, int set_bindk)
makezleparams(0); makezleparams(0);
sfcontext = SFC_WIDGET; sfcontext = SFC_WIDGET;
opts[XTRACE] = 0; opts[XTRACE] = 0;
ret = doshfunc(shf, largs, shf->node.flags, 1); ret = doshfunc(shf, largs, 1);
opts[XTRACE] = oxt; opts[XTRACE] = oxt;
sfcontext = osc; sfcontext = osc;
endparamscope(); endparamscope();

View file

@ -1384,7 +1384,7 @@ iremovesuffix(ZLE_INT_T c, int keep)
startparamscope(); startparamscope();
makezleparams(0); makezleparams(0);
sfcontext = SFC_COMPLETE; sfcontext = SFC_COMPLETE;
doshfunc(shfunc, args, 0, 1); doshfunc(shfunc, args, 1);
sfcontext = osc; sfcontext = osc;
endparamscope(); endparamscope();

View file

@ -518,7 +518,7 @@ commandnotfound(char *arg0, LinkList args)
return 127; return 127;
pushnode(args, arg0); pushnode(args, arg0);
return doshfunc(shf, args, shf->node.flags, 1); return doshfunc(shf, args, 1);
} }
/* execute an external command */ /* execute an external command */
@ -4064,7 +4064,7 @@ execshfunc(Shfunc shf, LinkList args)
cmdsp = 0; cmdsp = 0;
if ((osfc = sfcontext) == SFC_NONE) if ((osfc = sfcontext) == SFC_NONE)
sfcontext = SFC_DIRECT; sfcontext = SFC_DIRECT;
doshfunc(shf, args, shf->node.flags, 0); doshfunc(shf, args, 0);
sfcontext = osfc; sfcontext = osfc;
free(cmdstack); free(cmdstack);
cmdstack = ocs; cmdstack = ocs;
@ -4191,8 +4191,6 @@ loadautofn(Shfunc shf, int fksh, int autol)
* in which the first element is the function name (even if * in which the first element is the function name (even if
* FUNCTIONARGZERO is set as this is handled inside this function). * 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 * If noreturnval is nonzero, then reset the current return
* value (lastval) to its value before the shell function * value (lastval) to its value before the shell function
* was executed. However, in any case return the status value * was executed. However, in any case return the status value
@ -4202,13 +4200,14 @@ loadautofn(Shfunc shf, int fksh, int autol)
/**/ /**/
mod_export int mod_export int
doshfunc(Shfunc shfunc, LinkList doshargs, int flags, int noreturnval) doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
{ {
char **tab, **x, *oargv0; char **tab, **x, *oargv0;
int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret; int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
int *oldpipestats = NULL; int *oldpipestats = NULL;
char saveopts[OPT_SIZE], *oldscriptname = scriptname; char saveopts[OPT_SIZE], *oldscriptname = scriptname;
char *name = shfunc->node.nam; char *name = shfunc->node.nam;
int flags = shfunc->node.flags;
char *fname = dupstring(name); char *fname = dupstring(name);
int obreaks, saveemulation ; int obreaks, saveemulation ;
Eprog prog; Eprog prog;

View file

@ -872,7 +872,7 @@ callmathfunc(char *o)
if (!shfunc) if (!shfunc)
zerr("no such function: %s", shfnam); zerr("no such function: %s", shfnam);
else { else {
doshfunc(shfunc, l, 0, 1); doshfunc(shfunc, l, 1);
return lastmathval; return lastmathval;
} }
} else { } else {

View file

@ -1160,7 +1160,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
trapisfunc = isfunc = 1; trapisfunc = isfunc = 1;
sfcontext = SFC_SIGNAL; sfcontext = SFC_SIGNAL;
doshfunc((Shfunc)sigfn, args, 0, 1); doshfunc((Shfunc)sigfn, args, 1);
sfcontext = osc; sfcontext = osc;
freelinklist(args, (FreeFunc) NULL); freelinklist(args, (FreeFunc) NULL);
zsfree(name); zsfree(name);

View file

@ -528,7 +528,8 @@ filesubstr(char **namptr, int assign)
char *str = *namptr; char *str = *namptr;
if (*str == Tilde && str[1] != '=' && str[1] != Equals) { if (*str == Tilde && str[1] != '=' && str[1] != Equals) {
char *ptr; Shfunc dirfunc;
char *ptr, *tmp, *res;
int val; int val;
val = zstrtol(str + 1, &ptr, 10); val = zstrtol(str + 1, &ptr, 10);
@ -539,9 +540,23 @@ filesubstr(char **namptr, int assign)
*namptr = dyncat(pwd, str + 2); *namptr = dyncat(pwd, str + 2);
return 1; return 1;
} else if (str[1] == '-' && isend(str[2])) { /* ~- */ } else if (str[1] == '-' && isend(str[2])) { /* ~- */
char *tmp;
*namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2); *namptr = dyncat((tmp = oldpwd) ? tmp : pwd, str + 2);
return 1; 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) && } else if (!inblank(str[1]) && isend(*ptr) &&
(!idigit(str[1]) || (ptr - str < 4))) { (!idigit(str[1]) || (ptr - str < 4))) {
char *ds; char *ds;

View file

@ -826,6 +826,7 @@ finddir(char *s)
{ {
static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 }; static struct nameddir homenode = { {NULL, "", 0}, NULL, 0 };
static int ffsz; static int ffsz;
Shfunc func = getshfunc("zsh_directory_name");
/* Invalidate directory cache if argument is NULL. This is called * /* Invalidate directory cache if argument is NULL. This is called *
* whenever a node is added to or removed from the hash table, and * * whenever a node is added to or removed from the hash table, and *
@ -841,7 +842,8 @@ finddir(char *s)
return finddir_last = NULL; 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; return finddir_last;
if ((int)strlen(s) >= ffsz) { if ((int)strlen(s) >= ffsz) {
@ -853,6 +855,21 @@ finddir(char *s)
finddir_last=NULL; finddir_last=NULL;
finddir_scan(&homenode.node, 0); finddir_scan(&homenode.node, 0);
scanhashtable(nameddirtab, 0, 0, 0, finddir_scan, 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; return finddir_last;
} }
@ -1146,7 +1163,7 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
sfcontext = SFC_HOOK; sfcontext = SFC_HOOK;
if ((shfunc = getshfunc(name))) { if ((shfunc = getshfunc(name))) {
ret = doshfunc(shfunc, lnklst, 0, 1); ret = doshfunc(shfunc, lnklst, 1);
stat = 0; stat = 0;
} }
@ -1162,7 +1179,7 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
if ((arrptr = getaparam(arrnam))) { if ((arrptr = getaparam(arrnam))) {
for (; *arrptr; arrptr++) { for (; *arrptr; arrptr++) {
if ((shfunc = getshfunc(*arrptr))) { if ((shfunc = getshfunc(*arrptr))) {
int newret = doshfunc(shfunc, lnklst, 0, 1); int newret = doshfunc(shfunc, lnklst, 1);
if (!ret) if (!ret)
ret = newret; ret = newret;
stat = 0; stat = 0;
@ -2901,6 +2918,32 @@ getshfunc(char *nam)
return (Shfunc) shfunctab->getnode(shfunctab, 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 ** mod_export char **
mkarray(char *s) mkarray(char *s)

View file

@ -1,5 +1,7 @@
%prep %prep
mkdir prompt.tmp
cd prompt.tmp
mydir=$PWD mydir=$PWD
SHLVL=2 SHLVL=2
setopt extendedglob setopt extendedglob
@ -104,3 +106,44 @@
print "Years do not agree in $date2, $date3" print "Years do not agree in $date2, $date3"
fi fi
0:Dates produced by prompt escapes 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]