* [PATCH] Improved LINENO support @ 2010-11-08 20:33 Harald van Dijk 2010-11-10 22:00 ` Jilles Tjoelker 0 siblings, 1 reply; 12+ messages in thread From: Harald van Dijk @ 2010-11-08 20:33 UTC (permalink / raw) To: dash [-- Attachment #1: Type: text/plain, Size: 756 bytes --] Hi, This patch improves LINENO support by storing line numbers in the parse tree, for commands as well as for function definitions. It makes LINENO behaves properly when calling functions, and has the added benefit of improved line numbers in error messages when the last-parsed command is not the last-executed one. It removes the earlier LINENO support, and instead sets LINENO from evaltree when a command is executed. I have used it for a while, and have seen correct line numbers in basic testing and in error messages in fairly large and complicated autoconf-generated shell scripts. Does this look good enough to add to dash? If there are any problems with it, please let me know, I will be happy to look at this further. Cheers, Harald [-- Attachment #2: dash-improved-lineno.diff --] [-- Type: text/plain, Size: 14452 bytes --] diff --git a/src/error.c b/src/error.c index e304d3d..e60e430 100644 --- a/src/error.c +++ b/src/error.c @@ -62,6 +62,7 @@ struct jmploc *handler; int exception; int suppressint; volatile sig_atomic_t intpending; +int errlinno; static void exverror(int, const char *, va_list) @@ -116,13 +117,12 @@ exvwarning2(const char *msg, va_list ap) const char *fmt; errs = out2; - name = arg0 ?: "sh"; - fmt = "%s: "; - if (commandname) { - name = commandname; + name = arg0 ? arg0 : "sh"; + if (!commandname) fmt = "%s: %d: "; - } - outfmt(errs, fmt, name, startlinno); + else + fmt = "%s: %d: %s: "; + outfmt(errs, fmt, name, errlinno, commandname); doformat(errs, msg, ap); #if FLUSHERR outc('\n', errs); diff --git a/src/error.h b/src/error.h index 3162e15..08f96e9 100644 --- a/src/error.h +++ b/src/error.h @@ -122,6 +122,7 @@ void onint(void) __attribute__((__noreturn__)); #else void onint(void); #endif +extern int errlinno; void sh_error(const char *, ...) __attribute__((__noreturn__)); void exerror(int, const char *, ...) __attribute__((__noreturn__)); const char *errmsg(int, int); diff --git a/src/eval.c b/src/eval.c index b966749..8c5f82c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -32,6 +32,7 @@ * SUCH DAMAGE. */ +#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> @@ -73,7 +74,7 @@ int evalskip; /* set if we are skipping commands */ STATIC int skipcount; /* number of levels to skip */ MKINIT int loopnest; /* current loop nesting level */ -static int funcnest; /* depth of function calls */ +STATIC int funcline; /* starting line number of current function, or 0 if not in a function */ char *commandname; @@ -191,6 +192,9 @@ evalstring(char *s, int flags) void evaltree(union node *n, int flags) { + static char linenoeq[7+(sizeof(int)*CHAR_BIT/3)+2] = "LINENO="; + char *lineno = linenoeq+7; + int checkexit = 0; void (*evalfn)(union node *, int); unsigned isor; @@ -204,6 +208,12 @@ evaltree(union node *n, int flags) #endif TRACE(("pid %d, evaltree(%p: %d, %d) called\n", getpid(), n, n->type, flags)); + /* In a function, LINENO must refer to the line number from the start + * of the function. However, errlinno contains the source line number, + * used in error messages. */ + errlinno = n->ncmd.linno; + sprintf(lineno, "%d", n->ncmd.linno - (funcline ? funcline-1 : 0)); + setvareq(linenoeq, VTEXTFIXED); switch (n->type) { default: #ifdef DEBUG @@ -296,7 +306,7 @@ calleval: } goto success; case NDEFUN: - defun(n->narg.text, n->narg.next); + defun(n->ndefun.text, n->ndefun.body); success: status = 0; setstatus: @@ -730,7 +740,7 @@ evalcommand(union node *cmd, int flags) *nargv = NULL; lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) + if (iflag && funcline == 0 && argc > 0) lastarg = nargv[-1]; preverrout.fd = 2; @@ -928,8 +938,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) struct jmploc *volatile savehandler; struct jmploc jmploc; int e; + int savefuncline; saveparam = shellparam; + savefuncline = funcline; if ((e = setjmp(jmploc.loc))) { goto funcdone; } @@ -938,7 +950,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) handler = &jmploc; shellparam.malloc = 0; func->count++; - funcnest++; + funcline = func->n.ndefun.linno; INTON; shellparam.nparam = argc - 1; shellparam.p = argv + 1; @@ -949,7 +961,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) poplocalvars(0); funcdone: INTOFF; - funcnest--; + funcline = savefuncline; freefunc(func); freeparam(&shellparam); shellparam = saveparam; @@ -1039,7 +1051,7 @@ returncmd(int argc, char **argv) * If called outside a function, do what ksh does; * skip the rest of the file. */ - evalskip = funcnest ? SKIPFUNC : SKIPFILE; + evalskip = funcline ? SKIPFUNC : SKIPFILE; return argv[1] ? number(argv[1]) : exitstatus; } diff --git a/src/input.c b/src/input.c index e57ad76..1e198e9 100644 --- a/src/input.c +++ b/src/input.c @@ -53,7 +53,6 @@ #include "alias.h" #include "parser.h" #include "main.h" -#include "var.h" #ifndef SMALL #include "myhistedit.h" #endif @@ -529,12 +528,3 @@ closescript(void) parsefile->fd = 0; } } - - -int lineno_inc(void) -{ - int lineno = plinno++; - - setvarint("LINENO", lineno, 0); - return lineno; -} diff --git a/src/input.h b/src/input.h index bdf8857..50a7797 100644 --- a/src/input.h +++ b/src/input.h @@ -61,7 +61,6 @@ void setinputstring(char *); void popfile(void); void popallfiles(void); void closescript(void); -int lineno_inc(void); #define pgetc_macro() \ (--parsenleft >= 0 ? (signed char)*parsenextc++ : preadbuffer()) diff --git a/src/jobs.c b/src/jobs.c index 826a9af..3d7ce93 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -1284,7 +1284,7 @@ dotail: p = "; done"; goto dodo; case NDEFUN: - cmdputs(n->narg.text); + cmdputs(n->ndefun.text); p = "() { ... }"; goto dotail2; case NCMD: diff --git a/src/nodetypes b/src/nodetypes index 17a7b3c..ae467cb 100644 --- a/src/nodetypes +++ b/src/nodetypes @@ -51,17 +51,20 @@ NCMD ncmd # a simple command type int + linno int assign nodeptr # variable assignments args nodeptr # the arguments redirect nodeptr # list of file redirections NPIPE npipe # a pipeline type int + linno int backgnd int # set to run pipeline in background cmdlist nodelist # the commands in the pipeline NREDIR nredir # redirection (of a complex command) type int + linno int n nodeptr # the command redirect nodeptr # list of file redirections @@ -73,11 +76,13 @@ NOR nbinary # the || operator NSEMI nbinary # two commands separated by a semicolon type int + linno int ch1 nodeptr # the first child ch2 nodeptr # the second child NIF nif # the if statement. Elif clauses are handled type int # using multiple if nodes. + linno int test nodeptr # if test ifpart nodeptr # then ifpart elsepart nodeptr # else elsepart @@ -87,12 +92,14 @@ NUNTIL nbinary # the until statement NFOR nfor # the for statement type int + linno int args nodeptr # for var in args body nodeptr # do body; done var string # the for variable NCASE ncase # a case statement type int + linno int expr nodeptr # the word to switch on cases nodeptr # the list of cases (NCLIST nodes) @@ -102,9 +109,11 @@ NCLIST nclist # a case pattern nodeptr # list of patterns for this case body nodeptr # code to execute for this case - -NDEFUN narg # define a function. The "next" field contains - # the body of the function. +NDEFUN ndefun # a function + type int + linno int + text string + body nodeptr NARG narg # represents a word type int @@ -140,5 +149,6 @@ NXHERE nhere # fd<<! doc nodeptr # input to command (NARG node) NNOT nnot # ! command (actually pipeline) - type int - com nodeptr + type int + linno int + com nodeptr diff --git a/src/parser.c b/src/parser.c index be20ff7..1881114 100644 --- a/src/parser.c +++ b/src/parser.c @@ -93,7 +93,6 @@ struct nodelist *backquotelist; union node *redirnode; struct heredoc *heredoc; int quoteflag; /* set if (part of) last token was quoted */ -int startlinno; /* line # where last token started */ STATIC union node *list(int); @@ -185,6 +184,7 @@ list(int nlflag) else { n3 = (union node *)stalloc(sizeof (struct nbinary)); n3->type = NSEMI; + n3->nbinary.linno = n1->ncmd.linno; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; @@ -243,6 +243,7 @@ andor(void) n2 = pipeline(); n3 = (union node *)stalloc(sizeof (struct nbinary)); n3->type = t; + n3->nbinary.linno = n1->ncmd.linno; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; @@ -269,6 +270,7 @@ pipeline(void) if (readtoken() == TPIPE) { pipenode = (union node *)stalloc(sizeof (struct npipe)); pipenode->type = NPIPE; + pipenode->npipe.linno = n1->ncmd.linno; pipenode->npipe.backgnd = 0; lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); pipenode->npipe.cmdlist = lp; @@ -315,6 +317,7 @@ command(void) case TIF: n1 = (union node *)stalloc(sizeof (struct nif)); n1->type = NIF; + n1->nif.linno = plinno; n1->nif.test = list(0); if (readtoken() != TTHEN) synexpect(TTHEN); @@ -324,6 +327,7 @@ command(void) n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; + n2->nif.linno = plinno; n2->nif.test = list(0); if (readtoken() != TTHEN) synexpect(TTHEN); @@ -342,6 +346,7 @@ command(void) int got; n1 = (union node *)stalloc(sizeof (struct nbinary)); n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.linno = plinno; n1->nbinary.ch1 = list(0); if ((got=readtoken()) != TDO) { TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); @@ -356,6 +361,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); synerror("Bad for loop variable"); n1 = (union node *)stalloc(sizeof (struct nfor)); n1->type = NFOR; + n1->nfor.linno = plinno; n1->nfor.var = wordtext; checkkwd = CHKNL | CHKKWD | CHKALIAS; if (readtoken() == TIN) { @@ -395,6 +401,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); case TCASE: n1 = (union node *)stalloc(sizeof (struct ncase)); n1->type = NCASE; + n1->ncase.linno = plinno; if (readtoken() != TWORD) synexpect(TWORD); n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); @@ -445,6 +452,7 @@ next_case: case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; + n1->nredir.linno = plinno; n1->nredir.n = list(0); n1->nredir.redirect = NULL; t = TRP; @@ -477,6 +485,7 @@ redir: if (n1->type != NSUBSHELL) { n2 = (union node *)stalloc(sizeof (struct nredir)); n2->type = NREDIR; + n2->nredir.linno = plinno; n2->nredir.n = n1; n1 = n2; } @@ -494,6 +503,7 @@ simplecmd(void) { union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; + int savelinno = plinno; args = NULL; app = &args; @@ -531,7 +541,7 @@ simplecmd(void) { !vars && !redir ) { struct builtincmd *bcmd; - const char *name; + char *name; /* We have a function */ if (readtoken() != TRP) @@ -546,7 +556,9 @@ simplecmd(void) { synerror("Bad function name"); n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; - n->narg.next = command(); + n->ndefun.linno = plinno; + n->ndefun.text = name; + n->ndefun.body = command(); return n; } /* fall through */ @@ -561,6 +573,7 @@ out: *rpp = NULL; n = (union node *)stalloc(sizeof (struct ncmd)); n->type = NCMD; + n->ncmd.linno = savelinno; n->ncmd.args = args; n->ncmd.assign = vars; n->ncmd.redirect = redir; @@ -738,8 +751,6 @@ out: * quoted. * If the token is TREDIR, then we set redirnode to a structure containing * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the @@ -763,7 +774,6 @@ xxreadtoken(void) if (needprompt) { setprompt(2); } - startlinno = plinno; for (;;) { /* until token or start of word found */ c = pgetc_macro(); switch (c) { @@ -776,7 +786,7 @@ xxreadtoken(void) continue; case '\\': if (pgetc() == '\n') { - startlinno = lineno_inc(); + plinno++; if (doprompt) setprompt(2); continue; @@ -784,7 +794,7 @@ xxreadtoken(void) pungetc(); goto breakloop; case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; RETURN(TNL); case PEOF: @@ -855,7 +865,6 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) /* syntax before arithmetic */ char const *uninitialized_var(prevsyntax); - startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; @@ -886,7 +895,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - lineno_inc(); + plinno++; if (doprompt) setprompt(2); c = pgetc(); @@ -907,6 +916,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) USTPUTC('\\', out); pungetc(); } else if (c == '\n') { + plinno++; if (doprompt) setprompt(2); } else { @@ -1008,7 +1018,6 @@ endword: if (syntax != BASESYNTAX && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { - startlinno = plinno; /* { */ synerror("Missing '}'"); } @@ -1065,7 +1074,7 @@ checkend: { if (c == '\n' || c == PEOF) { c = PEOF; - lineno_inc(); + plinno++; needprompt = doprompt; } else { int len; @@ -1315,7 +1324,7 @@ parsebackq: { case '\\': if ((pc = pgetc()) == '\n') { - lineno_inc(); + plinno++; if (doprompt) setprompt(2); /* @@ -1336,11 +1345,10 @@ parsebackq: { case PEOF: case PEOA: - startlinno = plinno; synerror("EOF in backquote substitution"); case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; break; @@ -1472,6 +1480,7 @@ synexpect(int token) STATIC void synerror(const char *msg) { + errlinno = plinno; sh_error("Syntax error: %s", msg); /* NOTREACHED */ } diff --git a/src/parser.h b/src/parser.h index 6bdf1c9..e6caed6 100644 --- a/src/parser.h +++ b/src/parser.h @@ -77,7 +77,6 @@ extern int tokpushback; #define NEOF ((union node *)&tokpushback) extern int whichprompt; /* 1 == PS1, 2 == PS2 */ extern int checkkwd; -extern int startlinno; /* line # where last token started */ union node *parsecmd(int); diff --git a/src/var.c b/src/var.c index 25c2216..6be9ab4 100644 --- a/src/var.c +++ b/src/var.c @@ -99,7 +99,6 @@ struct var varinit[] = { { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, #endif - { 0, VSTRFIXED|VTEXTFIXED, "LINENO=1", 0 }, }; STATIC struct var *vartab[VTABSIZE]; ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-08 20:33 [PATCH] Improved LINENO support Harald van Dijk @ 2010-11-10 22:00 ` Jilles Tjoelker 2010-11-11 20:33 ` Harald van Dijk 2010-11-12 18:17 ` Harald van Dijk 0 siblings, 2 replies; 12+ messages in thread From: Jilles Tjoelker @ 2010-11-10 22:00 UTC (permalink / raw) To: Harald van Dijk; +Cc: dash On Mon, Nov 08, 2010 at 09:32:56PM +0059, Harald van Dijk wrote: > This patch improves LINENO support by storing line numbers in the parse > tree, for commands as well as for function definitions. It makes LINENO > behaves properly when calling functions, and has the added benefit of > improved line numbers in error messages when the last-parsed command is > not the last-executed one. It removes the earlier LINENO support, and > instead sets LINENO from evaltree when a command is executed. I have > used it for a while, and have seen correct line numbers in basic testing > and in error messages in fairly large and complicated autoconf-generated > shell scripts. Does this look good enough to add to dash? If there are > any problems with it, please let me know, I will be happy to look at > this further. What I was thinking of was adding a lineno field to narg instead of to all command types. The lineno variable would then be set by expand.c. I think that leads to a smaller patch and it should still give a sensible value for almost all errors. Downside is a few more int-to-ascii conversions. A few divisions is not that bad relative to other things that are done but printf in modern libcs is bloated and slow and might slow down things measurably. FreeBSD's code subtracts the function's starting line number in the parser rather than at run time. I wonder which is best. (FreeBSD neglects to save and restore the value, so nested function definitions may lead to incorrect line numbers, but that could be fixed.) I think this patch will increase dash's code size at least a little, which is always a consideration here. I can also look at getting this into FreeBSD sh. Thanks, -- Jilles Tjoelker ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-10 22:00 ` Jilles Tjoelker @ 2010-11-11 20:33 ` Harald van Dijk 2010-11-12 18:17 ` Harald van Dijk 1 sibling, 0 replies; 12+ messages in thread From: Harald van Dijk @ 2010-11-11 20:33 UTC (permalink / raw) To: dash; +Cc: Jilles Tjoelker On 10/11/10 22:59, Jilles Tjoelker wrote: > What I was thinking of was adding a lineno field to narg instead of to > all command types. The lineno variable would then be set by expand.c. I > think that leads to a smaller patch and it should still give a sensible > value for almost all errors. Downside is a few more int-to-ascii > conversions. A few divisions is not that bad relative to other things > that are done but printf in modern libcs is bloated and slow and might > slow down things measurably. I made an attempt at this, but managed to keep the printing in one place by making LINENO a special variable, which is stored in an int and set in expandarg. When $LINENO is evaluated, the code checks to see if the variable is LINENO, and if so, converts the value to a string. This is one simple pointer comparison in the common case in lookupvar: if (v == &vlineno && v->text == linenovar) and avoids sprintf in scripts that don't use LINENO. > FreeBSD's code subtracts the function's starting line number in the > parser rather than at run time. I wonder which is best. (FreeBSD > neglects to save and restore the value, so nested function definitions > may lead to incorrect line numbers, but that could be fixed.) I used this approach in my updated patch, by resetting plinno to 0 in simplecmd, and restoring it after calling command(). > I think this patch will increase dash's code size at least a little, > which is always a consideration here. The code size varies little: the current limited LINENO support gives an executable size of 67680 bytes (default configure settings, no libedit, gcc 4.5.1, CFLAGS=-Os, strip after compilation), my previous patch gives 67688 bytes, my latest patch gives 67704 bytes. I do not think 24 bytes should be a problem. > I can also look at getting this > into FreeBSD sh. That would be great. I will send the updated patch when I can properly test it. Basic testing shows it works, but more extensive testing fails. As it turns out, this is unrelated to my changes: current git, unmodified, has a problem: $ cat test.sh echo cat <<_ACEOF $0 $@ _ACEOF echo $ src/dash test.sh wtf test.sh wtf ����� $ I had not noticed it with my earlier patch, because I actually did my testing with modified 0.5.6.1 sources. Thanks for the comments. Cheers, Harald ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-10 22:00 ` Jilles Tjoelker 2010-11-11 20:33 ` Harald van Dijk @ 2010-11-12 18:17 ` Harald van Dijk 2010-11-12 18:35 ` Eric Blake 1 sibling, 1 reply; 12+ messages in thread From: Harald van Dijk @ 2010-11-12 18:17 UTC (permalink / raw) To: dash; +Cc: Jilles Tjoelker On 10/11/10 22:59, Jilles Tjoelker wrote: > What I was thinking of was adding a lineno field to narg instead of to > all command types. The lineno variable would then be set by expand.c. I > think that leads to a smaller patch and it should still give a sensible > value for almost all errors. Unfortunately, I do not believe this will work. In echo $LINENO \ $LINENO two identical numbers must be printed, because LINENO is supposed to be set when a command is executed. It is not supposed to change during the execution of the command. Taking the first word of a command also will not work: ( : : : : : ) >test-$LINENO should write to file test-1, not test-7, even though the only word is on line 7. bash gets this wrong, pdksh gets this almost right. If I am missing an obvious way to address these issues, or if I am missing some part of the standard that clarifies why these issues do not exist, please share, but otherwise I will take the rest of your suggestions and rework my first patch, storing the line number for each command. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-12 18:17 ` Harald van Dijk @ 2010-11-12 18:35 ` Eric Blake 2010-11-12 19:08 ` Harald van Dijk 0 siblings, 1 reply; 12+ messages in thread From: Eric Blake @ 2010-11-12 18:35 UTC (permalink / raw) To: Harald van Dijk; +Cc: dash, Jilles Tjoelker [-- Attachment #1: Type: text/plain, Size: 1202 bytes --] On 11/12/2010 11:17 AM, Harald van Dijk wrote: > ( > : > : > : > : > : > ) >test-$LINENO > > should write to file test-1, not test-7, even though the only word is on > line 7. bash gets this wrong, pdksh gets this almost right. Umm - there was more than one word in that example (each : in the subshell is a word). But I agree that the subshell command itself started on line 1, but the word containing $LINENO is on line 7. There are _so many_ variations on what this example would do that it's hard to say that any one version is definitively correct. In general, the standard tends to favor the ksh93 interpretation of things; but on your example, it touches the file test-6 (not test-7 nor test-1). All I can see in the standard is: "Set by the shell to a decimal number representing the current sequential line number (numbered starting with 1) within a script or function before it executes each command." with no hint as to whether LINENO auto-increments over the number of newlines encountered while parsing that command. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 619 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-12 18:35 ` Eric Blake @ 2010-11-12 19:08 ` Harald van Dijk 2010-11-12 19:40 ` Harald van Dijk 2010-11-12 21:29 ` Jilles Tjoelker 0 siblings, 2 replies; 12+ messages in thread From: Harald van Dijk @ 2010-11-12 19:08 UTC (permalink / raw) To: dash; +Cc: Eric Blake, Jilles Tjoelker On 12/11/10 19:35, Eric Blake wrote: > On 11/12/2010 11:17 AM, Harald van Dijk wrote: >> ( >> : >> : >> : >> : >> : >> )>test-$LINENO >> >> should write to file test-1, not test-7, even though the only word is on >> line 7. bash gets this wrong, pdksh gets this almost right. > > Umm - there was more than one word in that example (each : in the > subshell is a word). But I agree that the subshell command itself > started on line 1, but the word containing $LINENO is on line 7. That was because it was a poor example. I meant to use comments, but forgot to change this before posting. I haven't checked if they count as words in SUS, but they don't in the dash source code. > There are _so many_ variations on what this example would do that it's > hard to say that any one version is definitively correct. In general, > the standard tends to favor the ksh93 interpretation of things; but on > your example, it touches the file test-6 (not test-7 nor test-1). > > All I can see in the standard is: > > "Set by the shell to a decimal number representing the current > sequential line number (numbered starting with 1) within a script or > function before it executes each command." > > with no hint as to whether LINENO auto-increments over the number of > newlines encountered while parsing that command. You may be right. SUS doesn't really specify whether "current" refers to the first or the last line number of a command. test-1 and test-7 are equally defensible, so I was too quick to say that bash gets this wrong. I hope you agree, though, that test-6 is iffy, and for my other example: echo $LINENO \ $LINENO two identical numbers must be printed. SUS only requires and only allows LINENO to be set before a command is executed, so once it is set, it is set for both expansions. Given that, ( # # # # # )>test-$LINENO \ >>test2-$LINENO must not write to test-7 and test2-8. It may write to test-1 and test2-1, or it may write to test-8 and test2-8. Perhaps even to test-7 and test2-7. Again, though, LINENO must be the same in both expansions. So for dash, the problem doesn't change: storing the line number of each word, rather than each command, makes it very hard to get LINENO right. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-12 19:08 ` Harald van Dijk @ 2010-11-12 19:40 ` Harald van Dijk 2010-11-12 21:29 ` Jilles Tjoelker 1 sibling, 0 replies; 12+ messages in thread From: Harald van Dijk @ 2010-11-12 19:40 UTC (permalink / raw) To: dash; +Cc: Eric Blake, Jilles Tjoelker On 12/11/10 20:08, Harald van Dijk wrote: > On 12/11/10 19:35, Eric Blake wrote: >> On 11/12/2010 11:17 AM, Harald van Dijk wrote: >>> ( >>> : >>> : >>> : >>> : >>> : >>> )>test-$LINENO >>> >> Umm - there was more than one word in that example (each : in the >> subshell is a word) > > That was because it was a poor example. I meant to use comments, ...which don't work, because a command is required between ( and ). Never mind that part. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-12 19:08 ` Harald van Dijk 2010-11-12 19:40 ` Harald van Dijk @ 2010-11-12 21:29 ` Jilles Tjoelker 2010-11-27 16:56 ` Harald van Dijk 1 sibling, 1 reply; 12+ messages in thread From: Jilles Tjoelker @ 2010-11-12 21:29 UTC (permalink / raw) To: Harald van Dijk; +Cc: dash, Eric Blake On Fri, Nov 12, 2010 at 08:07:41PM +0059, Harald van Dijk wrote: > must not write to test-7 and test2-8. It may write to test-1 and > test2-1, or it may write to test-8 and test2-8. Perhaps even to test-7 > and test2-7. Again, though, LINENO must be the same in both expansions. > So for dash, the problem doesn't change: storing the line number of each > word, rather than each command, makes it very hard to get LINENO right. Hmm, I hadn't thought of that. I considered a LINENO that points to the affected word most useful and easiest to implement (also because FreeBSD sh's LINENO works this way). So some more ideas: A per-command LINENO does not require adding the number to all of the node types. Only node types that are commands that perform expansions need it: NCMD, NREDIR, NBACKGND, NSUBSHELL, NFOR, NCASE. NFOR and NCASE may be wrapped in an NREDIR node to hold the line number. This simplifies the code a bit at the cost of some extra memory usage. A further simplification might be removing the redirect field from NBACKGND and NSUBSHELL and putting any redirect in an additional NREDIR node inside the NBACKGND/NSUBSHELL (so that the file is opened in the child process and job control works correctly). However, NREDIR, NBACKGND and NSUBSHELL are compatible types so it does not seem to gain much. What about making the lineno an element of the redirection list like NTO? This minimizes changes to eval.c but perhaps it is too contrived. Second idea: use a per-word LINENO but somehow ensure it is the same for all words in a command. I think this is wrong, but perhaps I'm wrong. -- Jilles Tjoelker ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-12 21:29 ` Jilles Tjoelker @ 2010-11-27 16:56 ` Harald van Dijk 2011-03-10 8:10 ` Herbert Xu 0 siblings, 1 reply; 12+ messages in thread From: Harald van Dijk @ 2010-11-27 16:56 UTC (permalink / raw) To: dash; +Cc: Jilles Tjoelker [-- Attachment #1: Type: text/plain, Size: 1090 bytes --] On 12/11/10 22:29, Jilles Tjoelker wrote: > So some more ideas: > > A per-command LINENO does not require adding the number to all of the > node types. Only node types that are commands that perform expansions > need it: NCMD, NREDIR, NBACKGND, NSUBSHELL, NFOR, NCASE. This makes sense, and I've tried this with good results. I added NDEFUN to the list, to get the source to function line number mapping correct. I noticed when wrapping half-parsed commands in an NREDIR, I got the line number wrong (not necessarily nonconforming, but different from what I had intended). I changed it in this patch by saving the line number in a variable before starting the parsing of the command. > NFOR and NCASE may be wrapped in an NREDIR node to hold the line number. > This simplifies the code a bit at the cost of some extra memory usage. That's possible. I have not used that idea in this patch, but it should be an easy change. Your other suggestions would work, but I am not able to say whether they form an improvement, so I did not use those for now. Again, comments are welcome. [-- Attachment #2: dash-improved-lineno-3.diff --] [-- Type: text/plain, Size: 15873 bytes --] diff --git a/src/error.c b/src/error.c index e304d3d..e60e430 100644 --- a/src/error.c +++ b/src/error.c @@ -62,6 +62,7 @@ struct jmploc *handler; int exception; int suppressint; volatile sig_atomic_t intpending; +int errlinno; static void exverror(int, const char *, va_list) @@ -116,13 +117,12 @@ exvwarning2(const char *msg, va_list ap) const char *fmt; errs = out2; - name = arg0 ?: "sh"; - fmt = "%s: "; - if (commandname) { - name = commandname; + name = arg0 ? arg0 : "sh"; + if (!commandname) fmt = "%s: %d: "; - } - outfmt(errs, fmt, name, startlinno); + else + fmt = "%s: %d: %s: "; + outfmt(errs, fmt, name, errlinno, commandname); doformat(errs, msg, ap); #if FLUSHERR outc('\n', errs); diff --git a/src/error.h b/src/error.h index 3162e15..08f96e9 100644 --- a/src/error.h +++ b/src/error.h @@ -122,6 +122,7 @@ void onint(void) __attribute__((__noreturn__)); #else void onint(void); #endif +extern int errlinno; void sh_error(const char *, ...) __attribute__((__noreturn__)); void exerror(int, const char *, ...) __attribute__((__noreturn__)); const char *errmsg(int, int); diff --git a/src/eval.c b/src/eval.c index b966749..d85f66f 100644 --- a/src/eval.c +++ b/src/eval.c @@ -73,7 +73,7 @@ int evalskip; /* set if we are skipping commands */ STATIC int skipcount; /* number of levels to skip */ MKINIT int loopnest; /* current loop nesting level */ -static int funcnest; /* depth of function calls */ +STATIC int funcline; /* starting line number of current function, or 0 if not in a function */ char *commandname; @@ -218,6 +218,9 @@ evaltree(union node *n, int flags) status = !exitstatus; goto setstatus; case NREDIR: + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; expredir(n->nredir.redirect); pushredir(n->nredir.redirect); status = redirectsafe(n->nredir.redirect, REDIR_PUSH); @@ -296,7 +299,7 @@ calleval: } goto success; case NDEFUN: - defun(n->narg.text, n->narg.next); + defun(n); success: status = 0; setstatus: @@ -370,6 +373,10 @@ evalfor(union node *n, int flags) struct strlist *sp; struct stackmark smark; + errlinno = lineno = n->nfor.linno; + if (funcline) + lineno -= funcline - 1; + setstackmark(&smark); arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { @@ -411,6 +418,10 @@ evalcase(union node *n, int flags) struct arglist arglist; struct stackmark smark; + errlinno = lineno = n->ncase.linno; + if (funcline) + lineno -= funcline - 1; + setstackmark(&smark); arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); @@ -442,6 +453,10 @@ evalsubshell(union node *n, int flags) int backgnd = (n->type == NBACKGND); int status; + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; + expredir(n->nredir.redirect); if (!backgnd && flags & EV_EXIT && !have_traps()) goto nofork; @@ -697,6 +712,10 @@ evalcommand(union node *cmd, int flags) int status; char **nargv; + errlinno = lineno = cmd->ncmd.linno; + if (funcline) + lineno -= funcline - 1; + /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); @@ -730,7 +749,7 @@ evalcommand(union node *cmd, int flags) *nargv = NULL; lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) + if (iflag && funcline == 0 && argc > 0) lastarg = nargv[-1]; preverrout.fd = 2; @@ -928,8 +947,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) struct jmploc *volatile savehandler; struct jmploc jmploc; int e; + int savefuncline; saveparam = shellparam; + savefuncline = funcline; if ((e = setjmp(jmploc.loc))) { goto funcdone; } @@ -938,18 +959,18 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) handler = &jmploc; shellparam.malloc = 0; func->count++; - funcnest++; + funcline = func->n.ndefun.linno; INTON; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optind = 1; shellparam.optoff = -1; pushlocalvars(); - evaltree(&func->n, flags & EV_TESTED); + evaltree(func->n.ndefun.body, flags & EV_TESTED); poplocalvars(0); funcdone: INTOFF; - funcnest--; + funcline = savefuncline; freefunc(func); freeparam(&shellparam); shellparam = saveparam; @@ -1039,7 +1060,7 @@ returncmd(int argc, char **argv) * If called outside a function, do what ksh does; * skip the rest of the file. */ - evalskip = funcnest ? SKIPFUNC : SKIPFILE; + evalskip = funcline ? SKIPFUNC : SKIPFILE; return argv[1] ? number(argv[1]) : exitstatus; } diff --git a/src/exec.c b/src/exec.c index 42299ea..959b9f4 100644 --- a/src/exec.c +++ b/src/exec.c @@ -688,14 +688,14 @@ addcmdentry(char *name, struct cmdentry *entry) */ void -defun(char *name, union node *func) +defun(union node *func) { struct cmdentry entry; INTOFF; entry.cmdtype = CMDFUNCTION; entry.u.func = copyfunc(func); - addcmdentry(name, &entry); + addcmdentry(func->ndefun.text, &entry); INTON; } diff --git a/src/exec.h b/src/exec.h index daa6f10..9ccb305 100644 --- a/src/exec.h +++ b/src/exec.h @@ -71,7 +71,7 @@ void changepath(const char *); #ifdef notdef void getcmdentry(char *, struct cmdentry *); #endif -void defun(char *, union node *); +void defun(union node *); void unsetfunc(const char *); int typecmd(int, char **); int commandcmd(int, char **); diff --git a/src/input.c b/src/input.c index e57ad76..1e198e9 100644 --- a/src/input.c +++ b/src/input.c @@ -53,7 +53,6 @@ #include "alias.h" #include "parser.h" #include "main.h" -#include "var.h" #ifndef SMALL #include "myhistedit.h" #endif @@ -529,12 +528,3 @@ closescript(void) parsefile->fd = 0; } } - - -int lineno_inc(void) -{ - int lineno = plinno++; - - setvarint("LINENO", lineno, 0); - return lineno; -} diff --git a/src/input.h b/src/input.h index bdf8857..50a7797 100644 --- a/src/input.h +++ b/src/input.h @@ -61,7 +61,6 @@ void setinputstring(char *); void popfile(void); void popallfiles(void); void closescript(void); -int lineno_inc(void); #define pgetc_macro() \ (--parsenleft >= 0 ? (signed char)*parsenextc++ : preadbuffer()) diff --git a/src/jobs.c b/src/jobs.c index 826a9af..3d7ce93 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -1284,7 +1284,7 @@ dotail: p = "; done"; goto dodo; case NDEFUN: - cmdputs(n->narg.text); + cmdputs(n->ndefun.text); p = "() { ... }"; goto dotail2; case NCMD: diff --git a/src/nodetypes b/src/nodetypes index 17a7b3c..f7a2873 100644 --- a/src/nodetypes +++ b/src/nodetypes @@ -51,6 +51,7 @@ NCMD ncmd # a simple command type int + linno int assign nodeptr # variable assignments args nodeptr # the arguments redirect nodeptr # list of file redirections @@ -62,6 +63,7 @@ NPIPE npipe # a pipeline NREDIR nredir # redirection (of a complex command) type int + linno int n nodeptr # the command redirect nodeptr # list of file redirections @@ -87,12 +89,14 @@ NUNTIL nbinary # the until statement NFOR nfor # the for statement type int + linno int args nodeptr # for var in args body nodeptr # do body; done var string # the for variable NCASE ncase # a case statement type int + linno int expr nodeptr # the word to switch on cases nodeptr # the list of cases (NCLIST nodes) @@ -102,9 +106,11 @@ NCLIST nclist # a case pattern nodeptr # list of patterns for this case body nodeptr # code to execute for this case - -NDEFUN narg # define a function. The "next" field contains - # the body of the function. +NDEFUN ndefun # a function + type int + linno int + text string + body nodeptr NARG narg # represents a word type int diff --git a/src/parser.c b/src/parser.c index be20ff7..2f839f5 100644 --- a/src/parser.c +++ b/src/parser.c @@ -93,7 +93,6 @@ struct nodelist *backquotelist; union node *redirnode; struct heredoc *heredoc; int quoteflag; /* set if (part of) last token was quoted */ -int startlinno; /* line # where last token started */ STATIC union node *list(int); @@ -304,10 +303,13 @@ command(void) union node *redir, **rpp; union node **rpp2; int t; + int savelinno; redir = NULL; rpp2 = &redir; + savelinno = plinno; + switch (readtoken()) { default: synexpect(-1); @@ -356,6 +358,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); synerror("Bad for loop variable"); n1 = (union node *)stalloc(sizeof (struct nfor)); n1->type = NFOR; + n1->nfor.linno = savelinno; n1->nfor.var = wordtext; checkkwd = CHKNL | CHKKWD | CHKALIAS; if (readtoken() == TIN) { @@ -395,6 +398,7 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); case TCASE: n1 = (union node *)stalloc(sizeof (struct ncase)); n1->type = NCASE; + n1->ncase.linno = savelinno; if (readtoken() != TWORD) synexpect(TWORD); n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); @@ -445,6 +449,7 @@ next_case: case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; + n1->nredir.linno = savelinno; n1->nredir.n = list(0); n1->nredir.redirect = NULL; t = TRP; @@ -477,6 +482,7 @@ redir: if (n1->type != NSUBSHELL) { n2 = (union node *)stalloc(sizeof (struct nredir)); n2->type = NREDIR; + n2->nredir.linno = savelinno; n2->nredir.n = n1; n1 = n2; } @@ -494,6 +500,7 @@ simplecmd(void) { union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; + int savelinno; args = NULL; app = &args; @@ -503,6 +510,7 @@ simplecmd(void) { rpp = &redir; savecheckkwd = CHKALIAS; + savelinno = plinno; for (;;) { checkkwd = savecheckkwd; switch (readtoken()) { @@ -531,7 +539,7 @@ simplecmd(void) { !vars && !redir ) { struct builtincmd *bcmd; - const char *name; + char *name; /* We have a function */ if (readtoken() != TRP) @@ -546,7 +554,9 @@ simplecmd(void) { synerror("Bad function name"); n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; - n->narg.next = command(); + n->ndefun.text = n->narg.text; + n->ndefun.linno = plinno; + n->ndefun.body = command(); return n; } /* fall through */ @@ -561,6 +571,7 @@ out: *rpp = NULL; n = (union node *)stalloc(sizeof (struct ncmd)); n->type = NCMD; + n->ncmd.linno = savelinno; n->ncmd.args = args; n->ncmd.assign = vars; n->ncmd.redirect = redir; @@ -738,8 +749,6 @@ out: * quoted. * If the token is TREDIR, then we set redirnode to a structure containing * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the @@ -763,7 +772,6 @@ xxreadtoken(void) if (needprompt) { setprompt(2); } - startlinno = plinno; for (;;) { /* until token or start of word found */ c = pgetc_macro(); switch (c) { @@ -776,7 +784,7 @@ xxreadtoken(void) continue; case '\\': if (pgetc() == '\n') { - startlinno = lineno_inc(); + plinno++; if (doprompt) setprompt(2); continue; @@ -784,7 +792,7 @@ xxreadtoken(void) pungetc(); goto breakloop; case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; RETURN(TNL); case PEOF: @@ -855,7 +863,6 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) /* syntax before arithmetic */ char const *uninitialized_var(prevsyntax); - startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; @@ -886,7 +893,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - lineno_inc(); + plinno++; if (doprompt) setprompt(2); c = pgetc(); @@ -907,6 +914,7 @@ readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) USTPUTC('\\', out); pungetc(); } else if (c == '\n') { + plinno++; if (doprompt) setprompt(2); } else { @@ -1008,7 +1016,6 @@ endword: if (syntax != BASESYNTAX && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { - startlinno = plinno; /* { */ synerror("Missing '}'"); } @@ -1065,7 +1072,7 @@ checkend: { if (c == '\n' || c == PEOF) { c = PEOF; - lineno_inc(); + plinno++; needprompt = doprompt; } else { int len; @@ -1315,7 +1322,7 @@ parsebackq: { case '\\': if ((pc = pgetc()) == '\n') { - lineno_inc(); + plinno++; if (doprompt) setprompt(2); /* @@ -1336,11 +1343,10 @@ parsebackq: { case PEOF: case PEOA: - startlinno = plinno; synerror("EOF in backquote substitution"); case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; break; @@ -1472,6 +1478,7 @@ synexpect(int token) STATIC void synerror(const char *msg) { + errlinno = plinno; sh_error("Syntax error: %s", msg); /* NOTREACHED */ } diff --git a/src/parser.h b/src/parser.h index 6bdf1c9..e6caed6 100644 --- a/src/parser.h +++ b/src/parser.h @@ -77,7 +77,6 @@ extern int tokpushback; #define NEOF ((union node *)&tokpushback) extern int whichprompt; /* 1 == PS1, 2 == PS2 */ extern int checkkwd; -extern int startlinno; /* line # where last token started */ union node *parsecmd(int); diff --git a/src/var.c b/src/var.c index 25c2216..7e05e3d 100644 --- a/src/var.c +++ b/src/var.c @@ -33,6 +33,7 @@ */ #include <unistd.h> +#include <stdio.h> #include <stdlib.h> #include <paths.h> @@ -78,6 +79,9 @@ const char defifsvar[] = "IFS= \t\n"; const char defifs[] = " \t\n"; #endif +int lineno; +char linenovar[sizeof("LINENO=")+sizeof(int)*CHAR_BIT/3+1] = "LINENO="; + /* Some macros in var.h depend on the order, add new variables to the end. */ struct var varinit[] = { #if ATTY @@ -95,11 +99,11 @@ struct var varinit[] = { { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, + { 0, VSTRFIXED|VTEXTFIXED, linenovar, 0 }, #ifndef SMALL { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, #endif - { 0, VSTRFIXED|VTEXTFIXED, "LINENO=1", 0 }, }; STATIC struct var *vartab[VTABSIZE]; @@ -329,6 +333,9 @@ lookupvar(const char *name) struct var *v; if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { + if (v == &vlineno && v->text == linenovar) { + sprintf(linenoval(), "%d", lineno); + } return strchrnul(v->text, '=') + 1; } return NULL; diff --git a/src/var.h b/src/var.h index 7aa051f..3d2c2cb 100644 --- a/src/var.h +++ b/src/var.h @@ -88,8 +88,9 @@ extern struct var varinit[]; #define vps2 (&vps1)[1] #define vps4 (&vps2)[1] #define voptind (&vps4)[1] +#define vlineno (&voptind)[1] #ifndef SMALL -#define vterm (&voptind)[1] +#define vterm (&vlineno)[1] #define vhistsize (&vterm)[1] #endif @@ -102,6 +103,9 @@ extern const char defifs[]; extern const char defpathvar[]; #define defpath (defpathvar + 5) +extern int lineno; +extern char linenovar[]; + /* * The following macros access the values of the above variables. * They have to skip over the name. They return the null string @@ -117,6 +121,7 @@ extern const char defpathvar[]; #define ps2val() (vps2.text + 4) #define ps4val() (vps4.text + 4) #define optindval() (voptind.text + 7) +#define linenoval() (vlineno.text + 7) #ifndef SMALL #define histsizeval() (vhistsize.text + 9) #define termval() (vterm.text + 5) ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2010-11-27 16:56 ` Harald van Dijk @ 2011-03-10 8:10 ` Herbert Xu 2011-03-11 21:56 ` Harald van Dijk 0 siblings, 1 reply; 12+ messages in thread From: Herbert Xu @ 2011-03-10 8:10 UTC (permalink / raw) To: Harald van Dijk; +Cc: dash, Jilles Tjoelker On Sat, Nov 27, 2010 at 04:56:17PM +0000, Harald van Dijk wrote: > > Again, comments are welcome. Thanks for working on this! Just a few minor problems to correct. Oh and please add a changelog + sign-off. > diff --git a/src/eval.c b/src/eval.c > index b966749..d85f66f 100644 > --- a/src/eval.c > +++ b/src/eval.c > @@ -73,7 +73,7 @@ > int evalskip; /* set if we are skipping commands */ > STATIC int skipcount; /* number of levels to skip */ > MKINIT int loopnest; /* current loop nesting level */ > -static int funcnest; /* depth of function calls */ > +STATIC int funcline; /* starting line number of current function, or 0 if not in a function */ Please don't add any new uses of STATIC. I'm fine with leaving existing ones in place though. > @@ -730,7 +749,7 @@ evalcommand(union node *cmd, int flags) > *nargv = NULL; > > lastarg = NULL; > - if (iflag && funcnest == 0 && argc > 0) > + if (iflag && funcline == 0 && argc > 0) > lastarg = nargv[-1]; I like this bit :) > diff --git a/src/exec.c b/src/exec.c > index 42299ea..959b9f4 100644 > --- a/src/exec.c > +++ b/src/exec.c > @@ -688,14 +688,14 @@ addcmdentry(char *name, struct cmdentry *entry) > */ > > void > -defun(char *name, union node *func) > +defun(union node *func) > { > struct cmdentry entry; > > INTOFF; > entry.cmdtype = CMDFUNCTION; > entry.u.func = copyfunc(func); > - addcmdentry(name, &entry); > + addcmdentry(func->ndefun.text, &entry); > INTON; > } Please separate out unrelated changes like this into distinct patches. > @@ -329,6 +333,9 @@ lookupvar(const char *name) > struct var *v; > > if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { > + if (v == &vlineno && v->text == linenovar) { > + sprintf(linenoval(), "%d", lineno); > + } Please use fmtstr instead of sprintf for consistency. Cheers, -- Email: Herbert Xu <herbert@gondor.apana.org.au> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2011-03-10 8:10 ` Herbert Xu @ 2011-03-11 21:56 ` Harald van Dijk 2011-03-15 7:52 ` Herbert Xu 0 siblings, 1 reply; 12+ messages in thread From: Harald van Dijk @ 2011-03-11 21:56 UTC (permalink / raw) To: dash; +Cc: Jilles Tjoelker [-- Attachment #1: Type: text/plain, Size: 1003 bytes --] On Thu, 2011-03-10 at 16:10 +0800, Herbert Xu wrote: > On Sat, Nov 27, 2010 at 04:56:17PM +0000, Harald van Dijk wrote: > > > > Again, comments are welcome. > > Thanks for working on this! Just a few minor problems to correct. > Oh and please add a changelog + sign-off. Done. > Please don't add any new uses of STATIC. I'm fine with leaving > existing ones in place though. Changed to static. > I like this bit :) Thanks :) > Please separate out unrelated changes like this into distinct > patches. It is not unrelated: I changed the meaning of struct funcnode's field n to refer to the function definition, rather than the list of the function's commands, because I needed to refer to the function definition node from evalfun, which only gets passed a funcnode. But it is something that could be applied independently (without being useful by itself), so I've attached it as a separate patch for easier review. > Please use fmtstr instead of sprintf for consistency. Done. Cheers, Harald [-- Attachment #2: dash-funcnode.patch --] [-- Type: text/x-patch, Size: 1592 bytes --] Signed-off-by: Harald van Dijk <harald@gigawatt.nl> diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2011-03-11 Harald van Dijk <harald@gigawatt.nl> + + * Let funcnode refer to a function definition, not its first command. + 2011-03-10 Jonathan Nieder <jrnieder@gmail.com> * Dotcmd should exit with zero when doing nothing. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -296,7 +296,7 @@ calleval: } goto success; case NDEFUN: - defun(n->narg.text, n->narg.next); + defun(n); success: status = 0; setstatus: @@ -954,7 +954,7 @@ evalfun(struct funcnode *func, int argc, shellparam.optind = 1; shellparam.optoff = -1; pushlocalvars(); - evaltree(&func->n, flags & EV_TESTED); + evaltree(func->n.narg.next, flags & EV_TESTED); poplocalvars(0); funcdone: INTOFF; diff --git a/src/exec.c b/src/exec.c --- a/src/exec.c +++ b/src/exec.c @@ -688,14 +688,14 @@ addcmdentry(char *name, struct cmdentry */ void -defun(char *name, union node *func) +defun(union node *func) { struct cmdentry entry; INTOFF; entry.cmdtype = CMDFUNCTION; entry.u.func = copyfunc(func); - addcmdentry(name, &entry); + addcmdentry(func->narg.text, &entry); INTON; } diff --git a/src/exec.h b/src/exec.h --- a/src/exec.h +++ b/src/exec.h @@ -71,7 +71,7 @@ void changepath(const char *); #ifdef notdef void getcmdentry(char *, struct cmdentry *); #endif -void defun(char *, union node *); +void defun(union node *); void unsetfunc(const char *); int typecmd(int, char **); int commandcmd(int, char **); [-- Attachment #3: dash-lineno-4.patch --] [-- Type: text/x-patch, Size: 14213 bytes --] Signed-off-by: Harald van Dijk <harald@gigawatt.nl> diff -r 772aea7ed8c5 ChangeLog --- a/ChangeLog Fri Mar 11 22:39:28 2011 +0100 +++ b/ChangeLog Fri Mar 11 22:45:57 2011 +0100 @@ -1,3 +1,7 @@ +2011-03-11 Harald van Dijk <harald@gigawatt.nl> + + * Improve LINENO support. + 2011-03-11 Harald van Dijk <harald@gigawatt.nl> * Let funcnode refer to a function definition, not its first command. diff -r 772aea7ed8c5 src/error.c --- a/src/error.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/error.c Fri Mar 11 22:45:57 2011 +0100 @@ -62,6 +62,7 @@ int exception; int suppressint; volatile sig_atomic_t intpending; +int errlinno; static void exverror(int, const char *, va_list) @@ -116,13 +117,12 @@ const char *fmt; errs = out2; - name = arg0 ?: "sh"; - fmt = "%s: "; - if (commandname) { - name = commandname; + name = arg0 ? arg0 : "sh"; + if (!commandname) fmt = "%s: %d: "; - } - outfmt(errs, fmt, name, startlinno); + else + fmt = "%s: %d: %s: "; + outfmt(errs, fmt, name, errlinno, commandname); doformat(errs, msg, ap); #if FLUSHERR outc('\n', errs); diff -r 772aea7ed8c5 src/error.h --- a/src/error.h Fri Mar 11 22:39:28 2011 +0100 +++ b/src/error.h Fri Mar 11 22:45:57 2011 +0100 @@ -120,6 +120,7 @@ #else void onint(void); #endif +extern int errlinno; void sh_error(const char *, ...) __attribute__((__noreturn__)); void exerror(int, const char *, ...) __attribute__((__noreturn__)); const char *errmsg(int, int); diff -r 772aea7ed8c5 src/eval.c --- a/src/eval.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/eval.c Fri Mar 11 22:45:57 2011 +0100 @@ -73,7 +73,7 @@ int evalskip; /* set if we are skipping commands */ STATIC int skipcount; /* number of levels to skip */ MKINIT int loopnest; /* current loop nesting level */ -static int funcnest; /* depth of function calls */ +static int funcline; /* starting line number of current function, or 0 if not in a function */ char *commandname; @@ -218,6 +218,9 @@ status = !exitstatus; goto setstatus; case NREDIR: + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; expredir(n->nredir.redirect); pushredir(n->nredir.redirect); status = redirectsafe(n->nredir.redirect, REDIR_PUSH); @@ -376,6 +379,10 @@ struct strlist *sp; struct stackmark smark; + errlinno = lineno = n->nfor.linno; + if (funcline) + lineno -= funcline - 1; + setstackmark(&smark); arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { @@ -417,6 +424,10 @@ struct arglist arglist; struct stackmark smark; + errlinno = lineno = n->ncase.linno; + if (funcline) + lineno -= funcline - 1; + setstackmark(&smark); arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); @@ -448,6 +459,10 @@ int backgnd = (n->type == NBACKGND); int status; + errlinno = lineno = n->nredir.linno; + if (funcline) + lineno -= funcline - 1; + expredir(n->nredir.redirect); if (!backgnd && flags & EV_EXIT && !have_traps()) goto nofork; @@ -704,6 +719,10 @@ int status; char **nargv; + errlinno = lineno = cmd->ncmd.linno; + if (funcline) + lineno -= funcline - 1; + /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); @@ -737,7 +756,7 @@ *nargv = NULL; lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) + if (iflag && funcline == 0 && argc > 0) lastarg = nargv[-1]; preverrout.fd = 2; @@ -937,8 +956,10 @@ struct jmploc *volatile savehandler; struct jmploc jmploc; int e; + int savefuncline; saveparam = shellparam; + savefuncline = funcline; if ((e = setjmp(jmploc.loc))) { goto funcdone; } @@ -947,18 +968,18 @@ handler = &jmploc; shellparam.malloc = 0; func->count++; - funcnest++; + funcline = func->n.ndefun.linno; INTON; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optind = 1; shellparam.optoff = -1; pushlocalvars(); - evaltree(func->n.narg.next, flags & EV_TESTED); + evaltree(func->n.ndefun.body, flags & EV_TESTED); poplocalvars(0); funcdone: INTOFF; - funcnest--; + funcline = savefuncline; freefunc(func); freeparam(&shellparam); shellparam = saveparam; @@ -1048,7 +1069,7 @@ * If called outside a function, do what ksh does; * skip the rest of the file. */ - evalskip = funcnest ? SKIPFUNC : SKIPFILE; + evalskip = funcline ? SKIPFUNC : SKIPFILE; return argv[1] ? number(argv[1]) : exitstatus; } diff -r 772aea7ed8c5 src/exec.c --- a/src/exec.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/exec.c Fri Mar 11 22:45:57 2011 +0100 @@ -695,7 +695,7 @@ INTOFF; entry.cmdtype = CMDFUNCTION; entry.u.func = copyfunc(func); - addcmdentry(func->narg.text, &entry); + addcmdentry(func->ndefun.text, &entry); INTON; } diff -r 772aea7ed8c5 src/input.c --- a/src/input.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/input.c Fri Mar 11 22:45:57 2011 +0100 @@ -54,7 +54,6 @@ #include "alias.h" #include "parser.h" #include "main.h" -#include "var.h" #ifndef SMALL #include "myhistedit.h" #endif @@ -531,12 +530,3 @@ parsefile->fd = 0; } } - - -int lineno_inc(void) -{ - int lineno = plinno++; - - setvarint("LINENO", lineno, 0); - return lineno; -} diff -r 772aea7ed8c5 src/input.h --- a/src/input.h Fri Mar 11 22:39:28 2011 +0100 +++ b/src/input.h Fri Mar 11 22:45:57 2011 +0100 @@ -61,7 +61,6 @@ void popfile(void); void popallfiles(void); void closescript(void); -int lineno_inc(void); #define pgetc_macro() \ (--parsenleft >= 0 ? (signed char)*parsenextc++ : preadbuffer()) diff -r 772aea7ed8c5 src/jobs.c --- a/src/jobs.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/jobs.c Fri Mar 11 22:45:57 2011 +0100 @@ -1286,7 +1286,7 @@ p = "; done"; goto dodo; case NDEFUN: - cmdputs(n->narg.text); + cmdputs(n->ndefun.text); p = "() { ... }"; goto dotail2; case NCMD: diff -r 772aea7ed8c5 src/nodetypes --- a/src/nodetypes Fri Mar 11 22:39:28 2011 +0100 +++ b/src/nodetypes Fri Mar 11 22:45:57 2011 +0100 @@ -51,6 +51,7 @@ NCMD ncmd # a simple command type int + linno int assign nodeptr # variable assignments args nodeptr # the arguments redirect nodeptr # list of file redirections @@ -62,6 +63,7 @@ NREDIR nredir # redirection (of a complex command) type int + linno int n nodeptr # the command redirect nodeptr # list of file redirections @@ -87,12 +89,14 @@ NFOR nfor # the for statement type int + linno int args nodeptr # for var in args body nodeptr # do body; done var string # the for variable NCASE ncase # a case statement type int + linno int expr nodeptr # the word to switch on cases nodeptr # the list of cases (NCLIST nodes) @@ -103,8 +107,11 @@ body nodeptr # code to execute for this case -NDEFUN narg # define a function. The "next" field contains - # the body of the function. +NDEFUN ndefun # a function + type int + linno int + text string + body nodeptr NARG narg # represents a word type int diff -r 772aea7ed8c5 src/parser.c --- a/src/parser.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/parser.c Fri Mar 11 22:45:57 2011 +0100 @@ -93,7 +93,6 @@ union node *redirnode; struct heredoc *heredoc; int quoteflag; /* set if (part of) last token was quoted */ -int startlinno; /* line # where last token started */ STATIC union node *list(int); @@ -304,10 +303,13 @@ union node *redir, **rpp; union node **rpp2; int t; + int savelinno; redir = NULL; rpp2 = &redir; + savelinno = plinno; + switch (readtoken()) { default: synexpect(-1); @@ -356,6 +358,7 @@ synerror("Bad for loop variable"); n1 = (union node *)stalloc(sizeof (struct nfor)); n1->type = NFOR; + n1->nfor.linno = savelinno; n1->nfor.var = wordtext; checkkwd = CHKNL | CHKKWD | CHKALIAS; if (readtoken() == TIN) { @@ -395,6 +398,7 @@ case TCASE: n1 = (union node *)stalloc(sizeof (struct ncase)); n1->type = NCASE; + n1->ncase.linno = savelinno; if (readtoken() != TWORD) synexpect(TWORD); n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); @@ -445,6 +449,7 @@ case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; + n1->nredir.linno = savelinno; n1->nredir.n = list(0); n1->nredir.redirect = NULL; t = TRP; @@ -477,6 +482,7 @@ if (n1->type != NSUBSHELL) { n2 = (union node *)stalloc(sizeof (struct nredir)); n2->type = NREDIR; + n2->nredir.linno = savelinno; n2->nredir.n = n1; n1 = n2; } @@ -494,6 +500,7 @@ union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; + int savelinno; args = NULL; app = &args; @@ -503,6 +510,7 @@ rpp = &redir; savecheckkwd = CHKALIAS; + savelinno = plinno; for (;;) { checkkwd = savecheckkwd; switch (readtoken()) { @@ -546,7 +554,9 @@ synerror("Bad function name"); n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; - n->narg.next = command(); + n->ndefun.text = n->narg.text; + n->ndefun.linno = plinno; + n->ndefun.body = command(); return n; } /* fall through */ @@ -561,6 +571,7 @@ *rpp = NULL; n = (union node *)stalloc(sizeof (struct ncmd)); n->type = NCMD; + n->ncmd.linno = savelinno; n->ncmd.args = args; n->ncmd.assign = vars; n->ncmd.redirect = redir; @@ -738,8 +749,6 @@ * quoted. * If the token is TREDIR, then we set redirnode to a structure containing * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the @@ -763,7 +772,6 @@ if (needprompt) { setprompt(2); } - startlinno = plinno; for (;;) { /* until token or start of word found */ c = pgetc_macro(); switch (c) { @@ -776,7 +784,7 @@ continue; case '\\': if (pgetc() == '\n') { - startlinno = lineno_inc(); + plinno++; if (doprompt) setprompt(2); continue; @@ -784,7 +792,7 @@ pungetc(); goto breakloop; case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; RETURN(TNL); case PEOF: @@ -855,7 +863,6 @@ /* syntax before arithmetic */ char const *uninitialized_var(prevsyntax); - startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; @@ -886,7 +893,7 @@ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - lineno_inc(); + plinno++; if (doprompt) setprompt(2); c = pgetc(); @@ -907,6 +914,7 @@ USTPUTC('\\', out); pungetc(); } else if (c == '\n') { + plinno++; if (doprompt) setprompt(2); } else { @@ -1008,7 +1016,6 @@ if (syntax != BASESYNTAX && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { - startlinno = plinno; /* { */ synerror("Missing '}'"); } @@ -1065,7 +1072,7 @@ if (c == '\n' || c == PEOF) { c = PEOF; - lineno_inc(); + plinno++; needprompt = doprompt; } else { int len; @@ -1315,7 +1322,7 @@ case '\\': if ((pc = pgetc()) == '\n') { - lineno_inc(); + plinno++; if (doprompt) setprompt(2); /* @@ -1336,11 +1343,10 @@ case PEOF: case PEOA: - startlinno = plinno; synerror("EOF in backquote substitution"); case '\n': - lineno_inc(); + plinno++; needprompt = doprompt; break; @@ -1472,6 +1478,7 @@ STATIC void synerror(const char *msg) { + errlinno = plinno; sh_error("Syntax error: %s", msg); /* NOTREACHED */ } diff -r 772aea7ed8c5 src/parser.h --- a/src/parser.h Fri Mar 11 22:39:28 2011 +0100 +++ b/src/parser.h Fri Mar 11 22:45:57 2011 +0100 @@ -77,7 +77,6 @@ #define NEOF ((union node *)&tokpushback) extern int whichprompt; /* 1 == PS1, 2 == PS2 */ extern int checkkwd; -extern int startlinno; /* line # where last token started */ union node *parsecmd(int); diff -r 772aea7ed8c5 src/var.c --- a/src/var.c Fri Mar 11 22:39:28 2011 +0100 +++ b/src/var.c Fri Mar 11 22:45:57 2011 +0100 @@ -33,6 +33,7 @@ */ #include <unistd.h> +#include <stdio.h> #include <stdlib.h> #include <paths.h> @@ -78,6 +79,9 @@ const char defifs[] = " \t\n"; #endif +int lineno; +char linenovar[sizeof("LINENO=")+sizeof(int)*CHAR_BIT/3+1] = "LINENO="; + /* Some macros in var.h depend on the order, add new variables to the end. */ struct var varinit[] = { #if ATTY @@ -95,11 +99,11 @@ { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, + { 0, VSTRFIXED|VTEXTFIXED, linenovar, 0 }, #ifndef SMALL { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, #endif - { 0, VSTRFIXED|VTEXTFIXED, "LINENO=1", 0 }, }; STATIC struct var *vartab[VTABSIZE]; @@ -329,6 +333,9 @@ struct var *v; if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { + if (v == &vlineno && v->text == linenovar) { + fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno); + } return strchrnul(v->text, '=') + 1; } return NULL; diff -r 772aea7ed8c5 src/var.h --- a/src/var.h Fri Mar 11 22:39:28 2011 +0100 +++ b/src/var.h Fri Mar 11 22:45:57 2011 +0100 @@ -88,8 +88,9 @@ #define vps2 (&vps1)[1] #define vps4 (&vps2)[1] #define voptind (&vps4)[1] +#define vlineno (&voptind)[1] #ifndef SMALL -#define vterm (&voptind)[1] +#define vterm (&vlineno)[1] #define vhistsize (&vterm)[1] #endif @@ -102,6 +103,9 @@ extern const char defpathvar[]; #define defpath (defpathvar + 5) +extern int lineno; +extern char linenovar[]; + /* * The following macros access the values of the above variables. * They have to skip over the name. They return the null string @@ -117,6 +121,7 @@ #define ps2val() (vps2.text + 4) #define ps4val() (vps4.text + 4) #define optindval() (voptind.text + 7) +#define linenoval() (vlineno.text + 7) #ifndef SMALL #define histsizeval() (vhistsize.text + 9) #define termval() (vterm.text + 5) ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Improved LINENO support 2011-03-11 21:56 ` Harald van Dijk @ 2011-03-15 7:52 ` Herbert Xu 0 siblings, 0 replies; 12+ messages in thread From: Herbert Xu @ 2011-03-15 7:52 UTC (permalink / raw) To: Harald van Dijk; +Cc: dash, Jilles Tjoelker On Fri, Mar 11, 2011 at 09:56:49PM +0000, Harald van Dijk wrote: > > Signed-off-by: Harald van Dijk <harald@gigawatt.nl> Both patches applied. Thanks a lot! -- Email: Herbert Xu <herbert@gondor.apana.org.au> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2011-03-15 7:52 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-11-08 20:33 [PATCH] Improved LINENO support Harald van Dijk 2010-11-10 22:00 ` Jilles Tjoelker 2010-11-11 20:33 ` Harald van Dijk 2010-11-12 18:17 ` Harald van Dijk 2010-11-12 18:35 ` Eric Blake 2010-11-12 19:08 ` Harald van Dijk 2010-11-12 19:40 ` Harald van Dijk 2010-11-12 21:29 ` Jilles Tjoelker 2010-11-27 16:56 ` Harald van Dijk 2011-03-10 8:10 ` Herbert Xu 2011-03-11 21:56 ` Harald van Dijk 2011-03-15 7:52 ` Herbert Xu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox