* 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)
"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 char *minusc; /* argument to -c option */
-
-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 */
-
-
-/* ============ Interrupts / exceptions */
+static const char homestr[] ALIGN1 = "HOME";
+static const char snlfmt[] ALIGN1 = "%s\n";
+static const char illnum[] ALIGN1 = "Illegal number: %s";
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
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 pendingsig;
-
-/*
- * 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. :-))
*/
raise_interrupt(void)
{
int i;
- sigset_t mask;
intpending = 0;
- /* Signal is not automatically re-enabled after it is raised,
- * do it ourself */
- sigemptyset(&mask);
- sigprocmask(SIG_SETMASK, &mask, 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);
}
raise_interrupt(); \
} while (0)
-#define EXSIGON \
- do { \
- exsig++; \
- xbarrier(); \
- if (pendingsig) \
- raise_exception(EXSIG); \
- } while (0)
-/* EXSIG is turned off by evalbltin(). */
-
/*
* Ignore a signal. Only one usage site - in forkchild()
*/
gotsig[signo - 1] = 1;
pendingsig = signo;
- if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+ if ( /* exsig || */ (signo == SIGINT && !trap[SIGINT])) {
if (!suppressint) {
pendingsig = 0;
- raise_interrupt();
+ raise_interrupt(); /* does not return */
}
intpending = 1;
}
#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
#define VSLENGTH 0xa /* ${#var} */
-static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+static const char dolatstr[] ALIGN1 = {
+ CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
+};
#define NCMD 0
#define NPIPE 1
}
}
#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);
* 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;
}
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 (;;) {
{
while (*pfx) {
if (*pfx++ != *string++)
- return 0;
+ return NULL;
}
return (char *) string;
}
/* ============ Shell variables */
-/* 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
-
-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";
-#endif
+/*
+ * 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 */
-
/*
* Free the list of positional parameters.
*/
{
char **ap;
- if (param->malloc) {
+ if (param->malloced) {
for (ap = param->p; *ap; ap++)
free(*ap);
free(param->p);
}
#if ENABLE_ASH_GETOPTS
-static void
-getoptsreset(const char *value)
-{
- shellparam.optind = number(value);
- shellparam.optoff = -1;
-}
+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;
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 */
/* Syntax classes */
#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
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, /* "=>?[\\]`|" */
#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;
};
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 */
* 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;
}
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;
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;
}
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
*/
#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
int i;
int fd;
int newfd;
- int *p;
- nullredirs++;
+
+ g_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;
+ sv = ckmalloc(sizeof(*sv));
+ sv->next = redirlist;
+ redirlist = sv;
+ sv->nullredirs = g_nullredirs - 1;
for (i = 0; i < 10; i++)
- q->renamed[i] = EMPTY;
- nullredirs = 0;
- sv = q;
+ sv->renamed[i] = EMPTY;
+ g_nullredirs = 0;
}
n = redir;
do {
continue; /* redirect from/to same file descriptor */
newfd = openredirect(n);
- if (fd == newfd)
+ 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 && *(p = &sv->renamed[fd]) == EMPTY) {
+ }
+ if (sv && sv->renamed[fd] == EMPTY) {
i = fcntl(fd, F_DUPFD, 10);
if (i == -1) {
/* NOTREACHED */
}
} else {
- *p = i;
+ sv->renamed[fd] = i;
close(fd);
}
} else {
dupredirect(n, newfd);
} while ((n = n->nfile.next));
INT_ON;
- if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+ if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
preverrout_fd = sv->renamed[2];
}
struct redirtab *rp;
int i;
- if (--nullredirs >= 0)
+ 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 (rp->renamed[i] != EMPTY) {
if (!drop) {
close(i);
}
}
redirlist = rp->next;
- nullredirs = rp->nullredirs;
+ g_nullredirs = rp->nullredirs;
free(rp);
INT_ON;
}
clearredir(int drop)
{
for (;;) {
- nullredirs = 0;
+ g_nullredirs = 0;
if (!redirlist)
break;
popredir(drop);
static char *
_rmescapes(char *str, int flag)
{
+ static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
+
char *p, *q, *r;
- static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
unsigned inquotes;
int notescaped;
int globbing;
ifsp = &ifsfirst;
} else {
INT_OFF;
- ifsp = ckmalloc(sizeof(*ifsp));
- ifsp->next = NULL;
+ ifsp = ckzalloc(sizeof(*ifsp));
+ /*ifsp->next = NULL; - ckzalloc did it */
ifslastp->next = ifsp;
INT_ON;
}
if (pipe(pip) < 0)
ash_msg_and_raise_error("pipe call failed");
- jp = makejob(n, 1);
+ 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;
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 */
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
-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;
-}
-#endif
-
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;
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE
if (strchr(cmd, '/') == NULL) {
- struct BB_applet *a;
- char **c;
-
- a = find_applet_by_name(cmd);
- if (a) {
- if (is_safe_applet(cmd)) {
- c = argv;
- while (*c)
- c++;
- applet_name = cmd;
- exit(a->main(c - argv, argv));
- }
+ 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(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
+ execve(bb_busybox_exec_path, argv, envp);
/* If they called chroot or otherwise made the binary no longer
* executable, fall through */
}
;
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], '/')
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- || find_applet_by_name(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)
findkwd(const char *s)
{
return bsearch(s, tokname_array + KWDOFFSET,
- (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
- sizeof(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;
pendingsig = 0;
skip = evalstring(p, SKIPEVAL);
exitstatus = savestatus;
if (skip)
- break;
+ return skip;
}
- return skip;
+ return 0;
}
/* forward declarations - evaluation is fairly recursive business... */
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 !ENABLE_FEATURE_SH_EXTRA_QUIET
if (is_interactive > 1) {
/* Looks like they want an interactive shell */
- static smallint do_banner;
+ static smallint did_banner;
- if (!do_banner) {
+ if (!did_banner) {
out1fmt(
"\n\n"
- "%s Built-in shell (ash)\n"
+ "%s built-in shell (ash)\n"
"Enter 'help' for a list of built-in commands."
"\n\n",
- BB_BANNER);
- do_banner = 1;
+ bb_banner);
+ did_banner = 1;
}
}
#endif
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;
}
static int
-falsecmd(int argc, char **argv)
+falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
return 1;
}
static int
-truecmd(int argc, char **argv)
+truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
return 0;
}
static int
-execcmd(int argc, char **argv)
+execcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
- if (argc > 1) {
+ if (argv[1]) {
iflag = 0; /* exit on error */
mflag = 0;
optschanged();
* The return command.
*/
static int
-returncmd(int argc, char **argv)
+returncmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
/*
* If called outside a function, do what ksh does;
{ BUILTIN_REGULAR "wait", waitcmd },
};
-#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
#define COMMANDCMD (builtintab + 5 + \
2 * ENABLE_ASH_BUILTIN_TEST + \
struct builtincmd *bp;
bp = bsearch(
- name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
+ 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;
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 = pendingsig;
- 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;
* 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]);
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 */
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;
}
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;
/* 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 */
}
*ap = NULL;
freeparam(&shellparam);
- shellparam.malloc = 1;
+ shellparam.malloced = 1;
shellparam.nparam = nparam;
shellparam.p = newparam;
#if ENABLE_ASH_GETOPTS
/*
* 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 void
+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)
ash_msg_and_raise_error("illegal option -%c", flag);
/* NOTREACHED */
}
-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;
}
}
}
+ 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;
* 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
/* ============ 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
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 {
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;
int typeloc;
int flags;
char *p;
- static const char types[] = "}-+?=";
+ static const char types[] ALIGN1 = "}-+?=";
c = pgetc();
if (
*/
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;
/* XXX Fix (char *) cast. */
setinputstring((char *)ps);
- readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+ readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
popfile();
n.narg.type = NARG;
* 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);
}
numeof++;
} else if (nflag == 0) {
- job_warning = (job_warning == 2) ? 1 : 0;
+ /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
+ job_warning >>= 1;
numeof = 0;
evaltree(n, 0);
}
for (sp = cmdenviron; sp; sp = sp->next)
setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
- if (argc >= 2) { /* That's what SVR2 does */
- char *fullname;
-
- fullname = find_dot_file(argv[1]);
-
- if (argc > 2) {
+ 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.malloc = 0;
- shellparam.nparam = argc - 2;
- shellparam.p = argv + 2;
+ shellparam.malloced = 0;
+ shellparam.nparam = argc;
+ shellparam.p = argv;
};
setinputfile(fullname, INPUT_PUSH_FILE);
cmdloop(0);
popfile();
- if (argc > 2) {
+ if (argc) {
freeparam(&shellparam);
shellparam = saveparam;
};
}
static int
-exitcmd(int argc, char **argv)
+exitcmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
if (stoppedjobs())
return 0;
- if (argc > 1)
+ if (argv[1])
exitstatus = number(argv[1]);
raise_exception(EXEXIT);
/* NOTREACHED */
static int
echocmd(int argc, char **argv)
{
- return bb_echo(argv);
+ return echo_main(argc, argv);
}
#endif
static int
testcmd(int argc, char **argv)
{
- return bb_test(argc, argv);
+ return test_main(argc, argv);
}
#endif
return;
}
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }
- /* Already caught above
- if (is_safe_applet(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }*/
-#endif
+/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
updatetbl = (path == pathval());
if (!updatetbl) {
}
}
+#if ENABLE_FEATURE_SH_STANDALONE
+ if (find_applet_by_name(name) >= 0) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -1;
+ return;
+ }
+#endif
+
/* We have to search path. */
prev = -1; /* where to start */
if (cmdp && cmdp->rehash) { /* doing a rehash */
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")) {
+ } else if (!(act & DO_NOFUNC)
+ && prefix(pathopt, "func")) {
/* handled below */
} else {
/* ignore unimplemented options */
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)
* 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' : ' '),
builtintab[i].name + 1);
if (col > 60) {
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
- if (nch_flag || silent)
+ while (!n_flag || --nchars);
+#else
+ while (1);
+#endif
+
+#if ENABLE_ASH_READ_NCHARS
+ 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
*/
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");