Thanks!
This is certainly something I've wanted ever since funcstack was improved but had been holding back on.
The fretting about what letter to use after % would be reduced and if the promptsubst option also applied to PS4. In my opinion not only is it more readable and requires less memory on a users part, but is also more flexible.
On Tue, Sep 16, 2008 at 10:50 AM, Peter Stephenson
<pws@xxxxxxx> wrote:
Now we have logic for finding the source file and corresponding line
number of executed code, this adds the prompt escapes %x and %I which
are like %N and %i but for the file where the code was defined. %x isn't
ideal but upper and lower case %s, %f and %n are all used. It stands
for "execution file", or something. The idea is that you set
PS4='+%x:%I>'
While doing this, I spotted that we could improve the information
available to funcstack and the interface to doshfunc() by passing in a
Shfunc instead of an Eprog. This is a *much* cleaner interface. Now
the funcstack entry is guaranteed to get the details of the shell
function correct.
Index: Doc/Zsh/prompt.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/prompt.yo,v
retrieving revision 1.15
diff -u -r1.15 prompt.yo
--- Doc/Zsh/prompt.yo 24 Jun 2008 08:44:16 -0000 1.15
+++ Doc/Zsh/prompt.yo 16 Sep 2008 14:41:10 -0000
@@ -113,6 +113,11 @@
shell function given by tt(%N). This is most useful for debugging as part
of tt($PS4).
)
+item(tt(%I))(
+The line number currently being executed in the file tt(%x). This is
+similar to tt(%i), but the line number is always a line number in the
+file where the code was defined, even if the code is a shell function.
+)
item(tt(%j))(
The number of jobs.
)
@@ -126,6 +131,11 @@
the `tt(%)' to specify a number of trailing path components to show; zero
means the full path. A negative integer specifies leading components.
)
+item(tt(%x))(
+The name of the file containing the source code currently being
+executed. This behaves as tt(%N) except that function and eval command
+names are not shown, instead the file where they were defined.
+)
xitem(tt(%c))
xitem(tt(%.))
item(tt(%C))(
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.151
diff -u -r1.151 exec.c
--- Src/exec.c 11 Sep 2008 17:14:39 -0000 1.151
+++ Src/exec.c 16 Sep 2008 14:41:10 -0000
@@ -518,7 +518,7 @@
return 127;
pushnode(args, arg0);
- return doshfunc(shf->node.nam, shf->funcdef, args, shf->node.flags, 1);
+ return doshfunc(shf, args, shf->node.flags, 1);
}
/* execute an external command */
@@ -4064,7 +4064,7 @@
cmdsp = 0;
if ((osfc = sfcontext) == SFC_NONE)
sfcontext = SFC_DIRECT;
- doshfunc(shf->node.nam, shf->funcdef, args, shf->node.flags, 0);
+ doshfunc(shf, args, shf->node.flags, 0);
sfcontext = osfc;
free(cmdstack);
cmdstack = ocs;
@@ -4200,18 +4200,20 @@
/**/
mod_export int
-doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
+doshfunc(Shfunc shfunc, LinkList doshargs, int flags, int noreturnval)
{
char **tab, **x, *oargv0;
int oldzoptind, oldlastval, oldoptcind, oldnumpipestats, ret;
int *oldpipestats = NULL;
- char saveopts[OPT_SIZE], *oldscriptname = scriptname, *fname = dupstring(name);
+ char saveopts[OPT_SIZE], *oldscriptname = scriptname;
+ char *name = shfunc->node.nam;
+ char *fname = dupstring(name);
int obreaks, saveemulation ;
+ Eprog prog;
struct funcstack fstack;
#ifdef MAX_FUNCTION_DEPTH
static int funcdepth;
#endif
- Shfunc shf;
pushheap();
@@ -4291,14 +4293,10 @@
fstack.tp = FS_FUNC;
funcstack = &fstack;
- if ((shf = (Shfunc) shfunctab->getnode(shfunctab, name))) {
- fstack.flineno = shf->lineno;
- fstack.filename = dupstring(shf->filename);
- } else {
- fstack.flineno = 0;
- fstack.filename = dupstring(fstack.caller);
- }
+ fstack.flineno = shfunc->lineno;
+ fstack.filename = dupstring(shfunc->filename);
+ prog = shfunc->funcdef;
if (prog->flags & EF_RUN) {
Shfunc shf;
Index: Src/init.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/init.c,v
retrieving revision 1.96
diff -u -r1.96 init.c
--- Src/init.c 11 Sep 2008 17:14:39 -0000 1.96
+++ Src/init.c 16 Sep 2008 14:41:10 -0000
@@ -149,7 +149,7 @@
int toksav = tok;
if (toplevel &&
- (getshfunc("preexec") != &dummy_eprog ||
+ (getshfunc("preexec") ||
paramtab->getnode(paramtab, "preexec_functions"))) {
LinkList args;
char *cmdstr;
Index: Src/math.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/math.c,v
retrieving revision 1.33
diff -u -r1.33 math.c
--- Src/math.c 12 Jun 2008 13:45:06 -0000 1.33
+++ Src/math.c 16 Sep 2008 14:41:10 -0000
@@ -868,11 +868,11 @@
argc <= f->maxargs)) {
if (f->flags & MFF_USERFUNC) {
char *shfnam = f->module ? f->module : n;
- Eprog prog = getshfunc(shfnam);
- if (prog == &dummy_eprog)
+ Shfunc shfunc = getshfunc(shfnam);
+ if (!shfunc)
zerr("no such function: %s", shfnam);
else {
- doshfunc(n, prog, l, 0, 1);
+ doshfunc(shfunc, l, 0, 1);
return lastmathval;
}
} else {
Index: Src/prompt.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/prompt.c,v
retrieving revision 1.53
diff -u -r1.53 prompt.c
--- Src/prompt.c 15 Sep 2008 16:18:06 -0000 1.53
+++ Src/prompt.c 16 Sep 2008 14:41:11 -0000
@@ -725,11 +725,37 @@
if(Rstring)
stradd(Rstring);
break;
+ case 'I':
+ if (funcstack && funcstack->tp != FS_SOURCE) {
+ /*
+ * We're in a function or an eval with
+ * EVALLINENO. Calculate the line number in
+ * the file.
+ */
+ zlong flineno = lineno + funcstack->flineno;
+ /* take account of eval line nos. starting at 1 */
+ if (funcstack->tp == FS_EVAL)
+ lineno--;
+ addbufspc(DIGBUFSIZE);
+ sprintf(bp, "%ld", (long)flineno);
+ bp += strlen(bp);
+ break;
+ }
+ /* else we're in a file and lineno is already correct */
+ /* FALLTHROUGH */
case 'i':
addbufspc(DIGBUFSIZE);
sprintf(bp, "%ld", (long)lineno);
bp += strlen(bp);
break;
+ case 'x':
+ if (funcstack && funcstack->tp != FS_SOURCE)
+ promptpath(funcstack->filename ? funcstack->filename : "",
+ arg, 0);
+ else
+ promptpath(scriptfilename ? scriptfilename : argzero,
+ arg, 0);
+ break;
case '\0':
return 0;
case Meta:
Index: Src/signals.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/signals.c,v
retrieving revision 1.50
diff -u -r1.50 signals.c
--- Src/signals.c 11 Aug 2008 19:22:54 -0000 1.50
+++ Src/signals.c 16 Sep 2008 14:41:11 -0000
@@ -963,8 +963,7 @@
}
if (exittr) {
- dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ?
- ((Shfunc)exitfn)->funcdef : (Eprog) exitfn);
+ dotrapargs(SIGEXIT, &exittr, exitfn);
if (exittr & ZSIG_FUNC)
shfunctab->freenode((HashNode)exitfn);
else
@@ -1077,8 +1076,16 @@
/**/
int trapisfunc;
+/*
+ * sig is the signal number.
+ * *sigtr is the value to be taken as the field in sigtrapped (since
+ * that may have changed by this point if we are exiting).
+ * sigfn is an Eprog with a non-function eval list, or a Shfunc
+ * with a function trap. It may be NULL with an ignored signal.
+ */
+
/**/
-void
+static void
dotrapargs(int sig, int *sigtr, void *sigfn)
{
LinkList args;
@@ -1153,7 +1160,7 @@
trapisfunc = isfunc = 1;
sfcontext = SFC_SIGNAL;
- doshfunc(name, sigfn, args, 0, 1);
+ doshfunc((Shfunc)sigfn, args, 0, 1);
sfcontext = osc;
freelinklist(args, (FreeFunc) NULL);
zsfree(name);
@@ -1162,7 +1169,7 @@
trap_state = TRAP_STATE_PRIMED;
trapisfunc = isfunc = 0;
- execode(sigfn, 1, 0);
+ execode((Eprog)sigfn, 1, 0);
}
runhookdef(AFTERTRAPHOOK, NULL);
@@ -1215,12 +1222,12 @@
void
dotrap(int sig)
{
- Eprog funcprog;
+ void *funcprog;
if (sigtrapped[sig] & ZSIG_FUNC) {
HashNode hn = gettrapnode(sig, 0);
if (hn)
- funcprog = ((Shfunc)hn)->funcdef;
+ funcprog = hn;
else {
#ifdef DEBUG
dputs("BUG: running function trap which has escaped.");
@@ -1230,7 +1237,11 @@
} else
funcprog = siglists[sig];
- /* Copied from dotrapargs(). */
+ /*
+ * Copied from dotrapargs().
+ * (In fact, the gain from duplicating this appears to be virtually
+ * zero. Not sure why it's here.)
+ */
if ((sigtrapped[sig] & ZSIG_IGNORED) || !funcprog || errflag)
return;
Index: Src/utils.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/utils.c,v
retrieving revision 1.199
diff -u -r1.199 utils.c
--- Src/utils.c 11 Aug 2008 19:22:54 -0000 1.199
+++ Src/utils.c 16 Sep 2008 14:41:11 -0000
@@ -35,6 +35,8 @@
/**/
mod_export char *scriptname; /* is sometimes a function name */
+/* filename of script or other file containing code source e.g. autoload */
+
/**/
mod_export char *scriptfilename;
@@ -1134,7 +1136,7 @@
mod_export int
callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval)
{
- Eprog prog;
+ Shfunc shfunc;
/*
* Save stopmsg, since user doesn't get a chance to respond
* to a list of jobs generated in a hook.
@@ -1143,8 +1145,8 @@
sfcontext = SFC_HOOK;
- if ((prog = getshfunc(name)) != &dummy_eprog) {
- ret = doshfunc(name, prog, lnklst, 0, 1);
+ if ((shfunc = getshfunc(name))) {
+ ret = doshfunc(shfunc, lnklst, 0, 1);
stat = 0;
}
@@ -1159,8 +1161,8 @@
if ((arrptr = getaparam(arrnam))) {
for (; *arrptr; arrptr++) {
- if ((prog = getshfunc(*arrptr)) != &dummy_eprog) {
- int newret = doshfunc(arrnam, prog, lnklst, 0, 1);
+ if ((shfunc = getshfunc(*arrptr))) {
+ int newret = doshfunc(shfunc, lnklst, 0, 1);
if (!ret)
ret = newret;
stat = 0;
@@ -2893,15 +2895,10 @@
/* Get the definition of a shell function */
/**/
-mod_export Eprog
+mod_export Shfunc
getshfunc(char *nam)
{
- Shfunc shf;
-
- if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, nam)))
- return &dummy_eprog;
-
- return shf->funcdef;
+ return (Shfunc) shfunctab->getnode(shfunctab, nam);
}
/**/
Index: Src/Modules/zftp.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/zftp.c,v
retrieving revision 1.48
diff -u -r1.48 zftp.c
--- Src/Modules/zftp.c 4 Sep 2008 22:23:52 -0000 1.48
+++ Src/Modules/zftp.c 16 Sep 2008 14:41:11 -0000
@@ -1469,9 +1469,9 @@
char lsbuf[ZF_BUFSIZE], *ascbuf = NULL, *optr;
off_t sofar = 0, last_sofar = 0;
readwrite_t read_ptr = zfread, write_ptr = zfwrite;
- Eprog prog;
+ Shfunc shfunc;
- if (progress && (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+ if (progress && (shfunc = getshfunc("zftp_progress"))) {
/*
* progress to set up: ZFTP_COUNT is zero.
* We do this here in case we needed to wait for a RETR
@@ -1480,7 +1480,7 @@
int osc = sfcontext;
sfcontext = SFC_HOOK;
- doshfunc("zftp_progress", prog, NULL, 0, 1);
+ doshfunc(shfunc, NULL, 0, 1);
sfcontext = osc;
/* Now add in the bit of the file we've got/sent already */
sofar = last_sofar = startat;
@@ -1608,12 +1608,12 @@
} else
break;
if (!ret && sofar != last_sofar && progress &&
- (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+ (shfunc = getshfunc("zftp_progress"))) {
int osc = sfcontext;
zfsetparam("ZFTP_COUNT", &sofar, ZFPM_READONLY|ZFPM_INTEGER);
sfcontext = SFC_HOOK;
- doshfunc("zftp_progress", prog, NULL, 0, 1);
+ doshfunc(shfunc, NULL, 0, 1);
sfcontext = osc;
last_sofar = sofar;
}
@@ -2364,7 +2364,7 @@
{
char *ptr, *eptr;
int endc;
- Eprog prog;
+ Shfunc shfunc;
if (zfprefs & ZFPF_DUMB)
return 1;
@@ -2391,11 +2391,11 @@
* front end. By putting it here, and in close when ZFTP_PWD is unset,
* we at least cover the bases.
*/
- if ((prog = getshfunc("zftp_chpwd")) != &dummy_eprog) {
+ if ((shfunc = getshfunc("zftp_chpwd"))) {
int osc = sfcontext;
sfcontext = SFC_HOOK;
- doshfunc("zftp_chpwd", prog, NULL, 0, 1);
+ doshfunc(shfunc, NULL, 0, 1);
sfcontext = osc;
}
return 0;
@@ -2549,7 +2549,7 @@
{
int ret = 0, recv = (flags & ZFTP_RECV), getsize = 0, progress = 1;
char *cmd = recv ? "RETR " : (flags & ZFTP_APPE) ? "APPE " : "STOR ";
- Eprog prog;
+ Shfunc shfunc;
/*
* At this point I'd like to set progress to 0 if we're
@@ -2567,7 +2567,7 @@
for (; *args; args++) {
char *ln, *rest = NULL;
off_t startat = 0;
- if (progress && (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+ if (progress && (shfunc = getshfunc("zftp_progress"))) {
off_t sz = -1;
/*
* This calls the SIZE command to get the size for remote
@@ -2608,14 +2608,14 @@
* if and only if we called zfsenddata();
*/
if (progress && ret != 2 &&
- (prog = getshfunc("zftp_progress")) != &dummy_eprog) {
+ (shfunc = getshfunc("zftp_progress"))) {
/* progress to finish: ZFTP_TRANSFER set to GF or PF */
int osc = sfcontext;
zfsetparam("ZFTP_TRANSFER", ztrdup(recv ? "GF" : "PF"),
ZFPM_READONLY);
sfcontext = SFC_HOOK;
- doshfunc("zftp_progress", prog, NULL, 0, 1);
+ doshfunc(shfunc, NULL, 0, 1);
sfcontext = osc;
}
if (rest) {
@@ -2715,7 +2715,7 @@
zfclose(int leaveparams)
{
char **aptr;
- Eprog prog;
+ Shfunc shfunc;
if (!zfsess->control)
return;
@@ -2766,11 +2766,11 @@
zfunsetparam(*aptr);
/* Now ZFTP_PWD is unset. It's up to zftp_chpwd to notice. */
- if ((prog = getshfunc("zftp_chpwd")) != &dummy_eprog) {
+ if ((shfunc = getshfunc("zftp_chpwd"))) {
int osc = sfcontext;
sfcontext = SFC_HOOK;
- doshfunc("zftp_chpwd", prog, NULL, 0, 1);
+ doshfunc(shfunc, NULL, 0, 1);
sfcontext = osc;
}
}
Index: Src/Zle/compcore.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compcore.c,v
retrieving revision 1.96
diff -u -r1.96 compcore.c
--- Src/Zle/compcore.c 7 Jul 2008 08:33:28 -0000 1.96
+++ Src/Zle/compcore.c 16 Sep 2008 14:41:12 -0000
@@ -540,13 +540,13 @@
static void
callcompfunc(char *s, char *fn)
{
- Eprog prog;
+ Shfunc shfunc;
int lv = lastval;
char buf[20];
METACHECK();
- if ((prog = getshfunc(fn)) != &dummy_eprog) {
+ if ((shfunc = getshfunc(fn))) {
char **p, *tmp;
int aadd = 0, usea = 1, icf = incompfunc, osc = sfcontext;
unsigned int rset, kset;
@@ -814,7 +814,7 @@
while (*p)
addlinknode(largs, dupstring(*p++));
}
- doshfunc(fn, prog, largs, 0, 0);
+ doshfunc(shfunc, largs, 0, 0);
cfret = lastval;
lastval = olv;
} OLDHEAPS;
Index: Src/Zle/compctl.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/compctl.c,v
retrieving revision 1.35
diff -u -r1.35 compctl.c
--- Src/Zle/compctl.c 3 Oct 2007 16:18:38 -0000 1.35
+++ Src/Zle/compctl.c 16 Sep 2008 14:41:12 -0000
@@ -3635,12 +3635,12 @@
}
if (cc->func) {
/* This handles the compctl -K flag. */
- Eprog prog;
+ Shfunc shfunc;
char **r;
int lv = lastval;
/* Get the function. */
- if ((prog = getshfunc(cc->func)) != &dummy_eprog) {
+ if ((shfunc = getshfunc(cc->func))) {
/* We have it, so build a argument list. */
LinkList args = newlinklist();
int osc = sfcontext;
@@ -3664,7 +3664,7 @@
incompctlfunc = 1;
sfcontext = SFC_COMPLETE;
/* Call the function. */
- doshfunc(cc->func, prog, args, 0, 1);
+ doshfunc(shfunc, args, 0, 1);
sfcontext = osc;
incompctlfunc = 0;
/* And get the result from the reply parameter. */
@@ -3809,12 +3809,12 @@
/* generate the user-defined display list: if anything fails, *
* we silently allow the normal completion list to be used. */
char **yaptr = NULL, *uv = NULL;
- Eprog prog;
+ Shfunc shfunc;
if (cc->ylist[0] == '$' || cc->ylist[0] == '(') {
/* from variable */
uv = cc->ylist + (cc->ylist[0] == '$');
- } else if ((prog = getshfunc(cc->ylist)) != &dummy_eprog) {
+ } else if ((shfunc = getshfunc(cc->ylist))) {
/* from function: pass completions as arg list */
LinkList args = newlinklist();
LinkNode ln;
@@ -3839,7 +3839,7 @@
if (incompfunc != 1)
incompctlfunc = 1;
sfcontext = SFC_COMPLETE;
- doshfunc(cc->ylist, prog, args, 0, 1);
+ doshfunc(shfunc, args, 0, 1);
sfcontext = osc;
incompctlfunc = 0;
uv = "reply";
Index: Src/Zle/zle_main.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_main.c,v
retrieving revision 1.115
diff -u -r1.115 zle_main.c
--- Src/Zle/zle_main.c 8 Sep 2008 06:24:23 -0000 1.115
+++ Src/Zle/zle_main.c 16 Sep 2008 14:41:12 -0000
@@ -1304,9 +1304,8 @@
r = 1;
} else {
Shfunc shf = (Shfunc) shfunctab->getnode(shfunctab, w->u.fnnam);
- Eprog prog = (shf ? shf->funcdef : &dummy_eprog);
- if(prog == &dummy_eprog) {
+ if (!shf) {
/* the shell function doesn't exist */
char *nm = nicedup(w->u.fnnam, 0);
char *msg = tricat("No such shell function `", nm, "'");
@@ -1330,7 +1329,7 @@
makezleparams(0);
sfcontext = SFC_WIDGET;
opts[XTRACE] = 0;
- ret = doshfunc(w->u.fnnam, prog, largs, shf->node.flags, 1);
+ ret = doshfunc(shf, largs, shf->node.flags, 1);
opts[XTRACE] = oxt;
sfcontext = osc;
endparamscope();
Index: Src/Zle/zle_misc.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Zle/zle_misc.c,v
retrieving revision 1.54
diff -u -r1.54 zle_misc.c
--- Src/Zle/zle_misc.c 4 May 2008 18:30:04 -0000 1.54
+++ Src/Zle/zle_misc.c 16 Sep 2008 14:41:12 -0000
@@ -1358,9 +1358,9 @@
iremovesuffix(ZLE_INT_T c, int keep)
{
if (suffixfunc) {
- Eprog prog = getshfunc(suffixfunc);
+ Shfunc shfunc = getshfunc(suffixfunc);
- if (prog != &dummy_eprog) {
+ if (shfunc) {
LinkList args = newlinklist();
char buf[20];
int osc = sfcontext;
@@ -1384,7 +1384,7 @@
startparamscope();
makezleparams(0);
sfcontext = SFC_COMPLETE;
- doshfunc(suffixfunc, prog, args, 0, 1);
+ doshfunc(shfunc, args, 0, 1);
sfcontext = osc;
endparamscope();
Index: Test/E02xtrace.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/E02xtrace.ztst,v
retrieving revision 1.7
diff -u -r1.7 E02xtrace.ztst
--- Test/E02xtrace.ztst 11 Aug 2008 08:40:58 -0000 1.7
+++ Test/E02xtrace.ztst 16 Sep 2008 14:41:12 -0000
@@ -90,3 +90,18 @@
>Tracing: function
?+xtf:1> local regression_test_dummy_variable
?+xtf:2> print 'Tracing: function'
+
+ echo 'PS4="+%x:%I> "
+ fn() {
+ print This is fn.
+ }
+ :
+ fn
+ ' >fnfile
+ $ZTST_testdir/../Src/zsh -fx ./fnfile
+0:Trace output with sourcefile and line number.
+>This is fn.
+?+./fnfile:1> PS4='+%x:%I> '
+?+./fnfile:5> :
+?+./fnfile:6> fn
+?+./fnfile:3> print This is fn.
--
Peter Stephenson <pws@xxxxxxx> Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070