From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 2630 invoked from network); 9 Aug 2008 18:21:56 -0000 X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=AWL,BAYES_00 autolearn=ham version=3.2.5 Received: from news.dotsrc.org (HELO a.mx.sunsite.dk) (130.225.247.88) by ns1.primenet.com.au with SMTP; 9 Aug 2008 18:21:56 -0000 Received-SPF: none (ns1.primenet.com.au: domain at sunsite.dk does not designate permitted sender hosts) Received: (qmail 95036 invoked from network); 9 Aug 2008 18:21:41 -0000 Received: from sunsite.dk (130.225.247.90) by a.mx.sunsite.dk with SMTP; 9 Aug 2008 18:21:41 -0000 Received: (qmail 2856 invoked by alias); 9 Aug 2008 18:21:23 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 25427 Received: (qmail 2836 invoked from network); 9 Aug 2008 18:21:20 -0000 Received: from bifrost.dotsrc.org (130.225.254.106) by sunsite.dk with SMTP; 9 Aug 2008 18:21:20 -0000 Received: from mtaout01-winn.ispmail.ntl.com (mtaout01-winn.ispmail.ntl.com [81.103.221.47]) by bifrost.dotsrc.org (Postfix) with ESMTP id 9A73780561D1 for ; Sat, 9 Aug 2008 20:21:15 +0200 (CEST) Received: from aamtaout02-winn.ispmail.ntl.com ([81.103.221.35]) by mtaout01-winn.ispmail.ntl.com with ESMTP id <20080809182114.QDMZ777.mtaout01-winn.ispmail.ntl.com@aamtaout02-winn.ispmail.ntl.com> for ; Sat, 9 Aug 2008 19:21:14 +0100 Received: from pws-pc ([81.107.40.67]) by aamtaout02-winn.ispmail.ntl.com with ESMTP id <20080809182114.PBIW19289.aamtaout02-winn.ispmail.ntl.com@pws-pc> for ; Sat, 9 Aug 2008 19:21:14 +0100 Date: Sat, 9 Aug 2008 19:21:11 +0100 From: Peter Stephenson To: zsh-workers@sunsite.dk Subject: Re: Getting source file and line number of a function. Message-ID: <20080809192111.7213e919@pws-pc> In-Reply-To: <6cd6de210807280546k14a1a59fo40cb6ed968b1227b@mail.gmail.com> References: <6cd6de210807261806r7ff184fdtcc7859cca0a98aef@mail.gmail.com> <200807280834.m6S8YEVo026326@news01.csr.com> <6cd6de210807280546k14a1a59fo40cb6ed968b1227b@mail.gmail.com> X-Mailer: Claws Mail 3.5.0 (GTK+ 2.12.11; x86_64-redhat-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-Virus-Scanned: ClamAV 0.92.1/7993/Sat Aug 9 16:54:16 2008 on bifrost X-Virus-Status: Clean On Mon, 28 Jul 2008 08:46:43 -0400 "Rocky Bernstein" wrote: > Patch should be added. It's not the only way to do this and it changes > the functrace (which could easily be addressed, so there might be some > discussion. I've finally got something that's at least consistent. Whether it does what is needed is another matter, but I suspect it's at least going in the right direction. It's a little different from Rocky's original. Instead of hijacking $functrace, I've added another array, $funcfiletrace. I've made their behaviours' parallel one another, i.e. they're always the same length. What was really doing my brain in was working out what was supposed to be relative to what, given that I wanted to keep $functrace doing what it always did unless that really proved to be senseless (and I don't think it did). Eventually, I realised $functrace doesn't give you the current context, it gives you the calling context (i.e. if you want the current $LINENO or function name you get it some other way---the may be more work to do here about the name part). So I've made $funcfiletrace do the same, except that it's the context where the function was originally defined, not the point of the current call. Example below. In more detail, this modified version checks for a NULL scriptfilename, sets and also restores it in some more places (in particular autoload files), frees the filename from the Shfunc structure, tries to ensure that that is NULL if there isn't a name (that meant tracking down some more places where Shfunc's are created), handles NULL filenames in parameter.c, and fixes up an additional oddity with autoload (we don't know on the first execution of the function where the file is, so we need to fix up funcstack when it's already in use). I haven't done anything about the sourced script tracing yet. It's probably a good idea; we should obviously make sure it's consistent. There could be more stuff I've missed. For example, I haven't looked at whether ksh-style autoloads need something different. It's perfectly possible we'll need additional variables in zsh/parameter. It would be nice to have some way of telling what sort of context we were in (function defined in line or autloaded, sourced file, script). For now I'm happy if this looks like it's going the right way. Anyway, here's an example script showing this in action: ## start print Started functrace.zsh zmodload zsh/parameter print $LINENO; print $functrace; print $funcfiletrace fn() { print Inside function $0 print $LINENO; print $functrace; print $funcfiletrace } fn fpath=(. $fpath) echo 'print Inside $0 print $LINENO; print $functrace; print $funcfiletrace ' >autofn autoload autofn autofn autofn ## end Output: ## start Started functrace.zsh 4 Inside function fn 2 ../functrace.zsh:11 ../functrace.zsh:6 Inside autofn 2 ../functrace.zsh:21 ./autofn:0 Inside autofn 2 ../functrace.zsh:22 ./autofn:0 ## end Notes: - (As I said above) only when we're inside a function do $functrace and $funcfiletrace come alive, and then they give the calling context. - $functrace says where the function got called. $funcfiletrace says where the function got defined. - The line number 0 for the autoloaded function is not an error. The file is autofn; this is a zsh autoload, so the line that started the definition was not in the file at all. In other words, this is telling you the function was autoloaded from the entire contents of that file. It's a relative path here because it got picked up from "." in fpath; that's an unusual case in practice, so although it's true that the file name could be ambiguous I haven't felt like sanitizing the path. - The two autofn's are there to check the problem I noted above with the use of funcstack the first time. Index: Src/exec.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/exec.c,v retrieving revision 1.138 diff -u -r1.138 exec.c --- Src/exec.c 7 Aug 2008 16:25:16 -0000 1.138 +++ Src/exec.c 9 Aug 2008 18:10:14 -0000 @@ -191,7 +191,7 @@ parse_string(char *s) { Eprog p; - int oldlineno = lineno; + zlong oldlineno = lineno; lexsave(); inpush(s, INP_LINENO, NULL); @@ -1016,7 +1016,8 @@ Wordcode next; wordcode code; int ret, cj, csp, ltype; - int old_pline_level, old_list_pipe, oldlineno; + int old_pline_level, old_list_pipe; + zlong oldlineno; /* * ERREXIT only forces the shell to exit if the last command in a && * or || fails. This is the case even if an earlier command is a @@ -3961,6 +3962,8 @@ shf = (Shfunc) zalloc(sizeof(*shf)); shf->funcdef = prog; shf->node.flags = 0; + shf->filename = ztrdup(scriptfilename); + shf->lineno = lineno; if (!names) { /* @@ -4059,15 +4062,24 @@ execautofn(Estate state, UNUSED(int do_exec)) { Shfunc shf; - char *oldscriptname; + char *oldscriptname, *oldscriptfilename; if (!(shf = loadautofn(state->prog->shf, 1, 0))) return 1; + /* + * Probably we didn't know the filename where this function was + * defined yet. + */ + if (funcstack && !funcstack->filename) + funcstack->filename = dupstring(shf->filename); + oldscriptname = scriptname; - scriptname = dupstring(shf->node.nam); + oldscriptfilename = scriptfilename; + scriptname = scriptfilename = dupstring(shf->node.nam); execode(shf->funcdef, 1, 0); scriptname = oldscriptname; + scriptfilename = oldscriptfilename; return lastval; } @@ -4078,11 +4090,12 @@ { int noalias = noaliases, ksh = 1; Eprog prog; + char *fname; pushheap(); noaliases = (shf->node.flags & PM_UNALIASED); - prog = getfpfunc(shf->node.nam, &ksh); + prog = getfpfunc(shf->node.nam, &ksh, &fname); noaliases = noalias; if (ksh == 1) { @@ -4112,6 +4125,7 @@ else shf->funcdef = dupeprog(prog, 0); shf->node.flags &= ~PM_UNDEFINED; + shf->filename = fname; } else { VARARR(char, n, strlen(shf->node.nam) + 1); strcpy(n, shf->node.nam); @@ -4123,6 +4137,7 @@ zwarn("%s: function not defined by file", n); locallevel++; popheap(); + zsfree(fname); return NULL; } } @@ -4133,6 +4148,7 @@ else shf->funcdef = dupeprog(stripkshdef(prog, shf->node.nam), 0); shf->node.flags &= ~PM_UNDEFINED; + shf->filename = fname; } popheap(); @@ -4172,6 +4188,7 @@ #ifdef MAX_FUNCTION_DEPTH static int funcdepth; #endif + Shfunc shf; pushheap(); @@ -4243,6 +4260,15 @@ fstack.prev = funcstack; 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); + } + + if (prog->flags & EF_RUN) { Shfunc shf; @@ -4362,7 +4388,7 @@ /**/ Eprog -getfpfunc(char *s, int *ksh) +getfpfunc(char *s, int *ksh, char **fname) { char **pp, buf[PATH_MAX]; off_t len; @@ -4397,6 +4423,9 @@ r = parse_string(d); scriptname = oldscriptname; + if (fname) + *fname = ztrdup(buf); + zfree(d, len + 1); return r; Index: Src/hashtable.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/hashtable.c,v retrieving revision 1.27 diff -u -r1.27 hashtable.c --- Src/hashtable.c 1 Nov 2007 10:59:40 -0000 1.27 +++ Src/hashtable.c 9 Aug 2008 18:10:14 -0000 @@ -852,6 +852,7 @@ zsfree(shf->node.nam); if (shf->funcdef) freeeprog(shf->funcdef); + zsfree(shf->filename); zfree(shf, sizeof(struct shfunc)); } Index: Src/init.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/init.c,v retrieving revision 1.91 diff -u -r1.91 init.c --- Src/init.c 7 Aug 2008 16:25:16 -0000 1.91 +++ Src/init.c 9 Aug 2008 18:10:14 -0000 @@ -268,7 +268,7 @@ /* -c command */ cmd = *argv; opts[INTERACTIVE] &= 1; - scriptname = ztrdup("zsh"); + scriptname = scriptfilename = ztrdup("zsh"); } else if (**argv == 'o') { if (!*++*argv) argv++; @@ -325,6 +325,7 @@ } opts[INTERACTIVE] &= 1; argzero = *argv; + scriptfilename = argzero; argv++; } while (*argv) @@ -1051,10 +1052,12 @@ source(char *s) { Eprog prog; - int tempfd = -1, fd, cj, oldlineno; + int tempfd = -1, fd, cj; + zlong oldlineno; int oldshst, osubsh, oloops; FILE *obshin; char *old_scriptname = scriptname, *us; + char *old_scriptfilename = scriptfilename; unsigned char *ocs; int ocsp; int otrap_return = trap_return, otrap_state = trap_state; @@ -1087,6 +1090,7 @@ loops = 0; dosetopt(SHINSTDIN, 0, 1); scriptname = s; + scriptfilename = s; /* * The special return behaviour of traps shouldn't @@ -1096,6 +1100,17 @@ trap_state = TRAP_STATE_INACTIVE; sourcelevel++; + /* { */ + /* struct funcstack fstack; */ + /* fstack.name = dupstring("source"); */ + /* fstack.caller = dupstring(scriptfilename); */ + /* fstack.flineno = oldlineno; */ + /* fstack.lineno = oldlineno; */ + /* fstack.filename = NULL; */ + /* fstack.prev = funcstack; */ + /* funcstack = &fstack; */ + /* } */ + if (prog) { pushheap(); errflag = 0; @@ -1103,6 +1118,7 @@ popheap(); } else loop(0, 0); /* loop through the file to be sourced */ + /* funcstack = funcstack->prev; */ sourcelevel--; trap_state = otrap_state; @@ -1126,6 +1142,7 @@ if (!exit_pending) retflag = 0; scriptname = old_scriptname; + scriptfilename = old_scriptfilename; free(cmdstack); cmdstack = ocs; cmdsp = ocsp; Index: Src/parse.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/parse.c,v retrieving revision 1.70 diff -u -r1.70 parse.c --- Src/parse.c 1 Jul 2008 18:38:40 -0000 1.70 +++ Src/parse.c 9 Aug 2008 18:10:15 -0000 @@ -720,7 +720,8 @@ static int par_pline(int *complex) { - int p, line = lineno; + int p; + zlong line = lineno; p = ecadd(0); @@ -1414,8 +1415,9 @@ static void par_funcdef(void) { - int oecused = ecused, oldlineno = lineno, num = 0, onp, p, c = 0; + int oecused = ecused, num = 0, onp, p, c = 0; int so, oecssub = ecssub; + zlong oldlineno = lineno; lineno = 0; nocorrect = 1; @@ -1646,7 +1648,8 @@ p += nrediradd; sr += nrediradd; } else if (tok == INOUTPAR) { - int oldlineno = lineno, onp, so, oecssub = ecssub; + zlong oldlineno = lineno; + int onp, so, oecssub = ecssub; *complex = c; lineno = 0; @@ -2860,7 +2863,8 @@ return 1; } noaliases = (shf->node.flags & PM_UNALIASED); - if (!(prog = getfpfunc(shf->node.nam, NULL)) || prog == &dummy_eprog) { + if (!(prog = getfpfunc(shf->node.nam, NULL, NULL)) || + prog == &dummy_eprog) { noaliases = ona; zwarnnam(nam, "can't load function: %s", shf->node.nam); return 1; Index: Src/utils.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/utils.c,v retrieving revision 1.198 diff -u -r1.198 utils.c --- Src/utils.c 31 Jul 2008 08:44:21 -0000 1.198 +++ Src/utils.c 9 Aug 2008 18:10:17 -0000 @@ -33,7 +33,10 @@ /* name of script being sourced */ /**/ -mod_export char *scriptname; +mod_export char *scriptname; /* is sometimes a function name */ + +/**/ +mod_export char *scriptfilename; #ifdef MULTIBYTE_SUPPORT struct widechar_array { Index: Src/zsh.h =================================================================== RCS file: /cvsroot/zsh/zsh/Src/zsh.h,v retrieving revision 1.140 diff -u -r1.140 zsh.h --- Src/zsh.h 7 Aug 2008 16:25:16 -0000 1.140 +++ Src/zsh.h 9 Aug 2008 18:10:18 -0000 @@ -1061,6 +1061,8 @@ struct shfunc { struct hashnode node; + char *filename; /* Name of file located in */ + int lineno; /* line number in above file */ Eprog funcdef; /* function definition */ }; @@ -1079,8 +1081,10 @@ struct funcstack { Funcstack prev; /* previous in stack */ char *name; /* name of function called */ + char *filename; /* file function resides in */ char *caller; /* name of caller */ - int lineno; /* line number in file */ + zlong flineno; /* line number in file */ + zlong lineno; /* line offset from beginning of function */ }; /* node in list of function call wrappers */ Index: Src/Modules/parameter.c =================================================================== RCS file: /cvsroot/zsh/zsh/Src/Modules/parameter.c,v retrieving revision 1.45 diff -u -r1.45 parameter.c --- Src/Modules/parameter.c 5 Sep 2007 16:16:17 -0000 1.45 +++ Src/Modules/parameter.c 9 Aug 2008 18:10:18 -0000 @@ -286,7 +286,7 @@ zsfree(val); return; } - shf = (Shfunc) zalloc(sizeof(*shf)); + shf = (Shfunc) zshcalloc(sizeof(*shf)); shf->funcdef = dupeprog(prog, 0); shf->node.flags = dis; @@ -529,7 +529,35 @@ char *colonpair; colonpair = zhalloc(strlen(f->caller) + (f->lineno > 9999 ? 24 : 6)); - sprintf(colonpair, "%s:%d", f->caller, f->lineno); + sprintf(colonpair, "%s:%ld", f->caller, (long)f->lineno); + + *p = colonpair; + } + *p = NULL; + + return ret; +} + +/* Functions for the funcfiletrace special parameter. */ + +/**/ +static char ** +funcfiletracegetfn(UNUSED(Param pm)) +{ + Funcstack f; + int num; + char **ret, **p; + + for (f = funcstack, num = 0; f; f = f->prev, num++); + + ret = (char **) zhalloc((num + 1) * sizeof(char *)); + + for (f = funcstack, p = ret; f; f = f->prev, p++) { + char *colonpair; + char *fname = f->filename ? f->filename : ""; + + colonpair = zhalloc(strlen(fname) + (f->flineno > 9999 ? 24 : 6)); + sprintf(colonpair, "%s:%ld", fname, (long)f->flineno); *p = colonpair; } @@ -1773,6 +1801,8 @@ { funcstackgetfn, arrsetfn, stdunsetfn }; static const struct gsu_array functrace_gsu = { functracegetfn, arrsetfn, stdunsetfn }; +static const struct gsu_array funcfiletrace_gsu = +{ funcfiletracegetfn, arrsetfn, stdunsetfn }; static const struct gsu_array reswords_gsu = { reswordsgetfn, arrsetfn, stdunsetfn }; static const struct gsu_array disreswords_gsu = @@ -1807,6 +1837,8 @@ scanpmfunctions), SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY, &functrace_gsu, NULL, NULL), + SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY, + &funcfiletrace_gsu, NULL, NULL), SPECIALPMDEF("galiases", 0, &pmgaliases_gsu, getpmgalias, scanpmgaliases), SPECIALPMDEF("history", PM_READONLY, -- Peter Stephenson Web page now at http://homepage.ntlworld.com/p.w.stephenson/