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.
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)

Reply via email to