* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized.
*
- *
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* used in busybox and size optimizations,
* rewrote arith (see notes to this), added locale support,
* rewrote dynamic variables.
- *
*/
/*
* 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 */
+//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN?
+//#include "applet_tables.h" doesn't work
#include <paths.h>
#include <setjmp.h>
#include <fnmatch.h>
-#if JOBS || ENABLE_ASH_READ_NCHARS
-#include <termios.h>
+
+#if defined SINGLE_APPLET_MAIN
+/* STANDALONE does not make sense, and won't compile */
+#undef CONFIG_FEATURE_SH_STANDALONE
+#undef ENABLE_FEATURE_SH_STANDALONE
+#undef USE_FEATURE_SH_STANDALONE
+#undef SKIP_FEATURE_SH_STANDALONE(...)
+#define ENABLE_FEATURE_SH_STANDALONE 0
+#define USE_FEATURE_SH_STANDALONE(...)
+#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
+#endif
+
+#ifndef PIPE_BUF
+#define PIPE_BUF 4096 /* amount of buffering in a pipe */
#endif
-extern char **environ;
#if defined(__uClinux__)
-#error "Do not even bother, ash will not run on uClinux"
+#error "Do not even bother, ash will not run on NOMMU machine"
#endif
+/* ============ Hash table sizes. Configurable. */
+
+#define VTABSIZE 39
+#define ATABSIZE 39
+#define CMDTABLESIZE 31 /* should be prime */
+
+
/* ============ Misc helpers */
#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
-/* C99 say: "char" declaration may be signed or unsigned default */
+/* C99 says: "char" declaration may be signed or unsigned by default */
#define signed_char2int(sc) ((int)((signed char)sc))
enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
-static char optlist[NOPTS] ALIGN1;
-
-#define eflag optlist[0]
-#define fflag optlist[1]
-#define Iflag optlist[2]
-#define iflag optlist[3]
-#define mflag optlist[4]
-#define nflag optlist[5]
-#define sflag optlist[6]
-#define xflag optlist[7]
-#define vflag optlist[8]
-#define Cflag optlist[9]
-#define aflag optlist[10]
-#define bflag optlist[11]
-#define uflag optlist[12]
-#define viflag optlist[13]
-#if DEBUG
-#define nolog optlist[14]
-#define debug optlist[15]
-#endif
-
/* ============ Misc data */
-static char nullstr[1] ALIGN1; /* zero length string */
static const char homestr[] ALIGN1 = "HOME";
static const char snlfmt[] ALIGN1 = "%s\n";
static const char illnum[] ALIGN1 = "Illegal number: %s";
-static char *minusc; /* argument to -c option */
-
-/* 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];
-static smallint isloginsh;
-/* 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 */
-
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location 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,
- */
+ smallint isloginsh;
+ char nullstr[1]; /* zero length string */
-#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) */
+ char optlist[NOPTS];
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Cflag optlist[9]
+#define aflag optlist[10]
+#define bflag optlist[11]
+#define uflag optlist[12]
+#define viflag optlist[13]
+#if DEBUG
+#define nolog optlist[14]
+#define debug optlist[15]
+#endif
+
+ /* trap handler commands */
+ /*
+ * 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]; /* offset by 1: "signal" 0 is meaningless */
+ char *trap[NSIG];
+
+ /* Rarely referenced stuff */
+#if ENABLE_ASH_RANDOM_SUPPORT
+ /* Random number generators */
+ int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */
+ uint32_t random_LCG; /* LCG (fast but weak) */
+#endif
+ pid_t backgndpid; /* pid of last background process */
+ smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
+};
+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 nullstr (G_misc.nullstr )
+#define optlist (G_misc.optlist )
+#define sigmode (G_misc.sigmode )
+#define gotsig (G_misc.gotsig )
+#define trap (G_misc.trap )
+#define random_galois_LFSR (G_misc.random_galois_LFSR)
+#define random_LCG (G_misc.random_LCG )
+#define backgndpid (G_misc.backgndpid )
+#define job_warning (G_misc.job_warning)
+#define INIT_G_misc() do { \
+ (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
+ barrier(); \
+ curdir = nullstr; \
+ physdir = nullstr; \
+} while (0)
+
+
+/* ============ Utility functions */
+static int isdigit_str9(const char *str)
+{
+ int maxlen = 9 + 1; /* max 9 digits: 999999999 */
+ while (--maxlen && isdigit(*str))
+ str++;
+ return (*str == '\0');
+}
+
+
+/* ============ Interrupts / exceptions */
/*
* These macros allow the user to suspend the handling of interrupt signals
- * over a period of time. This is similar to SIGHOLD to or sigblock, but
+ * over a period of time. This is similar to SIGHOLD or to sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
-#define INT_OFF \
- do { \
- suppressint++; \
- xbarrier(); \
- } while (0)
+#define INT_OFF do { \
+ suppressint++; \
+ xbarrier(); \
+} while (0)
/*
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception".
*/
-static void raise_exception(int) ATTRIBUTE_NORETURN;
+static void raise_exception(int) NORETURN;
static void
raise_exception(int e)
{
* are held using the INT_OFF macro. (The test for iflag is just
* defensive programming.)
*/
-static void raise_interrupt(void) ATTRIBUTE_NORETURN;
+static void raise_interrupt(void) NORETURN;
static void
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);
}
}
#define FORCE_INT_ON force_int_on()
#else
-#define INT_ON \
- do { \
- xbarrier(); \
- if (--suppressint == 0 && intpending) \
- raise_interrupt(); \
- } while (0)
-#define FORCE_INT_ON \
- do { \
- xbarrier(); \
- suppressint = 0; \
- if (intpending) \
- raise_interrupt(); \
- } while (0)
+#define INT_ON do { \
+ xbarrier(); \
+ if (--suppressint == 0 && intpending) \
+ raise_interrupt(); \
+} while (0)
+#define FORCE_INT_ON do { \
+ xbarrier(); \
+ suppressint = 0; \
+ if (intpending) \
+ raise_interrupt(); \
+} while (0)
#endif /* ASH_OPTIMIZE_FOR_SIZE */
#define SAVE_INT(v) ((v) = suppressint)
-#define RESTORE_INT(v) \
- do { \
- xbarrier(); \
- suppressint = (v); \
- if (suppressint == 0 && intpending) \
- raise_interrupt(); \
- } while (0)
-
-#define EXSIGON \
- do { \
- exsig++; \
- xbarrier(); \
- if (pendingsig) \
- raise_exception(EXSIG); \
- } while (0)
-/* EXSIG is turned off by evalbltin(). */
+#define RESTORE_INT(v) do { \
+ xbarrier(); \
+ suppressint = (v); \
+ if (suppressint == 0 && intpending) \
+ raise_interrupt(); \
+} while (0)
/*
- * Ignore a signal. Only one usage site - in forkchild()
+ * Ignore a signal. Avoids unnecessary system calls.
*/
static void
ignoresig(int signo)
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} */
+#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
-#define NREDIR 2
-#define NBACKGND 3
+#define NCMD 0
+#define NPIPE 1
+#define NREDIR 2
+#define NBACKGND 3
#define NSUBSHELL 4
-#define NAND 5
-#define NOR 6
-#define NSEMI 7
-#define NIF 8
-#define NWHILE 9
-#define NUNTIL 10
-#define NFOR 11
-#define NCASE 12
-#define NCLIST 13
-#define NDEFUN 14
-#define NARG 15
-#define NTO 16
-#define NCLOBBER 17
-#define NFROM 18
-#define NFROMTO 19
-#define NAPPEND 20
-#define NTOFD 21
-#define NFROMFD 22
-#define NHERE 23
-#define NXHERE 24
-#define NNOT 25
+#define NAND 5
+#define NOR 6
+#define NSEMI 7
+#define NIF 8
+#define NWHILE 9
+#define NUNTIL 10
+#define NFOR 11
+#define NCASE 12
+#define NCLIST 13
+#define NDEFUN 14
+#define NARG 15
+#define NTO 16
+#if ENABLE_ASH_BASH_COMPAT
+#define NTO2 17
+#endif
+#define NCLOBBER 18
+#define NFROM 19
+#define NFROMTO 20
+#define NAPPEND 21
+#define NTOFD 22
+#define NFROMFD 23
+#define NHERE 24
+#define NXHERE 25
+#define NNOT 26
+#define N_NUMBER 27
union node;
struct ncmd {
- int type;
+ smallint type; /* Nxxxx */
union node *assign;
union node *args;
union node *redirect;
};
struct npipe {
- int type;
- int backgnd;
+ smallint type;
+ smallint pipe_backgnd;
struct nodelist *cmdlist;
};
struct nredir {
- int type;
+ smallint type;
union node *n;
union node *redirect;
};
struct nbinary {
- int type;
+ smallint type;
union node *ch1;
union node *ch2;
};
struct nif {
- int type;
+ smallint type;
union node *test;
union node *ifpart;
union node *elsepart;
};
struct nfor {
- int type;
+ smallint type;
union node *args;
union node *body;
char *var;
};
struct ncase {
- int type;
+ smallint type;
union node *expr;
union node *cases;
};
struct nclist {
- int type;
+ smallint type;
union node *next;
union node *pattern;
union node *body;
};
struct narg {
- int type;
+ smallint type;
union node *next;
char *text;
struct nodelist *backquote;
};
+/* nfile and ndup layout must match!
+ * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
+ * that it is actually NTO2 (>&file), and change its type.
+ */
struct nfile {
- int type;
+ smallint type;
union node *next;
int fd;
+ int _unused_dupfd;
union node *fname;
char *expfname;
};
struct ndup {
- int type;
+ smallint type;
union node *next;
int fd;
int dupfd;
union node *vname;
+ char *_unused_expfname;
};
struct nhere {
- int type;
+ smallint type;
union node *next;
int fd;
union node *doc;
};
struct nnot {
- int type;
+ smallint type;
union node *com;
};
union node {
- int type;
+ smallint type;
struct ncmd ncmd;
struct npipe npipe;
struct nredir nredir;
case NTO: s = ">>"+1; dftfd = 1; break;
case NCLOBBER: s = ">|"; dftfd = 1; break;
case NAPPEND: s = ">>"; dftfd = 1; break;
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
case NTOFD: s = ">&"; dftfd = 1; break;
- case NFROM: s = "<"; break;
+ case NFROM: s = "<"; break;
case NFROMFD: s = "<&"; break;
case NFROMTO: s = "<>"; break;
default: s = "*error*"; break;
if (lp->next)
fputs(" | ", fp);
}
- if (n->npipe.backgnd)
+ if (n->npipe.pipe_backgnd)
fputs(" &", fp);
if (ind >= 0)
putc('\n', fp);
char *text;
};
-#if ENABLE_ASH_ALIAS
struct alias;
-#endif
struct strpush {
struct strpush *prev; /* preceding string on stack */
- char *prevstring;
- int prevnleft;
+ char *prev_string;
+ int prev_left_in_line;
#if ENABLE_ASH_ALIAS
struct alias *ap; /* if push was associated with an alias */
#endif
struct parsefile *prev; /* preceding file on stack */
int linno; /* current line */
int fd; /* file descriptor (or -1 if string) */
- int nleft; /* number of chars left in this line */
- int lleft; /* number of chars left in this buffer */
- char *nextc; /* next char in buffer */
+ int left_in_line; /* number of chars left in this line */
+ int left_in_buffer; /* number of chars left in this buffer past the line */
+ char *next_to_pgetc; /* next char in buffer */
char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */
};
-static struct parsefile basepf; /* top level input file */
-static struct parsefile *parsefile = &basepf; /* current input file */
+static struct parsefile basepf; /* top level 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 */
-static int exitstatus; /* exit status of last command */
+static uint8_t exitstatus; /* exit status of last command */
/* ============ Message printing */
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);
* is not NULL then error prints an error message using printf style
* formatting. It then raises the error exception.
*/
-static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN;
+static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
static void
ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
{
/* NOTREACHED */
}
-static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN;
+static void ash_msg_and_raise_error(const char *, ...) NORETURN;
static void
ash_msg_and_raise_error(const char *msg, ...)
{
va_end(ap);
}
-static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN;
+static void ash_msg_and_raise(int, const char *, ...) NORETURN;
static void
ash_msg_and_raise(int cond, const char *msg, ...)
{
* 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;
}
#define STARTSTACKSTR(p) ((p) = stackblock())
#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
-#define CHECKSTRSPACE(n, p) \
- do { \
- char *q = (p); \
- size_t l = (n); \
- size_t m = sstrend - q; \
- if (l > m) \
- (p) = makestrspace(l, q); \
- } while (0)
-#define USTPUTC(c, p) (*p++ = (c))
-#define STACKSTRNUL(p) \
- do { \
- if ((p) == sstrend) \
- p = growstackstr(); \
- *p = '\0'; \
- } while (0)
-#define STUNPUTC(p) (--p)
-#define STTOPC(p) (p[-1])
-#define STADJUST(amount, p) (p += (amount))
+#define CHECKSTRSPACE(n, p) do { \
+ char *q = (p); \
+ size_t l = (n); \
+ size_t m = sstrend - q; \
+ if (l > m) \
+ (p) = makestrspace(l, q); \
+} while (0)
+#define USTPUTC(c, p) (*(p)++ = (c))
+#define STACKSTRNUL(p) do { \
+ if ((p) == sstrend) \
+ (p) = growstackstr(); \
+ *(p) = '\0'; \
+} while (0)
+#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;
static char *optptr; /* used by nextopt */
/*
- * XXX - should get rid of. have all builtins use getopt(3). the
- * library getopt must have the BSD extension static variable "optreset"
- * otherwise it can't be used within the shell safely.
+ * XXX - should get rid of. Have all builtins use getopt(3).
+ * The library getopt must have the BSD extension static variable
+ * "optreset", otherwise it can't be used within the shell safely.
*
- * Standard option processing (a la getopt) for builtin routines. The
- * only argument that is passed to nextopt is the option string; the
- * other arguments are unnecessary. It return the character, or '\0' on
- * end of input.
+ * Standard option processing (a la getopt) for builtin routines.
+ * The only argument that is passed to nextopt is the option string;
+ * the other arguments are unnecessary. It returns the character,
+ * or '\0' on end of input.
*/
static int
nextopt(const char *optstring)
p = optptr;
if (p == NULL || *p == '\0') {
+ /* We ate entire "-param", take next one */
p = *argptr;
- if (p == NULL || *p != '-' || *++p == '\0')
+ if (p == NULL)
+ return '\0';
+ if (*p != '-')
+ return '\0';
+ if (*++p == '\0') /* just "-" ? */
return '\0';
argptr++;
- if (LONE_DASH(p)) /* check for "--" */
+ if (LONE_DASH(p)) /* "--" ? */
return '\0';
+ /* p => next "-param" */
}
+ /* p => some option char in the middle of a "-param" */
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 == ':')
q++;
}
if (*++q == ':') {
- if (*p == '\0' && (p = *argptr++) == NULL)
- ash_msg_and_raise_error("no arg for -%c option", c);
+ if (*p == '\0') {
+ p = *argptr++;
+ if (p == NULL)
+ ash_msg_and_raise_error("no arg for -%c option", c);
+ }
optionarg = p;
p = NULL;
}
}
-/* ============ Math support definitions */
-
-#if ENABLE_ASH_MATH_SUPPORT_64
-typedef int64_t arith_t;
-#define arith_t_type long long
-#else
-typedef long arith_t;
-#define arith_t_type long
-#endif
-
-#if ENABLE_ASH_MATH_SUPPORT
-static arith_t dash_arith(const char *);
-static arith_t arith(const char *expr, int *perrcode);
-#endif
-
-#if ENABLE_ASH_RANDOM_SUPPORT
-static unsigned long rseed;
-#ifndef DYNAMIC_VAR
-#define DYNAMIC_VAR
-#endif
-#endif
-
-
/* ============ 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[] ALIGN1 = "IFS= \t\n";
-#define defifs (defifsvar + 4)
-#else
-static const char defifs[] ALIGN1 = " \t\n";
-#endif
-
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
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.
*/
static void
freeparam(volatile struct shparam *param)
{
- char **ap;
-
- if (param->malloc) {
- for (ap = param->p; *ap; ap++)
- free(*ap);
- free(param->p);
+ if (param->malloced) {
+ char **ap, **ap1;
+ ap = ap1 = param->p;
+ while (*ap)
+ free(*ap++);
+ free(ap1);
}
}
#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 */
+#if ENABLE_ASH_RANDOM_SUPPORT
+# 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 redirtab;
+
+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).
v = *findvar(hashvar(name), name);
if (v) {
-#ifdef DYNAMIC_VAR
+#if ENABLE_ASH_RANDOM_SUPPORT
/*
* Dynamic variables are implemented roughly the same way they are
* in bash. Namely, they're "special" so long as they aren't unset.
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);
retval = 1;
if (flags & VREADONLY)
goto out;
-#ifdef DYNAMIC_VAR
+#if ENABLE_ASH_RANDOM_SUPPORT
vp->flags &= ~VDYNAMIC;
#endif
if (flags & VUNSET)
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;
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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
const char *dest;
const char *path;
}
static int
-pwdcmd(int argc, char **argv)
+pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
int flags;
const char *dir = curdir;
/* ============ ... */
+
#define IBUFSIZ COMMON_BUFSIZE
-#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
+/* buffer for top level input file */
+#define basebuf bb_common_bufsiz1
/* Syntax classes */
-#define CWORD 0 /* character is nothing special */
-#define CNL 1 /* newline character */
-#define CBACK 2 /* a backslash character */
-#define CSQUOTE 3 /* single quote */
-#define CDQUOTE 4 /* double quote */
+#define CWORD 0 /* character is nothing special */
+#define CNL 1 /* newline character */
+#define CBACK 2 /* a backslash character */
+#define CSQUOTE 3 /* single quote */
+#define CDQUOTE 4 /* double quote */
#define CENDQUOTE 5 /* a terminating quote */
-#define CBQUOTE 6 /* backwards single quote */
-#define CVAR 7 /* a dollar sign */
-#define CENDVAR 8 /* a '}' character */
-#define CLP 9 /* a left paren in arithmetic */
-#define CRP 10 /* a right paren in arithmetic */
+#define CBQUOTE 6 /* backwards single quote */
+#define CVAR 7 /* a dollar sign */
+#define CENDVAR 8 /* a '}' character */
+#define CLP 9 /* a left paren in arithmetic */
+#define CRP 10 /* a right paren in arithmetic */
#define CENDFILE 11 /* end of file */
-#define CCTL 12 /* like CWORD, except it must be escaped */
-#define CSPCL 13 /* these terminate a word */
-#define CIGN 14 /* character should be ignored */
+#define CCTL 12 /* like CWORD, except it must be escaped */
+#define CSPCL 13 /* these terminate a word */
+#define CIGN 14 /* character should be ignored */
#if ENABLE_ASH_ALIAS
-#define SYNBASE 130
-#define PEOF -130
-#define PEOA -129
+#define SYNBASE 130
+#define PEOF -130
+#define PEOA -129
#define PEOA_OR_PEOF PEOA
#else
-#define SYNBASE 129
-#define PEOF -129
+#define SYNBASE 129
+#define PEOF -129
#define PEOA_OR_PEOF PEOF
#endif
indx = 0;
else
#endif
-#define U_C(c) ((unsigned char)(c))
if ((unsigned char)c >= (unsigned char)(CTLESC)
&& (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
) {
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 UNUSED_PARAM, 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
int i;
struct job *prev_job; /* previous job */
};
-static pid_t backgndpid; /* pid of last background process */
-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
-enum { jobctl = 0 };
+enum { doing_jobctl = 0 };
#define setjobctl(on) do {} while (0)
#else
-static smallint jobctl; /* true if doing job control */
+static smallint doing_jobctl; //references:8
static void setjobctl(int);
#endif
static void
setsignal(int signo)
{
- int action;
- char *t, tsig;
+ char *t;
+ char cur_act, new_act;
struct sigaction act;
t = trap[signo];
- if (t == NULL)
- action = S_DFL;
- else if (*t != '\0')
- action = S_CATCH;
- else
- action = S_IGN;
- if (rootshell && action == S_DFL) {
+ new_act = S_DFL;
+ if (t != NULL) { /* trap for this sig is set */
+ new_act = S_CATCH;
+ if (t[0] == '\0') /* trap is "": ignore this sig */
+ new_act = S_IGN;
+ }
+
+ if (rootshell && new_act == S_DFL) {
switch (signo) {
case SIGINT:
if (iflag || minusc || sflag == 0)
- action = S_CATCH;
+ new_act = S_CATCH;
break;
case SIGQUIT:
#if DEBUG
if (debug)
break;
#endif
- /* FALLTHROUGH */
+ /* man bash:
+ * "In all cases, bash ignores SIGQUIT. Non-builtin
+ * commands run by bash have signal handlers
+ * set to the values inherited by the shell
+ * from its parent". */
+ new_act = S_IGN;
+ break;
case SIGTERM:
if (iflag)
- action = S_IGN;
+ new_act = S_IGN;
break;
#if JOBS
case SIGTSTP:
case SIGTTOU:
if (mflag)
- action = S_IGN;
+ new_act = S_IGN;
break;
#endif
}
}
+//TODO: if !rootshell, we reset SIGQUIT to DFL,
+//whereas we have to restore it to what shell got on entry
+//from the parent. See comment above
t = &sigmode[signo - 1];
- tsig = *t;
- if (tsig == 0) {
- /*
- * current setting unknown
- */
- if (sigaction(signo, 0, &act) == -1) {
- /*
- * Pretend it worked; maybe we should give a warning
- * here, but other shells don't. We don't alter
- * sigmode, so that we retry every time.
- */
+ cur_act = *t;
+ if (cur_act == 0) {
+ /* current setting is not yet known */
+ if (sigaction(signo, NULL, &act)) {
+ /* pretend it worked; maybe we should give a warning,
+ * but other shells don't. We don't alter sigmode,
+ * so we retry every time.
+ * btw, in Linux it never fails. --vda */
return;
}
if (act.sa_handler == SIG_IGN) {
+ cur_act = 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 */
+ cur_act = S_IGN; /* don't hard ignore these */
+ }
}
}
- if (tsig == S_HARD_IGN || tsig == action)
+ if (cur_act == S_HARD_IGN || cur_act == new_act)
return;
- switch (action) {
+
+ act.sa_handler = SIG_DFL;
+ switch (new_act) {
case S_CATCH:
act.sa_handler = onsig;
+ act.sa_flags = 0; /* matters only if !DFL and !IGN */
+ sigfillset(&act.sa_mask); /* ditto */
break;
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);
+
+ *t = new_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 */
-static int initialpgrp;
-static int ttyfd = -1;
+static int initialpgrp; //references:2
+static int ttyfd = -1; //5
#endif
/* array of jobs */
-static struct job *jobtab;
+static struct job *jobtab; //5
/* size of array */
-static unsigned njobs;
+static unsigned njobs; //4
/* current job */
-static struct job *curjob;
+static struct job *curjob; //lots
/* number of presumed living untracked jobs */
-static int jobless;
+static int jobless; //4
static void
set_curjob(struct job *jp, unsigned mode)
/*
* 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;
xtcsetpgrp(int fd, pid_t pgrp)
{
if (tcsetpgrp(fd, pgrp))
- ash_msg_and_raise_error("cannot set tty process group (%m)");
+ ash_msg_and_raise_error("can't set tty process group (%m)");
}
/*
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;
+ /* fd is a tty at this point */
close_on_exec_on(fd);
do { /* while we are in the background */
pgrp = tcgetpgrp(fd);
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 UNUSED_PARAM, char **argv)
{
struct job *jp;
FILE *out;
return col;
}
-/*
- * Do a wait system call. If job control is compiled in, we accept
- * stopped processes. If block is zero, we return a value of zero
- * rather than blocking.
- *
- * System V doesn't have a non-blocking wait system call. It does
- * have a SIGCLD signal that is sent to a process when one of it's
- * children dies. The obvious way to use SIGCLD would be to install
- * a handler for SIGCLD which simply bumped a counter when a SIGCLD
- * was received, and have waitproc bump another counter when it got
- * the status of a process. Waitproc would then know that a wait
- * system call would not block if the two counters were different.
- * This approach doesn't work because if a process has children that
- * have not been waited for, System V will send it a SIGCLD when it
- * installs a signal handler for SIGCLD. What this means is that when
- * a child exits, the shell will be sent SIGCLD signals continuously
- * until is runs out of stack space, unless it does a wait call before
- * restoring the signal handler. The code below takes advantage of
- * this (mis)feature by installing a signal handler for SIGCLD and
- * then checking to see whether it was called. If there are any
- * children to be waited for, it will be.
- *
- * If neither SYSV nor BSD is defined, we don't implement nonblocking
- * waits at all. In this case, the user will not be informed when
- * a background process until the next time she runs a real program
- * (as opposed to running a builtin command or just typing return),
- * and the jobs command may give out of date information.
- */
-static int
-waitproc(int block, int *status)
-{
- int flags = 0;
-
-#if JOBS
- if (jobctl)
- flags |= WUNTRACED;
-#endif
- if (block == 0)
- flags |= WNOHANG;
- return wait3(status, flags, (struct rusage *)NULL);
-}
-
-/*
- * 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));
+ TRACE(("dowait(0x%x) called\n", wait_flags));
+
+ /* Do a wait system call. If job control is compiled in, we accept
+ * stopped processes. wait_flags may have WNOHANG, preventing blocking.
+ * NB: _not_ safe_waitpid, we need to detect EINTR */
+ pid = waitpid(-1, &status,
+ (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
+ TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
if (pid <= 0)
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);
}
}
return pid;
}
+static int
+blocking_wait_with_raise_on_sig(struct job *job)
+{
+ pid_t pid = dowait(DOWAIT_BLOCK, job);
+ if (pid <= 0 && pendingsig)
+ raise_exception(EXSIG);
+ return pid;
+}
+
#if JOBS
static void
showjob(FILE *out, struct job *jp, int mode)
TRACE(("showjobs(%x) called\n", mode));
- /* If not even one one job changed, there is nothing to do */
- while (dowait(DOWAIT_NORMAL, NULL) > 0)
+ /* Handle all finished jobs */
+ while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
continue;
for (jp = curjob; jp; jp = jp->prev_job) {
}
static int
-jobscmd(int argc, char **argv)
+jobscmd(int argc UNUSED_PARAM, char **argv)
{
int mode, m;
}
static int
-waitcmd(int argc, char **argv)
+waitcmd(int argc UNUSED_PARAM, 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);
+ /* man bash:
+ * "When bash is waiting for an asynchronous command via
+ * the wait builtin, the reception of a signal for which a trap
+ * has been set will cause the wait builtin to return immediately
+ * with an exit status greater than 128, immediately after which
+ * the trap is executed."
+ * Do we do it that way? */
+ blocking_wait_with_raise_on_sig(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);
+ blocking_wait_with_raise_on_sig(NULL);
job->waited = 1;
retval = getstatus(job);
- repeat:
- ;
+ 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);
#if JOBS
/* jp->jobctl is a bitfield.
* "jp->jobctl |= jobctl" likely to give awful code */
- if (jobctl)
+ 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++;
union node *np;
struct nodelist *lp;
const char *p;
- char s[2];
if (!n)
return;
case NAPPEND:
p = ">>";
goto redir;
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
case NTOFD:
p = ">&";
goto redir;
case NFROMTO:
p = "<>";
redir:
- s[0] = n->nfile.fd + '0';
- s[1] = '\0';
- cmdputs(s);
+ cmdputs(utoa(n->nfile.fd));
cmdputs(p);
if (n->type == NTOFD || n->type == NFROMFD) {
- s[0] = n->ndup.dupfd + '0';
- p = s;
- goto dotail2;
+ cmdputs(utoa(n->ndup.dupfd));
+ break;
}
n = n->nfile.fname;
goto donode;
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;
/* 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;
oldlvl = shlvl;
shlvl++;
+ /* man bash: "Non-builtin commands run by bash have signal handlers
+ * set to the values inherited by the shell from its parent".
+ * Do we do it correctly? */
+
closescript();
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;
pgrp = getpid();
else
pgrp = jp->ps[0].pid;
- /* This can fail because we are doing it in the parent also */
- (void)setpgid(0, pgrp);
+ /* this can fail because we are doing it in the parent also */
+ setpgid(0, pgrp);
if (mode == FORK_FG)
xtcsetpgrp(ttyfd, pgrp);
setsignal(SIGTSTP);
} else
#endif
if (mode == FORK_BG) {
+ /* man bash: "When job control is not in effect,
+ * asynchronous commands ignore SIGINT and SIGQUIT" */
ignoresig(SIGINT);
ignoresig(SIGQUIT);
if (jp->nprocs == 0) {
ash_msg_and_raise_error("can't open %s", bb_dev_null);
}
}
- if (!oldlvl && iflag) {
- setsignal(SIGINT);
+ if (!oldlvl) {
+ if (iflag) { /* why if iflag only? */
+ setsignal(SIGINT);
+ setsignal(SIGTERM);
+ }
+ /* man bash:
+ * "In all cases, bash ignores SIGQUIT. Non-builtin
+ * commands run by bash have signal handlers
+ * set to the values inherited by the shell
+ * from its parent".
+ * Take care of the second rule: */
setsignal(SIGQUIT);
- setsignal(SIGTERM);
}
for (jp = curjob; jp; jp = jp->prev_job)
freejob(jp);
}
/* 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
}
TRACE(("Fork failed, errno=%d", errno));
if (jp)
freejob(jp);
- ash_msg_and_raise_error("cannot fork");
+ ash_msg_and_raise_error("can't fork");
}
if (pid == 0)
- forkchild(jp, n, mode);
+ forkchild(jp, /*n,*/ mode);
else
forkparent(jp, n, mode, pid);
return pid;
/*
* Wait for job to finish.
*
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell. This means that an infinite loop started by an inter-
- * active user may be hard to kill. With job control turned off, an
- * interactive user may place an interactive program inside a loop. If
- * the interactive program catches interrupts, the user doesn't want
+ * Under job control we have the problem that while a child process
+ * is running interrupts generated by the user are sent to the child
+ * but not to the shell. This means that an infinite loop started by
+ * an interactive user may be hard to kill. With job control turned off,
+ * an interactive user may place an interactive program inside a loop.
+ * If the interactive program catches interrupts, the user doesn't want
* these interrupts to also abort the loop. The approach we take here
* is to have the shell ignore interrupt signals while waiting for a
* foreground process to terminate, and then send itself an interrupt
int st;
TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+
+ INT_OFF;
while (jp->state == JOBRUNNING) {
+ /* In non-interactive shells, we _can_ get
+ * a keyboard signal here and be EINTRed,
+ * but we just loop back, waiting for command to complete.
+ *
+ * man bash:
+ * "If bash is waiting for a command to complete and receives
+ * a signal for which a trap has been set, the trap
+ * will not be executed until the command completes."
+ *
+ * Reality is that even if trap is not set, bash
+ * will not act on the signal until command completes.
+ * Try this. sleep5intoff.c:
+ * #include <signal.h>
+ * #include <unistd.h>
+ * int main() {
+ * sigset_t set;
+ * sigemptyset(&set);
+ * sigaddset(&set, SIGINT);
+ * sigaddset(&set, SIGQUIT);
+ * sigprocmask(SIG_BLOCK, &set, NULL);
+ * sleep(5);
+ * return 0;
+ * }
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^C^C^C^C <--- pressing ^C once a second
+ * $ _
+ * $ bash -c './sleep5intoff; echo hi'
+ * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
+ * $ _
+ */
dowait(DOWAIT_BLOCK, jp);
}
+ INT_ON;
+
st = getstatus(jp);
#if JOBS
if (jp->jobctl) {
* 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
-# define PIPESIZE PIPE_BUF
-#endif
/*
* Open a file in noclobber mode.
ash_msg_and_raise_error("pipe call failed");
if (redir->type == NHERE) {
len = strlen(redir->nhere.doc->narg.text);
- if (len <= PIPESIZE) {
+ if (len <= PIPE_BUF) {
full_write(pip[1], redir->nhere.doc->narg.text, len);
goto out;
}
}
if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+ /* child */
close(pip[0]);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
- signal(SIGTSTP, SIG_IGN);
-#endif
+ ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
+ ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
+ ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
+ ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
signal(SIGPIPE, SIG_DFL);
if (redir->type == NHERE)
full_write(pip[1], redir->nhere.doc->narg.text, len);
- else
+ else /* NXHERE */
expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
+ _exit(EXIT_SUCCESS);
}
out:
close(pip[1]);
goto ecreate;
break;
case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
/* Take care of noclobber mode. */
if (Cflag) {
fname = redir->nfile.expfname;
abort();
#endif
/* Fall through to eliminate warning. */
- case NTOFD:
- case NFROMFD:
- f = -1;
- break;
+/* Our single caller does this itself */
+// case NTOFD:
+// case NFROMFD:
+// f = -1;
+// break;
case NHERE:
case NXHERE:
f = openhere(redir);
return f;
ecreate:
- ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
+ ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
eopen:
- ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
+ ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
}
/*
* if the source file descriptor is closed, EMPTY if there are no unused
* file descriptors left.
*/
+/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
+ * old code was doing close(to) prior to copyfd() to achieve the same */
+enum {
+ COPYFD_EXACT = (int)~(INT_MAX),
+ COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
+};
static int
copyfd(int from, int to)
{
int newfd;
- newfd = fcntl(from, F_DUPFD, to);
+ if (to & COPYFD_EXACT) {
+ to &= ~COPYFD_EXACT;
+ /*if (from != to)*/
+ newfd = dup2(from, to);
+ } else {
+ newfd = fcntl(from, F_DUPFD, to);
+ }
if (newfd < 0) {
if (errno == EMFILE)
return EMPTY;
+ /* Happens when source fd is not open: try "echo >&99" */
ash_msg_and_raise_error("%d: %m", from);
}
return newfd;
}
-static void
-dupredirect(union node *redir, int f)
+/* Struct def and variable are moved down to the first usage site */
+struct two_fd_t {
+ int orig, copy;
+};
+struct redirtab {
+ struct redirtab *next;
+ int nullredirs;
+ int pair_count;
+ struct two_fd_t two_fd[0];
+};
+#define redirlist (G_var.redirlist)
+
+static int need_to_remember(struct redirtab *rp, int fd)
{
- int fd = redir->nfile.fd;
+ int i;
+
+ if (!rp) /* remembering was not requested */
+ return 0;
- if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
- if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
- copyfd(redir->ndup.dupfd, fd);
+ for (i = 0; i < rp->pair_count; i++) {
+ if (rp->two_fd[i].orig == fd) {
+ /* already remembered */
+ return 0;
}
- return;
}
+ return 1;
+}
- if (f != fd) {
- copyfd(f, fd);
- close(f);
+/* "hidden" fd is a fd used to read scripts, or a copy of such */
+static int is_hidden_fd(struct redirtab *rp, int fd)
+{
+ int i;
+ struct parsefile *pf;
+
+ if (fd == -1)
+ return 0;
+ pf = g_parsefile;
+ while (pf) {
+ if (fd == pf->fd) {
+ return 1;
+ }
+ pf = pf->prev;
}
+ if (!rp)
+ return 0;
+ fd |= COPYFD_RESTORE;
+ for (i = 0; i < rp->pair_count; i++) {
+ if (rp->two_fd[i].copy == fd) {
+ return 1;
+ }
+ }
+ return 0;
}
/*
static void
redirect(union node *redir, int flags)
{
- union node *n;
struct redirtab *sv;
+ int sv_pos;
int i;
int fd;
int newfd;
+ int copied_fd2 = -1;
- nullredirs++;
+ g_nullredirs++;
if (!redir) {
return;
}
+
sv = NULL;
+ sv_pos = 0;
INT_OFF;
if (flags & REDIR_PUSH) {
- sv = ckmalloc(sizeof(*sv));
+ union node *tmp = redir;
+ do {
+ sv_pos++;
+#if ENABLE_ASH_BASH_COMPAT
+ if (redir->nfile.type == NTO2)
+ sv_pos++;
+#endif
+ tmp = tmp->nfile.next;
+ } while (tmp);
+ sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
sv->next = redirlist;
+ sv->pair_count = sv_pos;
redirlist = sv;
- sv->nullredirs = nullredirs - 1;
- for (i = 0; i < 10; i++)
- sv->renamed[i] = EMPTY;
- nullredirs = 0;
+ sv->nullredirs = g_nullredirs - 1;
+ g_nullredirs = 0;
+ while (sv_pos > 0) {
+ sv_pos--;
+ sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
+ }
}
- n = redir;
+
do {
- fd = n->nfile.fd;
- if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
- && n->ndup.dupfd == fd)
- continue; /* redirect from/to same file descriptor */
-
- newfd = openredirect(n);
- if (fd == newfd) {
- /* Descriptor wasn't open before redirect.
- * Mark it for close in the future */
- if (sv && sv->renamed[fd] == EMPTY)
- sv->renamed[fd] = CLOSED;
- continue;
+ fd = redir->nfile.fd;
+ if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+ int right_fd = redir->ndup.dupfd;
+ /* redirect from/to same file descriptor? */
+ if (right_fd == fd)
+ continue;
+ /* echo >&10 and 10 is a fd opened to the sh script? */
+ if (is_hidden_fd(sv, right_fd)) {
+ errno = EBADF; /* as if it is closed */
+ ash_msg_and_raise_error("%d: %m", right_fd);
+ }
+ newfd = -1;
+ } else {
+ newfd = openredirect(redir); /* always >= 0 */
+ if (fd == newfd) {
+ /* Descriptor wasn't open before redirect.
+ * Mark it for close in the future */
+ if (need_to_remember(sv, fd)) {
+ goto remember_to_close;
+ }
+ continue;
+ }
}
- if (sv && sv->renamed[fd] == EMPTY) {
+#if ENABLE_ASH_BASH_COMPAT
+ redirect_more:
+#endif
+ if (need_to_remember(sv, fd)) {
+ /* Copy old descriptor */
i = fcntl(fd, F_DUPFD, 10);
-
+/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
+ * are closed in popredir() in the child, preventing them from leaking
+ * into child. (popredir() also cleans up the mess in case of failures)
+ */
if (i == -1) {
i = errno;
if (i != EBADF) {
- close(newfd);
+ /* Strange error (e.g. "too many files" EMFILE?) */
+ if (newfd >= 0)
+ close(newfd);
errno = i;
ash_msg_and_raise_error("%d: %m", fd);
/* NOTREACHED */
}
- } else {
- sv->renamed[fd] = i;
+ /* EBADF: it is not open - good, remember to close it */
+ remember_to_close:
+ i = CLOSED;
+ } else { /* fd is open, save its copy */
+ /* "exec fd>&-" should not close fds
+ * which point to script file(s).
+ * Force them to be restored afterwards */
+ if (is_hidden_fd(sv, fd))
+ i |= COPYFD_RESTORE;
+ }
+ if (fd == 2)
+ copied_fd2 = i;
+ sv->two_fd[sv_pos].orig = fd;
+ sv->two_fd[sv_pos].copy = i;
+ sv_pos++;
+ }
+ if (newfd < 0) {
+ /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
+ if (redir->ndup.dupfd < 0) { /* "fd>&-" */
close(fd);
+ } else {
+ copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
}
- } else {
- close(fd);
+ } else if (fd != newfd) { /* move newfd to fd */
+ copyfd(newfd, fd | COPYFD_EXACT);
+#if ENABLE_ASH_BASH_COMPAT
+ if (!(redir->nfile.type == NTO2 && fd == 2))
+#endif
+ close(newfd);
+ }
+#if ENABLE_ASH_BASH_COMPAT
+ if (redir->nfile.type == NTO2 && fd == 1) {
+ /* We already redirected it to fd 1, now copy it to 2 */
+ newfd = 1;
+ fd = 2;
+ goto redirect_more;
}
- dupredirect(n, newfd);
- } while ((n = n->nfile.next));
+#endif
+ } while ((redir = redir->nfile.next) != NULL);
+
INT_ON;
- if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0)
- preverrout_fd = sv->renamed[2];
+ if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
+ preverrout_fd = copied_fd2;
}
/*
* Undo the effects of the last redirection.
*/
static void
-popredir(int drop)
+popredir(int drop, int restore)
{
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) {
+ for (i = 0; i < rp->pair_count; i++) {
+ int fd = rp->two_fd[i].orig;
+ int copy = rp->two_fd[i].copy;
+ if (copy == CLOSED) {
if (!drop)
- close(i);
+ close(fd);
continue;
}
- if (rp->renamed[i] != EMPTY) {
- if (!drop) {
- close(i);
- copyfd(rp->renamed[i], i);
+ if (copy != EMPTY) {
+ if (!drop || (restore && (copy & COPYFD_RESTORE))) {
+ copy &= ~COPYFD_RESTORE;
+ /*close(fd);*/
+ copyfd(copy, fd | COPYFD_EXACT);
}
- close(rp->renamed[i]);
+ close(copy);
}
}
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);
+ popredir(drop, /*restore:*/ 0);
}
}
struct jmploc jmploc;
SAVE_INT(saveint);
- err = setjmp(jmploc.loc) * 2;
+ /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
+ err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
if (!err) {
exception_handler = &jmploc;
redirect(redir, flags);
* We have to deal with backquotes, shell variables, and file metacharacters.
*/
+#if ENABLE_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#define arith_t_type long long
+#else
+typedef long arith_t;
+#define arith_t_type long
+#endif
+
+#if ENABLE_ASH_MATH_SUPPORT
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
/*
* expandarg flags
*/
}
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 uint8_t back_exitstatus; /* exit status of backquoted command */
#define EV_EXIT 01 /* exit after evaluating tree */
static void evaltree(union node *, int);
result->buf = NULL;
result->nleft = 0;
result->jp = NULL;
- if (n == NULL) {
+ if (n == NULL)
goto out;
- }
saveherefd = herefd;
herefd = -1;
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]);
if (pip[1] != 1) {
- close(1);
- copyfd(pip[1], 1);
+ /*close(1);*/
+ copyfd(pip[1], 1 | COPYFD_EXACT);
close(pip[1]);
}
eflag = 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;
int flag;
int len;
- /* ifsfree(); */
+ /* ifsfree(); */
/*
* This routine is slightly over-complicated for
#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[] ALIGN1 = {
'=',
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 UNUSED_PARAM, 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++;
return 0;
}
-static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN;
+static void varunset(const char *, const char *, const char *, int) NORETURN;
static void
varunset(const char *end, const char *var, const char *umsg, int varflags)
{
if (*end == CTLENDVAR) {
if (varflags & VSNUL)
tail = " or null";
- } else
+ } else {
msg = umsg;
+ }
}
ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
+#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)
+subevalvar(char *p, char *str, int strloc, int subtype,
+ int startloc, int varflags, int quotes, struct strlist *var_str_list)
{
+ struct nodelist *saveargbackq = argbackq;
char *startp;
char *loc;
- int saveherefd = herefd;
- struct nodelist *saveargbackq = argbackq;
- int amount;
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) {
- if (dp->d_name[0] == '.' && ! matchdot)
+ while (!intpending && (dp = readdir(dirp)) != NULL) {
+ if (dp->d_name[0] == '.' && !matchdot)
continue;
if (pmatch(start, dp->d_name)) {
if (atend) {
}
}
closedir(dirp);
- if (! atend)
+ if (!atend)
endname[-1] = '/';
}
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[] ALIGN1 = {
'*', '?', '[', 0
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)
- run_appletstruct_and_exit(a, argv);
- /* re-exec ourselves with the new arguments */
- execve(bb_busybox_exec_path, argv, envp);
- /* If they called chroot or otherwise made the binary no longer
- * executable, fall through */
+ if (applet_no >= 0) {
+ if (APPLET_IS_NOEXEC(applet_no)) {
+ while (*envp)
+ putenv(*envp++);
+ 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 **, const char *, int) 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], '/')
+ clearredir(/*drop:*/ 1);
+ 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
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 UNUSED_PARAM, 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
int c;
enum {
else if (c != 'p')
abort();
#endif
- if (verify)
+ /* Mimic bash: just "command -v" doesn't complain, it's a nop */
+ if (verify && (*argptr != NULL)) {
return describe_command(*argptr, verify - VERIFY_BRIEF);
+ }
return 0;
}
/* ============ eval.c */
-static int funcblocksize; /* size of structures in function */
-static int funcstringsize; /* size of strings in node */
-static void *funcblock; /* block to allocate function from */
-static char *funcstring; /* block to allocate strings from */
+static int funcblocksize; /* size of structures in function */
+static int funcstringsize; /* size of strings in node */
+static void *funcblock; /* block to allocate function from */
+static char *funcstring; /* block to allocate strings from */
/* flags in argument to evaltree */
-#define EV_EXIT 01 /* exit after evaluating tree */
-#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
+#define EV_EXIT 01 /* exit after evaluating tree */
+#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04 /* command executing within back quotes */
-static const short nodesize[26] = {
- SHELL_ALIGN(sizeof(struct ncmd)),
- SHELL_ALIGN(sizeof(struct npipe)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nredir)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nif)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nbinary)),
- SHELL_ALIGN(sizeof(struct nfor)),
- SHELL_ALIGN(sizeof(struct ncase)),
- SHELL_ALIGN(sizeof(struct nclist)),
- SHELL_ALIGN(sizeof(struct narg)),
- SHELL_ALIGN(sizeof(struct narg)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct nfile)),
- SHELL_ALIGN(sizeof(struct ndup)),
- SHELL_ALIGN(sizeof(struct ndup)),
- SHELL_ALIGN(sizeof(struct nhere)),
- SHELL_ALIGN(sizeof(struct nhere)),
- SHELL_ALIGN(sizeof(struct nnot)),
+static const short nodesize[N_NUMBER] = {
+ [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
+ [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
+ [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
+ [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
+ [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
+ [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
+ [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
+ [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
+ [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
+ [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
+ [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
+#if ENABLE_ASH_BASH_COMPAT
+ [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
+#endif
+ [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
+ [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
+ [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
+ [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
+ [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
+ [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
};
static void calcsize(union node *n);
calcsize(n->narg.next);
break;
case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
case NCLOBBER:
case NFROM:
case NFROMTO:
break;
case NPIPE:
new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
- new->npipe.backgnd = n->npipe.backgnd;
+ new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
break;
case NREDIR:
case NBACKGND:
new->narg.next = copynode(n->narg.next);
break;
case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
case NCLOBBER:
case NFROM:
case NFROMTO:
#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;
xbarrier();
- for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+ for (i = 1, q = gotsig; i < NSIG; i++, q++) {
if (!*q)
continue;
*q = '\0';
- p = trap[i + 1];
+ p = trap[i];
if (!p)
continue;
skip = evalstring(p, SKIPEVAL);
exitstatus = savestatus;
if (skip)
- break;
+ return skip;
}
- return skip;
+ return 0;
}
/* forward declarations - evaluation is fairly recursive business... */
static void
evaltree(union node *n, int flags)
{
+
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
int checkexit = 0;
void (*evalfn)(union node *, int);
- unsigned isor;
int status;
+
if (n == NULL) {
TRACE(("evaltree(NULL) called\n"));
- goto out;
+ goto out1;
}
TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
getpid(), n, n->type, flags));
+
+ exception_handler = &jmploc;
+ {
+ int err = setjmp(jmploc.loc);
+ if (err) {
+ /* if it was a signal, check for trap handlers */
+ if (exception == EXSIG)
+ goto out;
+ /* continue on the way out */
+ exception_handler = savehandler;
+ longjmp(exception_handler->loc, err);
+ }
+ }
+
switch (n->type) {
default:
#if DEBUG
evaltree(n->nredir.n, flags & EV_TESTED);
status = exitstatus;
}
- popredir(0);
+ popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
goto setstatus;
case NCMD:
evalfn = evalcommand;
goto calleval;
case NAND:
case NOR:
- case NSEMI:
+ case NSEMI: {
+
#if NAND + 1 != NOR
#error NAND + 1 != NOR
#endif
#if NOR + 1 != NSEMI
#error NOR + 1 != NSEMI
#endif
- isor = n->type - NAND;
+ unsigned is_or = n->type - NAND;
evaltree(
n->nbinary.ch1,
- (flags | ((isor >> 1) - 1)) & EV_TESTED
+ (flags | ((is_or >> 1) - 1)) & EV_TESTED
);
- if (!exitstatus == isor)
+ if (!exitstatus == is_or)
break;
if (!evalskip) {
n = n->nbinary.ch2;
break;
}
break;
+ }
case NIF:
evaltree(n->nif.test, EV_TESTED);
if (evalskip)
exitstatus = status;
break;
}
+
out:
- if ((checkexit & exitstatus))
+ exception_handler = savehandler;
+ out1:
+ if (checkexit & exitstatus)
evalskip |= SKIPEVAL;
else if (pendingsig && dotrap())
goto exexit;
#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;
/* never returns */
}
status = 0;
- if (! backgnd)
+ if (!backgnd)
status = waitforjob(jp);
exitstatus = status;
INT_ON;
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:
case NFROM:
case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+ case NTO2:
+#endif
case NCLOBBER:
case NAPPEND:
expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+#if ENABLE_ASH_BASH_COMPAT
+ store_expfname:
+#endif
redir->nfile.expfname = fn.list->text;
break;
case NFROMFD:
- case NTOFD:
+ case NTOFD: /* >& */
if (redir->ndup.vname) {
expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
if (fn.list == NULL)
ash_msg_and_raise_error("redir error");
+#if ENABLE_ASH_BASH_COMPAT
+//FIXME: we used expandarg with different args!
+ if (!isdigit_str9(fn.list->text)) {
+ /* >&file, not >&fd */
+ if (redir->nfile.fd != 1) /* 123>&file - BAD */
+ ash_msg_and_raise_error("redir error");
+ redir->type = NTO2;
+ goto store_expfname;
+ }
+#endif
fixredir(redir, fn.list->text, 1);
}
break;
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);
ash_msg_and_raise_error("pipe call failed");
}
}
- if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+ if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
INT_ON;
if (pip[1] >= 0) {
close(pip[0]);
prevfd = pip[0];
close(pip[1]);
}
- if (n->npipe.backgnd == 0) {
+ if (n->npipe.pipe_backgnd == 0) {
exitstatus = waitforjob(jp);
TRACE(("evalpipe: job done exit status %d\n", exitstatus));
}
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 UNUSED_PARAM, char **argv)
{
char *name;
}
static int
-falsecmd(int argc, char **argv)
+falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
return 1;
}
static int
-truecmd(int argc, char **argv)
+truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
return 0;
}
static int
-execcmd(int argc, char **argv)
+execcmd(int argc UNUSED_PARAM, 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 UNUSED_PARAM, 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
/* 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;
char *lastarg;
const char *path;
int spclbltin;
- int cmd_is_exec;
int status;
char **nargv;
struct builtincmd *bcmd;
- int pseudovarflag = 0;
+ smallint cmd_is_exec;
+ smallint pseudovarflag = 0;
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
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;
}
sp = arglist.list;
}
- full_write(preverrout_fd, "\n", 1);
+ safe_write(preverrout_fd, "\n", 1);
}
cmd_is_exec = 0;
for (;;) {
find_command(argv[0], &cmdentry, cmd_flag, path);
if (cmdentry.cmdtype == CMDUNKNOWN) {
- status = 127;
flush_stderr();
+ status = 127;
goto bail;
}
if (spclbltin < 0)
spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
if (cmdentry.u.cmd == EXECCMD)
- cmd_is_exec++;
+ cmd_is_exec = 1;
#if ENABLE_ASH_CMDCMD
if (cmdentry.u.cmd == COMMANDCMD) {
path = oldpath;
/* Execute the command. */
switch (cmdentry.cmdtype) {
default:
+
+#if ENABLE_FEATURE_SH_NOFORK
+/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
+ * Why "fork off a child process if necessary" doesn't apply to 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;
}
listsetvar(list, i);
}
+ /* Tight loop with builtins only:
+ * "while kill -0 $child; do true; done"
+ * will never exit even if $child died, unless we do this
+ * to reap the zombie and make kill detect that it's gone: */
+ dowait(DOWAIT_NONBLOCK, NULL);
+
if (evalbltin(cmdentry.u.cmd, argc, argv)) {
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);
case CMDFUNCTION:
listsetvar(varlist.list, 0);
+ /* See above for the rationale */
+ dowait(DOWAIT_NONBLOCK, NULL);
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
break;
}
out:
- popredir(cmd_is_exec);
- if (lastarg)
+ popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
+ if (lastarg) {
/* dsl: I think this is intended to be used to support
* '_' in 'vi' command mode during line editing...
* However I implemented that within libedit itself.
*/
setvar("_", lastarg, 0);
+ }
popstackmark(&smark);
}
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 UNUSED_PARAM, 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]);
* This implements the input routines used by the parser.
*/
-#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
-
enum {
INPUT_PUSH_FILE = 1,
INPUT_NOFILE_OK = 2,
};
-static int plinno = 1; /* input line number */
-/* number of characters left in input buffer */
-static int parsenleft; /* copy of parsefile->nleft */
-static int parselleft; /* copy of parsefile->lleft */
-/* 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
#define CHKNL 0x4
+/*
+ * 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, struct alias *ap)
+{
+ struct strpush *sp;
+ int len;
+
+ len = strlen(s);
+ INT_OFF;
+ if (g_parsefile->strpush) {
+ sp = ckzalloc(sizeof(*sp));
+ sp->prev = g_parsefile->strpush;
+ } else {
+ sp = &(g_parsefile->basestrpush);
+ }
+ g_parsefile->strpush = sp;
+ sp->prev_string = g_parsefile->next_to_pgetc;
+ sp->prev_left_in_line = g_parsefile->left_in_line;
+#if ENABLE_ASH_ALIAS
+ sp->ap = ap;
+ if (ap) {
+ ap->flag |= ALIASINUSE;
+ sp->string = s;
+ }
+#endif
+ g_parsefile->next_to_pgetc = s;
+ g_parsefile->left_in_line = len;
+ INT_ON;
+}
+
static void
popstring(void)
{
- struct strpush *sp = parsefile->strpush;
+ struct strpush *sp = g_parsefile->strpush;
INT_OFF;
#if ENABLE_ASH_ALIAS
if (sp->ap) {
- if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+ if (g_parsefile->next_to_pgetc[-1] == ' '
+ || g_parsefile->next_to_pgetc[-1] == '\t'
+ ) {
checkkwd |= CHKALIAS;
}
if (sp->string != sp->ap->val) {
}
}
#endif
- parsenextc = sp->prevstring;
- parsenleft = sp->prevnleft;
-/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
- parsefile->strpush = sp->prev;
- if (sp != &(parsefile->basestrpush))
+ g_parsefile->next_to_pgetc = sp->prev_string;
+ g_parsefile->left_in_line = sp->prev_left_in_line;
+ g_parsefile->strpush = sp->prev;
+ if (sp != &(g_parsefile->basestrpush))
free(sp);
INT_ON;
}
+//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
+//it peeks whether it is &>, and then pushes back both chars.
+//This function needs to save last *next_to_pgetc to buf[0]
+//to make two pungetc() reliable. Currently,
+// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
static int
preadfd(void)
{
int nr;
- char *buf = parsefile->buf;
- parsenextc = buf;
+ char *buf = g_parsefile->buf;
- retry:
+ g_parsefile->next_to_pgetc = buf;
#if ENABLE_FEATURE_EDITING
- if (!iflag || parsefile->fd)
- nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+ retry:
+ if (!iflag || g_parsefile->fd != STDIN_FILENO)
+ 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);
- if (flags >= 0 && flags & O_NONBLOCK) {
- flags &=~ O_NONBLOCK;
+ 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;
}
* Refill the input buffer and return the next input character:
*
* 1) If a string was pushed back on the input, pop it;
- * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
- * from a string so we can't refill the buffer, return EOF.
+ * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
+ * or we are reading from a string so we can't refill the buffer,
+ * return EOF.
* 3) If the is more stuff in this buffer, use it else call read to fill it.
* 4) Process input up to the next newline, deleting nul characters.
*/
+//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
+#define pgetc_debug(...) ((void)0)
static int
preadbuffer(void)
{
char *q;
int more;
- char savec;
- while (parsefile->strpush) {
+ while (g_parsefile->strpush) {
#if ENABLE_ASH_ALIAS
- if (parsenleft == -1 && parsefile->strpush->ap &&
- parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
+ if (g_parsefile->left_in_line == -1
+ && g_parsefile->strpush->ap
+ && g_parsefile->next_to_pgetc[-1] != ' '
+ && g_parsefile->next_to_pgetc[-1] != '\t'
+ ) {
+ pgetc_debug("preadbuffer PEOA");
return PEOA;
}
#endif
popstring();
- if (--parsenleft >= 0)
- return signed_char2int(*parsenextc++);
- }
- if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+ /* try "pgetc" now: */
+ pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
+ if (--g_parsefile->left_in_line >= 0)
+ return (unsigned char)(*g_parsefile->next_to_pgetc++);
+ }
+ /* on both branches above g_parsefile->left_in_line < 0.
+ * "pgetc" needs refilling.
+ */
+
+ /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
+ * pungetc() may increment it a few times.
+ * Assuming it won't increment it to less than -90.
+ */
+ if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
+ pgetc_debug("preadbuffer PEOF1");
+ /* even in failure keep left_in_line and next_to_pgetc
+ * in lock step, for correct multi-layer pungetc.
+ * left_in_line was decremented before preadbuffer(),
+ * must inc next_to_pgetc: */
+ g_parsefile->next_to_pgetc++;
return PEOF;
- flush_stdout_stderr();
+ }
- more = parselleft;
+ more = g_parsefile->left_in_buffer;
if (more <= 0) {
+ flush_stdout_stderr();
again:
more = preadfd();
if (more <= 0) {
- parselleft = parsenleft = EOF_NLEFT;
+ /* don't try reading again */
+ g_parsefile->left_in_line = -99;
+ pgetc_debug("preadbuffer PEOF2");
+ g_parsefile->next_to_pgetc++;
return PEOF;
}
}
- q = parsenextc;
-
- /* delete nul characters */
+ /* Find out where's the end of line.
+ * Set g_parsefile->left_in_line
+ * and g_parsefile->left_in_buffer acordingly.
+ * NUL chars are deleted.
+ */
+ q = g_parsefile->next_to_pgetc;
for (;;) {
- int c;
+ char c;
more--;
- c = *q;
- if (!c)
+ c = *q;
+ if (c == '\0') {
memmove(q, q + 1, more);
- else {
+ } else {
q++;
if (c == '\n') {
- parsenleft = q - parsenextc - 1;
+ g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
break;
}
}
if (more <= 0) {
- parsenleft = q - parsenextc - 1;
- if (parsenleft < 0)
+ g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
+ if (g_parsefile->left_in_line < 0)
goto again;
break;
}
}
- parselleft = more;
-
- savec = *q;
- *q = '\0';
+ g_parsefile->left_in_buffer = more;
if (vflag) {
- out2str(parsenextc);
+ char save = *q;
+ *q = '\0';
+ out2str(g_parsefile->next_to_pgetc);
+ *q = save;
}
- *q = savec;
-
- return signed_char2int(*parsenextc++);
+ pgetc_debug("preadbuffer at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
+ return (unsigned char)(*g_parsefile->next_to_pgetc++);
}
-#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer())
+#define pgetc_as_macro() \
+ (--g_parsefile->left_in_line >= 0 \
+ ? (unsigned char)(*g_parsefile->next_to_pgetc++) \
+ : preadbuffer() \
+ )
+
static int
pgetc(void)
{
+ pgetc_debug("pgetc_fast at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
return pgetc_as_macro();
}
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#define pgetc_macro() pgetc()
+#define pgetc_fast() pgetc()
#else
-#define pgetc_macro() pgetc_as_macro()
+#define pgetc_fast() pgetc_as_macro()
#endif
/*
pgetc2(void)
{
int c;
-
do {
- c = pgetc_macro();
+ pgetc_debug("pgetc_fast at %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
+ c = pgetc_fast();
} while (c == PEOA);
return c;
}
#else
-static int
-pgetc2(void)
-{
- return pgetc_macro();
-}
+#define pgetc2() pgetc()
#endif
/*
}
/*
- * Undo the last call to pgetc. Only one character may be pushed back.
- * PEOF may be pushed back.
- */
-static void
-pungetc(void)
-{
- parsenleft++;
- parsenextc--;
-}
-
-/*
- * Push a string back onto the input at this current parsefile level.
- * We handle aliases this way.
+ * Undo the last call to pgetc. Only one character may be pushed back.
+ * PEOF may be pushed back.
*/
static void
-pushstring(char *s, void *ap)
+pungetc(void)
{
- 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;
- } else
- sp = parsefile->strpush = &(parsefile->basestrpush);
- sp->prevstring = parsenextc;
- sp->prevnleft = parsenleft;
-#if ENABLE_ASH_ALIAS
- sp->ap = (struct alias *)ap;
- if (ap) {
- ((struct alias *)ap)->flag |= ALIASINUSE;
- sp->string = s;
- }
-#endif
- parsenextc = s;
- parsenleft = len;
- INT_ON;
+ g_parsefile->left_in_line++;
+ g_parsefile->next_to_pgetc--;
+ pgetc_debug("pushed back to %d:%p'%s'",
+ g_parsefile->left_in_line,
+ g_parsefile->next_to_pgetc,
+ g_parsefile->next_to_pgetc);
}
/*
{
struct parsefile *pf;
- parsefile->nleft = parsenleft;
- parsefile->lleft = parselleft;
- parsefile->nextc = parsenextc;
- parsefile->linno = plinno;
- pf = ckmalloc(sizeof(*pf));
- pf->prev = parsefile;
+ 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)
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;
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;
}
}
close_on_exec_on(fd);
if (push) {
pushfile();
- parsefile->buf = 0;
+ g_parsefile->buf = NULL;
}
- parsefile->fd = fd;
- if (parsefile->buf == NULL)
- parsefile->buf = ckmalloc(IBUFSIZ);
- parselleft = parsenleft = 0;
- plinno = 1;
+ g_parsefile->fd = fd;
+ if (g_parsefile->buf == NULL)
+ g_parsefile->buf = ckmalloc(IBUFSIZ);
+ g_parsefile->left_in_buffer = 0;
+ g_parsefile->left_in_line = 0;
+ g_parsefile->linno = 1;
}
/*
{
INT_OFF;
pushfile();
- parsenextc = string;
- parsenleft = strlen(string);
- parsefile->buf = NULL;
- plinno = 1;
+ g_parsefile->next_to_pgetc = string;
+ g_parsefile->left_in_line = strlen(string);
+ g_parsefile->buf = NULL;
+ g_parsefile->linno = 1;
INT_ON;
}
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 UNUSED_PARAM)
{
mail_var_path_changed = 1;
}
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;
+ }
+ 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));
+ }
}
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optnames(i),
- optlist[i] ? "on" : "off");
+ return 0;
}
static void
setoption(int flag, int val)
return;
}
}
- ash_msg_and_raise_error("illegal option -%c", flag);
+ ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
/* NOTREACHED */
}
-static void
+static int
options(int cmdline)
{
char *p;
if (c == 'c' && cmdline) {
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 == 'l')) { /* -l or +l == --login */
}
}
}
+ return 0;
}
/*
* The shift builtin command.
*/
static int
-shiftcmd(int argc, char **argv)
+shiftcmd(int argc UNUSED_PARAM, 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 = 0; /* bash compat, was = 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
- 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
-/* Roughly copied from bash.. */
static void
change_random(const char *value)
{
+ /* Galois LFSR parameter */
+ /* Taps at 32 31 29 1: */
+ enum { MASK = 0x8000000b };
+ /* Another example - taps at 32 31 30 10: */
+ /* MASK = 0x00400007 */
+
if (value == NULL) {
/* "get", generate */
- char buf[16];
-
- rseed = rseed * 1103515245 + 12345;
- sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+ uint32_t t;
+
+ /* LCG has period of 2^32 and alternating lowest bit */
+ random_LCG = 1664525 * random_LCG + 1013904223;
+ /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
+ t = (random_galois_LFSR << 1);
+ if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */
+ t ^= MASK;
+ random_galois_LFSR = t;
+ /* Both are weak, combining them gives better randomness
+ * and ~2^64 period. & 0x7fff is probably bash compat
+ * for $RANDOM range. Combining with subtraction is
+ * just for fun. + and ^ would work equally well. */
+ t = (t - random_LCG) & 0x7fff;
/* set without recursion */
- setvar(vrandom.text, buf, VNOFUNC);
+ setvar(vrandom.text, utoa(t), VNOFUNC);
vrandom.flags &= ~VNOFUNC;
} else {
/* set/reset */
- rseed = strtoul(value, (char **)NULL, 10);
+ random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10);
}
}
#endif
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 */
-/*
- * 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.
- */
+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 */
-#define NEOF ((union node *)&tokpushback)
static smallint parsebackquote; /* nonzero if we are inside backquotes */
-static int lasttoken; /* last token read */
+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 smallint 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 raise_error_syntax(const char *) NORETURN;
static void
raise_error_syntax(const char *msg)
{
* is the token that is expected, or -1 if more than one type of token can
* occur at this point.
*/
-static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
+static void raise_error_unexpected_syntax(int) NORETURN;
static void
raise_error_unexpected_syntax(int token)
{
char msg[64];
int l;
- l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+ l = sprintf(msg, "unexpected %s", tokname(lasttoken));
if (token >= 0)
sprintf(msg + l, " (expecting %s)", tokname(token));
raise_error_syntax(msg);
#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);
tok = readtoken();
if (tok == TBACKGND) {
if (n2->type == NPIPE) {
- n2->npipe.backgnd = 1;
+ n2->npipe.pipe_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;
}
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;
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.pipe_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;
}
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;
static void
fixredir(union node *n, const char *text, int err)
{
+ int fd;
+
TRACE(("Fix redir %s %d\n", text, err));
if (!err)
n->ndup.vname = NULL;
- if (isdigit(text[0]) && text[1] == '\0')
- n->ndup.dupfd = text[0] - '0';
+ fd = bb_strtou(text, NULL, 10);
+ if (!errno && fd >= 0)
+ n->ndup.dupfd = fd;
else if (LONE_DASH(text))
n->ndup.dupfd = -1;
else {
if (err)
- raise_error_syntax("Bad fd number");
+ raise_error_syntax("bad fd number");
n->ndup.vname = makename();
}
}
n->type = NXHERE;
TRACE(("Here document %d\n", n->type));
if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
- raise_error_syntax("Illegal eof marker for << redirection");
+ raise_error_syntax("illegal eof marker for << redirection");
rmescapes(wordtext);
here->eofmark = wordtext;
here->next = NULL;
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;
if (!goodname(name)
|| ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
) {
- raise_error_syntax("Bad function name");
+ raise_error_syntax("bad function name");
}
n->type = NDEFUN;
checkkwd = CHKNL | CHKKWD | CHKALIAS;
*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);
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();
break;
}
case TFOR:
- if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
- raise_error_syntax("Bad for loop variable");
- n1 = stalloc(sizeof(struct nfor));
+ if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
+ raise_error_syntax("bad for loop variable");
+ 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
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:
*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.
*/
-
#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)
{
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;
+ startlinno = g_parsefile->linno;
bqlist = NULL;
quotef = 0;
oldstyle = 0;
dqvarnest = 0;
STARTSTACKSTR(out);
- loop: { /* for each line, until end of word */
+ loop:
+ /* For each line, until end of word */
+ {
CHECKEND(); /* set c to PEOF if at end of here document */
for (;;) { /* until end of line or end of word */
CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
if (syntax == BASESYNTAX)
goto endword; /* exit outer loop */
USTPUTC(c, out);
- plinno++;
+ g_parsefile->linno++;
if (doprompt)
setprompt(2);
c = pgetc();
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 */
USTPUTC('\\', out);
}
#endif
- if (dblquote &&
- c != '\\' && c != '`' &&
- c != '$' && (
- c != '"' ||
- eofmark != NULL)
+ if (dblquote && c != '\\'
+ && c != '`' && c != '$'
+ && (c != '"' || eofmark != NULL)
) {
USTPUTC(CTLESC, out);
USTPUTC('\\', out);
dblquote = 1;
goto quotemark;
case CENDQUOTE:
+ USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
if (eofmark != NULL && arinest == 0
&& varnest == 0
) {
case CIGN:
break;
default:
- if (varnest == 0)
+ if (varnest == 0) {
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == '&') {
+ if (pgetc() == '>')
+ c = 0x100 + '>'; /* flag &> */
+ pungetc();
+ }
+#endif
goto endword; /* exit outer loop */
+ }
#if ENABLE_ASH_ALIAS
if (c != PEOA)
#endif
USTPUTC(c, out);
}
- c = pgetc_macro();
- }
+ c = pgetc_fast();
+ } /* for (;;) */
}
endword:
#if ENABLE_ASH_MATH_SUPPORT
if (syntax == ARISYNTAX)
- raise_error_syntax("Missing '))'");
+ raise_error_syntax("missing '))'");
#endif
if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
- raise_error_syntax("Unterminated quoted string");
+ raise_error_syntax("unterminated quoted string");
if (varnest != 0) {
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
/* { */
- raise_error_syntax("Missing '}'");
+ raise_error_syntax("missing '}'");
}
USTPUTC('\0', out);
len = out - (char *)stackblock();
out = stackblock();
if (eofmark == NULL) {
- if ((c == '>' || c == '<')
+ if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
&& quotef == 0
- && len <= 2
- && (*out == '\0' || isdigit(*out))) {
- PARSEREDIR();
- return lasttoken = TREDIR;
- } else {
- pungetc();
+ ) {
+ if (isdigit_str9(out)) {
+ PARSEREDIR(); /* passed as params: out, c */
+ lasttoken = TREDIR;
+ return lasttoken;
+ }
+ /* else: non-number X seen, interpret it
+ * as "NNNX>file" = "NNNX >file" */
}
+ 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++;
+ g_parsefile->linno++;
needprompt = doprompt;
} else {
pushstring(line, NULL);
* first character of the redirection operator.
*/
parseredir: {
- char fd = *out;
+ /* out is already checked to be a valid number or "" */
+ int fd = (*out == '\0' ? -1 : atoi(out));
union node *np;
- np = stalloc(sizeof(struct nfile));
+ np = stzalloc(sizeof(struct nfile));
if (c == '>') {
np->nfile.fd = 1;
c = pgetc();
np->type = NCLOBBER;
else if (c == '&')
np->type = NTOFD;
+ /* it also can be NTO2 (>&file), but we can't figure it out yet */
else {
np->type = NTO;
pungetc();
}
- } else { /* c == '<' */
- np->nfile.fd = 0;
+ }
+#if ENABLE_ASH_BASH_COMPAT
+ else if (c == 0x100 + '>') { /* this flags &> redirection */
+ np->nfile.fd = 1;
+ pgetc(); /* this is '>', no need to check */
+ np->type = NTO2;
+ }
+#endif
+ else { /* c == '<' */
+ /*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;
break;
}
}
- if (fd != '\0')
- np->nfile.fd = fd - '0';
+ if (fd >= 0)
+ np->nfile.fd = fd;
redirnode = np;
goto parseredir_return;
}
/* 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;
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();
} else if (is_special(c)) {
USTPUTC(c, out);
c = pgetc();
- } else
- badsub: raise_error_syntax("Bad substitution");
+ } else {
+ badsub:
+ raise_error_syntax("bad substitution");
+ }
STPUTC('=', out);
flags = 0;
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();
case '\\':
pc = pgetc();
if (pc == '\n') {
- plinno++;
+ g_parsefile->linno++;
if (doprompt)
setprompt(2);
/*
#if ENABLE_ASH_ALIAS
case PEOA:
#endif
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
raise_error_syntax("EOF in backquote substitution");
case '\n':
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
break;
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) {
#ifdef NEW_xxreadtoken
/* singles must be first! */
static const char xxreadtoken_chars[7] ALIGN1 = {
- '\n', '(', ')', '&', '|', ';', 0
+ '\n', '(', ')', /* singles */
+ '&', '|', ';', /* doubles */
+ 0
};
+#define xxreadtoken_singles 3
+#define xxreadtoken_doubles 3
+
static const char xxreadtoken_tokens[] ALIGN1 = {
TNL, TLP, TRP, /* only single occurrence allowed */
TBACKGND, TPIPE, TSEMI, /* if single occurrence */
TAND, TOR, TENDCASE /* if double occurrence */
};
-#define xxreadtoken_doubles \
- (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
-#define xxreadtoken_singles \
- (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
-
static int
xxreadtoken(void)
{
if (needprompt) {
setprompt(2);
}
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
- c = pgetc_macro();
+ c = pgetc_fast();
+ if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
+ continue;
- if ((c != ' ') && (c != '\t')
-#if ENABLE_ASH_ALIAS
- && (c != PEOA)
-#endif
- ) {
- if (c == '#') {
- while ((c = pgetc()) != '\n' && c != PEOF);
+ if (c == '#') {
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
+ pungetc();
+ } else if (c == '\\') {
+ if (pgetc() != '\n') {
pungetc();
- } else if (c == '\\') {
- if (pgetc() != '\n') {
- pungetc();
- goto READTOKEN1;
- }
- startlinno = ++plinno;
- if (doprompt)
- setprompt(2);
- } else {
- const char *p
- = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+ break; /* return readtoken1(...) */
+ }
+ startlinno = ++g_parsefile->linno;
+ if (doprompt)
+ setprompt(2);
+ } else {
+ const char *p;
- if (c != PEOF) {
- if (c == '\n') {
- plinno++;
- needprompt = doprompt;
- }
+ p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+ if (c != PEOF) {
+ if (c == '\n') {
+ g_parsefile->linno++;
+ needprompt = doprompt;
+ }
- p = strchr(xxreadtoken_chars, c);
- if (p == NULL) {
- READTOKEN1:
- return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
- }
+ p = strchr(xxreadtoken_chars, c);
+ if (p == NULL)
+ break; /* return readtoken1(...) */
- if (p - xxreadtoken_chars >= xxreadtoken_singles) {
- if (pgetc() == *p) { /* double occurrence? */
- p += xxreadtoken_doubles + 1;
- } else {
- pungetc();
- }
+ if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
+ int cc = pgetc();
+ if (cc == c) { /* double occurrence? */
+ p += xxreadtoken_doubles + 1;
+ } else {
+ pungetc();
+#if ENABLE_ASH_BASH_COMPAT
+ if (c == '&' && cc == '>') /* &> */
+ break; /* return readtoken1(...) */
+#endif
}
}
- return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
}
+ lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ return lasttoken;
}
- } /* for */
+ } /* for (;;) */
+
+ return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
}
-#else
+#else /* old xxreadtoken */
#define RETURN(token) return lasttoken = token
static int
xxreadtoken(void)
if (needprompt) {
setprompt(2);
}
- startlinno = plinno;
+ startlinno = g_parsefile->linno;
for (;;) { /* until token or start of word found */
- c = pgetc_macro();
+ c = pgetc_fast();
switch (c) {
case ' ': case '\t':
#if ENABLE_ASH_ALIAS
#endif
continue;
case '#':
- while ((c = pgetc()) != '\n' && c != PEOF);
+ while ((c = pgetc()) != '\n' && c != PEOF)
+ continue;
pungetc();
continue;
case '\\':
if (pgetc() == '\n') {
- startlinno = ++plinno;
+ startlinno = ++g_parsefile->linno;
if (doprompt)
setprompt(2);
continue;
pungetc();
goto breakloop;
case '\n':
- plinno++;
+ g_parsefile->linno++;
needprompt = doprompt;
RETURN(TNL);
case PEOF:
return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
#undef RETURN
}
-#endif /* NEW_xxreadtoken */
+#endif /* old xxreadtoken */
static int
readtoken(void)
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;
* The eval command.
*/
static int
-evalcmd(int argc, char **argv)
+evalcmd(int argc UNUSED_PARAM, 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;
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 UNUSED_PARAM, 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(argc, 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
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;
}
}
* The trap builtin.
*/
static int
-trapcmd(int argc, char **argv)
+trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
char *action;
char **ap;
if (!*ap) {
for (signo = 0; signo < NSIG; signo++) {
if (trap[signo] != NULL) {
- const char *sn;
-
- sn = get_signame(signo);
out1fmt("trap -- %s %s\n",
- single_quote(trap[signo]), sn);
+ single_quote(trap[signo]),
+ get_signame(signo));
}
}
return 0;
}
- if (!ap[1])
- action = NULL;
- else
+ action = NULL;
+ if (ap[1])
action = *ap++;
while (*ap) {
signo = get_signum(*ap);
* Lists available builtins
*/
static int
-helpcmd(int argc, char **argv)
+helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
- int col, i;
+ unsigned col;
+ unsigned i;
out1fmt("\nBuilt-in commands:\n-------------------\n");
for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
}
}
#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 UNUSED_PARAM, char **argv)
{
struct var *vp;
char *name;
const char *p;
char **aptr;
- int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
+ int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
if (nextopt("p") != 'p') {
aptr = argptr;
* with the same name.
*/
static int
-unsetcmd(int argc, char **argv)
+unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
char **ap;
int i;
};
static int
-timescmd(int ac, char **av)
+timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
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 UNUSED_PARAM, 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 UNUSED_PARAM, char **argv UNUSED_PARAM)
{
+ 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 n_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;
nchars = bb_strtou(optionarg, NULL, 10);
if (nchars < 0 || errno)
ash_msg_and_raise_error("invalid count");
- n_flag = nchars; /* just a flag "nchars is nonzero" */
+ /* nchars == 0: off (bash 3.2 does this too) */
break;
case 's':
silent = 1;
#endif
#if ENABLE_ASH_READ_TIMEOUT
case 't':
+ 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;
/* EINVAL means number is ok, but not terminated by NUL */
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 (n_flag || silent) {
- if (tcgetattr(0, &tty) != 0) {
- /* Not a tty */
- n_flag = 0;
- silent = 0;
- } else {
- old_tty = tty;
- if (n_flag) {
- tty.c_lflag &= ~ICANON;
- tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
- }
- if (silent) {
- tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
- }
- tcsetattr(0, TCSANOW, &tty);
+ tcgetattr(fd, &tty);
+ old_tty = tty;
+ if (nchars || silent) {
+ if (nchars) {
+ tty.c_lflag &= ~ICANON;
+ tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
}
- }
-#endif
-#if ENABLE_ASH_READ_TIMEOUT
- if (ts.tv_sec || ts.tv_usec) {
- FD_ZERO(&set);
- FD_SET(0, &set);
-
- /* poll-based wait produces bigger code, using select */
- i = select(1, &set, NULL, NULL, &ts);
- if (!i) { /* timed out! */
-#if ENABLE_ASH_READ_NCHARS
- if (n_flag)
- tcsetattr(0, TCSANOW, &old_tty);
-#endif
- return 1;
+ if (silent) {
+ tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
}
+ /* if tcgetattr failed, tcsetattr will fail too.
+ * Ignoring, it's harmless. */
+ tcsetattr(fd, TCSANOW, &tty);
}
#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 (read(0, &c, 1) != 1) {
+#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
+ tcsetattr(fd, TCSANOW, &old_tty);
+#endif
+ return 1;
+ }
+ }
+#endif
+ if (nonblock_safe_read(fd, &c, 1) != 1) {
status = 1;
break;
}
}
/* end of do {} while: */
#if ENABLE_ASH_READ_NCHARS
- while (!n_flag || --nchars);
+ while (--nchars);
#else
while (1);
#endif
#if ENABLE_ASH_READ_NCHARS
- if (n_flag || silent)
- tcsetattr(0, TCSANOW, &old_tty);
+ tcsetattr(fd, TCSANOW, &old_tty);
#endif
STACKSTRNUL(p);
}
static int
-umaskcmd(int argc, char **argv)
+umaskcmd(int argc UNUSED_PARAM, char **argv)
{
static const char permuser[3] ALIGN1 = "ugo";
static const char permmode[3] ALIGN1 = "rwx";
}
static int
-ulimitcmd(int argc, char **argv)
+ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
int c;
rlim_t val = 0;
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;
}
|| 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;
--NUMPTR;
numptr_val = rez;
if (op == TOK_CONDITIONAL) {
- if (! numptr_m1->contidional_second_val_initialized) {
+ if (!numptr_m1->contidional_second_val_initialized) {
/* protect $((expr1 ? expr2)) without ": expr" */
goto err;
}
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 */
}
for (o = expr; *p && *o == *p; p++)
o++;
- if (! *p) {
+ if (!*p) {
/* found */
expr = o - 1;
break;
/*
* Called to exit the shell.
*/
-static void exitshell(void) ATTRIBUTE_NORETURN;
+static void exitshell(void) NORETURN;
static void
exitshell(void)
{
init(void)
{
/* from input.c: */
- basepf.nextc = basepf.buf = basebuf;
+ basepf.next_to_pgetc = basepf.buf = basebuf;
/* from trap.c: */
signal(SIGCHLD, SIG_DFL);
* 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++;
evalskip = 0;
loopnest = 0;
/* from input.c: */
- parselleft = parsenleft = 0; /* clear input buffer */
+ g_parsefile->left_in_buffer = 0;
+ g_parsefile->left_in_line = 0; /* clear input buffer */
popallfiles();
/* from parser.c: */
tokpushback = 0;
checkkwd = 0;
/* from redir.c: */
- clearredir(0);
+ clearredir(/*drop:*/ 0);
}
#if PROFILE
* is used to figure out how far we had gotten.
*/
int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int ash_main(int argc, char **argv)
+int ash_main(int argc UNUSED_PARAM, 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
rootpid = getpid();
#if ENABLE_ASH_RANDOM_SUPPORT
- rseed = rootpid + time(NULL);
+ /* Can use monotonic_ns() for better randomness but for now it is
+ * not used anywhere else in busybox... so avoid bloat */
+ random_galois_LFSR = random_LCG = rootpid + monotonic_us();
#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)