#define DEBUG_TIME 0
#define DEBUG_PID 1
#define DEBUG_SIG 1
+#define DEBUG_INTONOFF 0
#define PROFILE 0
volatile int suppress_int; /* counter */
volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
- /* last pending signal */
- volatile /*sig_atomic_t*/ smallint pending_sig;
+ 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 */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXEXIT 4 /* exit the shell */
-#define EXSIG 5 /* trapped signal in wait(1) */
smallint isloginsh;
char nullstr[1]; /* zero length string */
#define exception_type (G_misc.exception_type )
#define suppress_int (G_misc.suppress_int )
#define pending_int (G_misc.pending_int )
+#define got_sigchld (G_misc.got_sigchld )
#define pending_sig (G_misc.pending_sig )
#define isloginsh (G_misc.isloginsh )
#define nullstr (G_misc.nullstr )
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
-#define INT_OFF do { \
+#if DEBUG_INTONOFF
+# define INT_OFF do { \
+ TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
suppress_int++; \
barrier(); \
} while (0)
+#else
+# define INT_OFF do { \
+ suppress_int++; \
+ barrier(); \
+} while (0)
+#endif
/*
* Called to raise an exception. Since C doesn't include exceptions, we
static void
raise_interrupt(void)
{
- int ex_type;
-
pending_int = 0;
/* Signal is not automatically unmasked after it is raised,
* do it ourself - unmask all signals */
sigprocmask_allsigs(SIG_UNBLOCK);
/* pending_sig = 0; - now done in signal_handler() */
- ex_type = EXSIG;
- if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
- if (!(rootshell && iflag)) {
- /* Kill ourself with SIGINT */
- signal(SIGINT, SIG_DFL);
- raise(SIGINT);
- }
- ex_type = EXINT;
+ if (!(rootshell && iflag)) {
+ /* Kill ourself with SIGINT */
+ signal(SIGINT, SIG_DFL);
+ raise(SIGINT);
}
/* bash: ^C even on empty command line sets $? */
exitstatus = SIGINT + 128;
- raise_exception(ex_type);
+ raise_exception(EXINT);
/* NOTREACHED */
}
#if DEBUG
raise_interrupt();
}
}
-#define INT_ON int_on()
+#if DEBUG_INTONOFF
+# define INT_ON do { \
+ TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
+ int_on(); \
+} while (0)
+#else
+# define INT_ON int_on()
+#endif
static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
force_int_on(void)
{
{
if (debug != 1)
return;
- if (DEBUG_TIME)
- fprintf(tracefile, "%u ", (int) time(NULL));
- if (DEBUG_PID)
- fprintf(tracefile, "[%u] ", (int) getpid());
- if (DEBUG_SIG)
- fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
vfprintf(tracefile, fmt, va);
+ fprintf(tracefile, "\n");
}
static void
{
#if DEBUG
if (msg) {
- TRACE(("ash_vmsg_and_raise(%d, \"", cond));
+ TRACE(("ash_vmsg_and_raise(%d):", cond));
TRACEV((msg, ap));
- TRACE(("\") pid=%d\n", getpid()));
} else
- TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
+ TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
if (msg)
#endif
ash_vmsg(msg, ap);
return memcpy(stalloc(len), p, len);
}
-static void
+static inline void
grabstackblock(size_t len)
{
- len = SHELL_ALIGN(len);
- g_stacknxt += len;
- g_stacknleft -= len;
+ stalloc(len);
}
static void
if (flags & VNOSAVE)
free(s);
n = vp->var_text;
+ exitstatus = 1;
ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
}
setvar(name, val, 0);
}
-#if ENABLE_ASH_GETOPTS
-/*
- * Safe version of setvar, returns 1 on success 0 on failure.
- */
-static int
-setvarsafe(const char *name, const char *val, int flags)
-{
- int err;
- volatile int saveint;
- struct jmploc *volatile savehandler = exception_handler;
- struct jmploc jmploc;
-
- SAVE_INT(saveint);
- if (setjmp(jmploc.loc))
- err = 1;
- else {
- exception_handler = &jmploc;
- setvar(name, val, flags);
- err = 0;
- }
- exception_handler = savehandler;
- RESTORE_INT(saveint);
- return err;
-}
-#endif
-
/*
* Unset the specified variable.
*/
static void hashcd(void);
/*
- * Actually do the chdir. We also call hashcd to let the routines in exec.c
+ * Actually do the chdir. We also call hashcd to let other routines
* know that the current directory has changed.
*/
static int
if (!dest)
dest = nullstr;
if (*dest == '/')
- goto step7;
+ goto step6;
if (*dest == '.') {
c = dest[1];
dotdot:
if (!*dest)
dest = ".";
path = bltinlookup("CDPATH");
- if (!path) {
- step6:
- step7:
- p = dest;
- goto docd;
- }
- do {
+ while (path) {
c = *path;
p = path_advance(&path, dest);
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
docd:
if (!docd(p, flags))
goto out;
- break;
+ goto err;
}
- } while (path);
+ }
+
+ step6:
+ p = dest;
+ goto docd;
+
+ err:
ash_msg_and_raise_error("can't cd to %s", dest);
/* NOTREACHED */
out:
static void
signal_handler(int signo)
{
+ if (signo == SIGCHLD) {
+ got_sigchld = 1;
+ if (!trap[SIGCHLD])
+ return;
+ }
+
gotsig[signo - 1] = 1;
+ pending_sig = signo;
if (signo == SIGINT && !trap[SIGINT]) {
if (!suppress_int) {
raise_interrupt(); /* does not return */
}
pending_int = 1;
- } else {
- pending_sig = signo;
}
}
//whereas we have to restore it to what shell got on entry
//from the parent. See comment above
+ if (signo == SIGCHLD)
+ new_act = S_CATCH;
+
t = &sigmode[signo - 1];
cur_act = *t;
if (cur_act == 0) {
#define CUR_RUNNING 1
#define CUR_STOPPED 0
-/* mode flags for dowait */
-#define DOWAIT_NONBLOCK WNOHANG
-#define DOWAIT_BLOCK 0
-
#if JOBS
/* pgrp of shell on invocation */
static int initialpgrp; //references:2
}
static int
-dowait(int wait_flags, struct job *job)
+wait_block_or_sig(int *status)
+{
+ int pid;
+
+ do {
+ sigset_t mask;
+
+ /* Poll all children for changes in their state */
+ got_sigchld = 0;
+ /* if job control is active, accept stopped processes too */
+ pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG);
+ if (pid != 0)
+ break; /* Error (e.g. EINTR, ECHILD) or pid */
+
+ /* Children exist, but none are ready. Sleep until interesting signal */
+#if 1
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, &mask);
+ while (!got_sigchld && !pending_sig)
+ sigsuspend(&mask);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+#else /* unsafe: a signal can set pending_sig after check, but before pause() */
+ while (!got_sigchld && !pending_sig)
+ pause();
+#endif
+
+ /* If it was SIGCHLD, poll children again */
+ } while (got_sigchld);
+
+ return pid;
+}
+
+#define DOWAIT_NONBLOCK 0
+#define DOWAIT_BLOCK 1
+#define DOWAIT_BLOCK_OR_SIG 2
+
+static int
+dowait(int block, struct job *job)
{
int pid;
int status;
struct job *jp;
- struct job *thisjob;
+ struct job *thisjob = NULL;
- TRACE(("dowait(0x%x) called\n", wait_flags));
+ TRACE(("dowait(0x%x) called\n", block));
- /* Do a wait system call. If job control is compiled in, we accept
- * stopped processes. wait_flags may have WNOHANG, preventing blocking.
- * NB: _not_ safe_waitpid, we need to detect EINTR */
- if (doing_jobctl)
- wait_flags |= WUNTRACED;
- pid = waitpid(-1, &status, wait_flags);
+ /* It's wrong to call waitpid() outside of INT_OFF region:
+ * signal can arrive just after syscall return and handler can
+ * longjmp away, losing stop/exit notification processing.
+ * Thus, for "jobs" builtin, and for waiting for a fg job,
+ * we call waitpid() (blocking or non-blocking) inside INT_OFF.
+ *
+ * However, for "wait" builtin it is wrong to simply call waitpid()
+ * in INT_OFF region: "wait" needs to wait for any running job
+ * to change state, but should exit on any trap too.
+ * In INT_OFF region, a signal just before syscall entry can set
+ * pending_sig variables, but we can't check them, and we would
+ * either enter a sleeping waitpid() (BUG), or need to busy-loop.
+ *
+ * Because of this, we run inside INT_OFF, but use a special routine
+ * which combines waitpid() and sigsuspend().
+ * This is the reason why we need to have a handler for SIGCHLD:
+ * SIG_DFL handler does not wake sigsuspend().
+ */
+ INT_OFF;
+ if (block == DOWAIT_BLOCK_OR_SIG) {
+ pid = wait_block_or_sig(&status);
+ } else {
+ int wait_flags = 0;
+ if (block == DOWAIT_NONBLOCK)
+ wait_flags = WNOHANG;
+ /* if job control is active, accept stopped processes too */
+ if (doing_jobctl)
+ wait_flags |= WUNTRACED;
+ /* NB: _not_ safe_waitpid, we need to detect EINTR */
+ pid = waitpid(-1, &status, wait_flags);
+ }
TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
pid, status, errno, strerror(errno)));
if (pid <= 0)
- return pid;
+ goto out;
- INT_OFF;
thisjob = NULL;
for (jp = curjob; jp; jp = jp->prev_job) {
int jobstate;
return pid;
}
-static int
-blocking_wait_with_raise_on_sig(void)
-{
- pid_t pid = dowait(DOWAIT_BLOCK, NULL);
- if (pid <= 0 && pending_sig)
- raise_exception(EXSIG);
- return pid;
-}
-
#if JOBS
static void
showjob(struct job *jp, int mode)
int retval;
struct job *jp;
- if (pending_sig)
- raise_exception(EXSIG);
-
nextopt(nullstr);
retval = 0;
jp->waited = 1;
jp = jp->prev_job;
}
- blocking_wait_with_raise_on_sig();
/* man bash:
* "When bash is waiting for an asynchronous command via
* the wait builtin, the reception of a signal for which a trap
* has been set will cause the wait builtin to return immediately
* with an exit status greater than 128, immediately after which
* the trap is executed."
- *
- * blocking_wait_with_raise_on_sig raises signal handlers
- * if it gets no pid (pid < 0). However,
- * if child sends us a signal *and immediately exits*,
- * blocking_wait_with_raise_on_sig gets pid > 0
- * and does not handle pending_sig. Check this case: */
+ */
+ dowait(DOWAIT_BLOCK_OR_SIG, NULL);
+ /* if child sends us a signal *and immediately exits*,
+ * dowait() returns pid > 0. Check this case,
+ * not "if (dowait() < 0)"!
+ */
if (pending_sig)
- raise_exception(EXSIG);
+ goto sigout;
}
}
job = getjob(*argv, 0);
}
/* loop until process terminated or stopped */
- while (job->state == JOBRUNNING)
- blocking_wait_with_raise_on_sig();
+ while (job->state == JOBRUNNING) {
+ dowait(DOWAIT_BLOCK_OR_SIG, NULL);
+ if (pending_sig)
+ goto sigout;
+ }
job->waited = 1;
retval = getstatus(job);
repeat: ;
ret:
return retval;
+ sigout:
+ retval = 128 + pending_sig;
+ return retval;
}
static struct job *
{
char **tp;
+ INT_OFF;
for (tp = trap; tp < &trap[NSIG]; tp++) {
if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
- INT_OFF;
if (trap_ptr == trap)
free(*tp);
/* else: it "belongs" to trap_ptr vector, don't free */
*tp = NULL;
if ((tp - trap) != 0)
setsignal(tp - trap);
- INT_ON;
}
}
may_have_traps = 0;
+ INT_ON;
}
/* Lives far away from here, needed for forkchild */
static void closescript(void);
/* Called after fork(), in child */
+/* jp and n are NULL when called by openhere() for heredoc support */
static NOINLINE void
forkchild(struct job *jp, union node *n, int mode)
{
{
TRACE(("In parent shell: child = %d\n", pid));
if (!jp) {
+ /* jp is NULL when called by openhere() for heredoc support */
while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
continue;
jobless++;
}
}
+/* jp and n are NULL when called by openhere() for heredoc support */
static int
forkshell(struct job *jp, union node *n, int mode)
{
}
-/* ============ redir.c
- *
+/*
* Code for dealing with input/output redirection.
*/
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
+ * searched for IFS characters have been stored by recordregion.
+ */
+static void
+ifsbreakup(char *string, struct arglist *arglist)
+{
+ struct ifsregion *ifsp;
+ struct strlist *sp;
+ char *start;
+ char *p;
+ char *q;
+ const char *ifs, *realifs;
+ int ifsspc;
+ int nulonly;
+
+ start = string;
+ if (ifslastp != NULL) {
+ ifsspc = 0;
+ nulonly = 0;
+ realifs = ifsset() ? ifsval() : defifs;
+ ifsp = &ifsfirst;
+ do {
+ p = string + ifsp->begoff;
+ nulonly = ifsp->nulonly;
+ ifs = nulonly ? nullstr : realifs;
+ ifsspc = 0;
+ while (p < string + ifsp->endoff) {
+ q = p;
+ if ((unsigned char)*p == CTLESC)
+ p++;
+ if (!strchr(ifs, *p)) {
+ p++;
+ continue;
+ }
+ if (!nulonly)
+ ifsspc = (strchr(defifs, *p) != NULL);
+ /* Ignore IFS whitespace at start */
+ if (q == start && ifsspc) {
+ p++;
+ start = p;
+ continue;
+ }
+ *q = '\0';
+ sp = stzalloc(sizeof(*sp));
+ sp->text = start;
+ *arglist->lastp = sp;
+ arglist->lastp = &sp->next;
+ p++;
+ if (!nulonly) {
+ for (;;) {
+ if (p >= string + ifsp->endoff) {
+ break;
+ }
+ q = p;
+ if ((unsigned char)*p == CTLESC)
+ p++;
+ if (strchr(ifs, *p) == NULL) {
+ p = q;
+ break;
+ }
+ if (strchr(defifs, *p) == NULL) {
+ if (ifsspc) {
+ p++;
+ ifsspc = 0;
+ } else {
+ p = q;
+ break;
+ }
+ } else
+ p++;
+ }
+ }
+ start = p;
+ } /* while */
+ ifsp = ifsp->next;
+ } while (ifsp != NULL);
+ if (nulonly)
+ goto add;
+ }
+
+ if (!*start)
+ return;
+
+ add:
+ sp = stzalloc(sizeof(*sp));
+ sp->text = start;
+ *arglist->lastp = sp;
+ arglist->lastp = &sp->next;
+}
+
+static void
+ifsfree(void)
+{
+ struct ifsregion *p = ifsfirst.next;
+
+ if (!p)
+ goto out;
+
+ INT_OFF;
+ do {
+ struct ifsregion *ifsp;
+ ifsp = p->next;
+ free(p);
+ p = ifsp;
+ } while (p);
+ ifsfirst.next = NULL;
+ INT_ON;
+ out:
+ ifslastp = NULL;
+}
+
static size_t
esclen(const char *start, const char *p)
{
ash_msg_and_raise_error("pipe call failed");
jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, FORK_NOJOB) == 0) {
+ /* child */
FORCE_INT_ON;
close(pip[0]);
if (pip[1] != 1) {
* For now, preserve bash-like behavior, it seems to be somewhat more useful:
*/
eflag = 0;
+ ifsfree();
evaltree(n, EV_EXIT); /* actually evaltreenr... */
/* NOTREACHED */
}
+ /* parent */
close(pip[1]);
result->fd = pip[0];
result->jp = jp;
return p;
}
-/*
- * Break the argument string into pieces based upon IFS and add the
- * strings to the argument list. The regions of the string to be
- * searched for IFS characters have been stored by recordregion.
- */
-static void
-ifsbreakup(char *string, struct arglist *arglist)
-{
- struct ifsregion *ifsp;
- struct strlist *sp;
- char *start;
- char *p;
- char *q;
- const char *ifs, *realifs;
- int ifsspc;
- int nulonly;
-
- start = string;
- if (ifslastp != NULL) {
- ifsspc = 0;
- nulonly = 0;
- realifs = ifsset() ? ifsval() : defifs;
- ifsp = &ifsfirst;
- do {
- p = string + ifsp->begoff;
- nulonly = ifsp->nulonly;
- ifs = nulonly ? nullstr : realifs;
- ifsspc = 0;
- while (p < string + ifsp->endoff) {
- q = p;
- if ((unsigned char)*p == CTLESC)
- p++;
- if (!strchr(ifs, *p)) {
- p++;
- continue;
- }
- if (!nulonly)
- ifsspc = (strchr(defifs, *p) != NULL);
- /* Ignore IFS whitespace at start */
- if (q == start && ifsspc) {
- p++;
- start = p;
- continue;
- }
- *q = '\0';
- sp = stzalloc(sizeof(*sp));
- sp->text = start;
- *arglist->lastp = sp;
- arglist->lastp = &sp->next;
- p++;
- if (!nulonly) {
- for (;;) {
- if (p >= string + ifsp->endoff) {
- break;
- }
- q = p;
- if ((unsigned char)*p == CTLESC)
- p++;
- if (strchr(ifs, *p) == NULL) {
- p = q;
- break;
- }
- if (strchr(defifs, *p) == NULL) {
- if (ifsspc) {
- p++;
- ifsspc = 0;
- } else {
- p = q;
- break;
- }
- } else
- p++;
- }
- }
- start = p;
- } /* while */
- ifsp = ifsp->next;
- } while (ifsp != NULL);
- if (nulonly)
- goto add;
- }
-
- if (!*start)
- return;
-
- add:
- sp = stzalloc(sizeof(*sp));
- sp->text = start;
- *arglist->lastp = sp;
- arglist->lastp = &sp->next;
-}
-
-static void
-ifsfree(void)
-{
- struct ifsregion *p;
-
- INT_OFF;
- p = ifsfirst.next;
- do {
- struct ifsregion *ifsp;
- ifsp = p->next;
- free(p);
- p = ifsp;
- } while (p);
- ifslastp = NULL;
- ifsfirst.next = NULL;
- INT_ON;
-}
-
/*
* Add a file name to the list.
*/
if (fflag)
goto nometa;
+
+ /* Avoid glob() (and thus, stat() et al) for words like "echo" */
+ p = str->text;
+ while (*p) {
+ if (*p == '*')
+ goto need_glob;
+ if (*p == '?')
+ goto need_glob;
+ if (*p == '[')
+ goto need_glob;
+ p++;
+ }
+ goto nometa;
+
+ need_glob:
INT_OFF;
p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
argbackq = arg->narg.backquote;
STARTSTACKSTR(expdest);
- ifsfirst.next = NULL;
- ifslastp = NULL;
TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
argstr(arg->narg.text, flag,
/* var_str_list: */ arglist ? arglist->list : NULL);
p = _STPUTC('\0', expdest);
expdest = p - 1;
if (arglist == NULL) {
- return; /* here document expanded */
+ /* here document expanded */
+ goto out;
}
p = grabstackstr(p);
TRACE(("expandarg: p:'%s'\n", p));
*exparg.lastp = sp;
exparg.lastp = &sp->next;
}
- if (ifsfirst.next)
- ifsfree();
*exparg.lastp = NULL;
if (exparg.list) {
*arglist->lastp = exparg.list;
arglist->lastp = exparg.lastp;
}
+
+ out:
+ ifsfree();
}
/*
setstackmark(&smark);
argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest);
- ifslastp = NULL;
argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
/* var_str_list: */ NULL);
STACKSTRNUL(expdest);
+ ifsfree();
result = patmatch(stackblock(), val);
popstackmark(&smark);
return result;
#else
execve(cmd, argv, envp);
#endif
- if (cmd == (char*) bb_busybox_exec_path) {
- /* We already visited ENOEXEC branch below, don't do it again */
-//TODO: try execve(initial_argv0_of_shell, argv, envp) before giving up?
- free(argv);
- return;
- }
- if (errno == ENOEXEC) {
+ if (cmd != (char*) bb_busybox_exec_path && errno == ENOEXEC) {
/* Run "cmd" as a shell script:
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
* "If the execve() function fails with ENOEXEC, the shell
* message and exit code 126. For one, this prevents attempts
* to interpret foreign ELF binaries as shell scripts.
*/
- char **ap;
- char **new;
-
- for (ap = argv; *ap; ap++)
- continue;
- new = ckmalloc((ap - argv + 2) * sizeof(new[0]));
- new[0] = (char*) "ash";
- new[1] = cmd;
- ap = new + 2;
- while ((*ap++ = *++argv) != NULL)
- continue;
+ argv[0] = cmd;
cmd = (char*) bb_busybox_exec_path;
- argv = new;
+ /* NB: this is only possible because all callers of shellexec()
+ * ensure that the argv[-1] slot exists!
+ */
+ argv--;
+ argv[0] = (char*) "ash";
goto repeat;
}
}
/*
* Exec a program. Never returns. If you change this routine, you may
* have to change the find_command routine as well.
+ * argv[-1] must exist and be writable! See tryexec() for why.
*/
static void shellexec(char **, const char *, int) NORETURN;
static void
static int
evaltree(union node *n, int flags)
{
- struct jmploc *volatile savehandler = exception_handler;
- struct jmploc jmploc;
int checkexit = 0;
int (*evalfn)(union node *, int);
int status = 0;
- int int_level;
-
- SAVE_INT(int_level);
if (n == NULL) {
TRACE(("evaltree(NULL) called\n"));
- goto out1;
+ goto out;
}
TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
dotrap();
- exception_handler = &jmploc;
- {
- int err = setjmp(jmploc.loc);
- if (err) {
- /* if it was a signal, check for trap handlers */
- if (exception_type == EXSIG) {
- TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
- exception_type, err));
- goto out;
- }
- /* continue on the way out */
- TRACE(("exception %d in evaltree, propagating err=%d\n",
- exception_type, err));
- exception_handler = savehandler;
- longjmp(exception_handler->loc, err);
- }
- }
-
switch (n->type) {
default:
#if DEBUG
exitstatus = status;
break;
}
-
out:
- exception_handler = savehandler;
-
- out1:
/* Order of checks below is important:
* signal handlers trigger before exit caused by "set -e".
*/
if (flags & EV_EXIT)
raise_exception(EXEXIT);
- RESTORE_INT(int_level);
TRACE(("leaving evaltree (no interrupts)\n"));
-
return exitstatus;
}
evaltreenr(n->nredir.n, flags);
/* never returns */
}
+ /* parent */
status = 0;
if (!backgnd)
status = waitforjob(jp);
}
}
if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
+ /* child */
INT_ON;
if (pip[1] >= 0) {
close(pip[0]);
evaltreenr(lp->n, flags);
/* never returns */
}
+ /* parent */
if (prevfd >= 0)
close(prevfd);
prevfd = pip[0];
/* else:
* it's a duplicate "local VAR" declaration, do nothing
*/
- return;
+ goto ret;
}
lvp = lvp->next;
}
lvp->vp = vp;
lvp->next = localvars;
localvars = lvp;
+ ret:
INT_ON;
}
argc++;
}
- argv = nargv = stalloc(sizeof(char *) * (argc + 1));
+ /* Reserve one extra spot at the front for shellexec. */
+ nargv = stalloc(sizeof(char *) * (argc + 2));
+ argv = ++nargv;
for (sp = arglist.list; sp; sp = sp->next) {
TRACE(("evalcommand arg: %s\n", sp->text));
*nargv++ = sp->text;
dowait(DOWAIT_NONBLOCK, NULL);
if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
- int exit_status;
- int i = exception_type;
- if (i == EXEXIT)
- goto raise;
- exit_status = 2;
- if (i == EXINT)
- exit_status = 128 + SIGINT;
- if (i == EXSIG)
- exit_status = 128 + pending_sig;
- exitstatus = exit_status;
- if (i == EXINT || spclbltin > 0) {
- raise:
- longjmp(exception_handler->loc, 1);
+ if (exception_type == EXERROR && spclbltin <= 0) {
+ FORCE_INT_ON;
+ goto readstatus;
}
- FORCE_INT_ON;
+ raise:
+ longjmp(exception_handler->loc, 1);
}
goto readstatus;
}
-/* ============ input.c
- *
+/*
* This implements the input routines used by the parser.
*/
{
struct parsefile *pf = g_parsefile;
+ if (pf == &basepf)
+ return;
+
INT_OFF;
if (pf->pf_fd >= 0)
close(pf->pf_fd);
}
-/* ============ mail.c
- *
+/*
* Routines to check for mail.
*/
#if ENABLE_ASH_GETOPTS
static int
-getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
+getopts(char *optstr, char *optvar, char **optfirst)
{
char *p, *q;
char c = '?';
int done = 0;
- int err = 0;
char sbuf[2];
char **optnext;
+ int ind = shellparam.optind;
+ int off = shellparam.optoff;
sbuf[1] = '\0';
- optnext = optfirst + *param_optind - 1;
+ shellparam.optind = -1;
+ optnext = optfirst + ind - 1;
- if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
+ if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
p = NULL;
else
- p = optnext[-1] + *optoff;
+ p = optnext[-1] + off;
if (p == NULL || *p == '\0') {
/* Current word is done, advance */
p = *optnext;
if (optstr[0] == ':') {
sbuf[0] = c;
/*sbuf[1] = '\0'; - already is */
- err |= setvarsafe("OPTARG", sbuf, 0);
+ setvar0("OPTARG", sbuf);
} else {
fprintf(stderr, "Illegal option -%c\n", c);
unsetvar("OPTARG");
if (optstr[0] == ':') {
sbuf[0] = c;
/*sbuf[1] = '\0'; - already is */
- err |= setvarsafe("OPTARG", sbuf, 0);
+ setvar0("OPTARG", sbuf);
c = ':';
} else {
fprintf(stderr, "No arg for -%c option\n", c);
if (p == *optnext)
optnext++;
- err |= setvarsafe("OPTARG", p, 0);
+ setvar0("OPTARG", p);
p = NULL;
} else
- err |= setvarsafe("OPTARG", nullstr, 0);
+ setvar0("OPTARG", nullstr);
out:
- *optoff = p ? p - *(optnext - 1) : -1;
- *param_optind = optnext - optfirst + 1;
- err |= setvarsafe("OPTIND", itoa(*param_optind), VNOFUNC);
+ ind = optnext - optfirst + 1;
+ setvar("OPTIND", itoa(ind), VNOFUNC);
sbuf[0] = c;
/*sbuf[1] = '\0'; - already is */
- err |= setvarsafe(optvar, sbuf, 0);
- if (err) {
- *param_optind = 1;
- *optoff = -1;
- flush_stdout_stderr();
- raise_exception(EXERROR);
- }
+ setvar0(optvar, sbuf);
+
+ shellparam.optoff = p ? p - *(optnext - 1) : -1;
+ shellparam.optind = ind;
+
return done;
}
ash_msg_and_raise_error("usage: getopts optstring var [arg]");
if (argc == 3) {
optbase = shellparam.p;
- if (shellparam.optind > shellparam.nparam + 1) {
+ if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
shellparam.optind = 1;
shellparam.optoff = -1;
}
} else {
optbase = &argv[3];
- if (shellparam.optind > argc - 2) {
+ if ((unsigned)shellparam.optind > argc - 2) {
shellparam.optind = 1;
shellparam.optoff = -1;
}
}
- return getopts(argv[1], argv[2], optbase, &shellparam.optind,
- &shellparam.optoff);
+ return getopts(argv[1], argv[2], optbase);
}
#endif /* ASH_GETOPTS */
if (c == *eofmark) {
if (pfgets(line, sizeof(line)) != NULL) {
char *p, *q;
+ int cc;
p = line;
- for (q = eofmark + 1; *q && *p == *q; p++, q++)
- continue;
- if (*p == '\n' && *q == '\0') {
+ for (q = eofmark + 1;; p++, q++) {
+ cc = *p;
+ if (cc == '\n')
+ cc = 0;
+ if (!*q || cc != *q)
+ break;
+ }
+ if (cc == *q) {
c = PEOF;
nlnoprompt();
} else {
parsesub: {
unsigned char subtype;
int typeloc;
- int flags = 0;
c = pgetc_eatbnl();
if (c > 255 /* PEOA or PEOF */
/* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
USTPUTC(CTLVAR, out);
typeloc = out - (char *)stackblock();
- USTPUTC(VSNORMAL, out);
+ STADJUST(1, out);
subtype = VSNORMAL;
if (c == '{') {
c = pgetc_eatbnl();
subtype = 0;
}
varname:
- if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
+ if (is_name(c)) {
/* $[{[#]]NAME[}] */
do {
STPUTC(c, out);
c = pgetc_eatbnl();
- } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
+ } while (is_in_name(c));
} else if (isdigit(c)) {
/* $[{[#]]NUM[}] */
do {
goto badsub;
}
- flags = 0;
if (subtype == 0) {
static const char types[] ALIGN1 = "}-+?=";
/* ${VAR...} but not $VAR or ${#VAR} */
break; /* "goto badsub" is bigger (!) */
}
#endif
- flags = VSNUL;
+ subtype = VSNUL;
/*FALLTHROUGH*/
default: {
const char *p = strchr(types, c);
if (p == NULL)
break;
- subtype = p - types + VSNORMAL;
+ subtype |= p - types + VSNORMAL;
break;
}
case '%':
badsub:
pungetc();
}
- ((unsigned char *)stackblock())[typeloc] = subtype | flags;
+ ((unsigned char *)stackblock())[typeloc] = subtype;
if (subtype != VSNORMAL) {
varnest++;
- if (dblquote) {
+ if (dblquote)
dqvarnest++;
- }
}
STPUTC('=', out);
}
static int
evalstring(char *s, int flags)
{
+ struct jmploc *volatile savehandler;
+ struct jmploc jmploc;
+ int ex;
+
union node *n;
struct stackmark smark;
int status;
setstackmark(&smark);
status = 0;
+ /* On exception inside execution loop, we must popfile().
+ * Try interactively:
+ * readonly a=a
+ * command eval "a=b" # throws "is read only" error
+ * "command BLTIN" is not supposed to abort (even in non-interactive use).
+ * But if we skip popfile(), we hit EOF in eval's string, and exit.
+ */
+ savehandler = exception_handler;
+ ex = setjmp(jmploc.loc);
+ if (ex)
+ goto out;
+ exception_handler = &jmploc;
+
while ((n = parsecmd(0)) != NODE_EOF) {
int i;
if (evalskip)
break;
}
+ out:
popstackmark(&smark);
popfile();
stunalloc(s);
+ exception_handler = savehandler;
+ if (ex)
+ longjmp(exception_handler->loc, ex);
+
return status;
}
if (action) {
if (LONE_DASH(action))
action = NULL;
- else
+ else {
+ if (action[0]) /* not NULL and not "" and not "-" */
+ may_have_traps = 1;
action = ckstrdup(action);
+ }
}
free(trap[signo]);
- if (action)
- may_have_traps = 1;
trap[signo] = action;
if (signo != 0)
setsignal(signo);
/* we will never free this */
basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
- signal(SIGCHLD, SIG_DFL);
+ sigmode[SIGCHLD - 1] = S_DFL;
+ setsignal(SIGCHLD);
+
/* bash re-enables SIGHUP which is SIG_IGNed on entry.
* Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
*/
}
/*
- * Read /etc/profile or .profile.
+ * Read /etc/profile, ~/.profile, $ENV.
*/
static void
read_profile(const char *name)
{
+ name = expandstr(name);
if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
return;
cmdloop(0);
/*
* 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 eval.c: */
evalskip = 0;
loopnest = 0;
+
+ /* from expand.c: */
+ ifsfree();
+
/* from input.c: */
g_parsefile->left_in_buffer = 0;
g_parsefile->left_in_line = 0; /* clear input buffer */
popallfiles();
- /* from parser.c: */
- tokpushback = 0;
- checkkwd = 0;
+
/* from redir.c: */
while (redirlist)
popredir(/*drop:*/ 0, /*restore:*/ 0);
int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ash_main(int argc UNUSED_PARAM, char **argv)
{
- const char *shinit;
volatile smallint state;
struct jmploc jmploc;
struct stackmark smark;
goto state4;
}
exception_handler = &jmploc;
-#if DEBUG
- opentrace();
- TRACE(("Shell args: "));
- trace_puts_args(argv);
-#endif
rootpid = getpid();
init();
setstackmark(&smark);
procargs(argv);
+#if DEBUG
+ TRACE(("Shell args: "));
+ trace_puts_args(argv);
+#endif
if (argv[0] && argv[0][0] == '-')
isloginsh = 1;
state1:
state = 2;
hp = lookupvar("HOME");
- if (hp) {
- hp = concat_path_file(hp, ".profile");
- read_profile(hp);
- free((char*)hp);
- }
+ if (hp)
+ read_profile("$HOME/.profile");
}
state2:
state = 3;
#endif
iflag
) {
- shinit = lookupvar("ENV");
- if (shinit != NULL && *shinit != '\0') {
+ const char *shinit = lookupvar("ENV");
+ if (shinit != NULL && *shinit != '\0')
read_profile(shinit);
- }
}
+ popstackmark(&smark);
state3:
state = 4;
if (minusc) {