* a quit signal will generate a core dump.
*/
#define DEBUG 0
-#define IFS_BROKEN
#define PROFILE 0
-#if ENABLE_ASH_JOB_CONTROL
-#define JOBS 1
-#else
-#define JOBS 0
-#endif
+
+#define IFS_BROKEN
+
+#define JOBS ENABLE_ASH_JOB_CONTROL
#if DEBUG
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
-#include "busybox.h"
+#endif
+
+#include "busybox.h" /* for applet_names */
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
#endif
+/* ============ Hash table sizes. Configurable. */
+
+#define VTABSIZE 39
+#define ATABSIZE 39
+#define CMDTABLESIZE 31 /* should be prime */
+
+
+/* ============ Misc helpers */
+
+#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
+
+/* C99 say: "char" declaration may be signed or unsigned default */
+#define signed_char2int(sc) ((int)((signed char)sc))
+
+
/* ============ Shell options */
static const char *const optletters_optnames[] = {
"a" "allexport",
"b" "notify",
"u" "nounset",
- "\0" "vi",
+ "\0" "vi"
#if DEBUG
- "\0" "nolog",
- "\0" "debug",
+ ,"\0" "nolog"
+ ,"\0" "debug"
#endif
};
#define optletters(n) optletters_optnames[(n)][0]
#define optnames(n) (&optletters_optnames[(n)][1])
-#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
+enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
-static char optlist[NOPTS];
+static char optlist[NOPTS] ALIGN1;
#define eflag optlist[0]
#define fflag optlist[1]
/* ============ Misc data */
-#ifdef __GLIBC__
-/* glibc sucks */
-static int *dash_errno;
-#undef errno
-#define errno (*dash_errno)
-#endif
-
-static char nullstr[1]; /* zero length string */
-static const char homestr[] = "HOME";
-static const char snlfmt[] = "%s\n";
-static const char illnum[] = "Illegal number: %s";
-
-static int isloginsh;
-/* pid of main shell */
-static int rootpid;
-/* shell level: 0 for the main shell, 1 for its children, and so on */
-static int shlvl;
-#define rootshell (!shlvl)
-/* trap handler commands */
-static char *trap[NSIG];
-/* current value of signal */
-static char sigmode[NSIG - 1];
-/* indicates specified signal received */
-static char gotsig[NSIG - 1];
-static char *arg0; /* value of $0 */
-
+static const char homestr[] ALIGN1 = "HOME";
+static const char snlfmt[] ALIGN1 = "%s\n";
+static const char illnum[] ALIGN1 = "Illegal number: %s";
-/* ============ Interrupts / exceptions
- *
+/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
struct jmploc {
jmp_buf loc;
};
-static struct jmploc *exception_handler;
-static int exception;
-/* exceptions */
+
+struct globals_misc {
+ /* pid of main shell */
+ int rootpid;
+ /* shell level: 0 for the main shell, 1 for its children, and so on */
+ int shlvl;
+#define rootshell (!shlvl)
+ char *minusc; /* argument to -c option */
+
+ char *curdir; // = nullstr; /* current working directory */
+ char *physdir; // = nullstr; /* physical working directory */
+
+ char *arg0; /* value of $0 */
+
+ struct jmploc *exception_handler;
+
+// disabled by vda: cannot understand how it was supposed to work -
+// cannot fix bugs. That's why you have to explain your non-trivial designs!
+// /* do we generate EXSIG events */
+// int exsig; /* counter */
+ volatile int suppressint; /* counter */
+ volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */
+ /* last pending signal */
+ volatile /*sig_atomic_t*/ smallint pendingsig;
+ smallint exception; /* kind of exception (0..5) */
+ /* exceptions */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXSHELLPROC 2 /* execute a shell procedure */
#define EXEXEC 3 /* command execution failed */
#define EXEXIT 4 /* exit the shell */
#define EXSIG 5 /* trapped signal in wait(1) */
-static volatile int suppressint;
-static volatile sig_atomic_t intpending;
-/* do we generate EXSIG events */
-static int exsig;
-/* last pending signal */
-static volatile sig_atomic_t pendingsigs;
-
-/*
- * Sigmode records the current value of the signal handlers for the various
- * modes. A value of zero means that the current handler is not known.
- * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
- */
+ /* trap handler commands */
+ char *trap[NSIG];
+ smallint isloginsh;
+ char nullstr[1]; /* zero length string */
+ /*
+ * Sigmode records the current value of the signal handlers for the various
+ * modes. A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
+ */
+ char sigmode[NSIG - 1];
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
#define S_RESET 5 /* temporary - to reset a hard ignored sig */
+ /* indicates specified signal received */
+ char gotsig[NSIG - 1];
+};
+extern struct globals_misc *const ash_ptr_to_globals_misc;
+#define G_misc (*ash_ptr_to_globals_misc)
+#define rootpid (G_misc.rootpid )
+#define shlvl (G_misc.shlvl )
+#define minusc (G_misc.minusc )
+#define curdir (G_misc.curdir )
+#define physdir (G_misc.physdir )
+#define arg0 (G_misc.arg0 )
+#define exception_handler (G_misc.exception_handler)
+#define exception (G_misc.exception )
+#define suppressint (G_misc.suppressint )
+#define intpending (G_misc.intpending )
+//#define exsig (G_misc.exsig )
+#define pendingsig (G_misc.pendingsig )
+#define trap (G_misc.trap )
+#define isloginsh (G_misc.isloginsh)
+#define nullstr (G_misc.nullstr )
+#define sigmode (G_misc.sigmode )
+#define gotsig (G_misc.gotsig )
+#define INIT_G_misc() do { \
+ (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
+ barrier(); \
+ curdir = nullstr; \
+ physdir = nullstr; \
+} while (0)
+
+
+/* ============ Interrupts / exceptions */
+
/*
* These macros allow the user to suspend the handling of interrupt signals
- * over a period of time. This is similar to SIGHOLD to or sigblock, but
+ * over a period of time. This is similar to SIGHOLD or to sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
-#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
#define INT_OFF \
- ({ \
+ do { \
suppressint++; \
xbarrier(); \
- 0; \
- })
+ } while (0)
/*
* Called to raise an exception. Since C doesn't include exceptions, we
int i;
intpending = 0;
+ /* Signal is not automatically unmasked after it is raised,
+ * do it ourself - unmask all signals */
+ sigprocmask_allsigs(SIG_UNBLOCK);
+ /* pendingsig = 0; - now done in onsig() */
+
i = EXSIG;
if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
if (!(rootshell && iflag)) {
+ /* Kill ourself with SIGINT */
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
#define FORCE_INT_ON force_int_on()
#else
#define INT_ON \
- ({ \
+ do { \
xbarrier(); \
- if (--suppressint == 0 && intpending) raise_interrupt(); \
- 0; \
- })
+ if (--suppressint == 0 && intpending) \
+ raise_interrupt(); \
+ } while (0)
#define FORCE_INT_ON \
- ({ \
+ do { \
xbarrier(); \
suppressint = 0; \
- if (intpending) raise_interrupt(); \
- 0; \
- })
+ if (intpending) \
+ raise_interrupt(); \
+ } while (0)
#endif /* ASH_OPTIMIZE_FOR_SIZE */
#define SAVE_INT(v) ((v) = suppressint)
#define RESTORE_INT(v) \
- ({ \
+ do { \
xbarrier(); \
suppressint = (v); \
- if (suppressint == 0 && intpending) raise_interrupt(); \
- 0; \
- })
-
-#define EXSIGON \
- ({ \
- exsig++; \
- xbarrier(); \
- if (pendingsigs) \
- raise_exception(EXSIG); \
- 0; \
- })
-/* EXSIG is turned off by evalbltin(). */
+ if (suppressint == 0 && intpending) \
+ raise_interrupt(); \
+ } while (0)
/*
* Ignore a signal. Only one usage site - in forkchild()
onsig(int signo)
{
gotsig[signo - 1] = 1;
- pendingsigs = signo;
+ pendingsig = signo;
- if (exsig || (signo == SIGINT && !trap[SIGINT])) {
- if (!suppressint)
- raise_interrupt();
+ if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
+ if (!suppressint) {
+ pendingsig = 0;
+ raise_interrupt(); /* does not return */
+ }
intpending = 1;
}
}
}
-/* ============ Parsing structures */
+/* ============ Parser structures */
/* control characters in argument strings */
#define CTLESC '\201' /* escape next character */
#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
#define VSLENGTH 0xa /* ${#var} */
+static const char dolatstr[] ALIGN1 = {
+ CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
+};
+
#define NCMD 0
#define NPIPE 1
#define NREDIR 2
}
}
#ifdef O_APPEND
- flags = fcntl(fileno(tracefile), F_GETFL, 0);
+ flags = fcntl(fileno(tracefile), F_GETFL);
if (flags >= 0)
fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
#endif
first = 1;
for (np = cmd->ncmd.args; np; np = np->narg.next) {
- if (! first)
- putchar(' ');
+ if (!first)
+ putc(' ', fp);
sharg(np, fp);
first = 0;
}
for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
- if (! first)
- putchar(' ');
+ if (!first)
+ putc(' ', fp);
+ dftfd = 0;
switch (np->nfile.type) {
- case NTO: s = ">"; dftfd = 1; break;
- case NCLOBBER: s = ">|"; dftfd = 1; break;
- case NAPPEND: s = ">>"; dftfd = 1; break;
- case NTOFD: s = ">&"; dftfd = 1; break;
- case NFROM: s = "<"; dftfd = 0; break;
- case NFROMFD: s = "<&"; dftfd = 0; break;
- case NFROMTO: s = "<>"; dftfd = 0; break;
- default: s = "*error*"; dftfd = 0; break;
+ case NTO: s = ">>"+1; dftfd = 1; break;
+ case NCLOBBER: s = ">|"; dftfd = 1; break;
+ case NAPPEND: s = ">>"; dftfd = 1; break;
+ case NTOFD: s = ">&"; dftfd = 1; break;
+ case NFROM: s = "<"; break;
+ case NFROMFD: s = "<&"; break;
+ case NFROMTO: s = "<>"; break;
+ default: s = "*error*"; break;
}
if (np->nfile.fd != dftfd)
fprintf(fp, "%d", np->nfile.fd);
{
fprintf(stderr, "%s: ", arg0);
if (commandname) {
- const char *fmt = (!iflag || parsefile->fd) ?
- "%s: %d: " : "%s: ";
- fprintf(stderr, fmt, commandname, startlinno);
+ if (strcmp(arg0, commandname))
+ fprintf(stderr, "%s: ", commandname);
+ if (!iflag || parsefile->fd)
+ fprintf(stderr, "line %d: ", startlinno);
}
vfprintf(stderr, msg, ap);
outcslow('\n', stderr);
* on many machines. */
SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1,
/* Minimum size of a block */
- MINSIZE = SHELL_ALIGN(504),
+ MINSIZE = SHELL_ALIGN(504),
};
struct stack_block {
struct stackmark *marknext;
};
-static struct stack_block stackbase;
-static struct stack_block *stackp = &stackbase;
-static struct stackmark *markp;
-static char *stacknxt = stackbase.space;
-static size_t stacknleft = MINSIZE;
-static char *sstrend = stackbase.space + MINSIZE;
-static int herefd = -1;
-#define stackblock() ((void *)stacknxt)
-#define stackblocksize() stacknleft
+struct globals_memstack {
+ struct stack_block *g_stackp; // = &stackbase;
+ struct stackmark *markp;
+ char *g_stacknxt; // = stackbase.space;
+ char *sstrend; // = stackbase.space + MINSIZE;
+ size_t g_stacknleft; // = MINSIZE;
+ int herefd; // = -1;
+ struct stack_block stackbase;
+};
+extern struct globals_memstack *const ash_ptr_to_globals_memstack;
+#define G_memstack (*ash_ptr_to_globals_memstack)
+#define g_stackp (G_memstack.g_stackp )
+#define markp (G_memstack.markp )
+#define g_stacknxt (G_memstack.g_stacknxt )
+#define sstrend (G_memstack.sstrend )
+#define g_stacknleft (G_memstack.g_stacknleft)
+#define herefd (G_memstack.herefd )
+#define stackbase (G_memstack.stackbase )
+#define INIT_G_memstack() do { \
+ (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
+ barrier(); \
+ g_stackp = &stackbase; \
+ g_stacknxt = stackbase.space; \
+ g_stacknleft = MINSIZE; \
+ sstrend = stackbase.space + MINSIZE; \
+ herefd = -1; \
+} while (0)
+
+#define stackblock() ((void *)g_stacknxt)
+#define stackblocksize() g_stacknleft
+
static void *
ckrealloc(void * p, size_t nbytes)
return ckrealloc(NULL, nbytes);
}
+static void *
+ckzalloc(size_t nbytes)
+{
+ return memset(ckmalloc(nbytes), 0, nbytes);
+}
+
/*
* Make a copy of a string in safe storage.
*/
size_t aligned;
aligned = SHELL_ALIGN(nbytes);
- if (aligned > stacknleft) {
+ if (aligned > g_stacknleft) {
size_t len;
size_t blocksize;
struct stack_block *sp;
ash_msg_and_raise_error(bb_msg_memory_exhausted);
INT_OFF;
sp = ckmalloc(len);
- sp->prev = stackp;
- stacknxt = sp->space;
- stacknleft = blocksize;
- sstrend = stacknxt + blocksize;
- stackp = sp;
+ sp->prev = g_stackp;
+ g_stacknxt = sp->space;
+ g_stacknleft = blocksize;
+ sstrend = g_stacknxt + blocksize;
+ g_stackp = sp;
INT_ON;
}
- p = stacknxt;
- stacknxt += aligned;
- stacknleft -= aligned;
+ p = g_stacknxt;
+ g_stacknxt += aligned;
+ g_stacknleft -= aligned;
return p;
}
+static void *
+stzalloc(size_t nbytes)
+{
+ return memset(stalloc(nbytes), 0, nbytes);
+}
+
static void
stunalloc(void *p)
{
#if DEBUG
- if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
+ if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
write(2, "stunalloc\n", 10);
abort();
}
#endif
- stacknleft += stacknxt - (char *)p;
- stacknxt = p;
+ g_stacknleft += g_stacknxt - (char *)p;
+ g_stacknxt = p;
}
/*
static void
setstackmark(struct stackmark *mark)
{
- mark->stackp = stackp;
- mark->stacknxt = stacknxt;
- mark->stacknleft = stacknleft;
+ mark->stackp = g_stackp;
+ mark->stacknxt = g_stacknxt;
+ mark->stacknleft = g_stacknleft;
mark->marknext = markp;
markp = mark;
}
{
struct stack_block *sp;
+ if (!mark->stackp)
+ return;
+
INT_OFF;
markp = mark->marknext;
- while (stackp != mark->stackp) {
- sp = stackp;
- stackp = sp->prev;
+ while (g_stackp != mark->stackp) {
+ sp = g_stackp;
+ g_stackp = sp->prev;
free(sp);
}
- stacknxt = mark->stacknxt;
- stacknleft = mark->stacknleft;
+ g_stacknxt = mark->stacknxt;
+ g_stacknleft = mark->stacknleft;
sstrend = mark->stacknxt + mark->stacknleft;
INT_ON;
}
{
size_t newlen;
- newlen = stacknleft * 2;
- if (newlen < stacknleft)
+ newlen = g_stacknleft * 2;
+ if (newlen < g_stacknleft)
ash_msg_and_raise_error(bb_msg_memory_exhausted);
if (newlen < 128)
newlen += 128;
- if (stacknxt == stackp->space && stackp != &stackbase) {
+ if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
struct stack_block *oldstackp;
struct stackmark *xmark;
struct stack_block *sp;
size_t grosslen;
INT_OFF;
- oldstackp = stackp;
- sp = stackp;
+ oldstackp = g_stackp;
+ sp = g_stackp;
prevstackp = sp->prev;
grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
sp = ckrealloc(sp, grosslen);
sp->prev = prevstackp;
- stackp = sp;
- stacknxt = sp->space;
- stacknleft = newlen;
+ g_stackp = sp;
+ g_stacknxt = sp->space;
+ g_stacknleft = newlen;
sstrend = sp->space + newlen;
/*
*/
xmark = markp;
while (xmark != NULL && xmark->stackp == oldstackp) {
- xmark->stackp = stackp;
- xmark->stacknxt = stacknxt;
- xmark->stacknleft = stacknleft;
+ xmark->stackp = g_stackp;
+ xmark->stacknxt = g_stacknxt;
+ xmark->stacknleft = g_stacknleft;
xmark = xmark->marknext;
}
INT_ON;
} else {
- char *oldspace = stacknxt;
- int oldlen = stacknleft;
+ char *oldspace = g_stacknxt;
+ int oldlen = g_stacknleft;
char *p = stalloc(newlen);
/* free the space we just allocated */
- stacknxt = memcpy(p, oldspace, oldlen);
- stacknleft += newlen;
+ g_stacknxt = memcpy(p, oldspace, oldlen);
+ g_stacknleft += newlen;
}
}
grabstackblock(size_t len)
{
len = SHELL_ALIGN(len);
- stacknxt += len;
- stacknleft -= len;
+ g_stacknxt += len;
+ g_stacknleft -= len;
}
/*
static char *
makestrspace(size_t newlen, char *p)
{
- size_t len = p - stacknxt;
+ size_t len = p - g_stacknxt;
size_t size = stackblocksize();
for (;;) {
return p;
}
-#define STARTSTACKSTR(p) ((p) = stackblock())
-#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
#define CHECKSTRSPACE(n, p) \
- ({ \
+ do { \
char *q = (p); \
size_t l = (n); \
size_t m = sstrend - q; \
if (l > m) \
(p) = makestrspace(l, q); \
- 0; \
- })
+ } while (0)
#define USTPUTC(c, p) (*p++ = (c))
-#define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STACKSTRNUL(p) \
+ do { \
+ if ((p) == sstrend) \
+ p = growstackstr(); \
+ *p = '\0'; \
+ } while (0)
#define STUNPUTC(p) (--p)
-#define STTOPC(p) p[-1]
+#define STTOPC(p) (p[-1])
#define STADJUST(amount, p) (p += (amount))
#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
{
while (*pfx) {
if (*pfx++ != *string++)
- return 0;
+ return NULL;
}
return (char *) string;
}
c = *p++;
for (q = optstring; *q != c; ) {
if (*q == '\0')
- ash_msg_and_raise_error("Illegal option -%c", c);
+ ash_msg_and_raise_error("illegal option -%c", c);
if (*++q == ':')
q++;
}
if (*++q == ':') {
if (*p == '\0' && (p = *argptr++) == NULL)
- ash_msg_and_raise_error("No arg for -%c option", c);
+ ash_msg_and_raise_error("no arg for -%c option", c);
optionarg = p;
p = NULL;
}
}
-/* ============ Shell variables */
+/* ============ Math support definitions */
-/* flags */
-#define VEXPORT 0x01 /* variable is exported */
-#define VREADONLY 0x02 /* variable cannot be modified */
-#define VSTRFIXED 0x04 /* variable struct is statically allocated */
-#define VTEXTFIXED 0x08 /* text is statically allocated */
-#define VSTACK 0x10 /* text is allocated on the stack */
-#define VUNSET 0x20 /* the variable is not set */
-#define VNOFUNC 0x40 /* don't call the callback function */
-#define VNOSET 0x80 /* do not set variable - just readonly test */
-#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
-#ifdef DYNAMIC_VAR
-# define VDYNAMIC 0x200 /* dynamic variable */
-# else
-# define VDYNAMIC 0
+#if ENABLE_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#define arith_t_type long long
+#else
+typedef long arith_t;
+#define arith_t_type long
#endif
-static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
-#ifdef IFS_BROKEN
-static const char defifsvar[] = "IFS= \t\n";
-#define defifs (defifsvar + 4)
-#else
-static const char defifs[] = " \t\n";
+#if ENABLE_ASH_MATH_SUPPORT
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#if ENABLE_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+#ifndef DYNAMIC_VAR
+#define DYNAMIC_VAR
+#endif
#endif
+
+/* ============ Shell variables */
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+struct redirtab {
+ struct redirtab *next;
+ int renamed[10];
+ int nullredirs;
+};
+
struct shparam {
int nparam; /* # of positional parameters (without $0) */
- unsigned char malloc; /* if parameter list dynamically allocated */
- char **p; /* parameter list */
#if ENABLE_ASH_GETOPTS
int optind; /* next parameter to be processed by getopts */
int optoff; /* used by getopts */
#endif
+ unsigned char malloced; /* if parameter list dynamically allocated */
+ char **p; /* parameter list */
};
-static struct shparam shellparam; /* $@ current positional parameters */
-
-#if ENABLE_ASH_GETOPTS
+/*
+ * Free the list of positional parameters.
+ */
static void
-getoptsreset(const char *value)
+freeparam(volatile struct shparam *param)
{
- shellparam.optind = number(value);
- shellparam.optoff = -1;
+ char **ap;
+
+ if (param->malloced) {
+ for (ap = param->p; *ap; ap++)
+ free(*ap);
+ free(param->p);
+ }
}
+
+#if ENABLE_ASH_GETOPTS
+static void getoptsreset(const char *value);
#endif
struct var {
const char *text; /* saved text */
};
-/* Forward decls for varinit[] */
+/* flags */
+#define VEXPORT 0x01 /* variable is exported */
+#define VREADONLY 0x02 /* variable cannot be modified */
+#define VSTRFIXED 0x04 /* variable struct is statically allocated */
+#define VTEXTFIXED 0x08 /* text is statically allocated */
+#define VSTACK 0x10 /* text is allocated on the stack */
+#define VUNSET 0x20 /* the variable is not set */
+#define VNOFUNC 0x40 /* don't call the callback function */
+#define VNOSET 0x80 /* do not set variable - just readonly test */
+#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC 0x200 /* dynamic variable */
+#else
+# define VDYNAMIC 0
+#endif
+
+#ifdef IFS_BROKEN
+static const char defifsvar[] ALIGN1 = "IFS= \t\n";
+#define defifs (defifsvar + 4)
+#else
+static const char defifs[] ALIGN1 = " \t\n";
+#endif
+
+
+/* Need to be before varinit_data[] */
#if ENABLE_LOCALE_SUPPORT
static void
change_lc_all(const char *value)
static void change_random(const char *);
#endif
-static struct var varinit[] = {
+static const struct {
+ int flags;
+ const char *text;
+ void (*func)(const char *);
+} varinit_data[] = {
#ifdef IFS_BROKEN
- { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL },
+ { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
#else
- { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL },
#endif
#if ENABLE_ASH_MAIL
- { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
- { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
#endif
- { NULL, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
- { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL },
- { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL },
- { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL },
+ { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
+ { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
+ { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
+ { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
#if ENABLE_ASH_GETOPTS
- { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
+ { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset },
#endif
#if ENABLE_ASH_RANDOM_SUPPORT
- { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+ { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
#endif
#if ENABLE_LOCALE_SUPPORT
- { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
- { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype },
#endif
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
- { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
+ { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL },
#endif
};
-#define vifs varinit[0]
+
+struct globals_var {
+ struct shparam shellparam; /* $@ current positional parameters */
+ struct redirtab *redirlist;
+ int g_nullredirs;
+ int preverrout_fd; /* save fd2 before print debug if xflag is set. */
+ struct var *vartab[VTABSIZE];
+ struct var varinit[ARRAY_SIZE(varinit_data)];
+};
+extern struct globals_var *const ash_ptr_to_globals_var;
+#define G_var (*ash_ptr_to_globals_var)
+#define shellparam (G_var.shellparam )
+#define redirlist (G_var.redirlist )
+#define g_nullredirs (G_var.g_nullredirs )
+#define preverrout_fd (G_var.preverrout_fd)
+#define vartab (G_var.vartab )
+#define varinit (G_var.varinit )
+#define INIT_G_var() do { \
+ int i; \
+ (*(struct globals_var**)&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; \
+ varinit[i].text = varinit_data[i].text; \
+ varinit[i].func = varinit_data[i].func; \
+ } \
+} while (0)
+
+#define vifs varinit[0]
#if ENABLE_ASH_MAIL
-#define vmail (&vifs)[1]
-#define vmpath (&vmail)[1]
+# define vmail (&vifs)[1]
+# define vmpath (&vmail)[1]
+# define vpath (&vmpath)[1]
#else
-#define vmpath vifs
+# define vpath (&vifs)[1]
#endif
-#define vpath (&vmpath)[1]
-#define vps1 (&vpath)[1]
-#define vps2 (&vps1)[1]
-#define vps4 (&vps2)[1]
-#define voptind (&vps4)[1]
+#define vps1 (&vpath)[1]
+#define vps2 (&vps1)[1]
+#define vps4 (&vps2)[1]
#if ENABLE_ASH_GETOPTS
-#define vrandom (&voptind)[1]
+# define voptind (&vps4)[1]
+# if ENABLE_ASH_RANDOM_SUPPORT
+# define vrandom (&voptind)[1]
+# endif
#else
-#define vrandom (&vps4)[1]
+# if ENABLE_ASH_RANDOM_SUPPORT
+# define vrandom (&vps4)[1]
+# endif
#endif
-#define defpath (defpathvar + 5)
/*
* The following macros access the values of the above variables.
*/
#define ifsval() (vifs.text + 4)
#define ifsset() ((vifs.flags & VUNSET) == 0)
-#define mailval() (vmail.text + 5)
-#define mpathval() (vmpath.text + 9)
+#if ENABLE_ASH_MAIL
+# define mailval() (vmail.text + 5)
+# define mpathval() (vmpath.text + 9)
+# define mpathset() ((vmpath.flags & VUNSET) == 0)
+#endif
#define pathval() (vpath.text + 5)
#define ps1val() (vps1.text + 4)
#define ps2val() (vps2.text + 4)
#define ps4val() (vps4.text + 4)
-#define optindval() (voptind.text + 7)
-
-#define mpathset() ((vmpath.flags & VUNSET) == 0)
-
-/*
- * The parsefile structure pointed to by the global variable parsefile
- * contains information about the current file being read.
- */
-struct redirtab {
- struct redirtab *next;
- int renamed[10];
- int nullredirs;
-};
-
-static struct redirtab *redirlist;
-static int nullredirs;
-extern char **environ;
-static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
-
-#define VTABSIZE 39
+#if ENABLE_ASH_GETOPTS
+# define optindval() (voptind.text + 7)
+#endif
-static struct var *vartab[VTABSIZE];
#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
+#if ENABLE_ASH_GETOPTS
+static void
+getoptsreset(const char *value)
+{
+ shellparam.optind = number(value);
+ shellparam.optoff = -1;
+}
+#endif
+
/*
* Return of a legal variable name (a letter or underscore followed by zero or
* more letters, underscores, and digits).
vps1.text = "PS1=# ";
#endif
vp = varinit;
- end = vp + sizeof(varinit) / sizeof(varinit[0]);
+ end = vp + ARRAY_SIZE(varinit);
do {
vpp = hashvar(vp->text);
vp->next = *vpp;
if (flags & VNOSET)
return;
/* not found */
- vp = ckmalloc(sizeof(*vp));
+ vp = ckzalloc(sizeof(*vp));
vp->next = *vpp;
- vp->func = NULL;
+ /*vp->func = NULL; - ckzalloc did it */
*vpp = vp;
}
if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
/* ============ Prompt */
-static int doprompt; /* if set, prompt the user */
-static int needprompt; /* true if interactive and at start of line */
+static smallint doprompt; /* if set, prompt the user */
+static smallint needprompt; /* true if interactive and at start of line */
#if ENABLE_FEATURE_EDITING
static line_input_t *line_input_state;
{
if (ENABLE_ASH_EXPAND_PRMT) {
free((char*)cmdedit_prompt);
- cmdedit_prompt = xstrdup(s);
+ cmdedit_prompt = ckstrdup(s);
return;
}
cmdedit_prompt = s;
static int docd(const char *, int);
-static char *curdir = nullstr; /* current working directory */
-static char *physdir = nullstr; /* physical working directory */
-
static int
cdopt(void)
{
break;
}
break;
- } else if (p[1] == '\0')
+ }
+ if (p[1] == '\0')
break;
/* fall through */
default:
static char *
getpwd(void)
{
- char *dir = getcwd(0, 0);
+ char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
return dir ? dir : nullstr;
}
}
static int
-cdcmd(int argc, char **argv)
+cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
const char *dest;
const char *path;
}
static int
-pwdcmd(int argc, char **argv)
+pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int flags;
const char *dir = curdir;
/* ============ ... */
-#define IBUFSIZ (BUFSIZ + 1)
+#define IBUFSIZ COMMON_BUFSIZE
#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
-
-/* shell.h */
-
-static const char spcstr[] = " ";
-static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
-
/* Syntax classes */
#define CWORD 0 /* character is nothing special */
#define CNL 1 /* newline character */
#define PEOA_OR_PEOF PEOF
#endif
-/* C99 say: "char" declaration may be signed or unsigned default */
-#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
-
-/*
- * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
- * (assuming ascii char codes, as the original implementation did)
- */
-#define is_special(c) \
- ( (((unsigned int)c) - 33 < 32) \
- && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+/* number syntax index */
+#define BASESYNTAX 0 /* not in quotes */
+#define DQSYNTAX 1 /* in double quotes */
+#define SQSYNTAX 2 /* in single quotes */
+#define ARISYNTAX 3 /* in arithmetic */
+#define PSSYNTAX 4 /* prompt */
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
#define USE_SIT_FUNCTION
#endif
-/* number syntax index */
-#define BASESYNTAX 0 /* not in quotes */
-#define DQSYNTAX 1 /* in double quotes */
-#define SQSYNTAX 2 /* in single quotes */
-#define ARISYNTAX 3 /* in arithmetic */
-
#if ENABLE_ASH_MATH_SUPPORT
static const char S_I_T[][4] = {
#if ENABLE_ASH_ALIAS
- {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
-#endif
- {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
- {CNL, CNL, CNL, CNL}, /* 2, \n */
- {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
- {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
- {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
- {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
- {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
- {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
- {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
- {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
- {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
+ { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
+#endif
+ { CSPCL, CWORD, CWORD, CWORD }, /* 1, ' ' */
+ { CNL, CNL, CNL, CNL }, /* 2, \n */
+ { CWORD, CCTL, CCTL, CWORD }, /* 3, !*-/:=?[]~ */
+ { CDQUOTE, CENDQUOTE, CWORD, CWORD }, /* 4, '"' */
+ { CVAR, CVAR, CWORD, CVAR }, /* 5, $ */
+ { CSQUOTE, CWORD, CENDQUOTE, CWORD }, /* 6, "'" */
+ { CSPCL, CWORD, CWORD, CLP }, /* 7, ( */
+ { CSPCL, CWORD, CWORD, CRP }, /* 8, ) */
+ { CBACK, CBACK, CCTL, CBACK }, /* 9, \ */
+ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* 10, ` */
+ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* 11, } */
#ifndef USE_SIT_FUNCTION
- {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
- {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
- {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
+ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
+ { CWORD, CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
+ { CCTL, CCTL, CCTL, CCTL } /* 14, CTLESC ... */
#endif
};
#else
static const char S_I_T[][3] = {
#if ENABLE_ASH_ALIAS
- {CSPCL, CIGN, CIGN}, /* 0, PEOA */
-#endif
- {CSPCL, CWORD, CWORD}, /* 1, ' ' */
- {CNL, CNL, CNL}, /* 2, \n */
- {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
- {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
- {CVAR, CVAR, CWORD}, /* 5, $ */
- {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
- {CSPCL, CWORD, CWORD}, /* 7, ( */
- {CSPCL, CWORD, CWORD}, /* 8, ) */
- {CBACK, CBACK, CCTL}, /* 9, \ */
- {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
- {CENDVAR, CENDVAR, CWORD}, /* 11, } */
+ { CSPCL, CIGN, CIGN }, /* 0, PEOA */
+#endif
+ { CSPCL, CWORD, CWORD }, /* 1, ' ' */
+ { CNL, CNL, CNL }, /* 2, \n */
+ { CWORD, CCTL, CCTL }, /* 3, !*-/:=?[]~ */
+ { CDQUOTE, CENDQUOTE, CWORD }, /* 4, '"' */
+ { CVAR, CVAR, CWORD }, /* 5, $ */
+ { CSQUOTE, CWORD, CENDQUOTE }, /* 6, "'" */
+ { CSPCL, CWORD, CWORD }, /* 7, ( */
+ { CSPCL, CWORD, CWORD }, /* 8, ) */
+ { CBACK, CBACK, CCTL }, /* 9, \ */
+ { CBQUOTE, CBQUOTE, CWORD }, /* 10, ` */
+ { CENDVAR, CENDVAR, CWORD }, /* 11, } */
#ifndef USE_SIT_FUNCTION
- {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
- {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
- {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
+ { CENDFILE, CENDFILE, CENDFILE }, /* 12, PEOF */
+ { CWORD, CWORD, CWORD }, /* 13, 0-9A-Za-z */
+ { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
#endif
};
#endif /* ASH_MATH_SUPPORT */
#ifdef USE_SIT_FUNCTION
-#define U_C(c) ((unsigned char)(c))
-
static int
SIT(int c, int syntax)
{
- static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+ static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
#if ENABLE_ASH_ALIAS
- static const char syntax_index_table[] = {
+ static const char syntax_index_table[] ALIGN1 = {
1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
11, 3 /* "}~" */
};
#else
- static const char syntax_index_table[] = {
+ static const char syntax_index_table[] ALIGN1 = {
0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
indx = 0;
else
#endif
- if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
+#define U_C(c) ((unsigned char)(c))
+
+ if ((unsigned char)c >= (unsigned char)(CTLESC)
+ && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
+ ) {
return CCTL;
- else {
+ } else {
s = strchr(spec_symbls, c);
if (s == NULL || *s == '\0')
return CWORD;
return S_I_T[indx][syntax];
}
-#else /* USE_SIT_FUNCTION */
-
-#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
+#else /* !USE_SIT_FUNCTION */
#if ENABLE_ASH_ALIAS
-#define CSPCL_CIGN_CIGN_CIGN 0
-#define CSPCL_CWORD_CWORD_CWORD 1
-#define CNL_CNL_CNL_CNL 2
-#define CWORD_CCTL_CCTL_CWORD 3
-#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
-#define CVAR_CVAR_CWORD_CVAR 5
-#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
-#define CSPCL_CWORD_CWORD_CLP 7
-#define CSPCL_CWORD_CWORD_CRP 8
-#define CBACK_CBACK_CCTL_CBACK 9
-#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
-#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
-#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
-#define CWORD_CWORD_CWORD_CWORD 13
-#define CCTL_CCTL_CCTL_CCTL 14
+#define CSPCL_CIGN_CIGN_CIGN 0
+#define CSPCL_CWORD_CWORD_CWORD 1
+#define CNL_CNL_CNL_CNL 2
+#define CWORD_CCTL_CCTL_CWORD 3
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
+#define CVAR_CVAR_CWORD_CVAR 5
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
+#define CSPCL_CWORD_CWORD_CLP 7
+#define CSPCL_CWORD_CWORD_CRP 8
+#define CBACK_CBACK_CCTL_CBACK 9
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
+#define CWORD_CWORD_CWORD_CWORD 13
+#define CCTL_CCTL_CCTL_CCTL 14
#else
-#define CSPCL_CWORD_CWORD_CWORD 0
-#define CNL_CNL_CNL_CNL 1
-#define CWORD_CCTL_CCTL_CWORD 2
-#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
-#define CVAR_CVAR_CWORD_CVAR 4
-#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
-#define CSPCL_CWORD_CWORD_CLP 6
-#define CSPCL_CWORD_CWORD_CRP 7
-#define CBACK_CBACK_CCTL_CBACK 8
-#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
-#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
-#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
-#define CWORD_CWORD_CWORD_CWORD 12
-#define CCTL_CCTL_CCTL_CCTL 13
+#define CSPCL_CWORD_CWORD_CWORD 0
+#define CNL_CNL_CNL_CNL 1
+#define CWORD_CCTL_CCTL_CWORD 2
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
+#define CVAR_CVAR_CWORD_CVAR 4
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
+#define CSPCL_CWORD_CWORD_CLP 6
+#define CSPCL_CWORD_CWORD_CRP 7
+#define CBACK_CBACK_CCTL_CBACK 8
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
+#define CWORD_CWORD_CWORD_CWORD 12
+#define CCTL_CCTL_CCTL_CCTL 13
#endif
static const char syntax_index_table[258] = {
/* 257 127 */ CWORD_CWORD_CWORD_CWORD,
};
-#endif /* USE_SIT_FUNCTION */
-
-
-/* exec.h */
-
-#if ENABLE_ASH_MATH_SUPPORT_64
-typedef int64_t arith_t;
-#define arith_t_type long long
-#else
-typedef long arith_t;
-#define arith_t_type long
-#endif
-
-#if ENABLE_ASH_MATH_SUPPORT
-static arith_t dash_arith(const char *);
-static arith_t arith(const char *expr, int *perrcode);
-#endif
-
-#if ENABLE_ASH_RANDOM_SUPPORT
-static unsigned long rseed;
-# ifndef DYNAMIC_VAR
-# define DYNAMIC_VAR
-# endif
-#endif
-
-
-/* main.h */
-
-static void readcmdfile(char *);
-
-
-/* options.h */
+#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
-static char *minusc; /* argument to -c option */
-
-static void optschanged(void);
-static void setparam(char **);
-static void freeparam(volatile struct shparam *);
-static int shiftcmd(int, char **);
-static int setcmd(int, char **);
-static int nextopt(const char *);
-
-
-/* redir.h */
-
-/* flags passed to redirect */
-#define REDIR_PUSH 01 /* save previous values of file descriptors */
-#define REDIR_SAVEFD2 03 /* set preverrout */
-
-static void redirect(union node *, int);
-static void popredir(int);
-static void clearredir(int);
-static int copyfd(int, int);
-static int redirectsafe(union node *, int);
+#endif /* USE_SIT_FUNCTION */
/* ============ Alias handling */
#define ALIASINUSE 1
#define ALIASDEAD 2
-#define ATABSIZE 39
-
struct alias {
struct alias *next;
char *name;
int flag;
};
-static struct alias *atab[ATABSIZE];
+
+static struct alias **atab; // [ATABSIZE];
+#define INIT_G_alias() do { \
+ atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
+} while (0)
+
static struct alias **
__lookupalias(const char *name) {
ap->flag &= ~ALIASDEAD;
} else {
/* not found */
- ap = ckmalloc(sizeof(struct alias));
+ ap = ckzalloc(sizeof(struct alias));
ap->name = ckstrdup(name);
ap->val = ckstrdup(val);
- ap->flag = 0;
- ap->next = 0;
+ /*ap->flag = 0; - ckzalloc did it */
+ /*ap->next = NULL;*/
*app = ap;
}
INT_ON;
* TODO - sort output
*/
static int
-aliascmd(int argc, char **argv)
+aliascmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *n, *v;
int ret = 0;
struct alias *ap;
- if (argc == 1) {
+ if (!argv[1]) {
int i;
- for (i = 0; i < ATABSIZE; i++)
+ for (i = 0; i < ATABSIZE; i++) {
for (ap = atab[i]; ap; ap = ap->next) {
printalias(ap);
}
+ }
return 0;
}
while ((n = *++argv) != NULL) {
}
static int
-unaliascmd(int argc, char **argv)
+unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int i;
#define SHOW_PID 0x04 /* include process pid */
#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
-
/*
* A job structure contains information about a job. A job is either a
* single process or a set of processes contained in a pipeline. In the
};
static pid_t backgndpid; /* pid of last background process */
-static int job_warning; /* user was warned about stopped jobs */
-#if JOBS
-static int jobctl; /* true if doing job control */
-#endif
+static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
-static struct job *makejob(union node *, int);
+static struct job *makejob(/*union node *,*/ int);
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
-#if ! JOBS
-#define setjobctl(on) /* do nothing */
+#if !JOBS
+enum { jobctl = 0 };
+#define setjobctl(on) do {} while (0)
#else
+static smallint jobctl; /* true if doing job control */
static void setjobctl(int);
-static void showjobs(FILE *, int);
#endif
/*
struct sigaction act;
t = trap[signo];
+ action = S_IGN;
if (t == NULL)
action = S_DFL;
else if (*t != '\0')
action = S_CATCH;
- else
- action = S_IGN;
if (rootshell && action == S_DFL) {
switch (signo) {
case SIGINT:
/*
* current setting unknown
*/
- if (sigaction(signo, 0, &act) == -1) {
+ if (sigaction(signo, NULL, &act) == -1) {
/*
* Pretend it worked; maybe we should give a warning
* here, but other shells don't. We don't alter
*/
return;
}
+ tsig = S_RESET; /* force to be set */
if (act.sa_handler == SIG_IGN) {
+ tsig = S_HARD_IGN;
if (mflag
&& (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
) {
tsig = S_IGN; /* don't hard ignore these */
- } else
- tsig = S_HARD_IGN;
- } else {
- tsig = S_RESET; /* force to be set */
+ }
}
}
if (tsig == S_HARD_IGN || tsig == action)
return;
+ act.sa_handler = SIG_DFL;
switch (action) {
case S_CATCH:
act.sa_handler = onsig;
case S_IGN:
act.sa_handler = SIG_IGN;
break;
- default:
- act.sa_handler = SIG_DFL;
}
*t = action;
act.sa_flags = 0;
sigfillset(&act.sa_mask);
- sigaction(signo, &act, 0);
+ sigaction_set(signo, &act);
}
/* mode flags for set_curjob */
#define CUR_STOPPED 0
/* mode flags for dowait */
-#define DOWAIT_NORMAL 0
-#define DOWAIT_BLOCK 1
+#define DOWAIT_NONBLOCK WNOHANG
+#define DOWAIT_BLOCK 0
#if JOBS
/* pgrp of shell on invocation */
xtcsetpgrp(int fd, pid_t pgrp)
{
if (tcsetpgrp(fd, pgrp))
- ash_msg_and_raise_error("Cannot set tty process group (%m)");
+ ash_msg_and_raise_error("cannot set tty process group (%m)");
}
/*
* That sometimes helps to acquire controlling tty.
* Obviously, a workaround for bugs when someone
* failed to provide a controlling tty to bash! :) */
- fd += 3;
- while (!isatty(fd) && --fd >= 0)
- ;
+ fd = 2;
+ while (!isatty(fd))
+ if (--fd < 0)
+ goto out;
}
fd = fcntl(fd, F_DUPFD, 10);
- close(ofd);
+ if (ofd >= 0)
+ close(ofd);
if (fd < 0)
goto out;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ /* fd is a tty at this point */
+ close_on_exec_on(fd);
do { /* while we are in the background */
pgrp = tcgetpgrp(fd);
if (pgrp < 0) {
/* turning job control off */
fd = ttyfd;
pgrp = initialpgrp;
- xtcsetpgrp(fd, pgrp);
+ /* was xtcsetpgrp, but this can make exiting ash
+ * loop forever if pty is already deleted */
+ tcsetpgrp(fd, pgrp);
setpgid(0, pgrp);
setsignal(SIGTSTP);
setsignal(SIGTTOU);
setsignal(SIGTTIN);
close:
- close(fd);
+ if (fd >= 0)
+ close(fd);
fd = -1;
}
ttyfd = fd;
static int
killcmd(int argc, char **argv)
{
- int signo = -1;
- int list = 0;
- int i;
- pid_t pid;
- struct job *jp;
-
- if (argc <= 1) {
- usage:
- ash_msg_and_raise_error(
-"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
-"kill -l [exitstatus]"
- );
- }
-
- if (**++argv == '-') {
- signo = get_signum(*argv + 1);
- if (signo < 0) {
- int c;
-
- while ((c = nextopt("ls:")) != '\0') {
- switch (c) {
- default:
-#if DEBUG
- abort();
-#endif
- case 'l':
- list = 1;
- break;
- case 's':
- signo = get_signum(optionarg);
- if (signo < 0) {
- ash_msg_and_raise_error(
- "invalid signal number or name: %s",
- optionarg
- );
- }
- break;
- }
- }
- argv = argptr;
- } else
- argv++;
- }
-
- if (!list && signo < 0)
- signo = SIGTERM;
-
- if ((signo < 0 || !*argv) ^ list) {
- goto usage;
- }
-
- if (list) {
- const char *name;
-
- if (!*argv) {
- for (i = 1; i < NSIG; i++) {
- name = get_signame(i);
- if (isdigit(*name))
- out1fmt(snlfmt, name);
+ int i = 1;
+ if (argv[1] && strcmp(argv[1], "-l") != 0) {
+ do {
+ if (argv[i][0] == '%') {
+ struct job *jp = getjob(argv[i], 0);
+ unsigned pid = jp->ps[0].pid;
+ /* Enough space for ' -NNN<nul>' */
+ argv[i] = alloca(sizeof(int)*3 + 3);
+ /* kill_main has matching code to expect
+ * leading space. Needed to not confuse
+ * negative pids with "kill -SIGNAL_NO" syntax */
+ sprintf(argv[i], " -%u", pid);
}
- return 0;
- }
- name = get_signame(signo);
- if (!isdigit(*name))
- ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
- out1fmt(snlfmt, name);
- return 0;
+ } while (argv[++i]);
}
-
- i = 0;
- do {
- if (**argv == '%') {
- jp = getjob(*argv, 0);
- pid = -jp->ps[0].pid;
- } else {
- pid = **argv == '-' ?
- -number(*argv + 1) : number(*argv);
- }
- if (kill(pid, signo) != 0) {
- ash_msg("(%d) - %m", pid);
- i = 1;
- }
- } while (*++argv);
-
- return i;
+ return kill_main(argc, argv);
}
static void
if (WIFSTOPPED(ps->status)) {
ps->status = -1;
}
- } while (ps++, --i);
+ ps++;
+ } while (--i);
out:
status = (mode == FORK_FG) ? waitforjob(jp) : 0;
INT_ON;
}
static int
-fg_bgcmd(int argc, char **argv)
+fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct job *jp;
FILE *out;
* and the jobs command may give out of date information.
*/
static int
-waitproc(int block, int *status)
+waitproc(int wait_flags, int *status)
{
- int flags = 0;
-
#if JOBS
if (jobctl)
- flags |= WUNTRACED;
+ wait_flags |= WUNTRACED;
#endif
- if (block == 0)
- flags |= WNOHANG;
- return wait3(status, flags, (struct rusage *)NULL);
+ /* NB: _not_ safe_waitpid, we need to detect EINTR */
+ return waitpid(-1, status, wait_flags);
}
/*
* Wait for a process to terminate.
*/
static int
-dowait(int block, struct job *job)
+dowait(int wait_flags, struct job *job)
{
int pid;
int status;
struct job *thisjob;
int state;
- TRACE(("dowait(%d) called\n", block));
- pid = waitproc(block, &status);
- TRACE(("wait returns pid %d, status=%d\n", pid, status));
- if (pid <= 0)
+ TRACE(("dowait(%d) called\n", wait_flags));
+ pid = waitproc(wait_flags, &status);
+ TRACE(("wait returns pid=%d, status=%d\n", pid, status));
+ if (pid <= 0) {
+ /* If we were doing blocking wait and (probably) got EINTR,
+ * check for pending sigs received while waiting.
+ * (NB: can be moved into callers if needed) */
+ if (wait_flags == DOWAIT_BLOCK && pendingsig)
+ raise_exception(EXSIG);
return pid;
+ }
INT_OFF;
thisjob = NULL;
for (jp = curjob; jp; jp = jp->prev_job) {
#if JOBS
if (!WIFSTOPPED(status))
#endif
-
jobless--;
goto out;
len = sprint_status(s, status, 1);
if (len) {
s[len] = '\n';
- s[len + 1] = 0;
+ s[len + 1] = '\0';
out2str(s);
}
}
struct procstat *ps;
struct procstat *psend;
int col;
- int indent;
+ int indent_col;
char s[80];
ps = jp->ps;
}
col = fmtstr(s, 16, "[%d] ", jobno(jp));
- indent = col;
+ indent_col = col;
if (jp == curjob)
s[col - 2] = '+';
do {
/* for each process */
- col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+ col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
start:
fprintf(out, "%s%*c%s",
s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
}
}
+/*
+ * Print a list of jobs. If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+static void
+showjobs(FILE *out, int mode)
+{
+ struct job *jp;
+
+ TRACE(("showjobs(%x) called\n", mode));
+
+ /* If not even one job changed, there is nothing to do */
+ while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
+ continue;
+
+ for (jp = curjob; jp; jp = jp->prev_job) {
+ if (!(mode & SHOW_CHANGED) || jp->changed) {
+ showjob(out, jp, mode);
+ }
+ }
+}
+
static int
-jobscmd(int argc, char **argv)
+jobscmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int mode, m;
- FILE *out;
mode = 0;
while ((m = nextopt("lp"))) {
mode = SHOW_PGID;
}
- out = stdout;
argv = argptr;
if (*argv) {
do
- showjob(out, getjob(*argv,0), mode);
+ showjob(stdout, getjob(*argv,0), mode);
while (*++argv);
} else
- showjobs(out, mode);
+ showjobs(stdout, mode);
return 0;
}
-
-/*
- * Print a list of jobs. If "change" is nonzero, only print jobs whose
- * statuses have changed since the last call to showjobs.
- */
-static void
-showjobs(FILE *out, int mode)
-{
- struct job *jp;
-
- TRACE(("showjobs(%x) called\n", mode));
-
- /* If not even one one job changed, there is nothing to do */
- while (dowait(DOWAIT_NORMAL, NULL) > 0)
- continue;
-
- for (jp = curjob; jp; jp = jp->prev_job) {
- if (!(mode & SHOW_CHANGED) || jp->changed)
- showjob(out, jp, mode);
- }
-}
#endif /* JOBS */
static int
}
static int
-waitcmd(int argc, char **argv)
+waitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct job *job;
int retval;
struct job *jp;
- EXSIGON;
+// exsig++;
+// xbarrier();
+ if (pendingsig)
+ raise_exception(EXSIG);
nextopt(nullstr);
retval = 0;
for (;;) {
jp = curjob;
while (1) {
- if (!jp) {
- /* no running procs */
- goto out;
- }
+ if (!jp) /* no running procs */
+ goto ret;
if (jp->state == JOBRUNNING)
break;
jp->waited = 1;
jp = jp->prev_job;
}
- dowait(DOWAIT_BLOCK, 0);
+ dowait(DOWAIT_BLOCK, NULL);
}
}
if (**argv != '%') {
pid_t pid = number(*argv);
job = curjob;
- goto start;
- do {
+ while (1) {
+ if (!job)
+ goto repeat;
if (job->ps[job->nprocs - 1].pid == pid)
break;
job = job->prev_job;
- start:
- if (!job)
- goto repeat;
- } while (1);
+ }
} else
job = getjob(*argv, 0);
/* loop until process terminated or stopped */
while (job->state == JOBRUNNING)
- dowait(DOWAIT_BLOCK, 0);
+ dowait(DOWAIT_BLOCK, NULL);
job->waited = 1;
retval = getstatus(job);
repeat:
;
} while (*++argv);
- out:
+ ret:
return retval;
}
* Called with interrupts off.
*/
static struct job *
-makejob(union node *node, int nprocs)
+makejob(/*union node *node,*/ int nprocs)
{
int i;
struct job *jp;
}
memset(jp, 0, sizeof(*jp));
#if JOBS
+ /* jp->jobctl is a bitfield.
+ * "jp->jobctl |= jobctl" likely to give awful code */
if (jobctl)
jp->jobctl = 1;
#endif
if (nprocs > 1) {
jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
}
- TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+ TRACE(("makejob(%d) returns %%%d\n", nprocs,
jobno(jp)));
return jp;
}
{
for (; np; np = np->narg.next) {
if (!sep)
- cmdputs(spcstr);
+ cmdputs(" ");
cmdtxt(np);
if (sep && np->narg.next)
- cmdputs(spcstr);
+ cmdputs(" ");
}
}
char **tp;
for (tp = trap; tp < &trap[NSIG]; tp++) {
- if (*tp && **tp) { /* trap not NULL or SIG_IGN */
+ if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
INT_OFF;
free(*tp);
*tp = NULL;
}
}
}
-/* lives far away from here, needed for forkchild */
+
+/* Lives far away from here, needed for forkchild */
static void closescript(void);
+
+/* Called after fork(), in child */
static void
-forkchild(struct job *jp, union node *n, int mode)
+forkchild(struct job *jp, /*union node *n,*/ int mode)
{
int oldlvl;
if (jp->nprocs == 0) {
close(0);
if (open(bb_dev_null, O_RDONLY) != 0)
- ash_msg_and_raise_error("Can't open %s", bb_dev_null);
+ ash_msg_and_raise_error("can't open %s", bb_dev_null);
}
}
if (!oldlvl && iflag) {
jobless = 0;
}
+/* Called after fork(), in parent */
static void
forkparent(struct job *jp, union node *n, int mode, pid_t pid)
{
TRACE(("In parent shell: child = %d\n", pid));
if (!jp) {
- while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+ while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
+ continue;
jobless++;
return;
}
TRACE(("Fork failed, errno=%d", errno));
if (jp)
freejob(jp);
- ash_msg_and_raise_error("Cannot fork");
+ ash_msg_and_raise_error("cannot fork");
}
if (pid == 0)
- forkchild(jp, n, mode);
+ forkchild(jp, /*n,*/ mode);
else
forkparent(jp, n, mode, pid);
return pid;
* intuit from the subprocess exit status whether a SIGINT
* occurred, and if so interrupt ourselves. Yuck. - mycroft
*/
- if (jp->sigint)
- raise(SIGINT);
+ if (jp->sigint) /* TODO: do the same with all signals */
+ raise(SIGINT); /* ... by raise(jp->sig) instead? */
}
if (jp->state == JOBDONE)
#endif
}
-/* ============ Routines to expand arguments to commands
+/* ============ redir.c
*
- * We have to deal with backquotes, shell variables, and file metacharacters.
+ * Code for dealing with input/output redirection.
*/
-/*
- * expandarg flags
- */
-#define EXP_FULL 0x1 /* perform word splitting & file globbing */
-#define EXP_TILDE 0x2 /* do normal tilde expansion */
-#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
-#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
-#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
-#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
-#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
-#define EXP_WORD 0x80 /* expand word in parameter expansion */
-#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
-/*
- * _rmescape() flags
- */
-#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
-#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
-#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
-#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
-#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
+#define EMPTY -2 /* marks an unused slot in redirtab */
+#define CLOSED -3 /* marks a slot of previously-closed fd */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096 /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
/*
- * Structure specifying which parts of the string should be searched
- * for IFS characters.
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
*/
-struct ifsregion {
- struct ifsregion *next; /* next region in list */
- int begoff; /* offset of start of region */
- int endoff; /* offset of end of region */
- int nulonly; /* search for nul bytes only */
-};
+static int
+noclobberopen(const char *fname)
+{
+ int r, fd;
+ struct stat finfo, finfo2;
-struct arglist {
- struct strlist *list;
- struct strlist **lastp;
-};
+ /*
+ * If the file exists and is a regular file, return an error
+ * immediately.
+ */
+ r = stat(fname, &finfo);
+ if (r == 0 && S_ISREG(finfo.st_mode)) {
+ errno = EEXIST;
+ return -1;
+ }
-/* output of current string */
-static char *expdest;
-/* list of back quote expressions */
-static struct nodelist *argbackq;
-/* first struct in list of ifs regions */
-static struct ifsregion ifsfirst;
-/* last struct in list */
-static struct ifsregion *ifslastp;
-/* holds expanded arg list */
-static struct arglist exparg;
+ /*
+ * If the file was not present (r != 0), make sure we open it
+ * exclusively so that if it is created before we open it, our open
+ * will fail. Make sure that we do not truncate an existing file.
+ * Note that we don't turn on O_EXCL unless the stat failed -- if the
+ * file was not a regular file, we leave O_EXCL off.
+ */
+ if (r != 0)
+ return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+ fd = open(fname, O_WRONLY|O_CREAT, 0666);
-/*
- * Our own itoa().
- */
-static int
-cvtnum(arith_t num)
-{
- int len;
+ /* If the open failed, return the file descriptor right away. */
+ if (fd < 0)
+ return fd;
- expdest = makestrspace(32, expdest);
-#if ENABLE_ASH_MATH_SUPPORT_64
- len = fmtstr(expdest, 32, "%lld", (long long) num);
-#else
- len = fmtstr(expdest, 32, "%ld", num);
-#endif
- STADJUST(len, expdest);
- return len;
-}
+ /*
+ * OK, the open succeeded, but the file may have been changed from a
+ * non-regular file to a regular file between the stat and the open.
+ * We are assuming that the O_EXCL open handles the case where FILENAME
+ * did not exist and is symlinked to an existing file between the stat
+ * and open.
+ */
-static size_t
-esclen(const char *start, const char *p)
-{
- size_t esc = 0;
+ /*
+ * If we can open it and fstat the file descriptor, and neither check
+ * revealed that it was a regular file, and the file has not been
+ * replaced, return the file descriptor.
+ */
+ if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
+ && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+ return fd;
- while (p > start && *--p == CTLESC) {
- esc++;
- }
- return esc;
+ /* The file has been replaced. badness. */
+ close(fd);
+ errno = EEXIST;
+ return -1;
}
/*
- * Remove any CTLESC characters from a string.
+ * Handle here documents. Normally we fork off a process to write the
+ * data to a pipe. If the document is short, we can stuff the data in
+ * the pipe without forking.
*/
-static char *
-_rmescapes(char *str, int flag)
+/* openhere needs this forward reference */
+static void expandhere(union node *arg, int fd);
+static int
+openhere(union node *redir)
{
- char *p, *q, *r;
- static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
- unsigned inquotes;
- int notescaped;
- int globbing;
-
- p = strpbrk(str, qchars);
- if (!p) {
- return str;
- }
- q = p;
- r = str;
- if (flag & RMESCAPE_ALLOC) {
- size_t len = p - str;
- size_t fulllen = len + strlen(p) + 1;
+ int pip[2];
+ size_t len = 0;
- if (flag & RMESCAPE_GROW) {
- r = makestrspace(fulllen, expdest);
- } else if (flag & RMESCAPE_HEAP) {
- r = ckmalloc(fulllen);
- } else {
- r = stalloc(fulllen);
- }
- q = r;
- if (len > 0) {
- q = memcpy(q, str, len) + len;
+ if (pipe(pip) < 0)
+ ash_msg_and_raise_error("pipe call failed");
+ if (redir->type == NHERE) {
+ len = strlen(redir->nhere.doc->narg.text);
+ if (len <= PIPESIZE) {
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
+ goto out;
}
}
- inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
- globbing = flag & RMESCAPE_GLOB;
- notescaped = globbing;
- while (*p) {
- if (*p == CTLQUOTEMARK) {
- inquotes = ~inquotes;
- p++;
- notescaped = globbing;
- continue;
- }
- if (*p == '\\') {
- /* naked back slash */
- notescaped = 0;
- goto copy;
- }
- if (*p == CTLESC) {
- p++;
- if (notescaped && inquotes && *p != '/') {
- *q++ = '\\';
- }
- }
- notescaped = globbing;
- copy:
- *q++ = *p++;
- }
- *q = '\0';
- if (flag & RMESCAPE_GROW) {
- expdest = r;
- STADJUST(q - r + 1, expdest);
+ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+ close(pip[0]);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+ signal(SIGPIPE, SIG_DFL);
+ if (redir->type == NHERE)
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
+ else
+ expandhere(redir->nhere.doc, pip[1]);
+ _exit(0);
}
- return r;
+ out:
+ close(pip[1]);
+ return pip[0];
}
-#define rmescapes(p) _rmescapes((p), 0)
-
-#define pmatch(a, b) !fnmatch((a), (b), 0)
-/*
- * Prepare a pattern for a expmeta (internal glob(3)) call.
- *
- * Returns an stalloced string.
- */
-static char *
-preglob(const char *pattern, int quoted, int flag)
+static int
+openredirect(union node *redir)
{
- flag |= RMESCAPE_GLOB;
- if (quoted) {
- flag |= RMESCAPE_QUOTED;
+ char *fname;
+ int f;
+
+ switch (redir->nfile.type) {
+ case NFROM:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDONLY);
+ if (f < 0)
+ goto eopen;
+ break;
+ case NFROMTO:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NTO:
+ /* Take care of noclobber mode. */
+ if (Cflag) {
+ fname = redir->nfile.expfname;
+ f = noclobberopen(fname);
+ if (f < 0)
+ goto ecreate;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NCLOBBER:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ default:
+#if DEBUG
+ abort();
+#endif
+ /* Fall through to eliminate warning. */
+ case NTOFD:
+ case NFROMFD:
+ f = -1;
+ break;
+ case NHERE:
+ case NXHERE:
+ f = openhere(redir);
+ break;
}
- return _rmescapes((char *)pattern, flag);
+
+ return f;
+ ecreate:
+ ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
+ eopen:
+ ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
}
/*
- * Put a string on the stack.
+ * Copy a file descriptor to be >= to. Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
*/
-static void
-memtodest(const char *p, size_t len, int syntax, int quotes)
+static int
+copyfd(int from, int to)
{
- char *q = expdest;
-
- q = makestrspace(len * 2, q);
+ int newfd;
- while (len--) {
- int c = SC2INT(*p++);
- if (!c)
- continue;
- if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
- USTPUTC(CTLESC, q);
- USTPUTC(c, q);
+ newfd = fcntl(from, F_DUPFD, to);
+ if (newfd < 0) {
+ if (errno == EMFILE)
+ return EMPTY;
+ ash_msg_and_raise_error("%d: %m", from);
}
-
- expdest = q;
+ return newfd;
}
static void
-strtodest(const char *p, int syntax, int quotes)
+dupredirect(union node *redir, int f)
{
- memtodest(p, strlen(p), syntax, quotes);
+ int fd = redir->nfile.fd;
+
+ if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+ if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
+ copyfd(redir->ndup.dupfd, fd);
+ }
+ return;
+ }
+
+ if (f != fd) {
+ copyfd(f, fd);
+ close(f);
+ }
}
/*
- * Record the fact that we have to scan this region of the
- * string for IFS characters.
+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
*/
+/* flags passed to redirect */
+#define REDIR_PUSH 01 /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03 /* set preverrout */
static void
-recordregion(int start, int end, int nulonly)
+redirect(union node *redir, int flags)
{
- struct ifsregion *ifsp;
+ union node *n;
+ struct redirtab *sv;
+ int i;
+ int fd;
+ int newfd;
- if (ifslastp == NULL) {
- ifsp = &ifsfirst;
- } else {
- INT_OFF;
- ifsp = ckmalloc(sizeof(*ifsp));
- ifsp->next = NULL;
- ifslastp->next = ifsp;
- INT_ON;
+ g_nullredirs++;
+ if (!redir) {
+ return;
}
- ifslastp = ifsp;
- ifslastp->begoff = start;
- ifslastp->endoff = end;
- ifslastp->nulonly = nulonly;
+ sv = NULL;
+ INT_OFF;
+ if (flags & REDIR_PUSH) {
+ sv = ckmalloc(sizeof(*sv));
+ sv->next = redirlist;
+ redirlist = sv;
+ sv->nullredirs = g_nullredirs - 1;
+ for (i = 0; i < 10; i++)
+ sv->renamed[i] = EMPTY;
+ g_nullredirs = 0;
+ }
+ n = redir;
+ do {
+ fd = n->nfile.fd;
+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
+ && n->ndup.dupfd == fd)
+ continue; /* redirect from/to same file descriptor */
+
+ newfd = openredirect(n);
+ if (fd == newfd) {
+ /* Descriptor wasn't open before redirect.
+ * Mark it for close in the future */
+ if (sv && sv->renamed[fd] == EMPTY)
+ sv->renamed[fd] = CLOSED;
+ continue;
+ }
+ if (sv && sv->renamed[fd] == EMPTY) {
+ i = fcntl(fd, F_DUPFD, 10);
+
+ if (i == -1) {
+ i = errno;
+ if (i != EBADF) {
+ close(newfd);
+ errno = i;
+ ash_msg_and_raise_error("%d: %m", fd);
+ /* NOTREACHED */
+ }
+ } else {
+ sv->renamed[fd] = i;
+ close(fd);
+ }
+ } else {
+ close(fd);
+ }
+ dupredirect(n, newfd);
+ } while ((n = n->nfile.next));
+ INT_ON;
+ if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
+ preverrout_fd = sv->renamed[2];
}
+/*
+ * Undo the effects of the last redirection.
+ */
static void
-removerecordregions(int endoff)
+popredir(int drop)
{
- if (ifslastp == NULL)
- return;
+ struct redirtab *rp;
+ int i;
- if (ifsfirst.endoff > endoff) {
- while (ifsfirst.next != NULL) {
- struct ifsregion *ifsp;
- INT_OFF;
- ifsp = ifsfirst.next->next;
- free(ifsfirst.next);
- ifsfirst.next = ifsp;
- INT_ON;
+ if (--g_nullredirs >= 0)
+ return;
+ INT_OFF;
+ rp = redirlist;
+ for (i = 0; i < 10; i++) {
+ if (rp->renamed[i] == CLOSED) {
+ if (!drop)
+ close(i);
+ continue;
}
- if (ifsfirst.begoff > endoff)
- ifslastp = NULL;
- else {
- ifslastp = &ifsfirst;
- ifsfirst.endoff = endoff;
+ if (rp->renamed[i] != EMPTY) {
+ if (!drop) {
+ close(i);
+ copyfd(rp->renamed[i], i);
+ }
+ close(rp->renamed[i]);
}
- return;
}
-
+ redirlist = rp->next;
+ g_nullredirs = rp->nullredirs;
+ free(rp);
+ INT_ON;
+}
+
+/*
+ * Undo all redirections. Called on error or interrupt.
+ */
+
+/*
+ * Discard all saved file descriptors.
+ */
+static void
+clearredir(int drop)
+{
+ for (;;) {
+ g_nullredirs = 0;
+ if (!redirlist)
+ break;
+ popredir(drop);
+ }
+}
+
+static int
+redirectsafe(union node *redir, int flags)
+{
+ int err;
+ volatile int saveint;
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
+
+ SAVE_INT(saveint);
+ err = setjmp(jmploc.loc) * 2;
+ if (!err) {
+ exception_handler = &jmploc;
+ redirect(redir, flags);
+ }
+ exception_handler = savehandler;
+ if (err && exception != EXERROR)
+ longjmp(exception_handler->loc, 1);
+ RESTORE_INT(saveint);
+ return err;
+}
+
+
+/* ============ Routines to expand arguments to commands
+ *
+ * We have to deal with backquotes, shell variables, and file metacharacters.
+ */
+
+/*
+ * expandarg flags
+ */
+#define EXP_FULL 0x1 /* perform word splitting & file globbing */
+#define EXP_TILDE 0x2 /* do normal tilde expansion */
+#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
+#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
+#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
+#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
+#define EXP_WORD 0x80 /* expand word in parameter expansion */
+#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
+/*
+ * _rmescape() flags
+ */
+#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
+#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
+#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
+#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
+#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+struct ifsregion {
+ struct ifsregion *next; /* next region in list */
+ int begoff; /* offset of start of region */
+ int endoff; /* offset of end of region */
+ int nulonly; /* search for nul bytes only */
+};
+
+struct arglist {
+ struct strlist *list;
+ struct strlist **lastp;
+};
+
+/* output of current string */
+static char *expdest;
+/* list of back quote expressions */
+static struct nodelist *argbackq;
+/* first struct in list of ifs regions */
+static struct ifsregion ifsfirst;
+/* last struct in list */
+static struct ifsregion *ifslastp;
+/* holds expanded arg list */
+static struct arglist exparg;
+
+/*
+ * Our own itoa().
+ */
+static int
+cvtnum(arith_t num)
+{
+ int len;
+
+ expdest = makestrspace(32, expdest);
+#if ENABLE_ASH_MATH_SUPPORT_64
+ len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
+ len = fmtstr(expdest, 32, "%ld", num);
+#endif
+ STADJUST(len, expdest);
+ return len;
+}
+
+static size_t
+esclen(const char *start, const char *p)
+{
+ size_t esc = 0;
+
+ while (p > start && *--p == CTLESC) {
+ esc++;
+ }
+ return esc;
+}
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+static char *
+_rmescapes(char *str, int flag)
+{
+ static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
+
+ char *p, *q, *r;
+ unsigned inquotes;
+ int notescaped;
+ int globbing;
+
+ p = strpbrk(str, qchars);
+ if (!p) {
+ return str;
+ }
+ q = p;
+ r = str;
+ if (flag & RMESCAPE_ALLOC) {
+ size_t len = p - str;
+ size_t fulllen = len + strlen(p) + 1;
+
+ if (flag & RMESCAPE_GROW) {
+ r = makestrspace(fulllen, expdest);
+ } else if (flag & RMESCAPE_HEAP) {
+ r = ckmalloc(fulllen);
+ } else {
+ r = stalloc(fulllen);
+ }
+ q = r;
+ if (len > 0) {
+ q = memcpy(q, str, len) + len;
+ }
+ }
+ inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
+ globbing = flag & RMESCAPE_GLOB;
+ notescaped = globbing;
+ while (*p) {
+ if (*p == CTLQUOTEMARK) {
+ inquotes = ~inquotes;
+ p++;
+ notescaped = globbing;
+ continue;
+ }
+ if (*p == '\\') {
+ /* naked back slash */
+ notescaped = 0;
+ goto copy;
+ }
+ if (*p == CTLESC) {
+ p++;
+ if (notescaped && inquotes && *p != '/') {
+ *q++ = '\\';
+ }
+ }
+ notescaped = globbing;
+ copy:
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (flag & RMESCAPE_GROW) {
+ expdest = r;
+ STADJUST(q - r + 1, expdest);
+ }
+ return r;
+}
+#define rmescapes(p) _rmescapes((p), 0)
+
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+
+/*
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
+ *
+ * Returns an stalloced string.
+ */
+static char *
+preglob(const char *pattern, int quoted, int flag)
+{
+ flag |= RMESCAPE_GLOB;
+ if (quoted) {
+ flag |= RMESCAPE_QUOTED;
+ }
+ return _rmescapes((char *)pattern, flag);
+}
+
+/*
+ * Put a string on the stack.
+ */
+static void
+memtodest(const char *p, size_t len, int syntax, int quotes)
+{
+ char *q = expdest;
+
+ q = makestrspace(len * 2, q);
+
+ while (len--) {
+ int c = signed_char2int(*p++);
+ if (!c)
+ continue;
+ if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
+ USTPUTC(CTLESC, q);
+ USTPUTC(c, q);
+ }
+
+ expdest = q;
+}
+
+static void
+strtodest(const char *p, int syntax, int quotes)
+{
+ memtodest(p, strlen(p), syntax, quotes);
+}
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+static void
+recordregion(int start, int end, int nulonly)
+{
+ struct ifsregion *ifsp;
+
+ if (ifslastp == NULL) {
+ ifsp = &ifsfirst;
+ } else {
+ INT_OFF;
+ ifsp = ckzalloc(sizeof(*ifsp));
+ /*ifsp->next = NULL; - ckzalloc did it */
+ ifslastp->next = ifsp;
+ INT_ON;
+ }
+ ifslastp = ifsp;
+ ifslastp->begoff = start;
+ ifslastp->endoff = end;
+ ifslastp->nulonly = nulonly;
+}
+
+static void
+removerecordregions(int endoff)
+{
+ if (ifslastp == NULL)
+ return;
+
+ if (ifsfirst.endoff > endoff) {
+ while (ifsfirst.next != NULL) {
+ struct ifsregion *ifsp;
+ INT_OFF;
+ ifsp = ifsfirst.next->next;
+ free(ifsfirst.next);
+ ifsfirst.next = ifsp;
+ INT_ON;
+ }
+ if (ifsfirst.begoff > endoff)
+ ifslastp = NULL;
+ else {
+ ifslastp = &ifsfirst;
+ ifsfirst.endoff = endoff;
+ }
+ return;
+ }
+
ifslastp = &ifsfirst;
while (ifslastp->next && ifslastp->next->begoff < endoff)
ifslastp=ifslastp->next;
struct job *jp;
if (pipe(pip) < 0)
- ash_msg_and_raise_error("Pipe call failed");
- jp = makejob(n, 1);
+ ash_msg_and_raise_error("pipe call failed");
+ jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, FORK_NOJOB) == 0) {
FORCE_INT_ON;
close(pip[0]);
read:
if (in.fd < 0)
break;
- i = safe_read(in.fd, buf, sizeof(buf));
+ i = nonblock_safe_read(in.fd, buf, sizeof(buf));
TRACE(("expbackq: read returns %d\n", i));
if (i <= 0)
break;
p = buf;
}
- if (in.buf)
- free(in.buf);
+ free(in.buf);
if (in.fd >= 0) {
close(in.fd);
back_exitstatus = waitforjob(in.jp);
#endif
/* argstr needs it */
-static char *evalvar(char *p, int flag);
+static char *evalvar(char *p, int flag, struct strlist *var_str_list);
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
* $@ like $* since no splitting will be performed.
+ *
+ * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
+ * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
+ * for correct expansion of "B=$A" word.
*/
static void
-argstr(char *p, int flag)
+argstr(char *p, int flag, struct strlist *var_str_list)
{
- static const char spclchars[] = {
+ static const char spclchars[] ALIGN1 = {
'=',
':',
CTLQUOTEMARK,
p[5] == CTLQUOTEMARK
))
) {
- p = evalvar(p + 1, flag) + 1;
+ p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1;
goto start;
}
inquotes = !inquotes;
length++;
goto addquote;
case CTLVAR:
- p = evalvar(p, flag);
+ p = evalvar(p, flag, var_str_list);
goto start;
case CTLBACKQ:
c = 0;
}
static char *
-scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+scanleft(char *startp, char *rmesc, char *rmescend ATTRIBUTE_UNUSED, char *str, int quotes,
int zero)
{
char *loc;
}
static const char *
-subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
+subevalvar(char *p, char *str, int strloc, int subtype,
+ int startloc, int varflags, int quotes, struct strlist *var_str_list)
{
char *startp;
char *loc;
char *(*scan)(char *, char *, char *, char *, int , int);
herefd = -1;
- argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
+ argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
+ var_str_list);
STPUTC('\0', expdest);
herefd = saveherefd;
argbackq = saveargbackq;
* Add the value of a specialized variable to the stack string.
*/
static ssize_t
-varvalue(char *name, int varflags, int flags)
+varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
{
int num;
char *p;
goto param;
/* fall through */
case '*':
- sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
+ sep = ifsset() ? signed_char2int(ifsval()[0]) : ' ';
if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
sepq = 1;
param:
p = num ? shellparam.p[num - 1] : arg0;
goto value;
default:
+ /* NB: name has form "VAR=..." */
+
+ /* "A=a B=$A" case: var_str_list is a list of "A=a" strings
+ * which should be considered before we check variables. */
+ if (var_str_list) {
+ unsigned name_len = (strchrnul(name, '=') - name) + 1;
+ p = NULL;
+ do {
+ char *str, *eq;
+ str = var_str_list->text;
+ eq = strchr(str, '=');
+ if (!eq) /* stop at first non-assignment */
+ break;
+ eq++;
+ if (name_len == (eq - str)
+ && strncmp(str, name, name_len) == 0) {
+ p = eq;
+ /* goto value; - WRONG! */
+ /* think "A=1 A=2 B=$A" */
+ }
+ var_str_list = var_str_list->next;
+ } while (var_str_list);
+ if (p)
+ goto value;
+ }
p = lookupvar(name);
value:
if (!p)
* input string.
*/
static char *
-evalvar(char *p, int flag)
+evalvar(char *p, int flag, struct strlist *var_str_list)
{
- int subtype;
- int varflags;
+ char varflags;
+ char subtype;
+ char quoted;
+ char easy;
char *var;
int patloc;
- int c;
int startloc;
ssize_t varlen;
- int easy;
- int quotes;
- int quoted;
- quotes = flag & (EXP_FULL | EXP_CASE);
varflags = *p++;
subtype = varflags & VSTYPE;
quoted = varflags & VSQUOTE;
p = strchr(p, '=') + 1;
again:
- varlen = varvalue(var, varflags, flag);
+ varlen = varvalue(var, varflags, flag, var_str_list);
if (varflags & VSNUL)
varlen--;
if (varlen < 0) {
argstr(
p, flag | EXP_TILDE |
- (quoted ? EXP_QWORD : EXP_WORD)
+ (quoted ? EXP_QWORD : EXP_WORD),
+ var_str_list
);
goto end;
}
if (subtype == VSASSIGN || subtype == VSQUESTION) {
if (varlen < 0) {
- if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
+ if (subevalvar(p, var, /* strloc: */ 0,
+ subtype, startloc, varflags,
+ /* quotes: */ 0,
+ var_str_list)
+ ) {
varflags &= ~VSNUL;
/*
* Remove any recorded regions beyond
}
if (subtype == VSNORMAL) {
- if (!easy)
- goto end;
- record:
- recordregion(startloc, expdest - (char *)stackblock(), quoted);
+ if (easy)
+ goto record;
goto end;
}
*/
STPUTC('\0', expdest);
patloc = expdest - (char *)stackblock();
- if (subevalvar(p, NULL, patloc, subtype,
- startloc, varflags, quotes) == 0) {
+ if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
+ startloc, varflags,
+ /* quotes: */ flag & (EXP_FULL | EXP_CASE),
+ var_str_list)
+ ) {
int amount = expdest - (
(char *)stackblock() + patloc - 1
);
}
/* Remove any recorded regions beyond start of variable */
removerecordregions(startloc);
- goto record;
+ record:
+ recordregion(startloc, expdest - (char *)stackblock(), quoted);
}
end:
if (subtype != VSNORMAL) { /* skip to end of alternative */
int nesting = 1;
for (;;) {
- c = *p++;
+ char c = *p++;
if (c == CTLESC)
p++;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
continue;
}
*q = '\0';
- sp = stalloc(sizeof(*sp));
+ sp = stzalloc(sizeof(*sp));
sp->text = start;
*arglist->lastp = sp;
arglist->lastp = &sp->next;
if (strchr(ifs, *p) == NULL ) {
p = q;
break;
- } else if (strchr(defifs, *p) == NULL) {
+ }
+ if (strchr(defifs, *p) == NULL) {
if (ifsspc) {
p++;
ifsspc = 0;
return;
add:
- sp = stalloc(sizeof(*sp));
+ sp = stzalloc(sizeof(*sp));
sp->text = start;
*arglist->lastp = sp;
arglist->lastp = &sp->next;
{
struct strlist *sp;
- sp = stalloc(sizeof(*sp));
+ sp = stzalloc(sizeof(*sp));
sp->text = ststrdup(name);
*exparg.lastp = sp;
exparg.lastp = &sp->next;
p++;
if (*p == '.')
matchdot++;
- while (! intpending && (dp = readdir(dirp)) != NULL) {
+ while (!intpending && (dp = readdir(dirp)) != NULL) {
if (dp->d_name[0] == '.' && ! matchdot)
continue;
if (pmatch(start, dp->d_name)) {
}
static void
-expandmeta(struct strlist *str, int flag)
+expandmeta(struct strlist *str /*, int flag*/)
{
- static const char metachars[] = {
+ static const char metachars[] ALIGN1 = {
'*', '?', '[', 0
};
/* TODO - EXP_REDIR */
STARTSTACKSTR(expdest);
ifsfirst.next = NULL;
ifslastp = NULL;
- argstr(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) {
ifsbreakup(p, &exparg);
*exparg.lastp = NULL;
exparg.lastp = &exparg.list;
- expandmeta(exparg.list, flag);
+ expandmeta(exparg.list /*, flag*/);
} else {
if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
rmescapes(p);
- sp = stalloc(sizeof(*sp));
+ sp = stzalloc(sizeof(*sp));
sp->text = p;
*exparg.lastp = sp;
exparg.lastp = &sp->next;
argbackq = pattern->narg.backquote;
STARTSTACKSTR(expdest);
ifslastp = NULL;
- argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+ argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
+ /* var_str_list: */ NULL);
STACKSTRNUL(expdest);
result = patmatch(stackblock(), val);
popstackmark(&smark);
/* ============ find_command */
-static int is_safe_applet(char *name)
-{
- /* It isn't a bug to have non-existent applet here... */
- /* ...just a waste of space... */
- static const char safe_applets[][8] = {
- "["
- USE_AWK (, "awk" )
- USE_CAT (, "cat" )
- USE_CHMOD (, "chmod" )
- USE_CHOWN (, "chown" )
- USE_CP (, "cp" )
- USE_CUT (, "cut" )
- USE_DD (, "dd" )
- USE_ECHO (, "echo" )
- USE_FIND (, "find" )
- USE_HEXDUMP(, "hexdump")
- USE_LN (, "ln" )
- USE_LS (, "ls" )
- USE_MKDIR (, "mkdir" )
- USE_RM (, "rm" )
- USE_SORT (, "sort" )
- USE_TEST (, "test" )
- USE_TOUCH (, "touch" )
- USE_XARGS (, "xargs" )
- };
- int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
- int i;
- for (i = 0; i < n; i++)
- if (strcmp(safe_applets[i], name) == 0)
- return 1;
-
- return 0;
-}
-
struct builtincmd {
const char *name;
int (*builtin)(int, char **);
/* unsigned flags; */
};
#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
+/* "regular" builtins always take precedence over commands,
+ * regardless of PATH=....%builtin... position */
#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
-#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
+#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
struct cmdentry {
int cmdtype;
* would make the command name "hash" a misnomer.
*/
-#define CMDTABLESIZE 31 /* should be prime */
#define ARB 1 /* actual size determined at run time */
struct tblentry {
char cmdname[ARB]; /* name of command */
};
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1; /* index in path of %builtin, or -1 */
+static struct tblentry **cmdtable;
+#define INIT_G_cmdtable() do { \
+ cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
+} while (0)
+
+static int builtinloc = -1; /* index in path of %builtin, or -1 */
+
static void
tryexec(char *cmd, char **argv, char **envp)
{
int repeated = 0;
- struct BB_applet *a;
- int argc = 0;
- char **c;
- if (strchr(cmd, '/') == NULL
- && (a = find_applet_by_name(cmd)) != NULL
- && is_safe_applet(cmd)
- ) {
- c = argv;
- while (*c != NULL) {
- c++; argc++;
+#if ENABLE_FEATURE_SH_STANDALONE
+ if (strchr(cmd, '/') == NULL) {
+ int a = find_applet_by_name(cmd);
+ if (a >= 0) {
+ if (APPLET_IS_NOEXEC(a))
+ run_applet_no_and_exit(a, argv);
+ /* re-exec ourselves with the new arguments */
+ execve(bb_busybox_exec_path, argv, envp);
+ /* If they called chroot or otherwise made the binary no longer
+ * executable, fall through */
}
- applet_name = cmd;
- exit(a->main(argc, argv));
- }
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(cmd) != NULL) {
- /* re-exec ourselves with the new arguments */
- execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
- /* If they called chroot or otherwise made the binary no longer
- * executable, fall through */
}
#endif
;
ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
ap[1] = cmd;
- *ap = cmd = (char *)DEFAULT_SHELL;
+ ap[0] = cmd = (char *)DEFAULT_SHELL;
ap += 2;
argv++;
while ((*ap++ = *argv++))
- ;
+ continue;
argv = new;
goto repeat;
}
clearredir(1);
envp = environment();
- if (strchr(argv[0], '/') || is_safe_applet(argv[0])
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- || find_applet_by_name(argv[0])
+ if (strchr(argv[0], '/')
+#if ENABLE_FEATURE_SH_STANDALONE
+ || find_applet_by_name(argv[0]) >= 0
#endif
) {
tryexec(argv[0], argv, envp);
pp = &cmdp->next;
}
if (add && cmdp == NULL) {
- cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
+ cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB
+ strlen(name) + 1);
- cmdp->next = NULL;
+ /*cmdp->next = NULL; - ckzalloc did it */
cmdp->cmdtype = CMDUNKNOWN;
strcpy(cmdp->cmdname, name);
}
}
static int
-hashcmd(int argc, char **argv)
+hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
struct tblentry **pp;
struct tblentry *cmdp;
struct cmdentry entry;
char *name;
- while ((c = nextopt("r")) != '\0') {
+ if (nextopt("r") != '\0') {
clearcmdentry(0);
return 0;
}
+
if (*argptr == NULL) {
for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
}
return 0;
}
+
c = 0;
while ((name = *argptr) != NULL) {
cmdp = cmdlookup(name, 0);
if (cmdp != NULL
&& (cmdp->cmdtype == CMDNORMAL
- || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+ || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+ ) {
delete_cmd_entry();
+ }
find_command(name, &entry, DO_ERR, pathval());
if (entry.cmdtype == CMDUNKNOWN)
c = 1;
for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
- if (cmdp->cmdtype == CMDNORMAL || (
- cmdp->cmdtype == CMDBUILTIN &&
- !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
- builtinloc > 0
- ))
+ if (cmdp->cmdtype == CMDNORMAL
+ || (cmdp->cmdtype == CMDBUILTIN
+ && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+ && builtinloc > 0)
+ ) {
cmdp->rehash = 1;
+ }
}
}
}
* Called with interrupts off.
*/
static void
-changepath(const char *newval)
+changepath(const char *new)
{
- const char *old, *new;
- int idx;
+ const char *old;
int firstchange;
+ int idx;
int idx_bltin;
old = pathval();
- new = newval;
firstchange = 9999; /* assume no change */
idx = 0;
idx_bltin = -1;
break;
if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
idx_bltin = idx;
- if (*new == ':') {
+ if (*new == ':')
idx++;
- }
new++, old++;
}
if (builtinloc < 0 && idx_bltin >= 0)
{
static char buf[16];
+//try this:
+//if (tok < TSEMI) return tokname_array[tok] + 1;
+//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
+//return buf;
+
if (tok >= TSEMI)
buf[0] = '"';
sprintf(buf + (tok >= TSEMI), "%s%c",
static int
pstrcmp(const void *a, const void *b)
{
- return strcmp((const char *) a, (*(const char *const *) b) + 1);
+ return strcmp((char*) a, (*(char**) b) + 1);
}
static const char *const *
findkwd(const char *s)
{
return bsearch(s, tokname_array + KWDOFFSET,
- (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
- sizeof(const char *), pstrcmp);
+ ARRAY_SIZE(tokname_array) - KWDOFFSET,
+ sizeof(tokname_array[0]), pstrcmp);
}
/*
* Locate and print what a word is...
*/
-#if ENABLE_ASH_CMDCMD
static int
describe_command(char *command, int describe_command_verbose)
-#else
-#define describe_command_verbose 1
-static int
-describe_command(char *command)
-#endif
{
struct cmdentry entry;
struct tblentry *cmdp;
/* Then look at the aliases */
ap = lookupalias(command, 0);
if (ap != NULL) {
- if (describe_command_verbose) {
- out1fmt(" is an alias for %s", ap->val);
- } else {
+ if (!describe_command_verbose) {
out1str("alias ");
printalias(ap);
return 0;
}
+ out1fmt(" is an alias for %s", ap->val);
goto out;
}
#endif
}
static int
-typecmd(int argc, char **argv)
+typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- int i;
+ int i = 1;
int err = 0;
+ int verbose = 1;
- for (i = 1; i < argc; i++) {
-#if ENABLE_ASH_CMDCMD
- err |= describe_command(argv[i], 1);
-#else
- err |= describe_command(argv[i]);
-#endif
+ /* type -p ... ? (we don't bother checking for 'p') */
+ if (argv[1] && argv[1][0] == '-') {
+ i++;
+ verbose = 0;
+ }
+ while (argv[i]) {
+ err |= describe_command(argv[i++], verbose);
}
return err;
}
#if ENABLE_ASH_CMDCMD
static int
-commandcmd(int argc, char **argv)
+commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int c;
enum {
char *q;
int i;
int savestatus;
- int skip = 0;
+ int skip;
savestatus = exitstatus;
- pendingsigs = 0;
+ pendingsig = 0;
xbarrier();
for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
skip = evalstring(p, SKIPEVAL);
exitstatus = savestatus;
if (skip)
- break;
+ return skip;
}
- return skip;
+ return 0;
}
/* forward declarations - evaluation is fairly recursive business... */
out:
if ((checkexit & exitstatus))
evalskip |= SKIPEVAL;
- else if (pendingsigs && dotrap())
+ else if (pendingsig && dotrap())
goto exexit;
if (flags & EV_EXIT) {
struct stackmark smark;
setstackmark(&smark);
+ arglist.list = NULL;
arglist.lastp = &arglist.list;
for (argp = n->nfor.args; argp; argp = argp->narg.next) {
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
struct stackmark smark;
setstackmark(&smark);
+ arglist.list = NULL;
arglist.lastp = &arglist.list;
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
exitstatus = 0;
if (!backgnd && flags & EV_EXIT && !trap[0])
goto nofork;
INT_OFF;
- jp = makejob(n, 1);
+ jp = makejob(/*n,*/ 1);
if (forkshell(jp, n, backgnd) == 0) {
INT_ON;
flags |= EV_EXIT;
for (redir = n; redir; redir = redir->nfile.next) {
struct arglist fn;
- memset(&fn, 0, sizeof(fn));
+ fn.list = NULL;
fn.lastp = &fn.list;
switch (redir->type) {
case NFROMTO:
pipelen++;
flags |= EV_EXIT;
INT_OFF;
- jp = makejob(n, pipelen);
+ jp = makejob(/*n,*/ pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
prehash(lp->n);
if (lp->next) {
if (pipe(pip) < 0) {
close(prevfd);
- ash_msg_and_raise_error("Pipe call failed");
+ ash_msg_and_raise_error("pipe call failed");
}
}
if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
INT_ON;
}
+/*
+ * Controls whether the shell is interactive or not.
+ */
+static void
+setinteractive(int on)
+{
+ static int is_interactive;
+
+ if (++on == is_interactive)
+ return;
+ is_interactive = on;
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+ if (is_interactive > 1) {
+ /* Looks like they want an interactive shell */
+ static smallint did_banner;
+
+ if (!did_banner) {
+ out1fmt(
+ "\n\n"
+ "%s built-in shell (ash)\n"
+ "Enter 'help' for a list of built-in commands."
+ "\n\n",
+ bb_banner);
+ did_banner = 1;
+ }
+ }
+#endif
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+#define setvimode(on) do { \
+ if (on) line_input_state->flags |= VI_MODE; \
+ else line_input_state->flags &= ~VI_MODE; \
+} while (0)
+#else
+#define setvimode(on) viflag = 0 /* forcibly keep the option off */
+#endif
+
+static void
+optschanged(void)
+{
+#if DEBUG
+ opentrace();
+#endif
+ setinteractive(iflag);
+ setjobctl(mflag);
+ setvimode(viflag);
+}
+
static struct localvar *localvars;
/*
savehandler = exception_handler;
exception_handler = &jmploc;
localvars = NULL;
- shellparam.malloc = 0;
+ shellparam.malloced = 0;
func->count++;
funcnest++;
INT_ON;
shellparam.optoff = -1;
#endif
evaltree(&func->n, flags & EV_TESTED);
-funcdone:
+ funcdone:
INT_OFF;
funcnest--;
freefunc(func);
do {
switch (c) {
case 'p':
- *path = defpath;
+ *path = bb_default_path;
break;
default:
/* run 'typecmd' for other options */
struct var *vp;
INT_OFF;
- lvp = ckmalloc(sizeof(struct localvar));
+ lvp = ckzalloc(sizeof(struct localvar));
if (LONE_DASH(name)) {
char *p;
p = ckmalloc(sizeof(optlist));
* The "local" command.
*/
static int
-localcmd(int argc, char **argv)
+localcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *name;
return 0;
}
+static int
+falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+static int
+truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int
+execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
+{
+ if (argv[1]) {
+ iflag = 0; /* exit on error */
+ mflag = 0;
+ optschanged();
+ shellexec(argv + 1, pathval(), 0);
+ }
+ return 0;
+}
+
+/*
+ * The return command.
+ */
+static int
+returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
+{
+ /*
+ * If called outside a function, do what ksh does;
+ * skip the rest of the file.
+ */
+ evalskip = funcnest ? SKIPFUNC : SKIPFILE;
+ return argv[1] ? number(argv[1]) : exitstatus;
+}
+
/* Forward declarations for builtintab[] */
-#if JOBS
-static int fg_bgcmd(int, char **);
-#endif
static int breakcmd(int, char **);
-#if ENABLE_ASH_CMDCMD
-static int commandcmd(int, char **);
-#endif
static int dotcmd(int, char **);
static int evalcmd(int, char **);
#if ENABLE_ASH_BUILTIN_ECHO
#if ENABLE_ASH_BUILTIN_TEST
static int testcmd(int, char **);
#endif
-static int execcmd(int, char **);
static int exitcmd(int, char **);
static int exportcmd(int, char **);
-static int falsecmd(int, char **);
#if ENABLE_ASH_GETOPTS
static int getoptscmd(int, char **);
#endif
-static int hashcmd(int, char **);
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
static int helpcmd(int argc, char **argv);
#endif
-#if JOBS
-static int jobscmd(int, char **);
-#endif
#if ENABLE_ASH_MATH_SUPPORT
static int letcmd(int, char **);
#endif
-static int pwdcmd(int, char **);
static int readcmd(int, char **);
-static int returncmd(int, char **);
static int setcmd(int, char **);
static int shiftcmd(int, char **);
static int timescmd(int, char **);
static int trapcmd(int, char **);
-static int truecmd(int, char **);
-static int typecmd(int, char **);
static int umaskcmd(int, char **);
static int unsetcmd(int, char **);
-static int waitcmd(int, char **);
static int ulimitcmd(int, char **);
-#if JOBS
-static int killcmd(int, char **);
-#endif
#define BUILTIN_NOSPEC "0"
#define BUILTIN_SPECIAL "1"
{ BUILTIN_NOSPEC "type", typecmd },
{ BUILTIN_NOSPEC "ulimit", ulimitcmd },
{ BUILTIN_REGULAR "umask", umaskcmd },
-#if ENABLE_ASH_ALIAS
- { BUILTIN_REGULAR "unalias", unaliascmd },
-#endif
- { BUILTIN_SPEC_REG "unset", unsetcmd },
- { BUILTIN_REGULAR "wait", waitcmd },
-};
-
-#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
-
-#define COMMANDCMD (builtintab + 5 + \
- 2 * ENABLE_ASH_BUILTIN_TEST + \
- ENABLE_ASH_ALIAS + \
- ENABLE_ASH_JOB_CONTROL)
-#define EXECCMD (builtintab + 7 + \
- 2 * ENABLE_ASH_BUILTIN_TEST + \
- ENABLE_ASH_ALIAS + \
- ENABLE_ASH_JOB_CONTROL + \
- ENABLE_ASH_CMDCMD + \
- ENABLE_ASH_BUILTIN_ECHO)
-
-/*
- * Search the table of builtin commands.
- */
-static struct builtincmd *
-find_builtin(const char *name)
-{
- struct builtincmd *bp;
-
- bp = bsearch(
- name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
- pstrcmp
- );
- return bp;
-}
-
-/*
- * Resolve a command name. If you change this routine, you may have to
- * change the shellexec routine as well.
- */
-static void
-find_command(char *name, struct cmdentry *entry, int act, const char *path)
-{
- struct tblentry *cmdp;
- int idx;
- int prev;
- char *fullname;
- struct stat statb;
- int e;
- int updatetbl;
- struct builtincmd *bcmd;
-
- /* If name contains a slash, don't use PATH or hash table */
- if (strchr(name, '/') != NULL) {
- entry->u.index = -1;
- if (act & DO_ABS) {
- while (stat(name, &statb) < 0) {
-#ifdef SYSV
- if (errno == EINTR)
- continue;
-#endif
- entry->cmdtype = CMDUNKNOWN;
- return;
- }
- }
- entry->cmdtype = CMDNORMAL;
- return;
- }
-
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }
-#endif
-
- if (is_safe_applet(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }
-
- updatetbl = (path == pathval());
- 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);
- if (cmdp != NULL) {
- int bit;
-
- switch (cmdp->cmdtype) {
- default:
-#if DEBUG
- abort();
-#endif
- case CMDNORMAL:
- bit = DO_ALTPATH;
- break;
- case CMDFUNCTION:
- bit = DO_NOFUNC;
- break;
- case CMDBUILTIN:
- bit = DO_ALTBLTIN;
- break;
- }
- if (act & bit) {
- updatetbl = 0;
- cmdp = NULL;
- } else if (cmdp->rehash == 0)
- /* if not invalidated by cd, we're done */
- goto success;
- }
-
- /* If %builtin not in path, check for builtin next */
- bcmd = find_builtin(name);
- if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
- act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
- )))
- goto builtin_success;
+#if ENABLE_ASH_ALIAS
+ { BUILTIN_REGULAR "unalias", unaliascmd },
+#endif
+ { BUILTIN_SPEC_REG "unset", unsetcmd },
+ { BUILTIN_REGULAR "wait", waitcmd },
+};
- /* We have to search path. */
- prev = -1; /* where to start */
- if (cmdp && cmdp->rehash) { /* doing a rehash */
- if (cmdp->cmdtype == CMDBUILTIN)
- prev = builtinloc;
- else
- prev = cmdp->param.index;
- }
- e = ENOENT;
- idx = -1;
- loop:
- while ((fullname = padvance(&path, name)) != NULL) {
- stunalloc(fullname);
- idx++;
- if (pathopt) {
- if (prefix(pathopt, "builtin")) {
- if (bcmd)
- goto builtin_success;
- continue;
- } else if (!(act & DO_NOFUNC) &&
- prefix(pathopt, "func")) {
- /* handled below */
- } else {
- /* ignore unimplemented options */
- continue;
- }
- }
- /* if rehash, don't redo absolute path names */
- if (fullname[0] == '/' && idx <= prev) {
- if (idx < prev)
- continue;
- TRACE(("searchexec \"%s\": no change\n", name));
- goto success;
- }
- while (stat(fullname, &statb) < 0) {
-#ifdef SYSV
- if (errno == EINTR)
- continue;
-#endif
- if (errno != ENOENT && errno != ENOTDIR)
- e = errno;
- goto loop;
- }
- 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);
- readcmdfile(fullname);
- cmdp = cmdlookup(name, 0);
- if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
- ash_msg_and_raise_error("%s not defined in %s", name, fullname);
- stunalloc(fullname);
- goto success;
- }
- TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
- if (!updatetbl) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = idx;
- return;
- }
- INT_OFF;
- cmdp = cmdlookup(name, 1);
- cmdp->cmdtype = CMDNORMAL;
- cmdp->param.index = idx;
- INT_ON;
- goto success;
- }
+#define COMMANDCMD (builtintab + 5 + \
+ 2 * ENABLE_ASH_BUILTIN_TEST + \
+ ENABLE_ASH_ALIAS + \
+ ENABLE_ASH_JOB_CONTROL)
+#define EXECCMD (builtintab + 7 + \
+ 2 * ENABLE_ASH_BUILTIN_TEST + \
+ ENABLE_ASH_ALIAS + \
+ ENABLE_ASH_JOB_CONTROL + \
+ ENABLE_ASH_CMDCMD + \
+ ENABLE_ASH_BUILTIN_ECHO)
- /* We failed. If there was an entry for this command, delete it */
- if (cmdp && updatetbl)
- delete_cmd_entry();
- if (act & DO_ERR)
- ash_msg("%s: %s", name, errmsg(e, "not found"));
- entry->cmdtype = CMDUNKNOWN;
- return;
+/*
+ * Search the table of builtin commands.
+ */
+static struct builtincmd *
+find_builtin(const char *name)
+{
+ struct builtincmd *bp;
- builtin_success:
- if (!updatetbl) {
- entry->cmdtype = CMDBUILTIN;
- entry->u.cmd = bcmd;
- return;
- }
- INT_OFF;
- cmdp = cmdlookup(name, 1);
- cmdp->cmdtype = CMDBUILTIN;
- cmdp->param.cmd = bcmd;
- INT_ON;
- success:
- cmdp->rehash = 0;
- entry->cmdtype = cmdp->cmdtype;
- entry->u = cmdp->param;
+ bp = bsearch(
+ name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
+ pstrcmp
+ );
+ return bp;
}
/*
return *q == '=';
}
static int
-bltincmd(int argc, char **argv)
+bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
/* Preserve exitstatus of a previous possible redirection
* as POSIX mandates */
static void
evalcommand(union node *cmd, int flags)
{
- static const struct builtincmd bltin = {
- "\0\0", bltincmd
+ static const struct builtincmd null_bltin = {
+ "\0\0", bltincmd /* why three NULs? */
};
struct stackmark smark;
union node *argp;
back_exitstatus = 0;
cmdentry.cmdtype = CMDBUILTIN;
- cmdentry.u.cmd = &bltin;
+ cmdentry.u.cmd = &null_bltin;
varlist.lastp = &varlist.list;
*varlist.lastp = NULL;
arglist.lastp = &arglist.list;
preverrout_fd = 2;
expredir(cmd->ncmd.redirect);
- status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
+ status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
path = vpath.text;
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
const char *p = " %s";
p++;
- dprintf(preverrout_fd, p, expandstr(ps4val()));
+ fdprintf(preverrout_fd, p, expandstr(ps4val()));
sp = varlist.list;
for (n = 0; n < 2; n++) {
while (sp) {
- dprintf(preverrout_fd, p, sp->text);
+ fdprintf(preverrout_fd, p, sp->text);
sp = sp->next;
if (*p == '%') {
p--;
}
sp = arglist.list;
}
- full_write(preverrout_fd, "\n", 1);
+ safe_write(preverrout_fd, "\n", 1);
}
cmd_is_exec = 0;
/* Fork off a child process if necessary. */
if (!(flags & EV_EXIT) || trap[0]) {
INT_OFF;
- jp = makejob(cmd, 1);
+ jp = makejob(/*cmd,*/ 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
exitstatus = waitforjob(jp);
INT_ON;
}
if (evalbltin(cmdentry.u.cmd, argc, argv)) {
int exit_status;
- int i, j;
-
- i = exception;
+ int i = exception;
if (i == EXEXIT)
goto raise;
-
exit_status = 2;
- j = 0;
if (i == EXINT)
- j = SIGINT;
+ exit_status = 128 + SIGINT;
if (i == EXSIG)
- j = pendingsigs;
- if (j)
- exit_status = j + 128;
+ exit_status = 128 + pendingsig;
exitstatus = exit_status;
-
if (i == EXINT || spclbltin > 0) {
raise:
longjmp(exception_handler->loc, 1);
exitstatus |= ferror(stdout);
clearerr(stdout);
commandname = savecmdname;
- exsig = 0;
+// exsig = 0;
exception_handler = savehandler;
return i;
}
-/*
- * Builtin commands. Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
+/* ============ Builtin commands
+ *
+ * Builtin commands whose functions are closely tied to evaluation
+ * are implemented here.
*/
/*
* be an error to break out of more loops than exist, but it isn't
* in the standard shell so we don't make it one here.
*/
-
static int
-breakcmd(int argc, char **argv)
+breakcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- int n = argc > 1 ? number(argv[1]) : 1;
+ int n = argv[1] ? number(argv[1]) : 1;
if (n <= 0)
ash_msg_and_raise_error(illnum, argv[1]);
if (n > loopnest)
n = loopnest;
if (n > 0) {
- evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+ evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
skipcount = n;
}
return 0;
}
-/*
- * The return command.
- */
-static int
-returncmd(int argc, char **argv)
-{
- /*
- * If called outside a function, do what ksh does;
- * skip the rest of the file.
- */
- evalskip = funcnest ? SKIPFUNC : SKIPFILE;
- return argv[1] ? number(argv[1]) : exitstatus;
-}
-
-static int
-falsecmd(int argc, char **argv)
-{
- return 1;
-}
-
-static int
-truecmd(int argc, char **argv)
-{
- return 0;
-}
-
-static int
-execcmd(int argc, char **argv)
-{
- if (argc > 1) {
- iflag = 0; /* exit on error */
- mflag = 0;
- optschanged();
- shellexec(argv + 1, pathval(), 0);
- }
- return 0;
-}
-
/* ============ input.c
*
INPUT_NOFILE_OK = 2,
};
-/*
- * NEOF is returned by parsecmd when it encounters an end of file. It
- * must be distinct from NULL, so we use the address of a variable that
- * happens to be handy.
- */
static int plinno = 1; /* input line number */
/* number of characters left in input buffer */
static int parsenleft; /* copy of parsefile->nleft */
#define CHKKWD 0x2
#define CHKNL 0x4
-
static void
popstring(void)
{
retry:
#if ENABLE_FEATURE_EDITING
if (!iflag || parsefile->fd)
- nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+ nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
else {
#if ENABLE_FEATURE_TAB_COMPLETION
line_input_state->path_lookup = pathval();
goto retry;
}
if (nr < 0 && errno == 0) {
- /* Ctrl+D presend */
+ /* Ctrl+D pressed */
nr = 0;
}
}
#else
- nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+ nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1);
#endif
+#if 0
+/* nonblock_safe_read() handles this problem */
if (nr < 0) {
if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
- int flags = fcntl(0, F_GETFL, 0);
- if (flags >= 0 && flags & O_NONBLOCK) {
- flags &=~ O_NONBLOCK;
+ int flags = fcntl(0, F_GETFL);
+ if (flags >= 0 && (flags & O_NONBLOCK)) {
+ flags &= ~O_NONBLOCK;
if (fcntl(0, F_SETFL, flags) >= 0) {
out2str("sh: turning off NDELAY mode\n");
goto retry;
}
}
}
+#endif
return nr;
}
#endif
popstring();
if (--parsenleft >= 0)
- return SC2INT(*parsenextc++);
+ return signed_char2int(*parsenextc++);
}
if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
return PEOF;
*q = savec;
- return SC2INT(*parsenextc++);
+ return signed_char2int(*parsenextc++);
}
-#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
-
-#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#define pgetc_macro() pgetc()
+#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
static int
pgetc(void)
{
return pgetc_as_macro();
}
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+#define pgetc_macro() pgetc()
#else
-#define pgetc_macro() pgetc_as_macro()
-static int
-pgetc(void)
-{
- return pgetc_macro();
-}
+#define pgetc_macro() pgetc_as_macro()
#endif
/*
INT_OFF;
/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
if (parsefile->strpush) {
- sp = ckmalloc(sizeof(struct strpush));
+ sp = ckzalloc(sizeof(struct strpush));
sp->prev = parsefile->strpush;
parsefile->strpush = sp;
} else
parsefile->lleft = parselleft;
parsefile->nextc = parsenextc;
parsefile->linno = plinno;
- pf = ckmalloc(sizeof(*pf));
+ pf = ckzalloc(sizeof(*pf));
pf->prev = parsefile;
pf->fd = -1;
- pf->strpush = NULL;
- pf->basestrpush.prev = NULL;
+ /*pf->strpush = NULL; - ckzalloc did it */
+ /*pf->basestrpush.prev = NULL;*/
parsefile = pf;
}
INT_OFF;
if (pf->fd >= 0)
close(pf->fd);
- if (pf->buf)
- free(pf->buf);
+ free(pf->buf);
while (pf->strpush)
popstring();
parsefile = pf->prev;
static void
setinputfd(int fd, int push)
{
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ close_on_exec_on(fd);
if (push) {
pushfile();
parsefile->buf = 0;
if (fd < 0) {
if (flags & INPUT_NOFILE_OK)
goto out;
- ash_msg_and_raise_error("Can't open %s", fname);
+ ash_msg_and_raise_error("can't open %s", fname);
}
if (fd < 10) {
fd2 = copyfd(fd, 10);
close(fd);
if (fd2 < 0)
- ash_msg_and_raise_error("Out of file descriptors");
+ ash_msg_and_raise_error("out of file descriptors");
fd = fd2;
}
setinputfd(fd, flags & INPUT_PUSH_FILE);
*
* Routines to check for mail.
*/
+
#if ENABLE_ASH_MAIL
#define MAXMBOXES 10
/* times of mailboxes */
static time_t mailtime[MAXMBOXES];
/* Set if MAIL or MAILPATH is changed. */
-static int mail_var_path_changed;
+static smallint mail_var_path_changed;
/*
* Print appropriate message(s) if mail has arrived.
}
static void
-changemail(const char *val)
+changemail(const char *val ATTRIBUTE_UNUSED)
{
- mail_var_path_changed++;
+ mail_var_path_changed = 1;
}
+
#endif /* ASH_MAIL */
/* ============ ??? */
/*
- * Take commands from a file. To be compatible we should do a path
- * search for the file, which is necessary to find sub-commands.
- */
-static char *
-find_dot_file(char *name)
-{
- char *fullname;
- const char *path = pathval();
- struct stat statb;
-
- /* don't try this for absolute or relative paths */
- if (strchr(name, '/'))
- return name;
-
- while ((fullname = padvance(&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;
- }
- stunalloc(fullname);
- }
-
- /* not found in the PATH */
- ash_msg_and_raise_error("%s: not found", name);
- /* NOTREACHED */
-}
-
-/*
- * Controls whether the shell is interactive or not.
+ * Set the shell parameters.
*/
static void
-setinteractive(int on)
+setparam(char **argv)
{
- static int is_interactive;
-
- if (++on == is_interactive)
- return;
- is_interactive = on;
- setsignal(SIGINT);
- setsignal(SIGQUIT);
- setsignal(SIGTERM);
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
- if (is_interactive > 1) {
- /* Looks like they want an interactive shell */
- static smallint do_banner;
+ char **newparam;
+ char **ap;
+ int nparam;
- if (!do_banner) {
- out1fmt(
- "\n\n"
- "%s Built-in shell (ash)\n"
- "Enter 'help' for a list of built-in commands."
- "\n\n",
- BB_BANNER);
- do_banner = 1;
- }
+ for (nparam = 0; argv[nparam]; nparam++);
+ ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
+ while (*argv) {
+ *ap++ = ckstrdup(*argv++);
}
+ *ap = NULL;
+ freeparam(&shellparam);
+ shellparam.malloced = 1;
+ shellparam.nparam = nparam;
+ shellparam.p = newparam;
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
#endif
}
-#if ENABLE_FEATURE_EDITING_VI
-#define setvimode(on) do { \
- if (on) line_input_state->flags |= VI_MODE; \
- else line_input_state->flags &= ~VI_MODE; \
-} while (0)
-#else
-#define setvimode(on) viflag = 0 /* forcibly keep the option off */
-#endif
-
-static void
-optschanged(void)
-{
-#if DEBUG
- opentrace();
-#endif
- setinteractive(iflag);
- setjobctl(mflag);
- setvimode(viflag);
-}
-
-static void
+/*
+ * Process shell options. The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ *
+ * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
+ * For a non-interactive shell, an error condition encountered
+ * by a special built-in ... shall cause the shell to write a diagnostic message
+ * to standard error and exit as shown in the following table:
+ * Error Special Built-In
+ * ...
+ * Utility syntax error (option or operand error) Shall exit
+ * ...
+ * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
+ * we see that bash does not do that (set "finishes" with error code 1 instead,
+ * and shell continues), and people rely on this behavior!
+ * Testcase:
+ * set -o barfoo 2>/dev/null
+ * echo $?
+ *
+ * Oh well. Let's mimic that.
+ */
+static int
minus_o(char *name, int val)
{
int i;
for (i = 0; i < NOPTS; i++) {
if (strcmp(name, optnames(i)) == 0) {
optlist[i] = val;
- return;
+ return 0;
}
}
- ash_msg_and_raise_error("Illegal option -o %s", name);
+ ash_msg("illegal option -o %s", name);
+ return 1;
}
out1str("Current option settings\n");
for (i = 0; i < NOPTS; i++)
out1fmt("%-16s%s\n", optnames(i),
optlist[i] ? "on" : "off");
+ return 0;
}
-
static void
setoption(int flag, int val)
{
return;
}
}
- ash_msg_and_raise_error("Illegal option -%c", flag);
+ ash_msg_and_raise_error("illegal option -%c", flag);
/* NOTREACHED */
}
-
-/*
- * Process shell options. The global variable argptr contains a pointer
- * to the argument list; we advance it past the options.
- */
-static void
+static int
options(int cmdline)
{
char *p;
if (cmdline)
minusc = NULL;
while ((p = *argptr) != NULL) {
- argptr++;
c = *p++;
+ if (c != '-' && c != '+')
+ break;
+ argptr++;
+ val = 0; /* val = 0 if c == '+' */
if (c == '-') {
val = 1;
if (p[0] == '\0' || LONE_DASH(p)) {
}
break; /* "-" or "--" terminates options */
}
- } else if (c == '+') {
- val = 0;
- } else {
- argptr--;
- break;
}
+ /* first char was + or - */
while ((c = *p++) != '\0') {
+ /* bash 3.2 indeed handles -c CMD and +c CMD the same */
if (c == 'c' && cmdline) {
- minusc = p; /* command is after shell args*/
+ minusc = p; /* command is after shell args */
} else if (c == 'o') {
- minus_o(*argptr, val);
+ if (minus_o(*argptr, val)) {
+ /* it already printed err message */
+ return 1; /* error */
+ }
if (*argptr)
argptr++;
- } else if (cmdline && (c == '-')) { // long options
+ } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
+ isloginsh = 1;
+ /* bash does not accept +-login, we also won't */
+ } else if (cmdline && val && (c == '-')) { /* long options */
if (strcmp(p, "login") == 0)
isloginsh = 1;
break;
}
}
}
-}
-
-/*
- * Set the shell parameters.
- */
-static void
-setparam(char **argv)
-{
- char **newparam;
- char **ap;
- int nparam;
-
- for (nparam = 0; argv[nparam]; nparam++);
- ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
- while (*argv) {
- *ap++ = ckstrdup(*argv++);
- }
- *ap = NULL;
- freeparam(&shellparam);
- shellparam.malloc = 1;
- shellparam.nparam = nparam;
- shellparam.p = newparam;
-#if ENABLE_ASH_GETOPTS
- shellparam.optind = 1;
- shellparam.optoff = -1;
-#endif
-}
-
-/*
- * Free the list of positional parameters.
- */
-static void
-freeparam(volatile struct shparam *param)
-{
- char **ap;
-
- if (param->malloc) {
- for (ap = param->p; *ap; ap++)
- free(*ap);
- free(param->p);
- }
+ return 0;
}
/*
* The shift builtin command.
*/
static int
-shiftcmd(int argc, char **argv)
+shiftcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int n;
char **ap1, **ap2;
n = 1;
- if (argc > 1)
+ if (argv[1])
n = number(argv[1]);
if (n > shellparam.nparam)
ash_msg_and_raise_error("can't shift that many");
INT_OFF;
shellparam.nparam -= n;
for (ap1 = shellparam.p; --n >= 0; ap1++) {
- if (shellparam.malloc)
+ if (shellparam.malloced)
free(*ap1);
}
ap2 = shellparam.p;
ep = listvars(on, off, &epend);
qsort(ep, epend - ep, sizeof(char *), vpcmp);
- sep = *sep_prefix ? spcstr : sep_prefix;
+ sep = *sep_prefix ? " " : sep_prefix;
for (; ep < epend; ep++) {
const char *p;
* The set command builtin.
*/
static int
-setcmd(int argc, char **argv)
+setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
- if (argc == 1)
+ int retval;
+
+ if (!argv[1])
return showvars(nullstr, 0, VUNSET);
INT_OFF;
- options(0);
- optschanged();
- if (*argptr != NULL) {
- setparam(argptr);
+ retval = 1;
+ if (!options(0)) { /* if no parse error... */
+ retval = 0;
+ optschanged();
+ if (*argptr != NULL) {
+ setparam(argptr);
+ }
}
INT_ON;
- return 0;
+ return retval;
}
#if ENABLE_ASH_RANDOM_SUPPORT
char **optbase;
if (argc < 3)
- ash_msg_and_raise_error("Usage: getopts optstring var [arg]");
+ ash_msg_and_raise_error("usage: getopts optstring var [arg]");
if (argc == 3) {
optbase = shellparam.p;
if (shellparam.optind > shellparam.nparam + 1) {
/* ============ Shell parser */
-static int tokpushback; /* last token pushed back */
+/*
+ * NEOF is returned by parsecmd when it encounters an end of file. It
+ * must be distinct from NULL, so we use the address of a variable that
+ * happens to be handy.
+ */
+static smallint tokpushback; /* last token pushed back */
#define NEOF ((union node *)&tokpushback)
-static int parsebackquote; /* nonzero if we are inside backquotes */
+static smallint parsebackquote; /* nonzero if we are inside backquotes */
static int lasttoken; /* last token read */
static char *wordtext; /* text of last word returned by readtoken */
static struct nodelist *backquotelist;
static union node *redirnode;
static struct heredoc *heredoc;
-static int quoteflag; /* set if (part of) last token was quoted */
+static smallint quoteflag; /* set if (part of) last token was quoted */
static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
static void
raise_error_syntax(const char *msg)
{
- ash_msg_and_raise_error("Syntax error: %s", msg);
+ ash_msg_and_raise_error("syntax error: %s", msg);
/* NOTREACHED */
}
struct heredoc {
struct heredoc *next; /* next here document in list */
- union node *here; /* redirection node */
+ union node *here; /* redirection node */
char *eofmark; /* string indicating end of input */
int striptabs; /* if set, strip leading tabs */
};
n2->npipe.backgnd = 1;
} else {
if (n2->type != NREDIR) {
- n3 = stalloc(sizeof(struct nredir));
+ n3 = stzalloc(sizeof(struct nredir));
n3->nredir.n = n2;
- n3->nredir.redirect = NULL;
+ /*n3->nredir.redirect = NULL; - stzalloc did it */
n2 = n3;
}
n2->type = NBACKGND;
if (n1 == NULL) {
n1 = n2;
} else {
- n3 = stalloc(sizeof(struct nbinary));
+ n3 = stzalloc(sizeof(struct nbinary));
n3->type = NSEMI;
n3->nbinary.ch1 = n1;
n3->nbinary.ch2 = n2;
if (nlflag == 1)
return n1;
} else {
- tokpushback++;
+ tokpushback = 1;
}
checkkwd = CHKNL | CHKKWD | CHKALIAS;
if (peektoken())
default:
if (nlflag == 1)
raise_error_unexpected_syntax(-1);
- tokpushback++;
+ tokpushback = 1;
return n1;
}
}
} else if (t == TOR) {
t = NOR;
} else {
- tokpushback++;
+ tokpushback = 1;
return n1;
}
checkkwd = CHKNL | CHKKWD | CHKALIAS;
n2 = pipeline();
- n3 = stalloc(sizeof(struct nbinary));
+ n3 = stzalloc(sizeof(struct nbinary));
n3->type = t;
n3->nbinary.ch1 = n1;
n3->nbinary.ch2 = n2;
negate = !negate;
checkkwd = CHKKWD | CHKALIAS;
} else
- tokpushback++;
+ tokpushback = 1;
n1 = parse_command();
if (readtoken() == TPIPE) {
- pipenode = stalloc(sizeof(struct npipe));
+ pipenode = stzalloc(sizeof(struct npipe));
pipenode->type = NPIPE;
- pipenode->npipe.backgnd = 0;
- lp = stalloc(sizeof(struct nodelist));
+ /*pipenode->npipe.backgnd = 0; - stzalloc did it */
+ lp = stzalloc(sizeof(struct nodelist));
pipenode->npipe.cmdlist = lp;
lp->n = n1;
do {
prev = lp;
- lp = stalloc(sizeof(struct nodelist));
+ lp = stzalloc(sizeof(struct nodelist));
checkkwd = CHKNL | CHKKWD | CHKALIAS;
lp->n = parse_command();
prev->next = lp;
lp->next = NULL;
n1 = pipenode;
}
- tokpushback++;
+ tokpushback = 1;
if (negate) {
- n2 = stalloc(sizeof(struct nnot));
+ n2 = stzalloc(sizeof(struct nnot));
n2->type = NNOT;
n2->nnot.com = n1;
return n2;
{
union node *n;
- n = stalloc(sizeof(struct narg));
+ n = stzalloc(sizeof(struct narg));
n->type = NARG;
- n->narg.next = NULL;
+ /*n->narg.next = NULL; - stzalloc did it */
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
return n;
if (heredoclist == NULL)
heredoclist = here;
else {
- for (p = heredoclist; p->next; p = p->next);
+ for (p = heredoclist; p->next; p = p->next)
+ continue;
p->next = here;
}
} else if (n->type == NTOFD || n->type == NFROMFD) {
checkkwd = savecheckkwd;
switch (readtoken()) {
case TWORD:
- n = stalloc(sizeof(struct narg));
+ n = stzalloc(sizeof(struct narg));
n->type = NARG;
+ /*n->narg.next = NULL; - stzalloc did it */
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
if (savecheckkwd && isassignment(wordtext)) {
}
/* fall through */
default:
- tokpushback++;
+ tokpushback = 1;
goto out;
}
}
*app = NULL;
*vpp = NULL;
*rpp = NULL;
- n = stalloc(sizeof(struct ncmd));
+ n = stzalloc(sizeof(struct ncmd));
n->type = NCMD;
n->ncmd.args = args;
n->ncmd.assign = vars;
raise_error_unexpected_syntax(-1);
/* NOTREACHED */
case TIF:
- n1 = stalloc(sizeof(struct nif));
+ n1 = stzalloc(sizeof(struct nif));
n1->type = NIF;
n1->nif.test = list(0);
if (readtoken() != TTHEN)
n1->nif.ifpart = list(0);
n2 = n1;
while (readtoken() == TELIF) {
- n2->nif.elsepart = stalloc(sizeof(struct nif));
+ n2->nif.elsepart = stzalloc(sizeof(struct nif));
n2 = n2->nif.elsepart;
n2->type = NIF;
n2->nif.test = list(0);
n2->nif.elsepart = list(0);
else {
n2->nif.elsepart = NULL;
- tokpushback++;
+ tokpushback = 1;
}
t = TFI;
break;
case TWHILE:
case TUNTIL: {
int got;
- n1 = stalloc(sizeof(struct nbinary));
+ n1 = stzalloc(sizeof(struct nbinary));
n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
n1->nbinary.ch1 = list(0);
got = readtoken();
case TFOR:
if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
raise_error_syntax("Bad for loop variable");
- n1 = stalloc(sizeof(struct nfor));
+ n1 = stzalloc(sizeof(struct nfor));
n1->type = NFOR;
n1->nfor.var = wordtext;
checkkwd = CHKKWD | CHKALIAS;
if (readtoken() == TIN) {
app = ≈
while (readtoken() == TWORD) {
- n2 = stalloc(sizeof(struct narg));
+ n2 = stzalloc(sizeof(struct narg));
n2->type = NARG;
+ /*n2->narg.next = NULL; - stzalloc did it */
n2->narg.text = wordtext;
n2->narg.backquote = backquotelist;
*app = n2;
if (lasttoken != TNL && lasttoken != TSEMI)
raise_error_unexpected_syntax(-1);
} else {
- n2 = stalloc(sizeof(struct narg));
+ n2 = stzalloc(sizeof(struct narg));
n2->type = NARG;
+ /*n2->narg.next = NULL; - stzalloc did it */
n2->narg.text = (char *)dolatstr;
- n2->narg.backquote = NULL;
- n2->narg.next = NULL;
+ /*n2->narg.backquote = NULL;*/
n1->nfor.args = n2;
/*
* Newline or semicolon here is optional (but note
* that the original Bourne shell only allowed NL).
*/
if (lasttoken != TNL && lasttoken != TSEMI)
- tokpushback++;
+ tokpushback = 1;
}
checkkwd = CHKNL | CHKKWD | CHKALIAS;
if (readtoken() != TDO)
t = TDONE;
break;
case TCASE:
- n1 = stalloc(sizeof(struct ncase));
+ n1 = stzalloc(sizeof(struct ncase));
n1->type = NCASE;
if (readtoken() != TWORD)
raise_error_unexpected_syntax(TWORD);
- n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
+ n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
n2->type = NARG;
+ /*n2->narg.next = NULL; - stzalloc did it */
n2->narg.text = wordtext;
n2->narg.backquote = backquotelist;
- n2->narg.next = NULL;
do {
checkkwd = CHKKWD | CHKALIAS;
} while (readtoken() == TNL);
while (t != TESAC) {
if (lasttoken == TLP)
readtoken();
- *cpp = cp = stalloc(sizeof(struct nclist));
+ *cpp = cp = stzalloc(sizeof(struct nclist));
cp->type = NCLIST;
app = &cp->nclist.pattern;
for (;;) {
- *app = ap = stalloc(sizeof(struct narg));
+ *app = ap = stzalloc(sizeof(struct narg));
ap->type = NARG;
+ /*ap->narg.next = NULL; - stzalloc did it */
ap->narg.text = wordtext;
ap->narg.backquote = backquotelist;
if (readtoken() != TPIPE)
app = &ap->narg.next;
readtoken();
}
- ap->narg.next = NULL;
+ //ap->narg.next = NULL;
if (lasttoken != TRP)
raise_error_unexpected_syntax(TRP);
cp->nclist.body = list(2);
*cpp = NULL;
goto redir;
case TLP:
- n1 = stalloc(sizeof(struct nredir));
+ n1 = stzalloc(sizeof(struct nredir));
n1->type = NSUBSHELL;
n1->nredir.n = list(0);
- n1->nredir.redirect = NULL;
+ /*n1->nredir.redirect = NULL; - stzalloc did it */
t = TRP;
break;
case TBEGIN:
break;
case TWORD:
case TREDIR:
- tokpushback++;
+ tokpushback = 1;
return simplecmd();
}
rpp = &n2->nfile.next;
parsefname();
}
- tokpushback++;
+ tokpushback = 1;
*rpp = NULL;
if (redir) {
if (n1->type != NSUBSHELL) {
- n2 = stalloc(sizeof(struct nredir));
+ n2 = stzalloc(sizeof(struct nredir));
n2->type = NREDIR;
n2->nredir.n = n1;
n1 = n2;
* will run code that appears at the end of readtoken1.
*/
-static int parsebackquote; /* nonzero if we are inside backquotes */
-
#define CHECKEND() {goto checkend; checkend_return:;}
#define PARSEREDIR() {goto parseredir; parseredir_return:;}
#define PARSESUB() {goto parsesub; parsesub_return:;}
static int
readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
{
+ /* NB: syntax parameter fits into smallint */
int c = firstc;
char *out;
int len;
char line[EOFMARKLEN + 1];
- struct nodelist *bqlist = 0;
- int quotef = 0;
- int dblquote = 0;
- int varnest = 0; /* levels of variables expansion */
- int arinest = 0; /* levels of arithmetic expansion */
- int parenlevel = 0; /* levels of parens in arithmetic */
- int dqvarnest = 0; /* levels of variables expansion within double quotes */
- int oldstyle = 0;
- int prevsyntax = 0; /* syntax before arithmetic */
+ struct nodelist *bqlist;
+ smallint quotef;
+ smallint dblquote;
+ smallint oldstyle;
+ smallint prevsyntax; /* syntax before arithmetic */
+#if ENABLE_ASH_EXPAND_PRMT
+ smallint pssyntax; /* we are expanding a prompt string */
+#endif
+ int varnest; /* levels of variables expansion */
+ int arinest; /* levels of arithmetic expansion */
+ int parenlevel; /* levels of parens in arithmetic */
+ int dqvarnest; /* levels of variables expansion within double quotes */
+
#if __GNUC__
/* Avoid longjmp clobbering */
(void) &out;
(void) &prevsyntax;
(void) &syntax;
#endif
-
startlinno = plinno;
- dblquote = 0;
- if (syntax == DQSYNTAX)
- dblquote = 1;
- quotef = 0;
bqlist = NULL;
+ quotef = 0;
+ oldstyle = 0;
+ prevsyntax = 0;
+#if ENABLE_ASH_EXPAND_PRMT
+ pssyntax = (syntax == PSSYNTAX);
+ if (pssyntax)
+ syntax = DQSYNTAX;
+#endif
+ dblquote = (syntax == DQSYNTAX);
varnest = 0;
arinest = 0;
parenlevel = 0;
if (doprompt)
setprompt(2);
} else {
+#if ENABLE_ASH_EXPAND_PRMT
+ if (c == '$' && pssyntax) {
+ USTPUTC(CTLESC, out);
+ USTPUTC('\\', out);
+ }
+#endif
if (dblquote &&
c != '\\' && c != '`' &&
c != '$' && (
if (SIT(c, SQSYNTAX) == CCTL)
USTPUTC(CTLESC, out);
USTPUTC(c, out);
- quotef++;
+ quotef = 1;
}
break;
case CSQUOTE:
syntax = BASESYNTAX;
dblquote = 0;
}
- quotef++;
+ quotef = 1;
goto quotemark;
}
break;
if (--arinest == 0) {
USTPUTC(CTLENDARI, out);
syntax = prevsyntax;
- if (syntax == DQSYNTAX)
- dblquote = 1;
- else
- dblquote = 0;
+ dblquote = (syntax == DQSYNTAX);
} else
USTPUTC(')', out);
} else {
return lasttoken;
/* end of readtoken routine */
-
/*
* Check to see whether we are at the end of the here document. When this
* is called, c is set to the first character of the next input line. If
goto checkend_return;
}
-
/*
* Parse a redirection operator. The variable "out" points to a string
* specifying the fd to be redirected. The variable "c" contains the
char fd = *out;
union node *np;
- np = stalloc(sizeof(struct nfile));
+ np = stzalloc(sizeof(struct nfile));
if (c == '>') {
np->nfile.fd = 1;
c = pgetc();
pungetc();
}
} else { /* c == '<' */
- np->nfile.fd = 0;
+ /*np->nfile.fd = 0; - stzalloc did it */
c = pgetc();
switch (c) {
case '<':
if (sizeof(struct nfile) != sizeof(struct nhere)) {
- np = stalloc(sizeof(struct nhere));
- np->nfile.fd = 0;
+ np = stzalloc(sizeof(struct nhere));
+ /*np->nfile.fd = 0; - stzalloc did it */
}
np->type = NHERE;
- heredoc = stalloc(sizeof(struct heredoc));
+ heredoc = stzalloc(sizeof(struct heredoc));
heredoc->here = np;
c = pgetc();
if (c == '-') {
heredoc->striptabs = 1;
} else {
- heredoc->striptabs = 0;
+ /*heredoc->striptabs = 0; - stzalloc did it */
pungetc();
}
break;
goto parseredir_return;
}
-
/*
* Parse a substitution. At this point, we have read the dollar sign
* and nothing else.
*/
+
+/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did) */
+#define is_special(c) \
+ ((((unsigned int)c) - 33 < 32) \
+ && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
parsesub: {
int subtype;
int typeloc;
int flags;
char *p;
- static const char types[] = "}-+?=";
+ static const char types[] ALIGN1 = "}-+?=";
c = pgetc();
if (
goto parsesub_return;
}
-
/*
* Called to parse command substitutions. Newstyle is set if the command
* is enclosed inside $(...); nlpp is a pointer to the head of the linked
*/
parsebackq: {
struct nodelist **nlpp;
- int savepbq;
+ smallint savepbq;
union node *n;
char *volatile str;
struct jmploc jmploc;
struct jmploc *volatile savehandler;
size_t savelen;
- int saveprompt = 0;
+ smallint saveprompt = 0;
+
#ifdef __GNUC__
(void) &saveprompt;
#endif
-
savepbq = parsebackquote;
if (setjmp(jmploc.loc)) {
- if (str)
- free(str);
+ free(str);
parsebackquote = 0;
exception_handler = savehandler;
longjmp(exception_handler->loc, 1);
nlpp = &bqlist;
while (*nlpp)
nlpp = &(*nlpp)->next;
- *nlpp = stalloc(sizeof(**nlpp));
- (*nlpp)->next = NULL;
+ *nlpp = stzalloc(sizeof(**nlpp));
+ /* (*nlpp)->next = NULL; - stzalloc did it */
parsebackquote = oldstyle;
if (oldstyle) {
#define NEW_xxreadtoken
#ifdef NEW_xxreadtoken
/* singles must be first! */
-static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
+static const char xxreadtoken_chars[7] ALIGN1 = {
+ '\n', '(', ')', '&', '|', ';', 0
+};
-static const char xxreadtoken_tokens[] = {
+static const char xxreadtoken_tokens[] ALIGN1 = {
TNL, TLP, TRP, /* only single occurrence allowed */
TBACKGND, TPIPE, TSEMI, /* if single occurrence */
TEOF, /* corresponds to trailing nul */
- TAND, TOR, TENDCASE, /* if double occurrence */
+ TAND, TOR, TENDCASE /* if double occurrence */
};
#define xxreadtoken_doubles \
{
int t;
#if DEBUG
- int alreadyseen = tokpushback;
+ smallint alreadyseen = tokpushback;
#endif
#if ENABLE_ASH_ALIAS
int t;
t = readtoken();
- tokpushback++;
+ tokpushback = 1;
return tokname_array[t][0];
}
return NEOF;
if (t == TNL)
return NULL;
- tokpushback++;
+ tokpushback = 1;
return list(1);
}
union node *n;
here = heredoclist;
- heredoclist = 0;
+ heredoclist = NULL;
while (here) {
if (needprompt) {
}
readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
here->eofmark, here->striptabs);
- n = stalloc(sizeof(struct narg));
+ n = stzalloc(sizeof(struct narg));
n->narg.type = NARG;
- n->narg.next = NULL;
+ /*n->narg.next = NULL; - stzalloc did it */
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
here->here->nhere.doc = n;
/*
- * called by editline -- any expansions to the prompt
- * should be added here.
+ * called by editline -- any expansions to the prompt should be added here.
*/
#if ENABLE_ASH_EXPAND_PRMT
static const char *
/* XXX Fix (char *) cast. */
setinputstring((char *)ps);
- readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+ readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
popfile();
n.narg.type = NARG;
}
#endif
-
/*
* Execute a command or commands contained in a string.
*/
* The eval command.
*/
static int
-evalcmd(int argc, char **argv)
+evalcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *p;
char *concat;
- char **ap;
- if (argc > 1) {
+ if (argv[1]) {
p = argv[1];
- if (argc > 2) {
+ argv += 2;
+ if (argv[0]) {
STARTSTACKSTR(concat);
- ap = argv + 2;
for (;;) {
concat = stack_putstr(p, concat);
- p = *ap++;
+ p = *argv++;
if (p == NULL)
break;
STPUTC(' ', concat);
if (n == NEOF) {
if (!top || numeof >= 50)
break;
- if (!stoppedjobs()) {
- if (!Iflag)
- break;
- out2str("\nUse \"exit\" to leave shell.\n");
- }
- numeof++;
- } else if (nflag == 0) {
- job_warning = (job_warning == 2) ? 1 : 0;
- numeof = 0;
- evaltree(n, 0);
- }
- popstackmark(&smark);
- skip = evalskip;
-
- if (skip) {
- evalskip = 0;
- return skip & SKIPEVAL;
- }
- }
- return 0;
-}
-
-static int
-dotcmd(int argc, char **argv)
-{
- struct strlist *sp;
- volatile struct shparam saveparam;
- int status = 0;
-
- for (sp = cmdenviron; sp; sp = sp->next)
- setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
-
- if (argc >= 2) { /* That's what SVR2 does */
- char *fullname;
-
- fullname = find_dot_file(argv[1]);
-
- if (argc > 2) {
- saveparam = shellparam;
- shellparam.malloc = 0;
- shellparam.nparam = argc - 2;
- shellparam.p = argv + 2;
- };
-
- setinputfile(fullname, INPUT_PUSH_FILE);
- commandname = fullname;
- cmdloop(0);
- popfile();
-
- if (argc > 2) {
- freeparam(&shellparam);
- shellparam = saveparam;
- };
- status = exitstatus;
- }
- return status;
-}
-
-static int
-exitcmd(int argc, char **argv)
-{
- if (stoppedjobs())
- return 0;
- if (argc > 1)
- exitstatus = number(argv[1]);
- raise_exception(EXEXIT);
- /* NOTREACHED */
-}
-
-#if ENABLE_ASH_BUILTIN_ECHO
-static int
-echocmd(int argc, char **argv)
-{
- return bb_echo(argv);
-}
-#endif
-
-#if ENABLE_ASH_BUILTIN_TEST
-static int
-testcmd(int argc, char **argv)
-{
- return bb_test(argc, argv);
-}
-#endif
-
-/*
- * Read a file containing shell functions.
- */
-static void
-readcmdfile(char *name)
-{
- setinputfile(name, INPUT_PUSH_FILE);
- cmdloop(0);
- popfile();
-}
-
-
-/* ============ redir.c */
-
-/*
- * Code for dealing with input/output redirection.
- */
-
-#define EMPTY -2 /* marks an unused slot in redirtab */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096 /* amount of buffering in a pipe */
-#else
-# define PIPESIZE PIPE_BUF
-#endif
-
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
- int r, fd;
- struct stat finfo, finfo2;
-
- /*
- * If the file exists and is a regular file, return an error
- * immediately.
- */
- r = stat(fname, &finfo);
- if (r == 0 && S_ISREG(finfo.st_mode)) {
- errno = EEXIST;
- return -1;
- }
-
- /*
- * If the file was not present (r != 0), make sure we open it
- * exclusively so that if it is created before we open it, our open
- * will fail. Make sure that we do not truncate an existing file.
- * Note that we don't turn on O_EXCL unless the stat failed -- if the
- * file was not a regular file, we leave O_EXCL off.
- */
- if (r != 0)
- return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
- fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
- /* If the open failed, return the file descriptor right away. */
- if (fd < 0)
- return fd;
-
- /*
- * OK, the open succeeded, but the file may have been changed from a
- * non-regular file to a regular file between the stat and the open.
- * We are assuming that the O_EXCL open handles the case where FILENAME
- * did not exist and is symlinked to an existing file between the stat
- * and open.
- */
-
- /*
- * If we can open it and fstat the file descriptor, and neither check
- * revealed that it was a regular file, and the file has not been
- * replaced, return the file descriptor.
- */
- if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
- && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
- return fd;
+ if (!stoppedjobs()) {
+ if (!Iflag)
+ break;
+ out2str("\nUse \"exit\" to leave shell.\n");
+ }
+ numeof++;
+ } else if (nflag == 0) {
+ /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
+ job_warning >>= 1;
+ numeof = 0;
+ evaltree(n, 0);
+ }
+ popstackmark(&smark);
+ skip = evalskip;
- /* The file has been replaced. badness. */
- close(fd);
- errno = EEXIST;
- return -1;
+ if (skip) {
+ evalskip = 0;
+ return skip & SKIPEVAL;
+ }
+ }
+ return 0;
}
/*
- * Handle here documents. Normally we fork off a process to write the
- * data to a pipe. If the document is short, we can stuff the data in
- * the pipe without forking.
+ * Take commands from a file. To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
*/
-static int
-openhere(union node *redir)
+static char *
+find_dot_file(char *name)
{
- int pip[2];
- size_t len = 0;
+ char *fullname;
+ const char *path = pathval();
+ struct stat statb;
- if (pipe(pip) < 0)
- ash_msg_and_raise_error("Pipe call failed");
- if (redir->type == NHERE) {
- len = strlen(redir->nhere.doc->narg.text);
- if (len <= PIPESIZE) {
- full_write(pip[1], redir->nhere.doc->narg.text, len);
- goto out;
+ /* don't try this for absolute or relative paths */
+ if (strchr(name, '/'))
+ return name;
+
+ while ((fullname = padvance(&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;
}
+ stunalloc(fullname);
}
- if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
- close(pip[0]);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
- signal(SIGTSTP, SIG_IGN);
-#endif
- signal(SIGPIPE, SIG_DFL);
- if (redir->type == NHERE)
- full_write(pip[1], redir->nhere.doc->narg.text, len);
- else
- expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
- }
- out:
- close(pip[1]);
- return pip[0];
+
+ /* not found in the PATH */
+ ash_msg_and_raise_error("%s: not found", name);
+ /* NOTREACHED */
}
static int
-openredirect(union node *redir)
+dotcmd(int argc, char **argv)
{
- char *fname;
- int f;
-
- switch (redir->nfile.type) {
- case NFROM:
- fname = redir->nfile.expfname;
- f = open(fname, O_RDONLY);
- if (f < 0)
- goto eopen;
- break;
- case NFROMTO:
- fname = redir->nfile.expfname;
- f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
- if (f < 0)
- goto ecreate;
- break;
- case NTO:
- /* Take care of noclobber mode. */
- if (Cflag) {
- fname = redir->nfile.expfname;
- f = noclobberopen(fname);
- if (f < 0)
- goto ecreate;
- break;
- }
- /* FALLTHROUGH */
- case NCLOBBER:
- fname = redir->nfile.expfname;
- f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
- if (f < 0)
- goto ecreate;
- break;
- case NAPPEND:
- fname = redir->nfile.expfname;
- f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
- if (f < 0)
- goto ecreate;
- break;
- default:
-#if DEBUG
- abort();
-#endif
- /* Fall through to eliminate warning. */
- case NTOFD:
- case NFROMFD:
- f = -1;
- break;
- case NHERE:
- case NXHERE:
- f = openhere(redir);
- break;
- }
+ struct strlist *sp;
+ volatile struct shparam saveparam;
+ int status = 0;
- return f;
- ecreate:
- ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
- eopen:
- ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
-}
+ for (sp = cmdenviron; sp; sp = sp->next)
+ setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
-static void
-dupredirect(union node *redir, int f)
-{
- int fd = redir->nfile.fd;
+ if (argv[1]) { /* That's what SVR2 does */
+ char *fullname = find_dot_file(argv[1]);
+ argv += 2;
+ argc -= 2;
+ if (argc) { /* argc > 0, argv[0] != NULL */
+ saveparam = shellparam;
+ shellparam.malloced = 0;
+ shellparam.nparam = argc;
+ shellparam.p = argv;
+ };
- if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
- if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
- copyfd(redir->ndup.dupfd, fd);
- }
- return;
- }
+ setinputfile(fullname, INPUT_PUSH_FILE);
+ commandname = fullname;
+ cmdloop(0);
+ popfile();
- if (f != fd) {
- copyfd(f, fd);
- close(f);
+ if (argc) {
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ };
+ status = exitstatus;
}
+ return status;
}
-
-/*
- * Process a list of redirection commands. If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
- */
-static void
-redirect(union node *redir, int flags)
+static int
+exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- union node *n;
- struct redirtab *sv;
- int i;
- int fd;
- int newfd;
- int *p;
- nullredirs++;
- if (!redir) {
- return;
- }
- sv = NULL;
- INT_OFF;
- if (flags & REDIR_PUSH) {
- struct redirtab *q;
- q = ckmalloc(sizeof(struct redirtab));
- q->next = redirlist;
- redirlist = q;
- q->nullredirs = nullredirs - 1;
- for (i = 0; i < 10; i++)
- q->renamed[i] = EMPTY;
- nullredirs = 0;
- sv = q;
- }
- n = redir;
- do {
- fd = n->nfile.fd;
- if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
- && n->ndup.dupfd == fd)
- continue; /* redirect from/to same file descriptor */
+ if (stoppedjobs())
+ return 0;
+ if (argv[1])
+ exitstatus = number(argv[1]);
+ raise_exception(EXEXIT);
+ /* NOTREACHED */
+}
- newfd = openredirect(n);
- if (fd == newfd)
- continue;
- if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
- i = fcntl(fd, F_DUPFD, 10);
+#if ENABLE_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
+{
+ return echo_main(argc, argv);
+}
+#endif
- if (i == -1) {
- i = errno;
- if (i != EBADF) {
- close(newfd);
- errno = i;
- ash_msg_and_raise_error("%d: %m", fd);
- /* NOTREACHED */
- }
- } else {
- *p = i;
- close(fd);
- }
- } else {
- close(fd);
- }
- dupredirect(n, newfd);
- } while ((n = n->nfile.next));
- INT_ON;
- if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
- preverrout_fd = sv->renamed[2];
+#if ENABLE_ASH_BUILTIN_TEST
+static int
+testcmd(int argc, char **argv)
+{
+ return test_main(argc, argv);
}
+#endif
/*
- * Undo the effects of the last redirection.
+ * Read a file containing shell functions.
*/
static void
-popredir(int drop)
+readcmdfile(char *name)
{
- struct redirtab *rp;
- int i;
-
- if (--nullredirs >= 0)
- return;
- INT_OFF;
- rp = redirlist;
- for (i = 0; i < 10; i++) {
- if (rp->renamed[i] != EMPTY) {
- if (!drop) {
- close(i);
- copyfd(rp->renamed[i], i);
- }
- close(rp->renamed[i]);
- }
- }
- redirlist = rp->next;
- nullredirs = rp->nullredirs;
- free(rp);
- INT_ON;
+ setinputfile(name, INPUT_PUSH_FILE);
+ cmdloop(0);
+ popfile();
}
-/*
- * Undo all redirections. Called on error or interrupt.
- */
+
+/* ============ find_command inplementation */
/*
- * Discard all saved file descriptors.
+ * Resolve a command name. If you change this routine, you may have to
+ * change the shellexec routine as well.
*/
static void
-clearredir(int drop)
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
{
- for (;;) {
- nullredirs = 0;
- if (!redirlist)
+ struct tblentry *cmdp;
+ int idx;
+ int prev;
+ char *fullname;
+ struct stat statb;
+ int e;
+ int updatetbl;
+ struct builtincmd *bcmd;
+
+ /* If name contains a slash, don't use PATH or hash table */
+ if (strchr(name, '/') != NULL) {
+ entry->u.index = -1;
+ if (act & DO_ABS) {
+ while (stat(name, &statb) < 0) {
+#ifdef SYSV
+ if (errno == EINTR)
+ continue;
+#endif
+ entry->cmdtype = CMDUNKNOWN;
+ return;
+ }
+ }
+ entry->cmdtype = CMDNORMAL;
+ return;
+ }
+
+/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
+
+ updatetbl = (path == pathval());
+ 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);
+ if (cmdp != NULL) {
+ int bit;
+
+ switch (cmdp->cmdtype) {
+ default:
+#if DEBUG
+ abort();
+#endif
+ case CMDNORMAL:
+ bit = DO_ALTPATH;
break;
- popredir(drop);
+ case CMDFUNCTION:
+ bit = DO_NOFUNC;
+ break;
+ case CMDBUILTIN:
+ bit = DO_ALTBLTIN;
+ break;
+ }
+ if (act & bit) {
+ updatetbl = 0;
+ cmdp = NULL;
+ } else if (cmdp->rehash == 0)
+ /* if not invalidated by cd, we're done */
+ goto success;
}
-}
-/*
- * Copy a file descriptor to be >= to. Returns -1
- * if the source file descriptor is closed, EMPTY if there are no unused
- * file descriptors left.
- */
-static int
-copyfd(int from, int to)
-{
- int newfd;
+ /* If %builtin not in path, check for builtin next */
+ bcmd = find_builtin(name);
+ 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) {
+ goto builtin_success;
+ }
+ }
- newfd = fcntl(from, F_DUPFD, to);
- if (newfd < 0) {
- if (errno == EMFILE)
- return EMPTY;
- ash_msg_and_raise_error("%d: %m", from);
+#if ENABLE_FEATURE_SH_STANDALONE
+ if (find_applet_by_name(name) >= 0) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -1;
+ return;
}
- return newfd;
-}
+#endif
-static int
-redirectsafe(union node *redir, int flags)
-{
- int err;
- volatile int saveint;
- struct jmploc *volatile savehandler = exception_handler;
- struct jmploc jmploc;
+ /* We have to search path. */
+ prev = -1; /* where to start */
+ if (cmdp && cmdp->rehash) { /* doing a rehash */
+ if (cmdp->cmdtype == CMDBUILTIN)
+ prev = builtinloc;
+ else
+ prev = cmdp->param.index;
+ }
- SAVE_INT(saveint);
- err = setjmp(jmploc.loc) * 2;
- if (!err) {
- exception_handler = &jmploc;
- redirect(redir, flags);
+ e = ENOENT;
+ idx = -1;
+ loop:
+ while ((fullname = padvance(&path, name)) != NULL) {
+ stunalloc(fullname);
+ /* NB: code below will still use fullname
+ * despite it being "unallocated" */
+ idx++;
+ if (pathopt) {
+ if (prefix(pathopt, "builtin")) {
+ if (bcmd)
+ goto builtin_success;
+ continue;
+ } else if (!(act & DO_NOFUNC)
+ && prefix(pathopt, "func")) {
+ /* handled below */
+ } else {
+ /* ignore unimplemented options */
+ continue;
+ }
+ }
+ /* if rehash, don't redo absolute path names */
+ if (fullname[0] == '/' && idx <= prev) {
+ if (idx < prev)
+ continue;
+ TRACE(("searchexec \"%s\": no change\n", name));
+ goto success;
+ }
+ while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+ if (errno == EINTR)
+ continue;
+#endif
+ if (errno != ENOENT && errno != ENOTDIR)
+ e = errno;
+ goto loop;
+ }
+ 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);
+ /* NB: stalloc will return space pointed by fullname
+ * (because we don't have any intervening allocations
+ * between stunalloc above and this stalloc) */
+ readcmdfile(fullname);
+ cmdp = cmdlookup(name, 0);
+ if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
+ ash_msg_and_raise_error("%s not defined in %s", name, fullname);
+ stunalloc(fullname);
+ goto success;
+ }
+ TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+ if (!updatetbl) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = idx;
+ return;
+ }
+ INT_OFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDNORMAL;
+ cmdp->param.index = idx;
+ INT_ON;
+ goto success;
}
- exception_handler = savehandler;
- if (err && exception != EXERROR)
- longjmp(exception_handler->loc, 1);
- RESTORE_INT(saveint);
- return err;
+
+ /* We failed. If there was an entry for this command, delete it */
+ if (cmdp && updatetbl)
+ delete_cmd_entry();
+ if (act & DO_ERR)
+ ash_msg("%s: %s", name, errmsg(e, "not found"));
+ entry->cmdtype = CMDUNKNOWN;
+ return;
+
+ builtin_success:
+ if (!updatetbl) {
+ entry->cmdtype = CMDBUILTIN;
+ entry->u.cmd = bcmd;
+ return;
+ }
+ INT_OFF;
+ cmdp = cmdlookup(name, 1);
+ cmdp->cmdtype = CMDBUILTIN;
+ cmdp->param.cmd = bcmd;
+ INT_ON;
+ success:
+ cmdp->rehash = 0;
+ entry->cmdtype = cmdp->cmdtype;
+ entry->u = cmdp->param;
}
* The trap builtin.
*/
static int
-trapcmd(int argc, char **argv)
+trapcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char *action;
char **ap;
else
action = ckstrdup(action);
}
- if (trap[signo])
- free(trap[signo]);
+ free(trap[signo]);
trap[signo] = action;
if (signo != 0)
setsignal(signo);
* Lists available builtins
*/
static int
-helpcmd(int argc, char **argv)
+helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int col, i;
out1fmt("\nBuilt-in commands:\n-------------------\n");
- for (col = 0, i = 0; i < NUMBUILTINS; i++) {
+ for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
- builtincmd[i].name + 1);
+ builtintab[i].name + 1);
if (col > 60) {
out1fmt("\n");
col = 0;
}
}
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- for (i = 0; i < NUM_APPLETS; i++) {
- col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
- if (col > 60) {
- out1fmt("\n");
- col = 0;
+#if ENABLE_FEATURE_SH_STANDALONE
+ {
+ const char *a = applet_names;
+ while (*a) {
+ col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
+ if (col > 60) {
+ out1fmt("\n");
+ col = 0;
+ }
+ a += strlen(a) + 1;
}
}
#endif
* The export and readonly commands.
*/
static int
-exportcmd(int argc, char **argv)
+exportcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
struct var *vp;
char *name;
* with the same name.
*/
static int
-unsetcmd(int argc, char **argv)
+unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char **ap;
int i;
#include <sys/times.h>
-static const unsigned char timescmd_str[] = {
+static const unsigned char timescmd_str[] ALIGN1 = {
' ', offsetof(struct tms, tms_utime),
'\n', offsetof(struct tms, tms_stime),
' ', offsetof(struct tms, tms_cutime),
};
static int
-timescmd(int ac, char **av)
+timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
long clk_tck, s, t;
const unsigned char *p;
* Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
*/
static int
-letcmd(int argc, char **argv)
+letcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- char **ap;
- arith_t i = 0;
+ arith_t i;
- ap = argv + 1;
- if (!*ap)
+ argv++;
+ if (!*argv)
ash_msg_and_raise_error("expression expected");
- for (ap = argv + 1; *ap; ap++) {
- i = dash_arith(*ap);
- }
+ do {
+ i = dash_arith(*argv);
+ } while (*++argv);
return !i;
}
* This uses unbuffered input, which may be avoidable in some cases.
*/
static int
-readcmd(int argc, char **argv)
+readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
char **ap;
int backslash;
int status;
int i;
#if ENABLE_ASH_READ_NCHARS
- int nch_flag = 0;
+ int n_flag = 0;
int nchars = 0;
int silent = 0;
struct termios tty, old_tty;
break;
#if ENABLE_ASH_READ_NCHARS
case 'n':
- nchars = strtol(optionarg, &p, 10);
- if (*p)
+ nchars = bb_strtou(optionarg, NULL, 10);
+ if (nchars < 0 || errno)
ash_msg_and_raise_error("invalid count");
- nch_flag = (nchars > 0);
+ n_flag = nchars; /* just a flag "nchars is nonzero" */
break;
case 's':
silent = 1;
#endif
#if ENABLE_ASH_READ_TIMEOUT
case 't':
- ts.tv_sec = strtol(optionarg, &p, 10);
+ ts.tv_sec = bb_strtou(optionarg, &p, 10);
ts.tv_usec = 0;
- if (*p == '.') {
+ /* EINVAL means number is ok, but not terminated by NUL */
+ if (*p == '.' && errno == EINVAL) {
char *p2;
if (*++p) {
int scale;
- ts.tv_usec = strtol(p, &p2, 10);
- if (*p2)
+ ts.tv_usec = bb_strtou(p, &p2, 10);
+ if (errno)
ash_msg_and_raise_error("invalid timeout");
scale = p2 - p;
/* normalize to usec */
while (scale++ < 6)
ts.tv_usec *= 10;
}
- } else if (*p) {
+ } else if (ts.tv_sec < 0 || errno) {
ash_msg_and_raise_error("invalid timeout");
}
- if ( ! ts.tv_sec && ! ts.tv_usec)
+ if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
ash_msg_and_raise_error("invalid timeout");
+ }
break;
#endif
case 'r':
if (ifs == NULL)
ifs = defifs;
#if ENABLE_ASH_READ_NCHARS
- if (nch_flag || silent) {
- tcgetattr(0, &tty);
- old_tty = tty;
- if (nch_flag) {
- tty.c_lflag &= ~ICANON;
- tty.c_cc[VMIN] = nchars;
- }
- if (silent) {
- tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
-
+ if (n_flag || silent) {
+ if (tcgetattr(0, &tty) != 0) {
+ /* Not a tty */
+ n_flag = 0;
+ silent = 0;
+ } else {
+ old_tty = tty;
+ if (n_flag) {
+ tty.c_lflag &= ~ICANON;
+ tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+ }
+ if (silent) {
+ tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
+ }
+ tcsetattr(0, TCSANOW, &tty);
}
- tcsetattr(0, TCSANOW, &tty);
}
#endif
#if ENABLE_ASH_READ_TIMEOUT
if (ts.tv_sec || ts.tv_usec) {
- FD_ZERO (&set);
- FD_SET (0, &set);
+ FD_ZERO(&set);
+ FD_SET(0, &set);
- i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
- if (!i) {
+ /* poll-based wait produces bigger code, using select */
+ i = select(1, &set, NULL, NULL, &ts);
+ if (!i) { /* timed out! */
#if ENABLE_ASH_READ_NCHARS
- if (nch_flag)
+ if (n_flag)
tcsetattr(0, TCSANOW, &old_tty);
#endif
return 1;
startword = 1;
backslash = 0;
STARTSTACKSTR(p);
-#if ENABLE_ASH_READ_NCHARS
- while (!nch_flag || nchars--)
-#else
- for (;;)
-#endif
- {
- if (read(0, &c, 1) != 1) {
+ do {
+ if (nonblock_safe_read(0, &c, 1) != 1) {
status = 1;
break;
}
STPUTC(c, p);
}
}
+/* end of do {} while: */
+#if ENABLE_ASH_READ_NCHARS
+ while (!n_flag || --nchars);
+#else
+ while (1);
+#endif
+
#if ENABLE_ASH_READ_NCHARS
- if (nch_flag || silent)
+ if (n_flag || silent)
tcsetattr(0, TCSANOW, &old_tty);
#endif
}
static int
-umaskcmd(int argc, char **argv)
+umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- static const char permuser[3] = "ugo";
- static const char permmode[3] = "rwx";
- static const short int permmask[] = {
+ static const char permuser[3] ALIGN1 = "ugo";
+ static const char permmode[3] ALIGN1 = "rwx";
+ static const short permmask[] ALIGN2 = {
S_IRUSR, S_IWUSR, S_IXUSR,
S_IRGRP, S_IWGRP, S_IXGRP,
S_IROTH, S_IWOTH, S_IXOTH
} else {
mask = ~mask & 0777;
if (!bb_parse_mode(ap, &mask)) {
- ash_msg_and_raise_error("Illegal mode: %s", ap);
+ ash_msg_and_raise_error("illegal mode: %s", ap);
}
umask(~mask & 0777);
}
*/
struct limits {
- const char *name;
- int cmd;
- int factor; /* multiply by to get rlim_{cur,max} values */
+ uint8_t cmd; /* RLIMIT_xxx fit into it */
+ uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
char option;
};
-static const struct limits limits[] = {
+static const struct limits limits_tbl[] = {
#ifdef RLIMIT_CPU
- { "time(seconds)", RLIMIT_CPU, 1, 't' },
+ { RLIMIT_CPU, 0, 't' },
#endif
#ifdef RLIMIT_FSIZE
- { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
+ { RLIMIT_FSIZE, 9, 'f' },
#endif
#ifdef RLIMIT_DATA
- { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
+ { RLIMIT_DATA, 10, 'd' },
#endif
#ifdef RLIMIT_STACK
- { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
+ { RLIMIT_STACK, 10, 's' },
#endif
-#ifdef RLIMIT_CORE
- { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
+#ifdef RLIMIT_CORE
+ { RLIMIT_CORE, 9, 'c' },
#endif
#ifdef RLIMIT_RSS
- { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
+ { RLIMIT_RSS, 10, 'm' },
#endif
#ifdef RLIMIT_MEMLOCK
- { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
+ { RLIMIT_MEMLOCK, 10, 'l' },
#endif
#ifdef RLIMIT_NPROC
- { "process", RLIMIT_NPROC, 1, 'p' },
+ { RLIMIT_NPROC, 0, 'p' },
#endif
#ifdef RLIMIT_NOFILE
- { "nofiles", RLIMIT_NOFILE, 1, 'n' },
+ { RLIMIT_NOFILE, 0, 'n' },
#endif
#ifdef RLIMIT_AS
- { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
+ { RLIMIT_AS, 10, 'v' },
#endif
#ifdef RLIMIT_LOCKS
- { "locks", RLIMIT_LOCKS, 1, 'w' },
+ { RLIMIT_LOCKS, 0, 'w' },
#endif
- { NULL, 0, 0, '\0' }
};
+static const char limits_name[] =
+#ifdef RLIMIT_CPU
+ "time(seconds)" "\0"
+#endif
+#ifdef RLIMIT_FSIZE
+ "file(blocks)" "\0"
+#endif
+#ifdef RLIMIT_DATA
+ "data(kb)" "\0"
+#endif
+#ifdef RLIMIT_STACK
+ "stack(kb)" "\0"
+#endif
+#ifdef RLIMIT_CORE
+ "coredump(blocks)" "\0"
+#endif
+#ifdef RLIMIT_RSS
+ "memory(kb)" "\0"
+#endif
+#ifdef RLIMIT_MEMLOCK
+ "locked memory(kb)" "\0"
+#endif
+#ifdef RLIMIT_NPROC
+ "process" "\0"
+#endif
+#ifdef RLIMIT_NOFILE
+ "nofiles" "\0"
+#endif
+#ifdef RLIMIT_AS
+ "vmemory(kb)" "\0"
+#endif
+#ifdef RLIMIT_LOCKS
+ "locks" "\0"
+#endif
+;
enum limtype { SOFT = 0x1, HARD = 0x2 };
if (val == RLIM_INFINITY)
out1fmt("unlimited\n");
else {
- val /= l->factor;
+ val >>= l->factor_shift;
out1fmt("%lld\n", (long long) val);
}
}
static int
-ulimitcmd(int argc, char **argv)
+ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
int c;
rlim_t val = 0;
what = optc;
}
- for (l = limits; l->option != what; l++)
- ;
+ for (l = limits_tbl; l->option != what; l++)
+ continue;
set = *argptr ? 1 : 0;
if (set) {
}
if (c)
ash_msg_and_raise_error("bad number");
- val *= l->factor;
+ val <<= l->factor_shift;
}
}
if (all) {
- for (l = limits; l->name; l++) {
+ const char *lname = limits_name;
+ for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
getrlimit(l->cmd, &limit);
- out1fmt("%-20s ", l->name);
+ out1fmt("%-20s ", lname);
+ lname += strlen(lname) + 1;
printlim(how, &limit, l);
}
return 0;
}
/* longest must be first */
-static const char op_tokens[] = {
+static const char op_tokens[] ALIGN1 = {
'<','<','=',0, TOK_LSHIFT_ASSIGN,
'>','>','=',0, TOK_RSHIFT_ASSIGN,
'<','<', 0, TOK_LSHIFT,
* Process the shell command line arguments.
*/
static void
-procargs(int argc, char **argv)
+procargs(char **argv)
{
int i;
const char *xminusc;
xargv = argv;
arg0 = xargv[0];
- if (argc > 0)
+ /* if (xargv[0]) - mmm, this is always true! */
xargv++;
for (i = 0; i < NOPTS; i++)
optlist[i] = 2;
argptr = xargv;
- options(1);
+ if (options(1)) {
+ /* it already printed err message */
+ raise_exception(EXERROR);
+ }
xargv = argptr;
xminusc = minusc;
if (*xargv == NULL) {
shellparam.optind = 1;
shellparam.optoff = -1;
#endif
- /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
+ /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
while (*xargv) {
shellparam.nparam++;
xargv++;
* exception occurs. When an exception occurs the variable "state"
* is used to figure out how far we had gotten.
*/
-int ash_main(int argc, char **argv);
-int ash_main(int argc, char **argv)
+int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ash_main(int argc ATTRIBUTE_UNUSED, char **argv)
{
char *shinit;
volatile int state;
struct jmploc jmploc;
struct stackmark smark;
-#ifdef __GLIBC__
- dash_errno = __errno_location();
+ /* Initialize global data */
+ INIT_G_misc();
+ INIT_G_memstack();
+ INIT_G_var();
+#if ENABLE_ASH_ALIAS
+ INIT_G_alias();
#endif
+ INIT_G_cmdtable();
#if PROFILE
monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
#endif
init();
setstackmark(&smark);
- procargs(argc, argv);
+ procargs(argv);
+
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
if (iflag) {
const char *hp = lookupvar("HISTFILE");