uint8_t exitstatus; /* exit status of last command */
uint8_t back_exitstatus;/* exit status of backquoted command */
smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
+ int savestatus; /* exit status of last command outside traps */
int rootpid; /* pid of main shell */
/* shell level: 0 for the main shell, 1 for its children, and so on */
int shlvl;
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 */
#define exitstatus (G_misc.exitstatus )
#define back_exitstatus (G_misc.back_exitstatus )
#define job_warning (G_misc.job_warning)
+#define savestatus (G_misc.savestatus )
#define rootpid (G_misc.rootpid )
#define shlvl (G_misc.shlvl )
#define errlinno (G_misc.errlinno )
#define random_gen (G_misc.random_gen )
#define backgndpid (G_misc.backgndpid )
#define INIT_G_misc() do { \
- (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
+ (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
barrier(); \
+ savestatus = -1; \
curdir = nullstr; \
physdir = nullstr; \
trap_ptr = trap; \
ret = vsnprintf(outbuf, length, fmt, ap);
va_end(ap);
INT_ON;
- return ret;
+ return ret > (int)length ? length : ret;
}
static void
#define g_stacknleft (G_memstack.g_stacknleft)
#define stackbase (G_memstack.stackbase )
#define INIT_G_memstack() do { \
- (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
+ (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \
barrier(); \
g_stackp = &stackbase; \
g_stacknxt = stackbase.space; \
* 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)
+{
+ if (stackblocksize() < len)
+ growstackblock(len);
+ return stackblock();
+}
+
/*
* Called from CHECKSTRSPACE.
*/
makestrspace(size_t newlen, char *p)
{
size_t len = p - g_stacknxt;
- size_t size;
- for (;;) {
- size_t nleft;
-
- size = stackblocksize();
- nleft = size - len;
- if (nleft >= newlen)
- break;
- growstackblock();
- }
- return (char *)stackblock() + len;
+ return growstackto(len + newlen) + len;
}
static char *
#endif
#define INIT_G_var() do { \
unsigned i; \
- (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
+ (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \
barrier(); \
for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
varinit[i].flags = varinit_data[i].flags; \
setvar(s, NULL, 0);
}
-/*
- * Process a linked list of variable assignments.
- */
-static void
-listsetvar(struct strlist *list_set_var, int flags)
-{
- struct strlist *lp = list_set_var;
-
- if (!lp)
- return;
- INT_OFF;
- do {
- setvareq(lp->text, flags);
- lp = lp->next;
- } while (lp);
- INT_ON;
-}
-
/*
* Generate a list of variables satisfying the given conditions.
*/
}
-/* ============ Path search helper
- *
+/* ============ Path search helper */
+static const char *
+legal_pathopt(const char *opt, const char *term, int magic)
+{
+ switch (magic) {
+ case 0:
+ opt = NULL;
+ break;
+
+ case 1:
+ opt = prefix(opt, "builtin") ?: prefix(opt, "func");
+ break;
+
+ default:
+ opt += strcspn(opt, term);
+ break;
+ }
+
+ if (opt && *opt == '%')
+ opt++;
+
+ return opt;
+}
+
+/*
* The variable path (passed by reference) should be set to the start
- * of the path before the first call; path_advance will update
- * this value as it proceeds. Successive calls to path_advance will return
+ * of the path before the first call; padvance will update
+ * this value as it proceeds. Successive calls to padvance will return
* the possible path expansions in sequence. If an option (indicated by
* a percent sign) appears in the path entry then the global variable
* pathopt will be set to point to it; otherwise pathopt will be set to
* NULL.
+ *
+ * If magic is 0 then pathopt recognition will be disabled. If magic is
+ * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
+ * pathopt.
*/
-static const char *pathopt; /* set by path_advance */
+static const char *pathopt; /* set by padvance */
-static char *
-path_advance(const char **path, const char *name)
+static int
+padvance_magic(const char **path, const char *name, int magic)
{
+ const char *term = "%:";
+ const char *lpathopt;
const char *p;
char *q;
const char *start;
+ size_t qlen;
size_t len;
if (*path == NULL)
- return NULL;
+ return -1;
+
+ lpathopt = NULL;
start = *path;
- for (p = start; *p && *p != ':' && *p != '%'; p++)
- continue;
- len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
- while (stackblocksize() < len)
- growstackblock();
- q = stackblock();
- if (p != start) {
- q = mempcpy(q, start, p - start);
- *q++ = '/';
+
+ if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
+ lpathopt = start + 1;
+ start = p;
+ term = ":";
}
- strcpy(q, name);
- pathopt = NULL;
+
+ len = strcspn(start, term);
+ p = start + len;
+
if (*p == '%') {
- pathopt = ++p;
- while (*p && *p != ':')
- p++;
+ size_t extra = strchrnul(p, ':') - p;
+
+ if (legal_pathopt(p + 1, term, magic))
+ lpathopt = p + 1;
+ else
+ len += extra;
+
+ p += extra;
}
- if (*p == ':')
- *path = p + 1;
- else
- *path = NULL;
- return stalloc(len);
+
+ pathopt = lpathopt;
+ *path = *p == ':' ? p + 1 : NULL;
+
+ /* "2" is for '/' and '\0' */
+ qlen = len + strlen(name) + 2;
+ q = growstackto(qlen);
+
+ if (len) {
+ q = mempcpy(q, start, len);
+ *q++ = '/';
+ }
+ strcpy(q, name);
+
+ return qlen;
+}
+
+static int
+padvance(const char **path, const char *name)
+{
+ return padvance_magic(path, name, 1);
}
char c;
struct stat statb;
int flags;
+ int len;
flags = cdopt();
dest = *argptr;
if (!*dest)
dest = ".";
path = bltinlookup("CDPATH");
- while (path) {
- c = *path;
- p = path_advance(&path, dest);
+ while (p = path, (len = padvance(&path, dest)) >= 0) {
+ c = *p;
+ p = stalloc(len);
+
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
if (c && c != ':')
flags |= CD_PRINT;
static unsigned njobs; //4
/* current job */
static struct job *curjob; //lots
-/* number of presumed living untracked jobs */
-static int jobless; //4
#if 0
/* Bash has a feature: it restores termios after a successful wait for
#endif
static int
-sprint_status48(char *s, int status, int sigonly)
+sprint_status48(char *os, int status, int sigonly)
{
- int col;
+ char *s = os;
int st;
- col = 0;
if (!WIFEXITED(status)) {
#if JOBS
if (WIFSTOPPED(status))
}
st &= 0x7f;
//TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata
- col = fmtstr(s, 32, strsignal(st));
+ //s = stpncpy(s, strsignal(st), 32); //not all libc have stpncpy()
+ s += fmtstr(s, 32, strsignal(st));
if (WCOREDUMP(status)) {
- strcpy(s + col, " (core dumped)");
- col += sizeof(" (core dumped)")-1;
+ s = stpcpy(s, " (core dumped)");
}
} else if (!sigonly) {
st = WEXITSTATUS(status);
- col = fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
+ s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
}
out:
- return col;
+ return s - os;
}
static int
#if 1
sigfillset(&mask);
sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */
- while (!got_sigchld && !pending_sig)
+ while (!got_sigchld && !pending_sig) {
sigsuspend(&mask);
+ /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend
+ * to make sure SIGCHLD is not masked off?
+ * It was reported that this:
+ * fn() { : | return; }
+ * shopt -s lastpipe
+ * fn
+ * exec ash SCRIPT
+ * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
+ * making "wait" commands in SCRIPT block forever.
+ */
+ }
sigprocmask(SIG_SETMASK, &mask, NULL);
#else /* unsafe: a signal can set pending_sig after check, but before pause() */
while (!got_sigchld && !pending_sig)
#endif
static int
-dowait(int block, struct job *job)
+waitone(int block, struct job *job)
{
int pid;
int status;
goto out;
}
/* The process wasn't found in job list */
-#if JOBS
- if (!WIFSTOPPED(status))
- jobless--;
-#endif
out:
INT_ON;
return pid;
}
+static int
+dowait(int block, struct job *jp)
+{
+ int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1;
+
+ while (jp ? jp->state == JOBRUNNING : pid > 0) {
+ if (!jp)
+ got_sigchld = 0;
+ pid = waitone(block, jp);
+ }
+
+ return pid;
+}
+
#if JOBS
static void
showjob(struct job *jp, int mode)
TRACE(("showjobs(0x%x) called\n", mode));
/* Handle all finished jobs */
- while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
- continue;
+ dowait(DOWAIT_NONBLOCK, NULL);
for (jp = curjob; jp; jp = jp->prev_job) {
if (!(mode & SHOW_CHANGED) || jp->changed) {
#else
dowait(DOWAIT_BLOCK_OR_SIG, NULL);
#endif
- /* if child sends us a signal *and immediately exits*,
- * dowait() returns pid > 0. Check this case,
- * not "if (dowait() < 0)"!
- */
+ /* if child sends us a signal *and immediately exits*,
+ * dowait() returns pid > 0. Check this case,
+ * not "if (dowait() < 0)"!
+ */
if (pending_sig)
goto sigout;
#if BASH_WAIT_N
job = getjob(*argv, 0);
}
/* loop until process terminated or stopped */
- while (job->state == JOBRUNNING) {
- dowait(DOWAIT_BLOCK_OR_SIG, NULL);
- if (pending_sig)
- goto sigout;
- }
+ dowait(DOWAIT_BLOCK_OR_SIG, NULL);
+ if (pending_sig)
+ goto sigout;
job->waited = 1;
retval = getstatus(job);
repeat: ;
str = "${";
goto dostr;
case CTLENDVAR:
- str = "\"}" + !(quoted & 1);
+ str = "\"}";
+ str += !(quoted & 1);
quoted >>= 1;
subtype = 0;
goto dostr;
#endif
for (jp = curjob; jp; jp = jp->prev_job)
freejob(jp);
- jobless = 0;
}
/* Called after fork(), in parent */
forkparent(struct job *jp, union node *n, int mode, pid_t pid)
{
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++;
+ if (!jp) /* jp is NULL when called by openhere() for heredoc support */
return;
- }
#if JOBS
if (mode != FORK_NOJOB && jp->jobctl) {
int pgrp;
{
int st;
- TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+ TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
- INT_OFF;
- while (jp->state == JOBRUNNING) {
- /* In non-interactive shells, we _can_ get
- * a keyboard signal here and be EINTRed,
- * but we just loop back, waiting for command to complete.
- *
- * man bash:
- * "If bash is waiting for a command to complete and receives
- * a signal for which a trap has been set, the trap
- * will not be executed until the command completes."
- *
- * Reality is that even if trap is not set, bash
- * will not act on the signal until command completes.
- * Try this. sleep5intoff.c:
- * #include <signal.h>
- * #include <unistd.h>
- * int main() {
- * sigset_t set;
- * sigemptyset(&set);
- * sigaddset(&set, SIGINT);
- * sigaddset(&set, SIGQUIT);
- * sigprocmask(SIG_BLOCK, &set, NULL);
- * sleep(5);
- * return 0;
- * }
- * $ bash -c './sleep5intoff; echo hi'
- * ^C^C^C^C <--- pressing ^C once a second
- * $ _
- * $ bash -c './sleep5intoff; echo hi'
- * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
- * $ _
- */
- dowait(DOWAIT_BLOCK, jp);
- }
- INT_ON;
+ /* In non-interactive shells, we _can_ get
+ * a keyboard signal here and be EINTRed, but we just loop
+ * inside dowait(), waiting for command to complete.
+ *
+ * man bash:
+ * "If bash is waiting for a command to complete and receives
+ * a signal for which a trap has been set, the trap
+ * will not be executed until the command completes."
+ *
+ * Reality is that even if trap is not set, bash
+ * will not act on the signal until command completes.
+ * Try this. sleep5intoff.c:
+ * #include <signal.h>
+ * #include <unistd.h>
+ * int main() {
+ * sigset_t set;
+ * sigemptyset(&set);
+ * sigaddset(&set, SIGINT);
+ * sigaddset(&set, SIGQUIT);
+ * sigprocmask(SIG_BLOCK, &set, NULL);
+ * sleep(5);
+ * return 0;
+ * }
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^C^C^C^C <--- pressing ^C once a second
+ * $ _
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
+ * $ _
+ */
+ dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
+ if (!jp)
+ return exitstatus;
st = getstatus(jp);
#if JOBS
#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
#define EXP_WORD 0x40 /* expand word in parameter expansion */
#define EXP_QUOTED 0x100 /* expand word in double quotes */
+#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
+
/*
* rmescape() flags
*/
/* Add CTLESC when necessary. */
#define QUOTES_ESC (EXP_FULL | EXP_CASE)
-/* Do not skip NUL characters. */
-#define QUOTES_KEEPNUL EXP_TILDE
/*
* Structure specifying which parts of the string should be searched
/* 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
-memtodest(const char *p, size_t len, int syntax, int quotes)
+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((quotes & QUOTES_ESC) ? len * 2 : len, expdest);
+ q = makestrspace(len * 2, expdest);
+ s = q;
do {
unsigned char c = *p++;
if (c) {
- if (quotes & QUOTES_ESC) {
+ if (flags & QUOTES_ESC) {
int n = SIT(c, syntax);
if (n == CCTL
- || (syntax != BASESYNTAX && n == CBACK)
+ || ((flags & EXP_QUOTED) && n == CBACK)
) {
USTPUTC(CTLESC, q);
}
}
- } else if (!(quotes & QUOTES_KEEPNUL))
+ } else if (!(flags & EXP_KEEPNUL))
continue;
USTPUTC(c, q);
} while (--len);
expdest = q;
+ return q - s;
}
static size_t
-strtodest(const char *p, int syntax, int quotes)
+strtodest(const char *p, int flags)
{
size_t len = strlen(p);
- memtodest(p, len, syntax, quotes);
+ memtodest(p, len, flags);
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.
}
static char *
-exptilde(char *startp, char *p, int flags)
+exptilde(char *startp, char *p, int flag)
{
unsigned char c;
char *name;
struct passwd *pw;
const char *home;
- int quotes = flags & QUOTES_ESC;
name = p + 1;
case CTLQUOTEMARK:
return startp;
case ':':
- if (flags & EXP_VARTILDE)
+ if (flag & EXP_VARTILDE)
goto done;
break;
case '/':
goto lose;
home = pw->pw_dir;
}
- if (!home || !*home)
+ if (!home)
goto lose;
*p = c;
- strtodest(home, SQSYNTAX, quotes);
+ strtodest(home, flag | EXP_QUOTED);
return p;
lose:
*p = c;
char *p;
char *dest;
int startloc;
- int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
struct stackmark smark;
INT_OFF;
if (i == 0)
goto read;
for (;;) {
- memtodest(p, i, syntax, flag & QUOTES_ESC);
+ memtodest(p, i, flag);
read:
if (in.fd < 0)
break;
/* Eat all trailing newlines */
dest = expdest;
- for (; dest > (char *)stackblock() && dest[-1] == '\n';)
+ for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
STUNPUTC(dest);
expdest = dest;
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);
int sep;
int subtype = varflags & VSTYPE;
int discard = subtype == VSPLUS || subtype == VSLENGTH;
- int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
- int syntax;
+ flags |= EXP_KEEPNUL;
+ flags &= discard ? ~QUOTES_ESC : ~0;
sep = (flags & EXP_FULL) << CHAR_BIT;
- syntax = quoted ? DQSYNTAX : BASESYNTAX;
switch (*name) {
case '$':
if (num == 0)
return -1;
numvar:
- len = cvtnum(num);
+ len = cvtnum(num, flags);
goto check_1char_name;
case '-':
expdest = makestrspace(NOPTS, expdest);
if (!ap)
return -1;
while ((p = *ap++) != NULL) {
- len += strtodest(p, syntax, quotes);
+ len += strtodest(p, flags);
if (*ap && sep) {
len++;
- memtodest(&sepc, 1, syntax, quotes);
+ memtodest(&sepc, 1, flags);
}
}
break;
if (!p)
return -1;
- len = strtodest(p, syntax, quotes);
+ len = strtodest(p, flags);
#if ENABLE_UNICODE_SUPPORT
if (subtype == VSLENGTH && len > 0) {
reinit_unicode_for_ash();
varunset(p, var, 0, 0);
if (subtype == VSLENGTH) {
- cvtnum(varlen > 0 ? varlen : 0);
+ cvtnum(varlen > 0 ? varlen : 0, flag);
goto record;
}
goto record;
}
+ varlen = 0;
+
end:
if (subtype != VSNORMAL) { /* skip to end of alternative */
int nesting = 1;
#define DO_ABS 0x02 /* checks absolute paths */
#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
#define DO_ALTPATH 0x08 /* using alternate path */
-#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
+#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
static void find_command(char *, struct cmdentry *, int, const char *);
} else {
try_PATH:
e = ENOENT;
- while ((cmdname = path_advance(&path, prog)) != NULL) {
+ while (padvance(&path, argv[0]) >= 0) {
+ cmdname = stackblock();
if (--idx < 0 && pathopt == NULL) {
tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
if (errno != ENOENT && errno != ENOTDIR)
e = errno;
}
- stunalloc(cmdname);
}
}
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 */
}
idx = cmdp->param.index;
path = pathval();
do {
- name = path_advance(&path, cmdp->cmdname);
- stunalloc(name);
+ padvance(&path, cmdp->cmdname);
} while (--idx >= 0);
+ name = stackblock();
out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
}
/*
- * Clear out command entries. The argument specifies the first entry in
- * PATH which has changed.
+ * Clear out command entries.
*/
static void
-clearcmdentry(int firstchange)
+clearcmdentry(void)
{
struct tblentry **tblp;
struct tblentry **pp;
for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
pp = tblp;
while ((cmdp = *pp) != NULL) {
- if ((cmdp->cmdtype == CMDNORMAL &&
- cmdp->param.index >= firstchange)
- || (cmdp->cmdtype == CMDBUILTIN &&
- builtinloc >= firstchange)
+ if (cmdp->cmdtype == CMDNORMAL
+ || (cmdp->cmdtype == CMDBUILTIN
+ && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+ && builtinloc > 0
+ )
) {
*pp = cmdp->next;
free(cmdp);
char *name;
if (nextopt("r") != '\0') {
- clearcmdentry(0);
+ clearcmdentry();
return 0;
}
cmdp = cmdlookup(name, 0);
if (cmdp != NULL
&& (cmdp->cmdtype == CMDNORMAL
- || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+ || (cmdp->cmdtype == CMDBUILTIN
+ && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+ && builtinloc > 0
+ )
+ )
) {
delete_cmd_entry();
}
* Called with interrupts off.
*/
static void FAST_FUNC
-changepath(const char *new)
+changepath(const char *newval)
{
- const char *old;
- int firstchange;
+ const char *new;
int idx;
- int idx_bltin;
+ int bltin;
- old = pathval();
- firstchange = 9999; /* assume no change */
+ new = newval;
idx = 0;
- idx_bltin = -1;
+ bltin = -1;
for (;;) {
- if (*old != *new) {
- firstchange = idx;
- if ((*old == '\0' && *new == ':')
- || (*old == ':' && *new == '\0')
- ) {
- firstchange++;
- }
- old = new; /* ignore subsequent differences */
+ if (*new == '%' && prefix(new + 1, "builtin")) {
+ bltin = idx;
+ break;
}
- if (*new == '\0')
+ new = strchr(new, ':');
+ if (!new)
break;
- if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
- idx_bltin = idx;
- if (*new == ':')
- idx++;
+ idx++;
new++;
- old++;
}
- if (builtinloc < 0 && idx_bltin >= 0)
- builtinloc = idx_bltin; /* zap builtins */
- if (builtinloc >= 0 && idx_bltin < 0)
- firstchange = 0;
- clearcmdentry(firstchange);
- builtinloc = idx_bltin;
+ builtinloc = bltin;
+ clearcmdentry();
}
enum {
TEOF,
p = command;
} else {
do {
- p = path_advance(&path, command);
- stunalloc(p);
+ padvance(&path, command);
} while (--j >= 0);
+ p = stackblock();
}
if (describe_command_verbose) {
out1fmt(" is %s", p);
}
#if ENABLE_ASH_CMDCMD
+static struct strlist *
+fill_arglist(struct arglist *arglist, union node **argpp)
+{
+ struct strlist **lastp = arglist->lastp;
+ union node *argp;
+
+ while ((argp = *argpp) != NULL) {
+ expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
+ *argpp = argp->narg.next;
+ if (*lastp)
+ break;
+ }
+
+ return *lastp;
+}
+
/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
-static char **
-parse_command_args(char **argv, const char **path)
+static int
+parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
{
+ struct strlist *sp = arglist->list;
char *cp, c;
for (;;) {
- cp = *++argv;
- if (!cp)
- return NULL;
+ sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
+ if (!sp)
+ return 0;
+ cp = sp->text;
if (*cp++ != '-')
break;
c = *cp++;
if (!c)
break;
if (c == '-' && !*cp) {
- if (!*++argv)
- return NULL;
+ if (!sp->next && !fill_arglist(arglist, argpp))
+ return 0;
+ sp = sp->next;
break;
}
do {
break;
default:
/* run 'typecmd' for other options */
- return NULL;
+ return 0;
}
c = *cp++;
} while (c);
}
- return argv;
+
+ arglist->list = sp;
+ return DO_NOFUNC;
}
static int FAST_FUNC
#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 */
{
uint8_t *g;
int sig;
- uint8_t last_status;
+ int status, last_status;
if (!pending_sig)
return;
- last_status = exitstatus;
+ status = savestatus;
+ last_status = status;
+ if (status < 0) {
+ status = exitstatus;
+ savestatus = status;
+ }
pending_sig = 0;
barrier();
if (!p)
continue;
evalstring(p, 0);
+ if (evalskip != SKIPFUNC)
+ exitstatus = status;
}
- exitstatus = last_status;
+
+ savestatus = last_status;
TRACE(("dotrap returns\n"));
}
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"));
case NFROMFD:
case NTOFD: /* >& */
if (redir->ndup.vname) {
- expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+ expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
if (fn.list == NULL)
ash_msg_and_raise_error("redir error");
#if BASH_REDIR_OUTPUT
-//FIXME: we used expandarg with different args!
if (!isdigit_str9(fn.list->text)) {
/* >&file, not >&fd */
if (redir->nfile.fd != 1) /* 123>&file - BAD */
return status;
}
+/* setinteractive needs this forward reference */
+#if EDITING_HAS_get_exe_name
+static const char *get_builtin_name(int i) FAST_FUNC;
+#endif
+
/*
* Controls whether the shell is interactive or not.
*/
}
#endif
#if ENABLE_FEATURE_EDITING
- if (!line_input_state)
+ if (!line_input_state) {
line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
+# if EDITING_HAS_get_exe_name
+ line_input_state->get_exe_name = get_builtin_name;
+# endif
+ }
#endif
}
}
* Create a new localvar environment.
*/
static struct localvar_list *
-pushlocalvars(void)
+pushlocalvars(int push)
{
struct localvar_list *ll;
+ struct localvar_list *top;
+
+ top = localvar_stack;
+ if (!push)
+ goto out;
INT_OFF;
ll = ckzalloc(sizeof(*ll));
/*ll->lv = NULL; - zalloc did it */
- ll->next = localvar_stack;
+ ll->next = top;
localvar_stack = ll;
INT_ON;
-
- return ll->next;
+ out:
+ return top;
}
static void
shellparam = saveparam;
exception_handler = savehandler;
INT_ON;
- evalskip &= ~SKIPFUNC;
+ evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
return e;
}
* (options will be restored on return from the function).
*/
static void
-mklocal(char *name)
+mklocal(char *name, int flags)
{
struct localvar *lvp;
struct var **vpp;
if (vp == NULL) {
/* variable did not exist yet */
if (eq)
- vp = setvareq(name, VSTRFIXED);
+ vp = setvareq(name, VSTRFIXED | flags);
else
- vp = setvar(name, NULL, VSTRFIXED);
+ vp = setvar(name, NULL, VSTRFIXED | flags);
lvp->flags = VUNSET;
} else {
lvp->text = vp->var_text;
*/
vp->flags |= VSTRFIXED|VTEXTFIXED;
if (eq)
- setvareq(name, 0);
+ setvareq(name, flags);
else
/* "local VAR" unsets VAR: */
setvar0(name, NULL);
argv = argptr;
while ((name = *argv++) != NULL) {
- mklocal(name);
+ mklocal(name, 0);
}
return 0;
}
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 ENABLE_ASH_GETOPTS
{ BUILTIN_REGULAR "getopts" , getoptscmd },
#endif
- { BUILTIN_NOSPEC "hash" , hashcmd },
+ { BUILTIN_REGULAR "hash" , hashcmd },
#if ENABLE_ASH_HELP
{ BUILTIN_NOSPEC "help" , helpcmd },
#endif
#if ENABLE_ASH_PRINTF
{ BUILTIN_REGULAR "printf" , printfcmd },
#endif
- { BUILTIN_NOSPEC "pwd" , pwdcmd },
+ { BUILTIN_REGULAR "pwd" , pwdcmd },
{ BUILTIN_REGULAR "read" , readcmd },
{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
{ BUILTIN_SPEC_REG "return" , returncmd },
{ BUILTIN_SPEC_REG "times" , timescmd },
{ BUILTIN_SPEC_REG "trap" , trapcmd },
{ BUILTIN_REGULAR "true" , truecmd },
- { BUILTIN_NOSPEC "type" , typecmd },
- { BUILTIN_NOSPEC "ulimit" , ulimitcmd },
+ { BUILTIN_REGULAR "type" , typecmd },
+ { BUILTIN_REGULAR "ulimit" , ulimitcmd },
{ BUILTIN_REGULAR "umask" , umaskcmd },
#if ENABLE_ASH_ALIAS
{ BUILTIN_REGULAR "unalias" , unaliascmd },
return bp;
}
+#if EDITING_HAS_get_exe_name
+static const char * FAST_FUNC
+get_builtin_name(int i)
+{
+ return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
+}
+#endif
+
/*
* Execute a simple command.
*/
evalcommand(union node *cmd, int flags)
{
static const struct builtincmd null_bltin = {
- "\0\0", bltincmd /* why three NULs? */
+ BUILTIN_REGULAR "", bltincmd
};
struct localvar_list *localvar_stop;
struct parsefile *file_stop;
struct arglist varlist;
char **argv;
int argc;
+ struct strlist *osp;
const struct strlist *sp;
struct cmdentry cmdentry;
struct job *jp;
char *lastarg;
const char *path;
int spclbltin;
+ int cmd_flag;
int status;
char **nargv;
smallint cmd_is_exec;
+ int vflags;
+ int vlocal;
errlinno = lineno = cmd->ncmd.linno;
if (funcline)
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
- localvar_stop = pushlocalvars();
file_stop = g_parsefile;
back_exitstatus = 0;
arglist.lastp = &arglist.list;
*arglist.lastp = NULL;
+ cmd_flag = 0;
+ cmd_is_exec = 0;
+ spclbltin = -1;
+ vflags = 0;
+ vlocal = 0;
+ path = NULL;
+
argc = 0;
- if (cmd->ncmd.args) {
- struct builtincmd *bcmd;
- smallint pseudovarflag;
+ argp = cmd->ncmd.args;
+ osp = fill_arglist(&arglist, &argp);
+ if (osp) {
+ int pseudovarflag = 0;
+
+ for (;;) {
+ find_command(arglist.list->text, &cmdentry,
+ cmd_flag | DO_REGBLTIN, pathval());
- bcmd = find_builtin(cmd->ncmd.args->narg.text);
- pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
+ vlocal++;
- for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
- struct strlist **spp;
+ /* implement bltin and command here */
+ if (cmdentry.cmdtype != CMDBUILTIN)
+ break;
- spp = arglist.lastp;
- if (pseudovarflag && isassignment(argp->narg.text))
- expandarg(argp, &arglist, EXP_VARTILDE);
- else
- expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+ pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
+ if (spclbltin < 0) {
+ spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
+ vlocal = !spclbltin;
+ }
+ cmd_is_exec = cmdentry.u.cmd == EXECCMD;
+ if (cmdentry.u.cmd != COMMANDCMD)
+ break;
- for (sp = *spp; sp; sp = sp->next)
- argc++;
+ cmd_flag = parse_command_args(&arglist, &argp, &path);
+ if (!cmd_flag)
+ break;
}
+
+ for (; argp; argp = argp->narg.next)
+ expandarg(argp, &arglist,
+ pseudovarflag &&
+ isassignment(argp->narg.text) ?
+ EXP_VARTILDE : EXP_FULL | EXP_TILDE);
+
+ for (sp = arglist.list; sp; sp = sp->next)
+ argc++;
+
+ if (cmd_is_exec && argc > 1)
+ vflags = VEXPORT;
}
+ localvar_stop = pushlocalvars(vlocal);
+
/* Reserve one extra spot at the front for shellexec. */
nargv = stalloc(sizeof(char *) * (argc + 2));
argv = ++nargv;
}
status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
- path = vpath.var_text;
+ if (status) {
+ bail:
+ exitstatus = status;
+
+ /* We have a redirection error. */
+ if (spclbltin > 0)
+ raise_exception(EXERROR);
+
+ goto out;
+ }
+
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
struct strlist **spp;
- char *p;
spp = varlist.lastp;
expandarg(argp, &varlist, EXP_VARTILDE);
- mklocal((*spp)->text);
-
- /*
- * Modify the command lookup path, if a PATH= assignment
- * is present
- */
- p = (*spp)->text;
- if (varcmp(p, path) == 0)
- path = p;
+ if (vlocal)
+ mklocal((*spp)->text, VEXPORT);
+ else
+ setvareq((*spp)->text, vflags);
}
/* Print the command if xflag is set. */
safe_write(preverrout_fd, "\n", 1);
}
- cmd_is_exec = 0;
- spclbltin = -1;
-
/* Now locate the command. */
- if (argc) {
- int cmd_flag = DO_ERR;
-#if ENABLE_ASH_CMDCMD
- const char *oldpath = path + 5;
-#endif
- path += 5;
- for (;;) {
- find_command(argv[0], &cmdentry, cmd_flag, path);
- if (cmdentry.cmdtype == CMDUNKNOWN) {
- flush_stdout_stderr();
- status = 127;
- goto bail;
- }
-
- /* implement bltin and command here */
- if (cmdentry.cmdtype != CMDBUILTIN)
- break;
- if (spclbltin < 0)
- spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
- if (cmdentry.u.cmd == EXECCMD)
- cmd_is_exec = 1;
-#if ENABLE_ASH_CMDCMD
- if (cmdentry.u.cmd == COMMANDCMD) {
- path = oldpath;
- nargv = parse_command_args(argv, &path);
- if (!nargv)
- break;
- /* It's "command [-p] PROG ARGS" (that is, no -Vv).
- * nargv => "PROG". path is updated if -p.
- */
- argc -= nargv - argv;
- argv = nargv;
- cmd_flag |= DO_NOFUNC;
- } else
-#endif
- break;
- }
+ if (cmdentry.cmdtype != CMDBUILTIN
+ || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
+ ) {
+ path = path ? path : pathval();
+ find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
}
- if (status) {
- bail:
- exitstatus = status;
-
- /* We have a redirection error. */
- if (spclbltin > 0)
- raise_exception(EXERROR);
-
- goto out;
- }
+ jp = NULL;
/* Execute the command. */
switch (cmdentry.cmdtype) {
+ case CMDUNKNOWN:
+ status = 127;
+ flush_stdout_stderr();
+ goto bail;
+
default: {
#if ENABLE_FEATURE_SH_STANDALONE \
* and/or wait for user input ineligible for NOFORK:
* for example, "yes" or "rm" (rm -i waits for input).
*/
- status = run_nofork_applet(applet_no, argv);
+ exitstatus = run_nofork_applet(applet_no, argv);
environ = sv_environ;
/*
* Try enabling NOFORK for "yes" applet.
jp = makejob(/*cmd,*/ 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
/* parent */
- status = waitforjob(jp);
- INT_ON;
TRACE(("forked child exited with %d\n", status));
break;
}
FORCE_INT_ON;
/* fall through to exec'ing external program */
}
- listsetvar(varlist.list, VEXPORT|VSTACK);
shellexec(argv[0], argv, path, cmdentry.u.index);
/* NOTREACHED */
} /* default */
case CMDBUILTIN:
- if (spclbltin > 0 || argc == 0) {
- poplocalvars(1);
- if (cmd_is_exec && argc > 1)
- listsetvar(varlist.list, VEXPORT);
- }
-
- /* Tight loop with builtins only:
- * "while kill -0 $child; do true; done"
- * will never exit even if $child died, unless we do this
- * to reap the zombie and make kill detect that it's gone: */
- dowait(DOWAIT_NONBLOCK, NULL);
-
- if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
- if (exception_type == EXERROR && spclbltin <= 0) {
- FORCE_INT_ON;
- goto readstatus;
- }
+ if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
+ && !(exception_type == EXERROR && spclbltin <= 0)
+ ) {
raise:
longjmp(exception_handler->loc, 1);
}
- goto readstatus;
+ break;
case CMDFUNCTION:
- /* See above for the rationale */
- dowait(DOWAIT_NONBLOCK, NULL);
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
- readstatus:
- status = exitstatus;
break;
} /* switch */
+ status = waitforjob(jp);
+ FORCE_INT_ON;
+
out:
if (cmd->ncmd.redirect)
popredir(/*drop:*/ cmd_is_exec);
struct synstack *next;
};
+static int
+pgetc_top(struct synstack *stack)
+{
+ return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
+}
+
static void
synstack_push(struct synstack **stack, struct synstack *next, int syntax)
{
mpath = mpathset() ? mpathval() : mailval();
new_hash = 0;
for (;;) {
- p = path_advance(&mpath, nullstr);
- if (p == NULL)
+ int len;
+
+ len = padvance_magic(&mpath, nullstr, 2);
+ if (!len)
+ break;
+ p = stackblock();
break;
if (*p == '\0')
continue;
}
USTPUTC(c, out);
nlprompt();
- c = pgetc();
+ c = pgetc_top(synstack);
goto loop; /* continue outer loop */
case CWORD:
USTPUTC(c, out);
USTPUTC(CTLESC, out);
USTPUTC('\\', out);
pungetc();
- } else if (c == '\n') {
- nlprompt();
} else {
if (pssyntax && c == '$') {
USTPUTC(CTLESC, out);
IF_ASH_ALIAS(if (c != PEOA))
USTPUTC(c, out);
}
- c = pgetc();
+ c = pgetc_top(synstack);
} /* for (;;) */
endword:
do {
STPUTC(c, out);
c = pgetc_eatbnl();
- } while (isdigit(c));
+ } while (!subtype && isdigit(c));
} else if (c != '}') {
/* $[{[#]]<specialchar>[}] */
int cc = c;
union node *n;
char *str;
size_t savelen;
+ struct heredoc *saveheredoclist;
smallint saveprompt = 0;
str = NULL;
*nlpp = stzalloc(sizeof(**nlpp));
/* (*nlpp)->next = NULL; - stzalloc did it */
+ saveheredoclist = heredoclist;
+ heredoclist = NULL;
+
if (oldstyle) {
saveprompt = doprompt;
doprompt = 0;
if (oldstyle)
doprompt = saveprompt;
- else if (readtoken() != TRP)
- raise_error_unexpected_syntax(TRP);
+ else {
+ if (readtoken() != TRP)
+ raise_error_unexpected_syntax(TRP);
+ setinputstring(nullstr);
+ }
+
+ parseheredoc();
+ heredoclist = saveheredoclist;
(*nlpp)->n = n;
- if (oldstyle) {
- /*
- * Start reading from old file again, ignoring any pushed back
- * tokens left from the backquote parsing
- */
- popfile();
+ /* Start reading from old file again. */
+ popfile();
+ /* Ignore any pushed back tokens left from the backquote parsing. */
+ if (oldstyle)
tokpushback = 0;
- }
- while (stackblocksize() <= savelen)
- growstackblock();
- STARTSTACKSTR(out);
+ out = growstackto(savelen + 1);
if (str) {
memcpy(out, str, savelen);
STADJUST(savelen, out);
while (here) {
tokpushback = 0;
setprompt_if(needprompt, 2);
- readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
- here->eofmark, here->striptabs);
+ if (here->here->type == NHERE)
+ readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
+ else
+ readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
n = stzalloc(sizeof(struct narg));
n->narg.type = NARG;
/*n->narg.next = NULL; - stzalloc did it */
volatile int saveint;
struct jmploc *volatile savehandler = exception_handler;
struct jmploc jmploc;
+ const char *volatile result;
+ int err;
/* XXX Fix (char *) cast. */
setinputstring((char *)ps);
saveprompt = doprompt;
doprompt = 0;
+ result = ps;
+
+ SAVE_INT(saveint);
+ err = setjmp(jmploc.loc);
+ if (err)
+ goto out;
/* readtoken1() might die horribly.
* Try a prompt with syntactically wrong command:
* PS1='$(date "+%H:%M:%S) > '
*/
- SAVE_INT(saveint);
- if (setjmp(jmploc.loc) == 0) {
- exception_handler = &jmploc;
- readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0);
- }
- exception_handler = savehandler;
- RESTORE_INT(saveint);
-
- doprompt = saveprompt;
-
- /* Try: PS1='`xxx(`' */
- unwindfiles(file_stop);
+ exception_handler = &jmploc;
+ readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0);
n.narg.type = NARG;
n.narg.next = NULL;
/* expandarg() might fail too:
* PS1='$((123+))'
*/
- SAVE_INT(saveint);
- if (setjmp(jmploc.loc) == 0) {
- exception_handler = &jmploc;
- expandarg(&n, NULL, EXP_QUOTED);
- } else if (exception_type == EXEXIT) {
- exitshell();
- }
+ expandarg(&n, NULL, EXP_QUOTED);
+ result = stackblock();
+
+out:
exception_handler = savehandler;
+ if (err && exception_type != EXERROR)
+ longjmp(exception_handler->loc, 1);
RESTORE_INT(saveint);
- return stackblock();
+ doprompt = saveprompt;
+ /* Try: PS1='`xxx(`' */
+ unwindfiles(file_stop);
+
+ return result;
}
static inline int
if (!top || numeof >= 50)
break;
if (!stoppedjobs()) {
- if (!Iflag)
+ if (!Iflag) {
+ if (iflag) {
+ newline_and_flush(stderr);
+ }
break;
+ }
out2str("\nUse \"exit\" to leave shell.\n");
}
numeof++;
skip = evalskip;
if (skip) {
- evalskip &= ~SKIPFUNC;
+ evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
break;
}
}
* search for the file, which is necessary to find sub-commands.
*/
static char *
-find_dot_file(char *name)
+find_dot_file(char *basename)
{
char *fullname;
const char *path = pathval();
struct stat statb;
+ int len;
/* don't try this for absolute or relative paths */
- if (strchr(name, '/'))
- return name;
+ if (strchr(basename, '/'))
+ return basename;
- while ((fullname = path_advance(&path, name)) != NULL) {
- if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
- /*
- * Don't bother freeing here, since it will
- * be freed by the caller.
- */
- return fullname;
+ while ((len = padvance(&path, basename)) >= 0) {
+ fullname = stackblock();
+ if ((!pathopt || *pathopt == 'f')
+ && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
+ ) {
+ /* This will be freed by the caller. */
+ return stalloc(len);
}
- if (fullname != name)
- stunalloc(fullname);
}
/* not found in PATH */
#if ENABLE_ASH_BASH_SOURCE_CURDIR
- return name;
+ return basename;
#else
- ash_msg_and_raise_error("%s: not found", name);
+ ash_msg_and_raise_error("%s: not found", basename);
/* NOTREACHED */
#endif
}
{
if (stoppedjobs())
return 0;
+
if (argv[1])
- exitstatus = number(argv[1]);
+ savestatus = number(argv[1]);
+
raise_exception(EXEXIT);
/* NOTREACHED */
}
int e;
int updatetbl;
struct builtincmd *bcmd;
+ int len;
/* If name contains a slash, don't use PATH or hash table */
if (strchr(name, '/') != NULL) {
/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
updatetbl = (path == pathval());
- if (!updatetbl) {
+ if (!updatetbl)
act |= DO_ALTPATH;
- if (strstr(path, "%builtin") != NULL)
- act |= DO_ALTBLTIN;
- }
/* If name is in the table, check answer will be ok */
cmdp = cmdlookup(name, 0);
abort();
#endif
case CMDNORMAL:
- bit = DO_ALTPATH;
+ bit = DO_ALTPATH | DO_REGBLTIN;
break;
case CMDFUNCTION:
bit = DO_NOFUNC;
break;
case CMDBUILTIN:
- bit = DO_ALTBLTIN;
+ bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
break;
}
if (act & bit) {
+ if (act & bit & DO_REGBLTIN)
+ goto fail;
+
updatetbl = 0;
cmdp = NULL;
} else if (cmdp->rehash == 0)
if (bcmd) {
if (IS_BUILTIN_REGULAR(bcmd))
goto builtin_success;
- if (act & DO_ALTPATH) {
- if (!(act & DO_ALTBLTIN))
- goto builtin_success;
- } else if (builtinloc <= 0) {
+ if (act & DO_ALTPATH)
+ goto builtin_success;
+ if (builtinloc <= 0)
goto builtin_success;
- }
}
+ if (act & DO_REGBLTIN)
+ goto fail;
+
#if ENABLE_FEATURE_SH_STANDALONE
{
int applet_no = find_applet_by_name(name);
e = ENOENT;
idx = -1;
loop:
- while ((fullname = path_advance(&path, name)) != NULL) {
- stunalloc(fullname);
- /* NB: code below will still use fullname
- * despite it being "unallocated" */
+ while ((len = padvance(&path, name)) >= 0) {
+ const char *lpathopt = pathopt;
+
+ fullname = stackblock();
idx++;
- if (pathopt) {
- if (prefix(pathopt, "builtin")) {
+ if (lpathopt) {
+ if (*lpathopt == 'b') {
if (bcmd)
goto builtin_success;
continue;
- }
- if ((act & DO_NOFUNC)
- || !prefix(pathopt, "func")
- ) { /* ignore unimplemented options */
+ } else if (!(act & DO_NOFUNC)) {
+ /* handled below */
+ } else {
+ /* ignore unimplemented options */
continue;
}
}
e = EACCES; /* if we fail, this will be the error */
if (!S_ISREG(statb.st_mode))
continue;
- if (pathopt) { /* this is a %func directory */
- stalloc(strlen(fullname) + 1);
+ if (lpathopt) { /* this is a %func directory */
+ stalloc(len);
/* NB: stalloc will return space pointed by fullname
* (because we don't have any intervening allocations
* between stunalloc above and this stalloc) */
#endif
ash_msg("%s: %s", name, errmsg(e, "not found"));
}
+ fail:
entry->cmdtype = CMDUNKNOWN;
return;
/* ============ 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.
*/
{
struct jmploc loc;
char *p;
- int status;
#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
if (line_input_state)
save_history(line_input_state);
#endif
- status = exitstatus;
- TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
- if (setjmp(loc.loc)) {
- if (exception_type == EXEXIT)
- status = exitstatus;
+ savestatus = exitstatus;
+ TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
+ if (setjmp(loc.loc))
goto out;
- }
exception_handler = &loc;
p = trap[0];
if (p) {
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(status);
+ _exit(exitstatus);
/* NOTREACHED */
}
sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
setsignal(SIGCHLD);
- /* bash re-enables SIGHUP which is SIG_IGNed on entry.
- * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
- */
- signal(SIGHUP, SIG_DFL);
-
{
char **envp;
const char *p;
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.
- * (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 redir.c: */
- unwindredir(NULL);
-
- /* from var.c: */
- unwindlocalvars(NULL);
-}
-
#if PROFILE
static short profile_buf[16384];
extern int etext();
smallint e;
smallint s;
- reset();
+ exitreset();
e = exception_type;
s = state;
- if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
+ if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
exitshell();
}
+
+ reset();
+
if (e == EXINT) {
newline_and_flush(stderr);
}
}
#endif
state4: /* XXX ??? - why isn't this before the "if" statement */
+
+ /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
+ * Try:
+ * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
+ * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
+ */
+ signal(SIGHUP, SIG_DFL);
+
cmdloop(1);
}
#if PROFILE