* 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" /* for struct bb_applet */
+#endif
+
+#include "busybox.h" /* for applet_names */
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
#if JOBS || ENABLE_ASH_READ_NCHARS
#include <termios.h>
#endif
-extern char **environ;
#if defined(__uClinux__)
#error "Do not even bother, ash will not run on uClinux"
#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
};
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 */
-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 */
+ smallint isloginsh;
+ char *trap[NSIG];
+ 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 isloginsh (G_misc.isloginsh)
+#define trap (G_misc.trap )
+#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 VSQUOTE 0x80 /* inside double quotes--suppress splitting */
/* values of VSTYPE field */
-#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
-#define VSMINUS 0x2 /* ${var-text} */
-#define VSPLUS 0x3 /* ${var+text} */
-#define VSQUESTION 0x4 /* ${var?message} */
-#define VSASSIGN 0x5 /* ${var=text} */
-#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
-#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
-#define VSTRIMLEFT 0x8 /* ${var#pattern} */
-#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
-#define VSLENGTH 0xa /* ${#var} */
-
-static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
+#define VSMINUS 0x2 /* ${var-text} */
+#define VSPLUS 0x3 /* ${var+text} */
+#define VSQUESTION 0x4 /* ${var?message} */
+#define VSASSIGN 0x5 /* ${var=text} */
+#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
+#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
+#define VSTRIMLEFT 0x8 /* ${var#pattern} */
+#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
+#define VSLENGTH 0xa /* ${#var} */
+#if ENABLE_ASH_BASH_COMPAT
+#define VSSUBSTR 0xc /* ${var:position:length} */
+#define VSREPLACE 0xd /* ${var/pattern/replacement} */
+#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
+#endif
+
+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
};
static struct parsefile basepf; /* top level input file */
-static struct parsefile *parsefile = &basepf; /* current input file */
+static struct parsefile *g_parsefile = &basepf; /* current input file */
static int startlinno; /* line # where last token started */
static char *commandname; /* currently executing command */
static struct strlist *cmdenviron; /* environment for builtin command */
if (commandname) {
if (strcmp(arg0, commandname))
fprintf(stderr, "%s: ", commandname);
- if (!iflag || parsefile->fd)
+ if (!iflag || g_parsefile->fd)
fprintf(stderr, "line %d: ", startlinno);
}
vfprintf(stderr, msg, ap);
* 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)) {
- write(2, "stunalloc\n", 10);
+ if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
+ write(STDERR_FILENO, "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;
+ size_t 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;
}
/*
return stackblock();
}
growstackblock();
- return stackblock() + len;
+ return (char *)stackblock() + 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 (;;) {
break;
growstackblock();
}
- return stackblock() + len;
+ return (char *)stackblock() + len;
}
static char *
stack_nputstr(const char *s, size_t n, char *p)
{
p = makestrspace(n, p);
- p = memcpy(p, s, n) + n;
+ p = (char *)memcpy(p, s, n) + n;
return p;
}
if (l > m) \
(p) = makestrspace(l, q); \
} while (0)
-#define USTPUTC(c, p) (*p++ = (c))
+#define USTPUTC(c, p) (*(p)++ = (c))
#define STACKSTRNUL(p) \
do { \
if ((p) == sstrend) \
- p = growstackstr(); \
- *p = '\0'; \
+ (p) = growstackstr(); \
+ *(p) = '\0'; \
} while (0)
-#define STUNPUTC(p) (--p)
-#define STTOPC(p) (p[-1])
-#define STADJUST(amount, p) (p += (amount))
+#define STUNPUTC(p) (--(p))
+#define STTOPC(p) ((p)[-1])
+#define STADJUST(amount, p) ((p) += (amount))
#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
-#define ungrabstackstr(s, p) stunalloc((s))
+#define ungrabstackstr(s, p) stunalloc(s)
#define stackstrend() ((void *)sstrend)
{
while (*pfx) {
if (*pfx++ != *string++)
- return 0;
+ return NULL;
}
return (char *) string;
}
q = p = makestrspace(len + 3, p);
*q++ = '\'';
- q = memcpy(q, s, len) + len;
+ q = (char *)memcpy(q, s, len) + len;
*q++ = '\'';
s += len;
q = p = makestrspace(len + 3, p);
*q++ = '"';
- q = memcpy(q, s, len) + len;
+ q = (char *)memcpy(q, s, len) + len;
*q++ = '"';
s += len;
return '\0';
}
c = *p++;
- for (q = optstring; *q != c; ) {
+ for (q = optstring; *q != c;) {
if (*q == '\0')
ash_msg_and_raise_error("illegal option -%c", c);
if (*++q == ':')
/* ============ 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
-
-#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, bb_PATH_root_path, 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 { \
+ unsigned 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 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;
-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).
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)))
}
INT_OFF;
nameeq = ckmalloc(namelen + vallen + 2);
- p = memcpy(nameeq, name, namelen) + namelen;
+ p = (char *)memcpy(nameeq, name, namelen) + namelen;
if (val) {
*p++ = '=';
- p = memcpy(p, val, vallen) + vallen;
+ p = (char *)memcpy(p, val, vallen) + vallen;
}
*p = '\0';
setvareq(nameeq, flags | VNOSAVE);
if (*path == NULL)
return NULL;
start = *path;
- for (p = start; *p && *p != ':' && *p != '%'; p++);
+ for (p = start; *p && *p != ':' && *p != '%'; p++)
+ continue;
len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
while (stackblocksize() < len)
growstackblock();
pathopt = NULL;
if (*p == '%') {
pathopt = ++p;
- while (*p && *p != ':') p++;
+ while (*p && *p != ':')
+ p++;
}
if (*p == ':')
*path = p + 1;
/* ============ 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)
{
new = stack_putstr(curdir, new);
}
new = makestrspace(strlen(dir) + 2, new);
- lim = stackblock() + 1;
+ lim = (char *)stackblock() + 1;
if (*dir != '/') {
if (new[-1] != '/')
USTPUTC('/', new);
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, /* "=>?[\\]`|" */
) {
return CCTL;
} else {
- s = strchr(spec_symbls, c);
- if (s == NULL || *s == '\0')
+ s = strchrnul(spec_symbls, c);
+ if (*s == '\0')
return CWORD;
- indx = syntax_index_table[(s - spec_symbls)];
+ indx = syntax_index_table[s - spec_symbls];
}
return S_I_T[indx][syntax];
}
#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);
+#if !JOBS
+#define forkshell(job, node, mode) forkshell(job, mode)
+#endif
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
-#if ! JOBS
-#define setjobctl(on) /* do nothing */
+#if !JOBS
+enum { doing_jobctl = 0 };
+#define setjobctl(on) do {} while (0)
#else
+static smallint doing_jobctl;
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 */
/*
* Convert a job name to a job structure.
*/
+#if !JOBS
+#define getjob(name, getctl) getjob(name)
+#endif
static struct job *
getjob(const char *name, int getctl)
{
}
if (is_number(p)) {
+// TODO: number() instead? It does error checking...
num = atoi(p);
if (num < njobs) {
jp = jobtab + num - 1;
int fd;
int pgrp;
- if (on == jobctl || rootshell == 0)
+ if (on == doing_jobctl || rootshell == 0)
return;
if (on) {
int ofd;
* 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) {
fd = ttyfd;
pgrp = initialpgrp;
/* was xtcsetpgrp, but this can make exiting ash
- * with pty already deleted loop forever */
+ * 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;
- jobctl = on;
+ doing_jobctl = on;
}
static int
killcmd(int argc, char **argv)
{
+ int i = 1;
if (argv[1] && strcmp(argv[1], "-l") != 0) {
- int i = 1;
do {
if (argv[i][0] == '%') {
struct job *jp = getjob(argv[i], 0);
}
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;
+ if (doing_jobctl)
+ 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);
}
}
}
}
+/*
+ * 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;
if (jp->state != JOBDONE || !jp->waited)
continue;
#if JOBS
- if (jobctl)
+ if (doing_jobctl)
continue;
#endif
freejob(jp);
}
memset(jp, 0, sizeof(*jp));
#if JOBS
- if (jobctl)
+ /* jp->jobctl is a bitfield.
+ * "jp->jobctl |= jobctl" likely to give awful code */
+ if (doing_jobctl)
jp->jobctl = 1;
#endif
jp->prev_job = curjob;
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;
}
static void
cmdputs(const char *s)
{
+ static const char vstype[VSTYPE + 1][3] = {
+ "", "}", "-", "+", "?", "=",
+ "%", "%%", "#", "##"
+ USE_ASH_BASH_COMPAT(, ":", "/", "//")
+ };
+
const char *p, *str;
char c, cc[2] = " ";
char *nextc;
int subtype = 0;
int quoted = 0;
- static const char vstype[VSTYPE + 1][4] = {
- "", "}", "-", "+", "?", "=",
- "%", "%%", "#", "##"
- };
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
p = s;
while ((c = *p++) != 0) {
- str = 0;
+ str = NULL;
switch (c) {
case CTLESC:
c = *p++;
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;
clear_traps();
#if JOBS
/* do job control only in root shell */
- jobctl = 0;
+ doing_jobctl = 0;
if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
pid_t pgrp;
jobless = 0;
}
+/* Called after fork(), in parent */
+#if !JOBS
+#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
+#endif
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;
}
ps->status = -1;
ps->cmd = nullstr;
#if JOBS
- if (jobctl && n)
+ if (doing_jobctl && n)
ps->cmd = commandtext(n);
#endif
}
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
full_write(pip[1], redir->nhere.doc->narg.text, len);
else
expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
out:
close(pip[1]);
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[] = { CTLESC, CTLQUOTEMARK, '\0' };
+ static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
char *p, *q, *r;
unsigned inquotes;
}
q = r;
if (len > 0) {
- q = memcpy(q, str, len) + len;
+ q = (char *)memcpy(q, str, len) + len;
}
}
inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
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;
}
*/
struct backcmd { /* result of evalbackcmd */
int fd; /* file descriptor to read from */
- char *buf; /* buffer */
int nleft; /* number of chars in buffer */
+ char *buf; /* buffer */
struct job *jp; /* job structure for command */
};
/* These forward decls are needed to use "eval" code for backticks handling: */
-static int back_exitstatus; /* exit status of backquoted command */
+static smalluint back_exitstatus; /* exit status of backquoted command */
#define EV_EXIT 01 /* exit after evaluating tree */
static void evaltree(union node *, int);
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]);
char *p;
char *dest;
int startloc;
- int syntax = quoted? DQSYNTAX : BASESYNTAX;
+ int syntax = quoted ? DQSYNTAX : BASESYNTAX;
struct stackmark smark;
INT_OFF;
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;
- char *loc2;
+// This commented out code was added by James Simmons <jsimmons@infradead.org>
+// as part of a larger change when he added support for ${var/a/b}.
+// However, it broke # and % operators:
+//
+//var=ababcdcd
+// ok bad
+//echo ${var#ab} abcdcd abcdcd
+//echo ${var##ab} abcdcd abcdcd
+//echo ${var#a*b} abcdcd ababcdcd (!)
+//echo ${var##a*b} cdcd cdcd
+//echo ${var#?} babcdcd ababcdcd (!)
+//echo ${var##?} babcdcd babcdcd
+//echo ${var#*} ababcdcd babcdcd (!)
+//echo ${var##*}
+//echo ${var%cd} ababcd ababcd
+//echo ${var%%cd} ababcd abab (!)
+//echo ${var%c*d} ababcd ababcd
+//echo ${var%%c*d} abab ababcdcd (!)
+//echo ${var%?} ababcdc ababcdc
+//echo ${var%%?} ababcdc ababcdcd (!)
+//echo ${var%*} ababcdcd ababcdcd
+//echo ${var%%*}
+//
+// Commenting it back out helped. Remove it completely if it really
+// is not needed.
+
+ char *loc, *loc2; //, *full;
char c;
loc = startp;
loc2 = rmesc;
do {
- int match;
+ int match; // = strlen(str);
const char *s = loc2;
+
c = *loc2;
if (zero) {
*loc2 = '\0';
s = rmesc;
}
- match = pmatch(str, s);
+ match = pmatch(str, s); // this line was deleted
+
+// // chop off end if its '*'
+// full = strrchr(str, '*');
+// if (full && full != str)
+// match--;
+//
+// // If str starts with '*' replace with s.
+// if ((*str == '*') && strlen(s) >= match) {
+// full = xstrdup(s);
+// strncpy(full+strlen(s)-match+1, str+1, match-1);
+// } else
+// full = xstrndup(str, match);
+// match = strncmp(s, full, strlen(full));
+// free(full);
+//
*loc2 = c;
- if (match)
+ if (match) // if (!match)
return loc;
if (quotes && *loc == CTLESC)
loc++;
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
-static const char *
-subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
+#if ENABLE_ASH_BASH_COMPAT
+static char *
+parse_sub_pattern(char *arg, int inquotes)
+{
+ char *idx, *repl = NULL;
+ unsigned char c;
+
+ idx = arg;
+ while (1) {
+ c = *arg;
+ if (!c)
+ break;
+ if (c == '/') {
+ /* Only the first '/' seen is our separator */
+ if (!repl) {
+ repl = idx + 1;
+ c = '\0';
+ }
+ }
+ *idx++ = c;
+ if (!inquotes && c == '\\' && arg[1] == '\\')
+ arg++; /* skip both \\, not just first one */
+ arg++;
+ }
+ *idx = c; /* NUL */
+
+ return repl;
+}
+#endif /* ENABLE_ASH_BASH_COMPAT */
+
+static const char *
+subevalvar(char *p, char *str, int strloc, int subtype,
+ int startloc, int varflags, int quotes, struct strlist *var_str_list)
{
- char *startp;
- char *loc;
- int saveherefd = herefd;
struct nodelist *saveargbackq = argbackq;
- int amount;
+ char *startp;
+ char *loc;
char *rmesc, *rmescend;
+ USE_ASH_BASH_COMPAT(char *repl = NULL;)
+ USE_ASH_BASH_COMPAT(char null = '\0';)
+ USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
+ int saveherefd = herefd;
+ int amount, workloc, resetloc;
int zero;
- char *(*scan)(char *, char *, char *, char *, int , int);
+ 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;
- startp = stackblock() + startloc;
+ startp = (char *)stackblock() + startloc;
switch (subtype) {
case VSASSIGN:
STADJUST(amount, expdest);
return startp;
+#if ENABLE_ASH_BASH_COMPAT
+ case VSSUBSTR:
+ loc = str = stackblock() + strloc;
+// TODO: number() instead? It does error checking...
+ pos = atoi(loc);
+ len = str - startp - 1;
+
+ /* *loc != '\0', guaranteed by parser */
+ if (quotes) {
+ char *ptr;
+
+ /* We must adjust the length by the number of escapes we find. */
+ for (ptr = startp; ptr < (str - 1); ptr++) {
+ if(*ptr == CTLESC) {
+ len--;
+ ptr++;
+ }
+ }
+ }
+ orig_len = len;
+
+ if (*loc++ == ':') {
+// TODO: number() instead? It does error checking...
+ len = atoi(loc);
+ } else {
+ len = orig_len;
+ while (*loc && *loc != ':')
+ loc++;
+ if (*loc++ == ':')
+// TODO: number() instead? It does error checking...
+ len = atoi(loc);
+ }
+ if (pos >= orig_len) {
+ pos = 0;
+ len = 0;
+ }
+ if (len > (orig_len - pos))
+ len = orig_len - pos;
+
+ for (str = startp; pos; str++, pos--) {
+ if (quotes && *str == CTLESC)
+ str++;
+ }
+ for (loc = startp; len; len--) {
+ if (quotes && *str == CTLESC)
+ *loc++ = *str++;
+ *loc++ = *str++;
+ }
+ *loc = '\0';
+ amount = loc - expdest;
+ STADJUST(amount, expdest);
+ return loc;
+#endif
+
case VSQUESTION:
varunset(p, str, startp, varflags);
/* NOTREACHED */
}
+ resetloc = expdest - (char *)stackblock();
- subtype -= VSTRIMRIGHT;
-#if DEBUG
- if (subtype < 0 || subtype > 3)
- abort();
-#endif
+ /* We'll comeback here if we grow the stack while handling
+ * a VSREPLACE or VSREPLACEALL, since our pointers into the
+ * stack will need rebasing, and we'll need to remove our work
+ * areas each time
+ */
+ USE_ASH_BASH_COMPAT(restart:)
+
+ amount = expdest - ((char *)stackblock() + resetloc);
+ STADJUST(-amount, expdest);
+ startp = (char *)stackblock() + startloc;
rmesc = startp;
- rmescend = stackblock() + strloc;
+ rmescend = (char *)stackblock() + strloc;
if (quotes) {
rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
if (rmesc != startp) {
rmescend = expdest;
- startp = stackblock() + startloc;
+ startp = (char *)stackblock() + startloc;
}
}
rmescend--;
- str = stackblock() + strloc;
+ str = (char *)stackblock() + strloc;
preglob(str, varflags & VSQUOTE, 0);
+ workloc = expdest - (char *)stackblock();
+
+#if ENABLE_ASH_BASH_COMPAT
+ if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+ char *idx, *end, *restart_detect;
+
+ if(!repl) {
+ repl = parse_sub_pattern(str, varflags & VSQUOTE);
+ if (!repl)
+ repl = &null;
+ }
+
+ /* If there's no pattern to match, return the expansion unmolested */
+ if (*str == '\0')
+ return 0;
+
+ len = 0;
+ idx = startp;
+ end = str - 1;
+ while (idx < end) {
+ loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
+ if (!loc) {
+ /* No match, advance */
+ restart_detect = stackblock();
+ STPUTC(*idx, expdest);
+ if (quotes && *idx == CTLESC) {
+ idx++;
+ len++;
+ STPUTC(*idx, expdest);
+ }
+ if (stackblock() != restart_detect)
+ goto restart;
+ idx++;
+ len++;
+ rmesc++;
+ continue;
+ }
+ if (subtype == VSREPLACEALL) {
+ while (idx < loc) {
+ if (quotes && *idx == CTLESC)
+ idx++;
+ idx++;
+ rmesc++;
+ }
+ } else
+ idx = loc;
+
+ for (loc = repl; *loc; loc++) {
+ restart_detect = stackblock();
+ STPUTC(*loc, expdest);
+ if (stackblock() != restart_detect)
+ goto restart;
+ len++;
+ }
+
+ if (subtype == VSREPLACE) {
+ while (*idx) {
+ restart_detect = stackblock();
+ STPUTC(*idx, expdest);
+ if (stackblock() != restart_detect)
+ goto restart;
+ len++;
+ idx++;
+ }
+ break;
+ }
+ }
+
+ /* We've put the replaced text into a buffer at workloc, now
+ * move it to the right place and adjust the stack.
+ */
+ startp = stackblock() + startloc;
+ STPUTC('\0', expdest);
+ memmove(startp, stackblock() + workloc, len);
+ startp[len++] = '\0';
+ amount = expdest - ((char *)stackblock() + startloc + len - 1);
+ STADJUST(-amount, expdest);
+ return startp;
+ }
+#endif /* ENABLE_ASH_BASH_COMPAT */
+
+ subtype -= VSTRIMRIGHT;
+#if DEBUG
+ if (subtype < 0 || subtype > 7)
+ abort();
+#endif
/* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
zero = subtype >> 1;
/* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
* 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;
case '7':
case '8':
case '9':
+// TODO: number() instead? It does error checking...
num = atoi(name);
if (num < 0 || num > shellparam.nparam)
return -1;
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 == (unsigned)(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;
}
case VSTRIMLEFTMAX:
case VSTRIMRIGHT:
case VSTRIMRIGHTMAX:
+#if ENABLE_ASH_BASH_COMPAT
+ case VSSUBSTR:
+ case VSREPLACE:
+ case VSREPLACEALL:
+#endif
break;
default:
abort();
*/
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;
q = p;
if (*p == CTLESC)
p++;
- if (strchr(ifs, *p) == NULL ) {
+ 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)) {
return list;
half = len >> 1;
p = list;
- for (n = half; --n >= 0; ) {
+ for (n = half; --n >= 0;) {
q = p;
p = p->next;
}
}
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);
/* 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;
+ smallint cmdtype; /* CMDxxx */
union param {
int index;
+ /* index >= 0 for commands without path (slashes) */
+ /* (TODO: what exactly does the value mean? PATH position?) */
+ /* index == -1 for commands with slashes */
+ /* index == (-2 - applet_no) for NOFORK applets */
const struct builtincmd *cmd;
struct funcnode *func;
} u;
* 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 {
struct tblentry *next; /* next entry in hash chain */
union param param; /* definition of builtin function */
- short cmdtype; /* index identifying command */
+ smallint cmdtype; /* CMDxxx */
char rehash; /* if set, cd done since entry created */
- char cmdname[ARB]; /* name of command */
+ char cmdname[1]; /* 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)
+tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
{
int repeated = 0;
#if ENABLE_FEATURE_SH_STANDALONE
- if (strchr(cmd, '/') == NULL) {
- const struct bb_applet *a;
-
- a = find_applet_by_name(cmd);
- if (a) {
- if (a->noexec) {
- current_applet = a;
- run_current_applet_and_exit(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 */
- }
+ if (applet_no >= 0) {
+ if (APPLET_IS_NOEXEC(applet_no))
+ run_applet_no_and_exit(applet_no, 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 */
}
#endif
#else
execve(cmd, argv, envp);
#endif
- if (repeated++) {
+ if (repeated) {
free(argv);
- } else if (errno == ENOEXEC) {
+ return;
+ }
+ if (errno == ENOEXEC) {
char **ap;
char **new;
for (ap = argv; *ap; ap++)
- ;
- ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
+ continue;
+ ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0]));
ap[1] = cmd;
ap[0] = cmd = (char *)DEFAULT_SHELL;
ap += 2;
argv++;
- while ((*ap++ = *argv++))
- ;
+ while ((*ap++ = *argv++) != NULL)
+ continue;
argv = new;
+ repeated++;
goto repeat;
}
}
* Exec a program. Never returns. If you change this routine, you may
* have to change the find_command routine as well.
*/
-#define environment() listvars(VEXPORT, VUNSET, 0)
static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
static void
shellexec(char **argv, const char *path, int idx)
int e;
char **envp;
int exerrno;
+#if ENABLE_FEATURE_SH_STANDALONE
+ int applet_no = -1;
+#endif
clearredir(1);
- envp = environment();
- if (strchr(argv[0], '/')
+ envp = listvars(VEXPORT, VUNSET, 0);
+ if (strchr(argv[0], '/') != NULL
#if ENABLE_FEATURE_SH_STANDALONE
- || find_applet_by_name(argv[0])
+ || (applet_no = find_applet_by_name(argv[0])) >= 0
#endif
) {
- tryexec(argv[0], argv, envp);
+ tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
e = errno;
} else {
e = ENOENT;
while ((cmdname = padvance(&path, argv[0])) != NULL) {
if (--idx < 0 && pathopt == NULL) {
- tryexec(cmdname, argv, envp);
+ tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
if (errno != ENOENT && errno != ENOTDIR)
e = errno;
}
}
exitstatus = exerrno;
TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
- argv[0], e, suppressint ));
+ argv[0], e, suppressint));
ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
/* NOTREACHED */
}
pp = &cmdp->next;
}
if (add && cmdp == NULL) {
- cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB
- + strlen(name) + 1);
- cmdp->next = NULL;
+ cmdp = *pp = ckzalloc(sizeof(struct tblentry)
+ + strlen(name)
+ /* + 1 - already done because
+ * tblentry::cmdname is char[1] */);
+ /*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)
#define TWHILE 26
#define TBEGIN 27
#define TEND 28
+typedef smallint token_id_t;
/* first char is indicating which tokens mark the end of a list */
static const char *const tokname_array[] = {
case CMDNORMAL: {
int j = entry.u.index;
char *p;
- if (j == -1) {
+ if (j < 0) {
p = command;
} else {
do {
}
static int
-typecmd(int argc, char **argv)
+typecmd(int argc ATTRIBUTE_UNUSED, char **argv)
{
int i = 1;
int err = 0;
i++;
verbose = 0;
}
- while (i < argc) {
+ 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 {
#define SKIPEVAL (1 << 4)
static int skipcount; /* number of levels to skip */
static int funcnest; /* depth of function calls */
+static int loopnest; /* current loop nesting level */
/* forward decl way out to parsing code - dotrap needs it */
static int evalstring(char *s, int mask);
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... */
#endif
void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
-static int loopnest; /* current loop nesting level */
-
static void
evalloop(union node *n, int flags)
{
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);
static void
setinteractive(int on)
{
- static int is_interactive;
+ static smallint is_interactive;
if (++on == is_interactive)
return;
#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)
{
#endif
setinteractive(iflag);
setjobctl(mflag);
- setvimode(viflag);
+#if ENABLE_FEATURE_EDITING_VI
+ if (viflag)
+ line_input_state->flags |= VI_MODE;
+ else
+ line_input_state->flags &= ~VI_MODE;
+#else
+ viflag = 0; /* forcibly keep the option off */
+#endif
}
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);
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;
static int breakcmd(int, char **);
static int dotcmd(int, char **);
static int evalcmd(int, char **);
-#if ENABLE_ASH_BUILTIN_ECHO
-static int echocmd(int, char **);
-#endif
-#if ENABLE_ASH_BUILTIN_TEST
-static int testcmd(int, char **);
-#endif
static int exitcmd(int, char **);
static int exportcmd(int, char **);
#if ENABLE_ASH_GETOPTS
static int getoptscmd(int, char **);
#endif
#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-static int helpcmd(int argc, char **argv);
+static int helpcmd(int, char **);
#endif
#if ENABLE_ASH_MATH_SUPPORT
static int letcmd(int, char **);
#define BUILTIN_REG_ASSG "6"
#define BUILTIN_SPEC_REG_ASSG "7"
-/* make sure to keep these in proper order since it is searched via bsearch() */
+/* We do not handle [[ expr ]] bashism bash-compatibly,
+ * we make it a synonym of [ expr ].
+ * Basically, word splitting and pathname expansion should NOT be performed
+ * Examples:
+ * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0"
+ * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0"
+ * Additional operators:
+ * || and && should work as -o and -a
+ * =~ regexp match
+ * Apart from the above, [[ expr ]] should work as [ expr ]
+ */
+
+#define echocmd echo_main
+#define printfcmd printf_main
+#define testcmd test_main
+
+/* Keep these in proper order since it is searched via bsearch() */
static const struct builtincmd builtintab[] = {
{ BUILTIN_SPEC_REG ".", dotcmd },
{ BUILTIN_SPEC_REG ":", truecmd },
#if ENABLE_ASH_BUILTIN_TEST
- { BUILTIN_REGULAR "[", testcmd },
- { BUILTIN_REGULAR "[[", testcmd },
+ { BUILTIN_REGULAR "[", testcmd },
+#if ENABLE_ASH_BASH_COMPAT
+ { BUILTIN_REGULAR "[[", testcmd },
+#endif
#endif
#if ENABLE_ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
{ BUILTIN_NOSPEC "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
+#if ENABLE_ASH_BUILTIN_PRINTF
+ { BUILTIN_REGULAR "printf", printfcmd },
+#endif
{ BUILTIN_NOSPEC "pwd", pwdcmd },
{ BUILTIN_REGULAR "read", readcmd },
{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
{ BUILTIN_SPEC_REG "shift", shiftcmd },
{ BUILTIN_SPEC_REG "source", dotcmd },
#if ENABLE_ASH_BUILTIN_TEST
- { BUILTIN_REGULAR "test", testcmd },
+ { BUILTIN_REGULAR "test", testcmd },
#endif
{ BUILTIN_SPEC_REG "times", timescmd },
{ BUILTIN_SPEC_REG "trap", trapcmd },
{ BUILTIN_REGULAR "wait", waitcmd },
};
-
-#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)
+/* Should match the above table! */
+#define COMMANDCMD (builtintab + \
+ 2 + \
+ 1 * ENABLE_ASH_BUILTIN_TEST + \
+ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+ 1 * ENABLE_ASH_ALIAS + \
+ 1 * ENABLE_ASH_JOB_CONTROL + \
+ 3)
+#define EXECCMD (builtintab + \
+ 2 + \
+ 1 * ENABLE_ASH_BUILTIN_TEST + \
+ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+ 1 * ENABLE_ASH_ALIAS + \
+ 1 * ENABLE_ASH_JOB_CONTROL + \
+ 3 + \
+ 1 * ENABLE_ASH_CMDCMD + \
+ 1 + \
+ ENABLE_ASH_BUILTIN_ECHO + \
+ 1)
/*
* Search the table of builtin commands.
/*
* Execute a simple command.
*/
-static int back_exitstatus; /* exit status of backquoted command */
static int
isassignment(const char *p)
{
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;
/* Execute the command. */
switch (cmdentry.cmdtype) {
default:
+#if ENABLE_FEATURE_SH_NOFORK
+ {
+ /* find_command() encodes applet_no as (-2 - applet_no) */
+ int applet_no = (- cmdentry.u.index - 2);
+ if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
+ listsetvar(varlist.list, VEXPORT|VSTACK);
+ /* run <applet>_main() */
+ exitstatus = run_nofork_applet(applet_no, argv);
+ break;
+ }
+ }
+#endif
+
/* 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 */
/* next character in input buffer */
static char *parsenextc; /* copy of parsefile->nextc */
-static int checkkwd;
+static smallint checkkwd;
/* values of checkkwd variable */
#define CHKALIAS 0x1
#define CHKKWD 0x2
static void
popstring(void)
{
- struct strpush *sp = parsefile->strpush;
+ struct strpush *sp = g_parsefile->strpush;
INT_OFF;
#if ENABLE_ASH_ALIAS
parsenextc = sp->prevstring;
parsenleft = sp->prevnleft;
/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
- parsefile->strpush = sp->prev;
- if (sp != &(parsefile->basestrpush))
+ g_parsefile->strpush = sp->prev;
+ if (sp != &(g_parsefile->basestrpush))
free(sp);
INT_ON;
}
preadfd(void)
{
int nr;
- char *buf = parsefile->buf;
+ char *buf = g_parsefile->buf;
parsenextc = buf;
- retry:
#if ENABLE_FEATURE_EDITING
- if (!iflag || parsefile->fd)
- nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+ retry:
+ if (!iflag || g_parsefile->fd)
+ nr = nonblock_safe_read(g_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(g_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 more;
char savec;
- while (parsefile->strpush) {
+ while (g_parsefile->strpush) {
#if ENABLE_ASH_ALIAS
- if (parsenleft == -1 && parsefile->strpush->ap &&
+ if (parsenleft == -1 && g_parsefile->strpush->ap &&
parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
return PEOA;
}
if (--parsenleft >= 0)
return signed_char2int(*parsenextc++);
}
- if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+ if (parsenleft == EOF_NLEFT || g_parsefile->buf == NULL)
return PEOF;
flush_stdout_stderr();
* Push a string back onto the input at this current parsefile level.
* We handle aliases this way.
*/
+#if !ENABLE_ASH_ALIAS
+#define pushstring(s, ap) pushstring(s)
+#endif
static void
-pushstring(char *s, void *ap)
+pushstring(char *s, struct alias *ap)
{
struct strpush *sp;
size_t len;
len = strlen(s);
INT_OFF;
/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
- if (parsefile->strpush) {
- sp = ckmalloc(sizeof(struct strpush));
- sp->prev = parsefile->strpush;
- parsefile->strpush = sp;
+ if (g_parsefile->strpush) {
+ sp = ckzalloc(sizeof(struct strpush));
+ sp->prev = g_parsefile->strpush;
+ g_parsefile->strpush = sp;
} else
- sp = parsefile->strpush = &(parsefile->basestrpush);
+ sp = g_parsefile->strpush = &(g_parsefile->basestrpush);
sp->prevstring = parsenextc;
sp->prevnleft = parsenleft;
#if ENABLE_ASH_ALIAS
- sp->ap = (struct alias *)ap;
+ sp->ap = ap;
if (ap) {
- ((struct alias *)ap)->flag |= ALIASINUSE;
+ ap->flag |= ALIASINUSE;
sp->string = s;
}
#endif
{
struct parsefile *pf;
- parsefile->nleft = parsenleft;
- parsefile->lleft = parselleft;
- parsefile->nextc = parsenextc;
- parsefile->linno = plinno;
- pf = ckmalloc(sizeof(*pf));
- pf->prev = parsefile;
+ g_parsefile->nleft = parsenleft;
+ g_parsefile->lleft = parselleft;
+ g_parsefile->nextc = parsenextc;
+ g_parsefile->linno = plinno;
+ pf = ckzalloc(sizeof(*pf));
+ pf->prev = g_parsefile;
pf->fd = -1;
- pf->strpush = NULL;
- pf->basestrpush.prev = NULL;
- parsefile = pf;
+ /*pf->strpush = NULL; - ckzalloc did it */
+ /*pf->basestrpush.prev = NULL;*/
+ g_parsefile = pf;
}
static void
popfile(void)
{
- struct parsefile *pf = parsefile;
+ struct parsefile *pf = g_parsefile;
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;
+ g_parsefile = pf->prev;
free(pf);
- parsenleft = parsefile->nleft;
- parselleft = parsefile->lleft;
- parsenextc = parsefile->nextc;
- plinno = parsefile->linno;
+ parsenleft = g_parsefile->nleft;
+ parselleft = g_parsefile->lleft;
+ parsenextc = g_parsefile->nextc;
+ plinno = g_parsefile->linno;
INT_ON;
}
static void
popallfiles(void)
{
- while (parsefile != &basepf)
+ while (g_parsefile != &basepf)
popfile();
}
closescript(void)
{
popallfiles();
- if (parsefile->fd > 0) {
- close(parsefile->fd);
- parsefile->fd = 0;
+ if (g_parsefile->fd > 0) {
+ close(g_parsefile->fd);
+ g_parsefile->fd = 0;
}
}
static void
setinputfd(int fd, int push)
{
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ close_on_exec_on(fd);
if (push) {
pushfile();
- parsefile->buf = 0;
+ g_parsefile->buf = 0;
}
- parsefile->fd = fd;
- if (parsefile->buf == NULL)
- parsefile->buf = ckmalloc(IBUFSIZ);
+ g_parsefile->fd = fd;
+ if (g_parsefile->buf == NULL)
+ g_parsefile->buf = ckmalloc(IBUFSIZ);
parselleft = parsenleft = 0;
plinno = 1;
}
pushfile();
parsenextc = string;
parsenleft = strlen(string);
- parsefile->buf = NULL;
+ g_parsefile->buf = NULL;
plinno = 1;
INT_ON;
}
/* 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.
break;
if (*p == '\0')
continue;
- for (q = p; *q; q++);
+ for (q = p; *q; q++)
+ continue;
#if DEBUG
if (q[-1] != '/')
abort();
}
static void
-changemail(const char *val)
+changemail(const char *val ATTRIBUTE_UNUSED)
{
- mail_var_path_changed++;
+ mail_var_path_changed = 1;
}
#endif /* ASH_MAIL */
char **ap;
int nparam;
- for (nparam = 0; argv[nparam]; nparam++);
+ for (nparam = 0; argv[nparam]; nparam++)
+ continue;
ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
while (*argv) {
*ap++ = ckstrdup(*argv++);
}
*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
-minus_o(char *name, int val)
+static int
+plus_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 %co %s", val ? '-' : '+', name);
+ return 1;
}
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optnames(i),
- optlist[i] ? "on" : "off");
+ for (i = 0; i < NOPTS; i++) {
+ if (val) {
+ out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
+ } else {
+ out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
+ }
+ }
+ 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%c", val ? '-' : '+', 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 (plus_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");
+ n = shellparam.nparam;
INT_OFF;
shellparam.nparam -= n;
for (ap1 = shellparam.p; --n >= 0; ap1++) {
- if (shellparam.malloc)
+ if (shellparam.malloced)
free(*ap1);
}
ap2 = shellparam.p;
- while ((*ap2++ = *ap1++) != NULL);
+ while ((*ap2++ = *ap1++) != NULL)
+ continue;
#if ENABLE_ASH_GETOPTS
shellparam.optind = 1;
shellparam.optoff = -1;
* 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
return 1;
optnext = optfirst + *param_optind - 1;
- if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+ if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
p = NULL;
else
p = optnext[-1] + *optoff;
}
c = *p++;
- for (q = optstr; *q != c; ) {
+ for (q = optstr; *q != c;) {
if (*q == '\0') {
if (optstr[0] == ':') {
s[0] = c;
/* ============ Shell parser */
-static int tokpushback; /* last token pushed back */
-#define NEOF ((union node *)&tokpushback)
-static int parsebackquote; /* nonzero if we are inside backquotes */
-static int lasttoken; /* last token read */
+struct heredoc {
+ struct heredoc *next; /* next here document in list */
+ union node *here; /* redirection node */
+ char *eofmark; /* string indicating end of input */
+ smallint striptabs; /* if set, strip leading tabs */
+};
+
+static smallint tokpushback; /* last token pushed back */
+static smallint parsebackquote; /* nonzero if we are inside backquotes */
+static smallint quoteflag; /* set if (part of) last token was quoted */
+static token_id_t lasttoken; /* last token read (integer id Txxx) */
+static struct heredoc *heredoclist; /* list of here documents to 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 */
+/*
+ * 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.
+ */
+#define NEOF ((union node *)&tokpushback)
static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
static void
#define EOFMARKLEN 79
-struct heredoc {
- struct heredoc *next; /* next here document in list */
- union node *here; /* redirection node */
- char *eofmark; /* string indicating end of input */
- int striptabs; /* if set, strip leading tabs */
-};
-
-static struct heredoc *heredoclist; /* list of here documents to read */
-
/* parsing is heavily cross-recursive, need these forward decls */
static union node *andor(void);
static union node *pipeline(void);
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) {
union node *vars, **vpp;
union node **rpp, *redir;
int savecheckkwd;
+#if ENABLE_ASH_BASH_COMPAT
+ smallint double_brackets_flag = 0;
+#endif
args = NULL;
app = &args;
savecheckkwd = CHKALIAS;
for (;;) {
+ int t;
checkkwd = savecheckkwd;
- switch (readtoken()) {
+ t = readtoken();
+ switch (t) {
+#if ENABLE_ASH_BASH_COMPAT
+ case TAND: /* "&&" */
+ case TOR: /* "||" */
+ if (!double_brackets_flag) {
+ tokpushback = 1;
+ goto out;
+ }
+ wordtext = (char *) (t == TAND ? "-a" : "-o");
+#endif
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;
+#if ENABLE_ASH_BASH_COMPAT
+ if (strcmp("[[", wordtext) == 0)
+ double_brackets_flag = 1;
+ else if (strcmp("]]", wordtext) == 0)
+ double_brackets_flag = 0;
+#endif
n->narg.backquote = backquotelist;
if (savecheckkwd && isassignment(wordtext)) {
*vpp = n;
}
/* 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;
return n1;
}
+#if ENABLE_ASH_BASH_COMPAT
+static int decode_dollar_squote(void)
+{
+ static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
+ int c, cnt;
+ char *p;
+ char buf[4];
+
+ c = pgetc();
+ p = strchr(C_escapes, c);
+ if (p) {
+ buf[0] = c;
+ p = buf;
+ cnt = 3;
+ if ((unsigned char)(c - '0') <= 7) { /* \ooo */
+ do {
+ c = pgetc();
+ *++p = c;
+ } while ((unsigned char)(c - '0') <= 7 && --cnt);
+ pungetc();
+ } else if (c == 'x') { /* \xHH */
+ do {
+ c = pgetc();
+ *++p = c;
+ } while (isxdigit(c) && --cnt);
+ pungetc();
+ if (cnt == 3) { /* \x but next char is "bad" */
+ c = 'x';
+ goto unrecognized;
+ }
+ } else { /* simple seq like \\ or \t */
+ p++;
+ }
+ *p = '\0';
+ p = buf;
+ c = bb_process_escape_sequence((void*)&p);
+ } else { /* unrecognized "\z": print both chars unless ' or " */
+ if (c != '\'' && c != '"') {
+ unrecognized:
+ c |= 0x100; /* "please encode \, then me" */
+ }
+ }
+ return c;
+}
+#endif
+
/*
* If eofmark is NULL, read a word or a redirection symbol. If eofmark
* is not NULL, read a here document. In the latter case, eofmark is the
* using goto's to implement the subroutine linkage. The following macros
* 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:;}
#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
#define PARSEARITH() {goto parsearith; parsearith_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 */
+
+ USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
+
#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;
case CCTL:
if (eofmark == NULL || dblquote)
USTPUTC(CTLESC, out);
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == '\\' && bash_dollar_squote) {
+ c = decode_dollar_squote();
+ if (c & 0x100) {
+ USTPUTC('\\', out);
+ c = (unsigned char)c;
+ }
+ }
+#endif
USTPUTC(c, out);
break;
case CBACK: /* backslash */
if (doprompt)
setprompt(2);
} else {
- if (dblquote &&
- c != '\\' && c != '`' &&
- c != '$' && (
- c != '"' ||
- eofmark != NULL)
+#if ENABLE_ASH_EXPAND_PRMT
+ if (c == '$' && pssyntax) {
+ USTPUTC(CTLESC, out);
+ USTPUTC('\\', out);
+ }
+#endif
+ if (dblquote && c != '\\'
+ && c != '`' && c != '$'
+ && (c != '"' || eofmark != NULL)
) {
USTPUTC(CTLESC, out);
USTPUTC('\\', out);
if (SIT(c, SQSYNTAX) == CCTL)
USTPUTC(CTLESC, out);
USTPUTC(c, out);
- quotef++;
+ quotef = 1;
}
break;
case CSQUOTE:
dblquote = 1;
goto quotemark;
case CENDQUOTE:
+ USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
if (eofmark != NULL && arinest == 0
&& varnest == 0
) {
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 {
}
c = pgetc_macro();
- }
+ } /* for(;;) */
}
endword:
#if ENABLE_ASH_MATH_SUPPORT
if ((c == '>' || c == '<')
&& quotef == 0
&& len <= 2
- && (*out == '\0' || isdigit(*out))) {
+ && (*out == '\0' || isdigit(*out))
+ ) {
PARSEREDIR();
- return lasttoken = TREDIR;
- } else {
- pungetc();
+ lasttoken = TREDIR;
+ return lasttoken;
}
+ pungetc();
}
quoteflag = quotef;
backquotelist = bqlist;
char *p, *q;
p = line;
- for (q = eofmark + 1; *q && *p == *q; p++, q++);
+ for (q = eofmark + 1; *q && *p == *q; p++, q++)
+ continue;
if (*p == '\n' && *q == '\0') {
c = PEOF;
plinno++;
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;
/* 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))
+ (((unsigned)(c) - 33 < 32) \
+ && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
parsesub: {
int subtype;
int typeloc;
int flags;
char *p;
- static const char types[] = "}-+?=";
+ static const char types[] ALIGN1 = "}-+?=";
c = pgetc();
- if (
- c <= PEOA_OR_PEOF ||
- (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+ if (c <= PEOA_OR_PEOF
+ || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
) {
- USTPUTC('$', out);
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == '\'')
+ bash_dollar_squote = 1;
+ else
+#endif
+ USTPUTC('$', out);
pungetc();
} else if (c == '(') { /* $(command) or $((arith)) */
if (pgetc() == '(') {
#if ENABLE_ASH_MATH_SUPPORT
PARSEARITH();
#else
- raise_error_syntax("We unsupport $((arith))");
+ raise_error_syntax("you disabled math support for $((arith)) syntax");
#endif
} else {
pungetc();
if (subtype == 0) {
switch (c) {
case ':':
- flags = VSNUL;
c = pgetc();
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == ':' || c == '$' || isdigit(c)) {
+ pungetc();
+ subtype = VSSUBSTR;
+ break;
+ }
+#endif
+ flags = VSNUL;
/*FALLTHROUGH*/
default:
p = strchr(types, c);
subtype = p - types + VSNORMAL;
break;
case '%':
- case '#':
- {
- int cc = c;
- subtype = c == '#' ? VSTRIMLEFT :
- VSTRIMRIGHT;
- c = pgetc();
- if (c == cc)
- subtype++;
- else
- pungetc();
- break;
- }
+ case '#': {
+ int cc = c;
+ subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
+ c = pgetc();
+ if (c == cc)
+ subtype++;
+ else
+ pungetc();
+ break;
+ }
+#if ENABLE_ASH_BASH_COMPAT
+ case '/':
+ subtype = VSREPLACE;
+ c = pgetc();
+ if (c == '/')
+ subtype++; /* VSREPLACEALL */
+ else
+ pungetc();
+ break;
+#endif
}
} else {
pungetc();
*/
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 \
#endif
) {
if (c == '#') {
- while ((c = pgetc()) != '\n' && c != PEOF);
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
pungetc();
} else if (c == '\\') {
if (pgetc() != '\n') {
return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
}
- if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+ if ((size_t)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
if (pgetc() == *p) { /* double occurrence? */
p += xxreadtoken_doubles + 1;
} else {
}
}
}
- return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ return lasttoken;
}
}
} /* for */
#endif
continue;
case '#':
- while ((c = pgetc()) != '\n' && c != PEOF);
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
pungetc();
continue;
case '\\':
{
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);
setstackmark(&smark);
#if JOBS
- if (jobctl)
+ if (doing_jobctl)
showjobs(stderr, SHOW_CHANGED);
#endif
inter = 0;
}
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 */
}
-#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 test_main(argc, argv);
-}
-#endif
-
/*
* Read a file containing shell functions.
*/
}
#if ENABLE_FEATURE_SH_STANDALONE
- if (find_applet_by_name(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
+ {
+ int applet_no = find_applet_by_name(name);
+ if (applet_no >= 0) {
+ entry->cmdtype = CMDNORMAL;
+ entry->u.index = -2 - applet_no;
+ return;
+ }
}
#endif
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 */
+ }
+ if ((act & DO_NOFUNC)
+ || !prefix(pathopt, "func")
+ ) { /* ignore unimplemented options */
continue;
}
}
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;
+ unsigned col;
+ unsigned i;
out1fmt("\nBuilt-in commands:\n-------------------\n");
- for (col = 0, i = 0; i < ARRAY_SIZE(builtintab) ; 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) {
}
}
#if ENABLE_FEATURE_SH_STANDALONE
- for (i = 0; i < NUM_APPLETS; i++) {
- col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
- if (col > 60) {
- out1fmt("\n");
- col = 0;
+ {
+ 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;
}
#endif
/*
- * The read builtin. The -e option causes backslashes to escape the
- * following character.
- *
+ * The read builtin. Options:
+ * -r Do not interpret '\' specially
+ * -s Turn off echo (tty only)
+ * -n NCHARS Read NCHARS max
+ * -p PROMPT Display PROMPT on stderr (if input is from tty)
+ * -t SECONDS Timeout after SECONDS (tty or pipe only)
+ * -u FD Read from given FD instead of fd 0
* This uses unbuffered input, which may be avoidable in some cases.
+ * TODO: bash also has:
+ * -a ARRAY Read into array[0],[1],etc
+ * -d DELIM End on DELIM char, not newline
+ * -e Use line editing (tty only)
*/
static int
-readcmd(int argc, char **argv)
+readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
{
+ static const char *const arg_REPLY[] = { "REPLY", NULL };
+
char **ap;
int backslash;
char c;
int startword;
int status;
int i;
+ int fd = 0;
#if ENABLE_ASH_READ_NCHARS
- int nch_flag = 0;
- int nchars = 0;
+ int nchars = 0; /* if != 0, -n is in effect */
int silent = 0;
struct termios tty, old_tty;
#endif
#if ENABLE_ASH_READ_TIMEOUT
- fd_set set;
- struct timeval ts;
-
- ts.tv_sec = ts.tv_usec = 0;
+ unsigned end_ms = 0;
+ unsigned timeout = 0;
#endif
rflag = 0;
prompt = NULL;
-#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT
- while ((i = nextopt("p:rt:n:s")) != '\0')
-#elif ENABLE_ASH_READ_NCHARS
- while ((i = nextopt("p:rn:s")) != '\0')
-#elif ENABLE_ASH_READ_TIMEOUT
- while ((i = nextopt("p:rt:")) != '\0')
-#else
- while ((i = nextopt("p:r")) != '\0')
-#endif
- {
+ while ((i = nextopt("p:u:r"
+ USE_ASH_READ_TIMEOUT("t:")
+ USE_ASH_READ_NCHARS("n:s")
+ )) != '\0') {
switch (i) {
case 'p':
prompt = optionarg;
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);
+ /* nchars == 0: off (bash 3.2 does this too) */
break;
case 's':
silent = 1;
#endif
#if ENABLE_ASH_READ_TIMEOUT
case 't':
- ts.tv_sec = strtol(optionarg, &p, 10);
+ timeout = bb_strtou(optionarg, NULL, 10);
+ if (errno || timeout > UINT_MAX / 2048)
+ ash_msg_and_raise_error("invalid timeout");
+ timeout *= 1000;
+#if 0 /* even bash have no -t N.NNN support */
+ 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");
+ }
+#endif /* if 0 */
break;
#endif
case 'r':
rflag = 1;
break;
+ case 'u':
+ fd = bb_strtou(optionarg, NULL, 10);
+ if (fd < 0 || errno)
+ ash_msg_and_raise_error("invalid file descriptor");
+ break;
default:
break;
}
}
- if (prompt && isatty(0)) {
+ if (prompt && isatty(fd)) {
out2str(prompt);
}
ap = argptr;
if (*ap == NULL)
- ash_msg_and_raise_error("arg count");
+ ap = (char**)arg_REPLY;
ifs = bltinlookup("IFS");
if (ifs == NULL)
ifs = defifs;
#if ENABLE_ASH_READ_NCHARS
- if (nch_flag || silent) {
- tcgetattr(0, &tty);
- old_tty = tty;
- if (nch_flag) {
+ tcgetattr(fd, &tty);
+ old_tty = tty;
+ if (nchars || silent) {
+ if (nchars) {
tty.c_lflag &= ~ICANON;
- tty.c_cc[VMIN] = nchars;
+ tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
}
if (silent) {
- tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
-
+ tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
}
- tcsetattr(0, TCSANOW, &tty);
+ /* if tcgetattr failed, tcsetattr will fail too.
+ * Ignoring, it's harmless. */
+ tcsetattr(fd, TCSANOW, &tty);
}
#endif
-#if ENABLE_ASH_READ_TIMEOUT
- if (ts.tv_sec || ts.tv_usec) {
- FD_ZERO(&set);
- FD_SET(0, &set);
- i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
- if (!i) {
-#if ENABLE_ASH_READ_NCHARS
- if (nch_flag)
- tcsetattr(0, TCSANOW, &old_tty);
-#endif
- return 1;
- }
- }
-#endif
status = 0;
startword = 1;
backslash = 0;
+#if ENABLE_ASH_READ_TIMEOUT
+ if (timeout) /* NB: ensuring end_ms is nonzero */
+ end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1;
+#endif
STARTSTACKSTR(p);
+ do {
+#if ENABLE_ASH_READ_TIMEOUT
+ if (end_ms) {
+ struct pollfd pfd[1];
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ timeout = end_ms - (unsigned)(monotonic_us() / 1000);
+ if ((int)timeout <= 0 /* already late? */
+ || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
+ ) { /* timed out! */
#if ENABLE_ASH_READ_NCHARS
- while (!nch_flag || nchars--)
-#else
- for (;;)
+ tcsetattr(fd, TCSANOW, &old_tty);
#endif
- {
- if (read(0, &c, 1) != 1) {
+ return 1;
+ }
+ }
+#endif
+ if (nonblock_safe_read(fd, &c, 1) != 1) {
status = 1;
break;
}
STPUTC(c, p);
}
}
+/* end of do {} while: */
#if ENABLE_ASH_READ_NCHARS
- if (nch_flag || silent)
- tcsetattr(0, TCSANOW, &old_tty);
+ while (--nchars);
+#else
+ while (1);
+#endif
+
+#if ENABLE_ASH_READ_NCHARS
+ tcsetattr(fd, TCSANOW, &old_tty);
#endif
STACKSTRNUL(p);
}
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) {
while ((c = *p++) >= '0' && c <= '9') {
val = (val * 10) + (long)(c - '0');
+ // val is actually 'unsigned long int' and can't get < 0
if (val < (rlim_t) 0)
break;
}
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;
|| prec == PREC(TOK_CONDITIONAL));
}
-typedef struct ARITCH_VAR_NUM {
+typedef struct {
arith_t val;
arith_t contidional_second_val;
char contidional_second_val_initialized;
else is variable name */
} v_n_t;
-typedef struct CHK_VAR_RECURSIVE_LOOPED {
+typedef struct chk_var_recursive_looped_t {
const char *var;
- struct CHK_VAR_RECURSIVE_LOOPED *next;
+ struct chk_var_recursive_looped_t *next;
} chk_var_recursive_looped_t;
static chk_var_recursive_looped_t *prev_chk_var_recursive;
}
/* 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,
0
};
/* ptr to ")" */
-#define endexpression &op_tokens[sizeof(op_tokens)-7]
+#define endexpression (&op_tokens[sizeof(op_tokens)-7])
static arith_t
arith(const char *expr, int *perrcode)
char arithval; /* Current character under analysis */
operator lasttok, op;
operator prec;
-
+ operator *stack, *stackptr;
const char *p = endexpression;
int errcode;
-
- size_t datasizes = strlen(expr) + 2;
+ v_n_t *numstack, *numstackptr;
+ unsigned datasizes = strlen(expr) + 2;
/* Stack of integers */
/* The proof that there can be no more than strlen(startbuf)/2+1 integers
* in any given correct or incorrect expression is left as an exercise to
* the reader. */
- v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
- *numstackptr = numstack;
+ numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
/* Stack of operator tokens */
- operator *stack = alloca((datasizes) * sizeof(operator)),
- *stackptr = stack;
+ stackptr = stack = alloca(datasizes * sizeof(stack[0]));
*stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
*perrcode = errcode = 0;
if (numstackptr != numstack+1) {
/* ... but if there isn't, it's bad */
err:
- return (*perrcode = -1);
+ *perrcode = -1;
+ return *perrcode;
}
if (numstack->var) {
/* expression is $((var)) only, lookup now */
* 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;
+ /* 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
#endif
init();
setstackmark(&smark);
- procargs(argc, argv);
+ procargs(argv);
+
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
if (iflag) {
const char *hp = lookupvar("HISTFILE");
if (sflag || minusc == NULL) {
#if ENABLE_FEATURE_EDITING_SAVEHISTORY
- if ( iflag ) {
+ if (iflag) {
const char *hp = lookupvar("HISTFILE");
if (hp != NULL)