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)