From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9059 invoked by alias); 28 Jan 2017 19:12:59 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 40440 Received: (qmail 10671 invoked from network); 28 Jan 2017 19:12:59 -0000 X-Qmail-Scanner-Diagnostics: from know-smtprelay-omc-7.server.virginmedia.net by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(80.0.253.71):SA:0(-1.2/5.0):. Processed in 1.362211 secs); 28 Jan 2017 19:12:59 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.1 X-Envelope-From: p.w.stephenson@ntlworld.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: pass (ns1.primenet.com.au: SPF record at _smtprelay.virginmedia.com designates 80.0.253.71 as permitted sender) X-Originating-IP: [86.21.219.59] X-Spam: 0 X-Authority: v=2.1 cv=SYcKDalu c=1 sm=1 tr=0 a=utowdAHh8RITBM/6U1BPxA==:117 a=utowdAHh8RITBM/6U1BPxA==:17 a=L9H7d07YOLsA:10 a=9cW_t1CCXrUA:10 a=s5jvgZ67dGcA:10 a=kj9zAlcOel0A:10 a=MWUjAzoEKyAA:10 a=q2GGsy2AAAAA:8 a=Jg3u3WzmltLfkISN-lcA:9 a=6ECzIxHset0PkAa3:21 a=l4ypOOSy9246_tJ0:21 a=CjuIK1q_8ugA:10 a=z9dJwno5l634igLiVhy-:22 Date: Sat, 28 Jan 2017 19:12:48 +0000 From: Peter Stephenson To: zsh-workers@zsh.org Subject: Re: PATH: autoload with explicit path Message-ID: <20170128191248.4718b24c@ntlworld.com> In-Reply-To: References: <1485529979.987251.861563792.06CCDCAC@webmail.messagingengine.com> <20170127162440.10a5c85b@pwslap01u.europe.root.pri> <1485542415.1037033.861766968.196E6FB9@webmail.messagingengine.com> <20170127184448.5591e976@pwslap01u.europe.root.pri> X-Mailer: Claws Mail 3.11.1 (GTK+ 2.24.28; x86_64-redhat-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit On Sat, 28 Jan 2017 10:05:46 -0800 (PST) Bart Schaefer wrote: > On Fri, 27 Jan 2017, Peter Stephenson wrote: > > > I'm still not clear if your "requested feature" is what I'm talking > > about or not, i.e. what you can do already with $funcsourcetrace. > > It's not so much about what Sebastian can do as it is about what he has > to rely upon other function writers to do. > > That is, if I'm in the habit of writing "autoload thing" in the bootstrap > function for a library of things, but Sebastian wants to put my entire > thing-library in a place outside of $fpath, he can't do so without first > editing all my "autoload"s to add a full path. > > If every autoload were relative to the absolute-path information of the > calling function context, Sebastian's plan would work without needing > to edit anything, provided that he autoloads the bootstrap function with > an absolute path. I'm afraid this is just confusing matters. Sebastian was asking for a new option. There's no question of changing the default behaviour to prevent any files needing editing, that just screws up the current fpath way of looking things up. The question is how to get the function to use the right directory, not by default, but using information that's available from the current context. (It's not entirely clear to me why you can't just "autoload /wherever/it/is/*" to pick up all the files in the first place, avoiding any additional autoload, but there are enough possible ways of arranging a function suite I don't necessarily need to understand that bit.) Instead of the option, I'm pointing out that using the absolute path mechanism and the ability to find the path of the function does exactly the same thing, so yet more potentially confusing (certainly to me) options aren't actually getting you anything further. The following patch means that zmodload zsh/parameter autoload -Uz ${functions_source[func]:h}/otherfunc works, further giving you the power to decide where relative to the source of "func" the code you want is to arbitrary complexity. ($functions_source is effectively a trick to access existing information; there is no additional storage demand beyond the addition to the code itself. It's just a generalisation of the option Sebastian wanted that presents path information in parameters rather than hiding it.) I've also made it turn the directory into an absolute one when it's loaded, so that if the function was found by e.g. fpath=(.) its directory is recorded based on $PWD. That's an optional extra, but I presume it's more useful that being told the directory of a function's source was ".". The resolution already happens for autoload -r, and you might imagine that loading the file should resolve the path in the same way, so I think it's natural anyway. As this does everything necessary, I will not be adding any new options, and I've done quite enough on this feature, but if you can argue among yourselves for something else that doesn't yet work, feel free to go ahead without me. diff --git a/Doc/Zsh/mod_parameter.yo b/Doc/Zsh/mod_parameter.yo index 3d260f8..942e4c5 100644 --- a/Doc/Zsh/mod_parameter.yo +++ b/Doc/Zsh/mod_parameter.yo @@ -37,6 +37,30 @@ vindex(dis_functions) item(tt(dis_functions))( Like tt(functions) but for disabled functions. ) +vindex(functions_source) +item(tt(functions_source))( +This readonly associative array maps names of enabled functions to the +name of the file containing the source of the function. + +For an autoloaded function that has already been loaded, or marked for +autoload with an absolute path, or that has had its path resolved with +`tt(functions -r)', this is the file found for autoloading, resolved +to an absolute path. + +For a function defined within the body of a script or sourced file, +this is the name of that file. In this case, this is the exact path +originally used to that file, which may be a relative path. + +For any other function, including any defined at an interactive prompt or +an autoload function whose path has not yet been resolved, this is +the empty string. However, the hash element is reported as defined +just so long as the function is present: the keys to this hash are +the same as those to tt($funcions). +) +vindex(dis_functions_source) +item(tt(dis_functions_source))( +Like tt(functions_source) but for disabled functions. +) vindex(builtins) item(tt(builtins))( This associative array gives information about the builtin commands @@ -202,10 +226,13 @@ defined. The line number is the line where the `tt(function) var(name)' or `var(name) tt(LPAR()RPAR())' started. In the case of an autoloaded function the line number is reported as zero. The format of each element is var(filename)tt(:)var(lineno). + For functions autoloaded from a file in native zsh format, where only the body of the function occurs in the file, or for files that have been executed by the tt(source) or `tt(.)' builtins, the trace information is -shown as var(filename)tt(:)var(0), since the entire file is the definition. +shown as var(filename)tt(:)var(0), since the entire file is the +definition. The source file name is resolved to an absolute path when +the function is loaded or the path to it otherwise resolved. Most users will be interested in the information in the tt(funcfiletrace) array instead. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 98bcaba..6e62287 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -515,6 +515,98 @@ scanpmdisfunctions(HashTable ht, ScanFunc func, int flags) scanfunctions(ht, func, flags, DISABLED); } +/* Functions for the functions_source special parameter. */ + +/* Retrieve the source file for a function by explicit name */ + +/**/ +static HashNode +getfunction_source(UNUSED(HashTable ht), const char *name, int dis) +{ + Shfunc shf; + Param pm = NULL; + + pm = (Param) hcalloc(sizeof(struct param)); + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR|PM_READONLY; + pm->gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; + + if ((shf = (Shfunc) shfunctab->getnode2(shfunctab, name)) && + (dis ? (shf->node.flags & DISABLED) : !(shf->node.flags & DISABLED))) { + pm->u.str = getshfuncfile(shf); + if (!pm->u.str) + pm->u.str = dupstring(""); + } + return &pm->node; +} + +/* Retrieve the source file for functions by scanning the table */ + +/**/ +static void +scanfunctions_source(UNUSED(HashTable ht), ScanFunc func, int flags, int dis) +{ + struct param pm; + int i; + HashNode hn; + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR|PM_READONLY; + pm.gsu.s = dis ? &pmdisfunction_gsu : &pmfunction_gsu; + + for (i = 0; i < shfunctab->hsize; i++) { + for (hn = shfunctab->nodes[i]; hn; hn = hn->next) { + if (dis ? (hn->flags & DISABLED) : !(hn->flags & DISABLED)) { + pm.node.nam = hn->nam; + if (func != scancountparams && + ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || + !(flags & SCANPM_WANTKEYS))) { + pm.u.str = getshfuncfile((Shfunc)hn); + if (!pm.u.str) + pm.u.str = dupstring(""); + } + func(&pm.node, flags); + } + } + } +} + +/* Param table entry for retrieving functions_source element */ + +/**/ +static HashNode +getpmfunction_source(HashTable ht, const char *name) +{ + return getfunction_source(ht, name, 0); +} + +/* Param table entry for retrieving ds_functions_source element */ + +/**/ +static HashNode +getpmdisfunction_source(HashTable ht, const char *name) +{ + return getfunction_source(ht, name, 1); +} + +/* Param table entry for scanning functions_source table */ + +/**/ +static void +scanpmfunction_source(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions_source(ht, func, flags, 0); +} + +/* Param table entry for scanning dis_functions_source table */ + +/**/ +static void +scanpmdisfunction_source(HashTable ht, ScanFunc func, int flags) +{ + scanfunctions_source(ht, func, flags, 1); +} + /* Functions for the funcstack special parameter. */ /**/ @@ -2095,6 +2187,8 @@ static struct paramdef partab[] = { NULL, getpmdisbuiltin, scanpmdisbuiltins), SPECIALPMDEF("dis_functions", 0, &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions), + SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL, + getpmdisfunction_source, scanpmdisfunction_source), SPECIALPMDEF("dis_galiases", 0, &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases), SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY, @@ -2111,6 +2205,8 @@ static struct paramdef partab[] = { &funcstack_gsu, NULL, NULL), SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction, scanpmfunctions), + SPECIALPMDEF("functions_source", PM_READONLY, NULL, + getpmfunction_source, scanpmfunction_source), SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY, &functrace_gsu, NULL, NULL), SPECIALPMDEF("galiases", 0, diff --git a/Src/hashtable.c b/Src/hashtable.c index 1f2789d..8987c85 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -1566,6 +1566,15 @@ dircache_set(char **name, char *value) *name = NULL; } else { /* + * As the function path has been resolved to a particular + * location, we'll store it as an absolute path. + */ + if (*value != '/') { + value = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), + "/", value); + value = xsymlink(value, 1); + } + /* * We'll maintain the cache at exactly the right size rather * than overallocating. The rationale here is that typically * we'll get a lot of functions in a small number of directories diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index 5edbe26..176841d 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -325,10 +325,10 @@ printf '%s\n' 'oops(){}' 'ninjas-earring(){}' 'oops "$@"' >oops autoload oops oops - whence -v oops + whence -v oops | sed -e "s%$PWD%CURDIR%" ) 0:whence -v of zsh-style autoload ->oops is a shell function from ./oops +>oops is a shell function from CURDIR/oops ( fpath=(.) diff --git a/Test/V06parameter.ztst b/Test/V06parameter.ztst index c7df35d..c2a2a4d 100644 --- a/Test/V06parameter.ztst +++ b/Test/V06parameter.ztst @@ -1,15 +1,15 @@ %test print 'print In sourced file - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace/#$PWD/CURDIR} ' >sourcedfile print -r -- 'print Started functrace.zsh module_path=(./Modules) - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace/#$PWD/CURDIR} : fn() { print Inside function $0 - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace/#$PWD/CURDIR} } : fn @@ -17,7 +17,7 @@ fpath=(. $fpath) : echo '\''print Inside $0 - print $LINENO + $functrace + $funcsourcetrace + print $LINENO + $functrace + ${funcsourcetrace/#$PWD/CURDIR} '\'' >autofn : autoload autofn @@ -32,9 +32,9 @@ >Inside function fn >2 + ./functrace.zsh:10 + ./functrace.zsh:5 >Inside autofn ->2 + ./functrace.zsh:20 + ./autofn:0 +>2 + ./functrace.zsh:20 + CURDIR/autofn:0 >Inside autofn ->2 + ./functrace.zsh:21 + ./autofn:0 +>2 + ./functrace.zsh:21 + CURDIR/autofn:0 >In sourced file >2 + ./functrace.zsh:22 + ./sourcedfile:0 @@ -66,6 +66,19 @@ >./rocky3.zsh:13 (eval):2 >./rocky3.zsh:14 ./rocky3.zsh:14 + ( + fpath=($PWD) + print "print I have been autoloaded" >myfunc + autoload $PWD/myfunc + print ${functions_source[myfunc]/#$PWD/CURDIR} + myfunc + print ${functions_source[myfunc]/#$PWD/CURDIR} + ) +0: $functions_source +>CURDIR/myfunc +>I have been autoloaded +>CURDIR/myfunc + %clean - rm -f autofn functrace.zsh rocky3.zsh sourcedfile + rm -f autofn functrace.zsh rocky3.zsh sourcedfile myfunc