volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
- smallint exception_type; /* kind of exception (0..5) */
- /* exceptions */
+ smallint exception_type; /* kind of exception: */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
-#define EXEXIT 4 /* exit the shell */
+#define EXEND 3 /* exit the shell */
+#define EXEXIT 4 /* exit the shell via exitcmd */
char nullstr[1]; /* zero length string */
* part of the block that has been used.
*/
static void
-growstackblock(void)
+growstackblock(size_t min)
{
size_t newlen;
newlen = g_stacknleft * 2;
if (newlen < g_stacknleft)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
- if (newlen < 128)
- newlen += 128;
+ min = SHELL_ALIGN(min | 128);
+ if (newlen < min)
+ newlen += min;
if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
struct stack_block *sp;
growstackstr(void)
{
size_t len = stackblocksize();
- growstackblock();
+ growstackblock(0);
return (char *)stackblock() + len;
}
static char *
growstackto(size_t len)
{
- while (stackblocksize() < len)
- growstackblock();
-
+ if (stackblocksize() < len)
+ growstackblock(len);
return stackblock();
}
/* holds expanded arg list */
static struct arglist exparg;
-/*
- * Our own itoa().
- * cvtnum() is used even if math support is off (to prepare $? values and such).
- */
-static int
-cvtnum(arith_t num)
-{
- int len;
-
- /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
- len = sizeof(arith_t) * 3;
- /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
- if (sizeof(arith_t) < 4) len += 2;
-
- expdest = makestrspace(len, expdest);
- len = fmtstr(expdest, len, ARITH_FMT, num);
- STADJUST(len, expdest);
- return len;
-}
-
/*
* Break the argument string into pieces based upon IFS and add the
* strings to the argument list. The regions of the string to be
/*
* Put a string on the stack.
*/
-static void
+static size_t
memtodest(const char *p, size_t len, int flags)
{
int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
char *q;
+ char *s;
if (!len)
- return;
+ return 0;
q = makestrspace(len * 2, expdest);
+ s = q;
do {
unsigned char c = *p++;
} while (--len);
expdest = q;
+ return q - s;
}
static size_t
return len;
}
+/*
+ * Our own itoa().
+ * cvtnum() is used even if math support is off (to prepare $? values and such).
+ */
+static int
+cvtnum(arith_t num, int flags)
+{
+ /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
+ /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
+ int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
+ char buf[len];
+
+ len = fmtstr(buf, len, ARITH_FMT, num);
+ return memtodest(buf, len, flags);
+}
+
/*
* Record the fact that we have to scan this region of the
* string for IFS characters.
if (flag & QUOTES_ESC)
rmescapes(p + 1, 0, NULL);
- len = cvtnum(ash_arith(p + 1));
+ len = cvtnum(ash_arith(p + 1), flag);
if (!(flag & EXP_QUOTED))
recordregion(begoff, begoff + len, 0);
if (num == 0)
return -1;
numvar:
- len = cvtnum(num);
+ len = cvtnum(num, flags);
goto check_1char_name;
case '-':
expdest = makestrspace(NOPTS, expdest);
varunset(p, var, 0, 0);
if (subtype == VSLENGTH) {
- cvtnum(varlen > 0 ? varlen : 0);
+ cvtnum(varlen > 0 ? varlen : 0, flag);
goto record;
}
exitstatus = exerrno;
TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
prog, e, suppress_int));
- ash_msg_and_raise(EXEXIT, "%s: %s", prog, errmsg(e, "not found"));
+ ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
/* NOTREACHED */
}
#define SKIPBREAK (1 << 0)
#define SKIPCONT (1 << 1)
#define SKIPFUNC (1 << 2)
+#define SKIPFUNCDEF (1 << 3)
static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
static int skipcount; /* number of levels to skip */
static int loopnest; /* current loop nesting level */
if (!p)
continue;
evalstring(p, 0);
- exitstatus = status;
+ if (evalskip != SKIPFUNC)
+ exitstatus = status;
}
savestatus = last_status;
dotrap();
if (checkexit & status)
- raise_exception(EXEXIT);
+ raise_exception(EXEND);
if (flags & EV_EXIT)
- raise_exception(EXEXIT);
+ raise_exception(EXEND);
popstackmark(&smark);
TRACE(("leaving evaltree (no interrupts)\n"));
shellparam = saveparam;
exception_handler = savehandler;
INT_ON;
- evalskip &= ~SKIPFUNC;
+ evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
return e;
}
static int FAST_FUNC
returncmd(int argc UNUSED_PARAM, char **argv)
{
+ int skip;
+ int status;
+
/*
* If called outside a function, do what ksh does;
* skip the rest of the file.
*/
- evalskip = SKIPFUNC;
- return argv[1] ? number(argv[1]) : exitstatus;
+ if (argv[1]) {
+ skip = SKIPFUNC;
+ status = number(argv[1]);
+ } else {
+ skip = SKIPFUNCDEF;
+ status = exitstatus;
+ }
+ evalskip = skip;
+
+ return status;
}
/* Forward declarations for builtintab[] */
if (readtoken() != TRP)
raise_error_unexpected_syntax(TRP);
setinputstring(nullstr);
- parseheredoc();
}
+ parseheredoc();
heredoclist = saveheredoclist;
(*nlpp)->n = n;
skip = evalskip;
if (skip) {
- evalskip &= ~SKIPFUNC;
+ evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
break;
}
}
/* ============ main() and helpers */
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop
+ * but prior to exitshell.
+ */
+static void
+exitreset(void)
+{
+ /* from eval.c: */
+ if (savestatus >= 0) {
+ if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
+ exitstatus = savestatus;
+ savestatus = -1;
+ }
+ evalskip = 0;
+ loopnest = 0;
+
+ /* from expand.c: */
+ ifsfree();
+
+ /* from redir.c: */
+ unwindredir(NULL);
+}
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ * (In dash, this function is auto-generated by build machinery).
+ */
+static void
+reset(void)
+{
+ /* from input.c: */
+ g_parsefile->left_in_buffer = 0;
+ g_parsefile->left_in_line = 0; /* clear input buffer */
+ popallfiles();
+
+ /* from var.c: */
+ unwindlocalvars(NULL);
+}
+
/*
* Called to exit the shell.
*/
trap[0] = NULL;
evalskip = 0;
evalstring(p, 0);
+ evalskip = SKIPFUNCDEF;
/*free(p); - we'll exit soon */
}
out:
+ exitreset();
/* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
* our setjobctl(0) does not panic if tcsetpgrp fails inside it.
*/
setjobctl(0);
flush_stdout_stderr();
- _exit(savestatus);
+ _exit(exitstatus);
/* NOTREACHED */
}
popfile();
}
-/*
- * This routine is called when an error or an interrupt occurs in an
- * interactive shell and control is returned to the main command loop
- * but prior to exitshell.
- */
-static void
-exitreset(void)
-{
- /* from eval.c: */
- evalskip = 0;
- loopnest = 0;
- if (savestatus >= 0) {
- exitstatus = savestatus;
- savestatus = -1;
- }
-
- /* from expand.c: */
- ifsfree();
-
- /* from redir.c: */
- unwindredir(NULL);
-}
-
-/*
- * This routine is called when an error or an interrupt occurs in an
- * interactive shell and control is returned to the main command loop.
- * (In dash, this function is auto-generated by build machinery).
- */
-static void
-reset(void)
-{
- /* from input.c: */
- g_parsefile->left_in_buffer = 0;
- g_parsefile->left_in_line = 0; /* clear input buffer */
- popallfiles();
-
- /* from var.c: */
- unwindlocalvars(NULL);
-}
-
#if PROFILE
static short profile_buf[16384];
extern int etext();
e = exception_type;
s = state;
- if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
+ if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
exitshell();
}