aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald van Dijk <harald@gigawatt.nl>2011-03-15 15:52:05 +0800
committermaximilian attems <max@stro.at>2011-06-03 18:44:14 +0200
commit8a6231c756cdfb04d636605211e808836f6bfb82 (patch)
treead8c477d34003d5b9ccd7c2752a98bd1601b9830
parentfe205dd103518dfc9e990b5bf8026bce2002dad8 (diff)
downloadklibc-8a6231c756cdfb04d636605211e808836f6bfb82.tar.gz
[klibc] [SHELL] Improve LINENO support
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 Signed-off-by: Harald van Dijk <harald@gigawatt.nl> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: maximilian attems <max@stro.at>
-rw-r--r--usr/dash/error.c12
-rw-r--r--usr/dash/error.h1
-rw-r--r--usr/dash/eval.c33
-rw-r--r--usr/dash/exec.c2
-rw-r--r--usr/dash/input.c10
-rw-r--r--usr/dash/input.h1
-rw-r--r--usr/dash/jobs.c2
-rw-r--r--usr/dash/nodetypes11
-rw-r--r--usr/dash/parser.c35
-rw-r--r--usr/dash/parser.h1
-rw-r--r--usr/dash/var.c9
-rw-r--r--usr/dash/var.h7
12 files changed, 80 insertions, 44 deletions
diff --git a/usr/dash/error.c b/usr/dash/error.c
index f1a358db04724..7ad73bca6bf18 100644
--- a/usr/dash/error.c
+++ b/usr/dash/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/usr/dash/error.h b/usr/dash/error.h
index 94e613ac93e6e..f91d11d14fd35 100644
--- a/usr/dash/error.h
+++ b/usr/dash/error.h
@@ -123,6 +123,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/usr/dash/eval.c b/usr/dash/eval.c
index 0ff430b624650..26302a7b60a33 100644
--- a/usr/dash/eval.c
+++ b/usr/dash/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);
@@ -376,6 +379,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) {
@@ -417,6 +424,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);
@@ -448,6 +459,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;
@@ -704,6 +719,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);
@@ -737,7 +756,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;
@@ -937,8 +956,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;
}
@@ -947,18 +968,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.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 @@ 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/usr/dash/exec.c b/usr/dash/exec.c
index a13ad67d80966..79e200740fa49 100644
--- a/usr/dash/exec.c
+++ b/usr/dash/exec.c
@@ -697,7 +697,7 @@ defun(union node *func)
INTOFF;
entry.cmdtype = CMDFUNCTION;
entry.u.func = copyfunc(func);
- addcmdentry(func->narg.text, &entry);
+ addcmdentry(func->ndefun.text, &entry);
INTON;
}
diff --git a/usr/dash/input.c b/usr/dash/input.c
index bd3a9a27d53e1..d31c45bb4a5e8 100644
--- a/usr/dash/input.c
+++ b/usr/dash/input.c
@@ -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 @@ closescript(void)
parsefile->fd = 0;
}
}
-
-
-int lineno_inc(void)
-{
- int lineno = plinno++;
-
- setvarint("LINENO", lineno, 0);
- return lineno;
-}
diff --git a/usr/dash/input.h b/usr/dash/input.h
index bdf8857b4b17c..50a77971ebfdf 100644
--- a/usr/dash/input.h
+++ b/usr/dash/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/usr/dash/jobs.c b/usr/dash/jobs.c
index 23b1b9d9a638d..009bbfeee47ec 100644
--- a/usr/dash/jobs.c
+++ b/usr/dash/jobs.c
@@ -1287,7 +1287,7 @@ dotail:
p = "; done";
goto dodo;
case NDEFUN:
- cmdputs(n->narg.text);
+ cmdputs(n->ndefun.text);
p = "() { ... }";
goto dotail2;
case NCMD:
diff --git a/usr/dash/nodetypes b/usr/dash/nodetypes
index 17a7b3c8315d4..ceaf478c40445 100644
--- a/usr/dash/nodetypes
+++ b/usr/dash/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)
@@ -103,8 +107,11 @@ NCLIST nclist # a 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/usr/dash/parser.c b/usr/dash/parser.c
index be20ff735498b..0bfd6203e901a 100644
--- a/usr/dash/parser.c
+++ b/usr/dash/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()) {
@@ -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/usr/dash/parser.h b/usr/dash/parser.h
index 6bdf1c9e873e0..e6caed632fd85 100644
--- a/usr/dash/parser.h
+++ b/usr/dash/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/usr/dash/var.c b/usr/dash/var.c
index aec10769af306..ecc8c90ed060a 100644
--- a/usr/dash/var.c
+++ b/usr/dash/var.c
@@ -33,6 +33,7 @@
*/
#include <unistd.h>
+#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
@@ -80,6 +81,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
@@ -97,11 +101,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];
@@ -331,6 +335,9 @@ lookupvar(const char *name)
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 --git a/usr/dash/var.h b/usr/dash/var.h
index 3e9568a2dc380..4c02eb243a519 100644
--- a/usr/dash/var.h
+++ b/usr/dash/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)