blob: a71549d6ac35bd1f0b96a511afba613c8ccf3f43 [file] [log] [blame]
This patch is a backport from dash of the combination of:
[SHELL] Add preliminary LINENO support
[VAR] Fix varinit ordering that broke fc
[SHELL] Improve LINENO support
Applies cleanly on top of:
commit 9832bbaba966f0e52e183f10cd93fad7f8f643fe
Date: Tue Aug 15 15:44:41 2017 +0200
Testsuite needs some tweaks (line numbers in some messages change).
Unfortunately, it is somewhat big:
function old new delta
parse_command 1581 1658 +77
calcsize 203 272 +69
copynode 195 257 +62
lookupvar 59 108 +49
evaltree 494 534 +40
evalfor 152 187 +35
evalcase 278 313 +35
evalcommand 1547 1581 +34
evalsubshell 169 199 +30
linenovar - 22 +22
raise_error_syntax 11 29 +18
evalfun 266 280 +14
varinit_data 96 108 +12
cmdtxt 626 631 +5
lineno - 4 +4
funcline - 4 +4
ash_vmsg 144 141 -3
startlinno 4 - -4
funcnest 4 - -4
xxreadtoken 272 259 -13
readtoken1 2635 2594 -41
------------------------------------------------------------------------------
(add/remove: 3/2 grow/shrink: 13/3 up/down: 510/-65) Total: 445 bytes
text data bss dec hex filename
912030 563 5844 918437 e03a5 busybox_old
912473 587 5844 918904 e0578 busybox_unstripped
diff --git a/shell/ash.c b/shell/ash.c
index 703802f..93a3814 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -312,6 +312,8 @@ struct globals_misc {
/* shell level: 0 for the main shell, 1 for its children, and so on */
int shlvl;
#define rootshell (!shlvl)
+ int errlinno;
+
char *minusc; /* argument to -c option */
char *curdir; // = nullstr; /* current working directory */
@@ -389,6 +391,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
#define job_warning (G_misc.job_warning)
#define rootpid (G_misc.rootpid )
#define shlvl (G_misc.shlvl )
+#define errlinno (G_misc.errlinno )
#define minusc (G_misc.minusc )
#define curdir (G_misc.curdir )
#define physdir (G_misc.physdir )
@@ -723,6 +726,7 @@ union node;
struct ncmd {
smallint type; /* Nxxxx */
+ int linno;
union node *assign;
union node *args;
union node *redirect;
@@ -736,6 +740,7 @@ struct npipe {
struct nredir {
smallint type;
+ int linno;
union node *n;
union node *redirect;
};
@@ -755,6 +760,7 @@ struct nif {
struct nfor {
smallint type;
+ int linno;
union node *args;
union node *body;
char *var;
@@ -762,6 +768,7 @@ struct nfor {
struct ncase {
smallint type;
+ int linno;
union node *expr;
union node *cases;
};
@@ -773,6 +780,13 @@ struct nclist {
union node *body;
};
+struct ndefun {
+ smallint type;
+ int linno;
+ char *text;
+ union node *body;
+};
+
struct narg {
smallint type;
union node *next;
@@ -824,6 +838,7 @@ union node {
struct nfor nfor;
struct ncase ncase;
struct nclist nclist;
+ struct ndefun ndefun;
struct narg narg;
struct nfile nfile;
struct ndup ndup;
@@ -1253,7 +1268,6 @@ struct parsefile {
static struct parsefile basepf; /* top level input file */
static struct parsefile *g_parsefile = &basepf; /* current input file */
-static int startlinno; /* line # where last token started */
static char *commandname; /* currently executing command */
@@ -1267,7 +1281,7 @@ ash_vmsg(const char *msg, va_list ap)
if (strcmp(arg0, commandname))
fprintf(stderr, "%s: ", commandname);
if (!iflag || g_parsefile->pf_fd > 0)
- fprintf(stderr, "line %d: ", startlinno);
+ fprintf(stderr, "line %d: ", errlinno);
}
vfprintf(stderr, msg, ap);
newline_and_flush(stderr);
@@ -1327,6 +1341,7 @@ static void raise_error_syntax(const char *) NORETURN;
static void
raise_error_syntax(const char *msg)
{
+ errlinno = g_parsefile->linno;
ash_msg_and_raise_error("syntax error: %s", msg);
/* NOTREACHED */
}
@@ -1993,6 +2008,9 @@ static void changepath(const char *) FAST_FUNC;
static void change_random(const char *) FAST_FUNC;
#endif
+static int lineno;
+static char linenovar[sizeof("LINENO=%d") + sizeof(int)*3] = "LINENO=";
+
static const struct {
int flags;
const char *var_text;
@@ -2014,6 +2032,7 @@ static const struct {
#if ENABLE_ASH_GETOPTS
{ VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
#endif
+ { VSTRFIXED|VTEXTFIXED , linenovar , NULL },
#if ENABLE_ASH_RANDOM_SUPPORT
{ VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
#endif
@@ -2066,12 +2085,14 @@ extern struct globals_var *const ash_ptr_to_globals_var;
#define vps4 (&vps2)[1]
#if ENABLE_ASH_GETOPTS
# define voptind (&vps4)[1]
+# define vlineno (&voptind)[1]
# if ENABLE_ASH_RANDOM_SUPPORT
-# define vrandom (&voptind)[1]
+# define vrandom (&vlineno)[1]
# endif
#else
+# define vlineno (&vps4)[1]
# if ENABLE_ASH_RANDOM_SUPPORT
-# define vrandom (&vps4)[1]
+# define vrandom (&vlineno)[1]
# endif
#endif
@@ -2209,8 +2230,12 @@ lookupvar(const char *name)
if (v->flags & VDYNAMIC)
v->var_func(NULL);
#endif
- if (!(v->flags & VUNSET))
+ if (!(v->flags & VUNSET)) {
+ if (v == &vlineno && v->var_text == linenovar) {
+ fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
+ }
return var_end(v->var_text);
+ }
}
return NULL;
}
@@ -4783,7 +4808,7 @@ cmdtxt(union node *n)
p = "; done";
goto dodo;
case NDEFUN:
- cmdputs(n->narg.text);
+ cmdputs(n->ndefun.text);
p = "() { ... }";
goto dotail2;
case NCMD:
@@ -8551,6 +8576,9 @@ calcsize(int funcblocksize, union node *n)
funcblocksize = calcsize(funcblocksize, n->nclist.next);
break;
case NDEFUN:
+ funcblocksize = calcsize(funcblocksize, n->ndefun.body);
+ funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
+ break;
case NARG:
funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
@@ -8626,6 +8654,7 @@ copynode(union node *n)
new->ncmd.redirect = copynode(n->ncmd.redirect);
new->ncmd.args = copynode(n->ncmd.args);
new->ncmd.assign = copynode(n->ncmd.assign);
+ new->ncmd.linno = n->ncmd.linno;
break;
case NPIPE:
new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
@@ -8636,6 +8665,7 @@ copynode(union node *n)
case NSUBSHELL:
new->nredir.redirect = copynode(n->nredir.redirect);
new->nredir.n = copynode(n->nredir.n);
+ new->nredir.linno = n->nredir.linno;
break;
case NAND:
case NOR:
@@ -8654,10 +8684,12 @@ copynode(union node *n)
new->nfor.var = nodeckstrdup(n->nfor.var);
new->nfor.body = copynode(n->nfor.body);
new->nfor.args = copynode(n->nfor.args);
+ new->nfor.linno = n->nfor.linno;
break;
case NCASE:
new->ncase.cases = copynode(n->ncase.cases);
new->ncase.expr = copynode(n->ncase.expr);
+ new->ncase.linno = n->ncase.linno;
break;
case NCLIST:
new->nclist.body = copynode(n->nclist.body);
@@ -8665,6 +8697,10 @@ copynode(union node *n)
new->nclist.next = copynode(n->nclist.next);
break;
case NDEFUN:
+ new->ndefun.body = copynode(n->ndefun.body);
+ new->ndefun.text = nodeckstrdup(n->ndefun.text);
+ new->ndefun.linno = n->ndefun.linno;
+ break;
case NARG:
new->narg.backquote = copynodelist(n->narg.backquote);
new->narg.text = nodeckstrdup(n->narg.text);
@@ -8733,7 +8769,7 @@ defun(union node *func)
INT_OFF;
entry.cmdtype = CMDFUNCTION;
entry.u.func = copyfunc(func);
- addcmdentry(func->narg.text, &entry);
+ addcmdentry(func->ndefun.text, &entry);
INT_ON;
}
@@ -8743,8 +8779,8 @@ defun(union node *func)
#define SKIPFUNC (1 << 2)
static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
static int skipcount; /* number of levels to skip */
-static int funcnest; /* depth of function calls */
static int loopnest; /* current loop nesting level */
+static int funcline; /* starting line number of current function, or 0 if not in a function */
/* Forward decl way out to parsing code - dotrap needs it */
static int evalstring(char *s, int flags);
@@ -8839,6 +8875,9 @@ evaltree(union node *n, int flags)
status = !evaltree(n->nnot.com, EV_TESTED);
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);
@@ -8993,6 +9032,10 @@ evalfor(union node *n, int flags)
struct stackmark smark;
int status = 0;
+ errlinno = lineno = n->ncase.linno;
+ if (funcline)
+ lineno -= funcline - 1;
+
setstackmark(&smark);
arglist.list = NULL;
arglist.lastp = &arglist.list;
@@ -9024,6 +9067,10 @@ evalcase(union node *n, int flags)
struct stackmark smark;
int status = 0;
+ errlinno = lineno = n->ncase.linno;
+ if (funcline)
+ lineno -= funcline - 1;
+
setstackmark(&smark);
arglist.list = NULL;
arglist.lastp = &arglist.list;
@@ -9058,6 +9105,10 @@ evalsubshell(union node *n, int flags)
int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
int status;
+ errlinno = lineno = n->nredir.linno;
+ if (funcline)
+ lineno -= funcline - 1;
+
expredir(n->nredir.redirect);
if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
goto nofork;
@@ -9365,8 +9416,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;
savehandler = exception_handler;
e = setjmp(jmploc.loc);
if (e) {
@@ -9376,7 +9429,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
exception_handler = &jmploc;
shellparam.malloced = 0;
func->count++;
- funcnest++;
+ funcline = func->n.ndefun.linno;
INT_ON;
shellparam.nparam = argc - 1;
shellparam.p = argv + 1;
@@ -9385,11 +9438,11 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
shellparam.optoff = -1;
#endif
pushlocalvars();
- evaltree(func->n.narg.next, flags & EV_TESTED);
+ evaltree(func->n.ndefun.body, flags & EV_TESTED);
poplocalvars(0);
funcdone:
INT_OFF;
- funcnest--;
+ funcline = savefuncline;
freefunc(func);
freeparam(&shellparam);
shellparam = saveparam;
@@ -9753,6 +9806,10 @@ evalcommand(union node *cmd, int flags)
char **nargv;
smallint cmd_is_exec;
+ 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);
@@ -9798,7 +9855,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];
expredir(cmd->ncmd.redirect);
@@ -11317,6 +11374,7 @@ simplecmd(void)
union node *vars, **vpp;
union node **rpp, *redir;
int savecheckkwd;
+ int savelinno;
#if BASH_TEST2
smallint double_brackets_flag = 0;
#endif
@@ -11330,6 +11388,7 @@ simplecmd(void)
rpp = &redir;
savecheckkwd = CHKALIAS;
+ savelinno = g_parsefile->linno;
for (;;) {
int t;
checkkwd = savecheckkwd;
@@ -11419,7 +11478,9 @@ simplecmd(void)
}
n->type = NDEFUN;
checkkwd = CHKNL | CHKKWD | CHKALIAS;
- n->narg.next = parse_command();
+ n->ndefun.text = n->narg.text;
+ n->ndefun.linno = g_parsefile->linno;
+ n->ndefun.body = parse_command();
return n;
}
IF_BASH_FUNCTION(function_flag = 0;)
@@ -11435,6 +11496,7 @@ simplecmd(void)
*rpp = NULL;
n = stzalloc(sizeof(struct ncmd));
n->type = NCMD;
+ n->ncmd.linno = savelinno;
n->ncmd.args = args;
n->ncmd.assign = vars;
n->ncmd.redirect = redir;
@@ -11450,10 +11512,13 @@ parse_command(void)
union node *redir, **rpp;
union node **rpp2;
int t;
+ int savelinno;
redir = NULL;
rpp2 = &redir;
+ savelinno = g_parsefile->linno;
+
switch (readtoken()) {
default:
raise_error_unexpected_syntax(-1);
@@ -11504,6 +11569,7 @@ parse_command(void)
raise_error_syntax("bad for loop variable");
n1 = stzalloc(sizeof(struct nfor));
n1->type = NFOR;
+ n1->nfor.linno = savelinno;
n1->nfor.var = wordtext;
checkkwd = CHKNL | CHKKWD | CHKALIAS;
if (readtoken() == TIN) {
@@ -11544,6 +11610,7 @@ parse_command(void)
case TCASE:
n1 = stzalloc(sizeof(struct ncase));
n1->type = NCASE;
+ n1->ncase.linno = savelinno;
if (readtoken() != TWORD)
raise_error_unexpected_syntax(TWORD);
n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
@@ -11595,6 +11662,7 @@ parse_command(void)
case TLP:
n1 = stzalloc(sizeof(struct nredir));
n1->type = NSUBSHELL;
+ n1->nredir.linno = savelinno;
n1->nredir.n = list(0);
/*n1->nredir.redirect = NULL; - stzalloc did it */
t = TRP;
@@ -11628,6 +11696,7 @@ parse_command(void)
if (n1->type != NSUBSHELL) {
n2 = stzalloc(sizeof(struct nredir));
n2->type = NREDIR;
+ n2->nredir.linno = savelinno;
n2->nredir.n = n1;
n1 = n2;
}
@@ -11726,10 +11795,8 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
IF_FEATURE_SH_MATH(int arinest;) /* levels of arithmetic expansion */
IF_FEATURE_SH_MATH(int parenlevel;) /* levels of parens in arithmetic */
int dqvarnest; /* levels of variables expansion within double quotes */
-
IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
- startlinno = g_parsefile->linno;
bqlist = NULL;
quotef = 0;
IF_FEATURE_SH_MATH(prevsyntax = 0;)
@@ -11906,7 +11973,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
if (syntax != BASESYNTAX && eofmark == NULL)
raise_error_syntax("unterminated quoted string");
if (varnest != 0) {
- startlinno = g_parsefile->linno;
/* { */
raise_error_syntax("missing '}'");
}
@@ -12298,7 +12364,6 @@ parsebackq: {
case PEOF:
IF_ASH_ALIAS(case PEOA:)
- startlinno = g_parsefile->linno;
raise_error_syntax("EOF in backquote substitution");
case '\n':
@@ -12380,8 +12445,6 @@ parsearith: {
* 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
@@ -12419,7 +12482,6 @@ xxreadtoken(void)
return lasttoken;
}
setprompt_if(needprompt, 2);
- startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
c = pgetc();
if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
@@ -12480,7 +12542,6 @@ xxreadtoken(void)
return lasttoken;
}
setprompt_if(needprompt, 2);
- startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
c = pgetc();
switch (c) {