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 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 Software Engineer > CSR PLC, Churchill House, Cambridge Business Park, Cowley Road > Cambridge, CB4 0WZ, UK Tel: +44 (0)1223 692070 >