* rewrite arith.y to micro stack based cryptic algorithm by
* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
*
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2003 to be
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
* used in busybox and size optimizations,
- * support locale, rewrited arith (see notes to this)
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
*
*/
#include <signal.h>
#include <stdint.h>
#include <sysexits.h>
-
+#include <time.h>
#include <fnmatch.h>
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
- * contains a code identifying the exeception. To implement nested
+ * contains a code identifying the exception. To implement nested
* exception handlers, the user should save the value of handler on entry
* to an inner scope, set handler to point to a jmploc structure for the
* inner scope, and restore handler on exit from the scope.
* more fun than worrying about efficiency and portability. :-))
*/
-#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
+#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
#define INTOFF \
({ \
suppressint++; \
- barrier(); \
+ xbarrier(); \
0; \
})
#define SAVEINT(v) ((v) = suppressint)
#define RESTOREINT(v) \
({ \
- barrier(); \
+ xbarrier(); \
if ((suppressint = (v)) == 0 && intpending) onint(); \
0; \
})
#define EXSIGON() \
({ \
exsig++; \
- barrier(); \
+ xbarrier(); \
if (pendingsigs) \
exraise(EXSIG); \
0; \
#else
#define INTON \
({ \
- barrier(); \
+ xbarrier(); \
if (--suppressint == 0 && intpending) onint(); \
0; \
})
#define FORCEINTON \
({ \
- barrier(); \
+ xbarrier(); \
suppressint = 0; \
if (intpending) onint(); \
0; \
#define __builtin_expect(x, expected_value) (x)
#endif
-#define likely(x) __builtin_expect((x),1)
+#define xlikely(x) __builtin_expect((x),1)
#define TEOF 0
static void defun(char *, union node *);
static void unsetfunc(const char *);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#else
+typedef long arith_t;
+#endif
+
#ifdef CONFIG_ASH_MATH_SUPPORT
-static int dash_arith(const char *);
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+static void change_random(const char *);
+# ifndef DYNAMIC_VAR
+# define DYNAMIC_VAR
+# endif
#endif
/* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
#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
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
const char *text; /* name=value */
- void (*func)(const char *);
- /* function to be called when */
+ void (*func)(const char *); /* function to be called when */
/* the variable gets set/unset */
};
static void change_lc_ctype(const char *value);
#endif
+
#define VTABSIZE 39
static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
#endif
{ 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
- { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
#ifdef CONFIG_ASH_GETOPTS
{ 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
#endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+ {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
#ifdef CONFIG_LOCALE_SUPPORT
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=", change_lc_all},
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=", change_lc_ctype},
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
#endif
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=", NULL},
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
#endif
};
#define vps2 (&vps1)[1]
#define vps4 (&vps2)[1]
#define voptind (&vps4)[1]
-
+#ifdef CONFIG_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
#define defpath (defpathvar + 5)
/*
static void outstr(const char *, FILE *);
static void outcslow(int, FILE *);
static void flushall(void);
-static void flushout(FILE *);
+static void flusherr(void);
static int out1fmt(const char *, ...)
__attribute__((__format__(__printf__,1,2)));
static int fmtstr(char *, size_t, const char *, ...)
__attribute__((__format__(__printf__,3,4)));
-static void xwrite(int, const void *, size_t);
static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
static void out2str(const char *p)
{
outstr(p, stderr);
- flushout(stderr);
+ flusherr();
}
/*
dest = bltinlookup(homestr);
else if (dest[0] == '-' && dest[1] == '\0') {
dest = bltinlookup("OLDPWD");
+ if ( !dest ) goto out;
flags |= CD_PRINT;
goto step7;
}
*/
/*
- * The eval commmand.
+ * The eval command.
*/
static int
}
sp = arglist.list;
}
- xwrite(preverrout_fd, "\n", 1);
+ bb_full_write(preverrout_fd, "\n", 1);
}
cmd_is_exec = 0;
find_command(argv[0], &cmdentry, cmd_flag, path);
if (cmdentry.cmdtype == CMDUNKNOWN) {
status = 127;
- flushout(stderr);
+ flusherr();
goto bail;
}
int flg_bb = 0;
char *name = cmd;
-#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
- name = bb_get_last_path_component(name);
- if(find_applet_by_name(name) != NULL)
- flg_bb = 1;
-#else
if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
flg_bb = 1;
}
-#endif
if(flg_bb) {
char **ap;
char **new;
static void expandmeta(struct strlist *, int);
static int patmatch(char *, const char *);
-static int cvtnum(long);
+static int cvtnum(arith_t);
static size_t esclen(const char *, const char *);
static char *scanleft(char *, char *, char *, char *, int, int);
static char *scanright(char *, char *, char *, char *, int, int);
{
herefd = fd;
expandarg(arg, (struct arglist *)NULL, 0);
- xwrite(fd, stackblock(), expdest - (char *)stackblock());
+ bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
}
*/
static int
-cvtnum(long num)
+cvtnum(arith_t num)
{
int len;
expdest = makestrspace(32, expdest);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+ len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
len = fmtstr(expdest, 32, "%ld", num);
+#endif
STADJUST(len, expdest);
return len;
}
static void pushfile(void);
-/*
- * Read a line from the script.
- */
-
-static inline char *
-pfgets(char *line, int len)
-{
- char *p = line;
- int nleft = len;
- int c;
-
- while (--nleft > 0) {
- c = pgetc2();
- if (c == PEOF) {
- if (p == line)
- return NULL;
- break;
- }
- *p++ = c;
- if (c == '\n')
- break;
- }
- *p = '\0';
- return line;
-}
-
-
/*
* Read a character from the script, returning PEOF on end of file.
* Nul characters in the input are silently discarded.
}
#endif
+/*
+ * Read a line from the script.
+ */
+
+static inline char *
+pfgets(char *line, int len)
+{
+ char *p = line;
+ int nleft = len;
+ int c;
+
+ while (--nleft > 0) {
+ c = pgetc2();
+ if (c == PEOF) {
+ if (p == line)
+ return NULL;
+ break;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = '\0';
+ return line;
+}
+
+
#ifdef CONFIG_FEATURE_COMMAND_EDITING
static const char *cmdedit_prompt;
if (!iflag || parsefile->fd)
nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
else {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
cmdedit_path_lookup = pathval();
+#endif
nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
if(nr == 0) {
/* Ctrl+C presend */
}
goto retry;
}
- if(nr < 0) {
+ if(nr < 0 && errno == 0) {
/* Ctrl+D presend */
nr = 0;
}
jq--;
#define joff(p) ((struct job *)((char *)(p) + l))
#define jmove(p) (p) = (void *)((char *)(p) + offset)
- if (likely(joff(jp)->ps == &jq->ps0))
+ if (xlikely(joff(jp)->ps == &jq->ps0))
jmove(joff(jp)->ps);
if (joff(jp)->prev_job)
jmove(joff(jp)->prev_job);
* 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
- * forground process to terminate, and then send itself an interrupt
+ * foreground process to terminate, and then send itself an interrupt
* signal if the child process was terminated by an interrupt signal.
* Unfortunately, some programs want to do a bit of cleanup and then
* exit on interrupt; unless these processes terminate themselves by
int quoted = 0;
static const char *const vstype[16] = {
nullstr, "}", "-", "+", "?", "=",
- "%", "%%", "#", "##", nullstr
+ "%", "%%", "#", "##", nullstr
};
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
trputs("Shell args: "); trargs(argv);
#endif
rootpid = getpid();
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+ rseed = rootpid + ((time_t)time((time_t *)0));
+#endif
rootshell = 1;
init();
setstackmark(&smark);
/* NOTREACHED */
}
-int
-dotcmd(int argc, char **argv)
+static int dotcmd(int argc, char **argv)
{
+ struct strlist *sp;
+ volatile struct shparam saveparam;
+
exitstatus = 0;
- if (argc >= 2) { /* That's what SVR2 does */
+ for (sp = cmdenviron; sp; sp = sp->next)
+ setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+
+ if (argc >= 2) { /* That's what SVR2 does */
char *fullname;
struct stackmark smark;
setstackmark(&smark);
fullname = find_dot_file(argv[1]);
+
+ if (argc > 2) {
+ saveparam = shellparam;
+ shellparam.malloc = 0;
+ shellparam.nparam = argc - 2;
+ shellparam.p = argv + 2;
+ };
+
setinputfile(fullname, 1);
commandname = fullname;
cmdloop(0);
popfile();
+
+ if (argc > 2) {
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ };
+
popstackmark(&smark);
}
return exitstatus;
{
size_t len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
- xwrite(herefd, stackblock(), len);
+ bb_full_write(herefd, stackblock(), len);
return stackblock();
}
growstackblock();
#endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void change_random(const char *value)
+{
+ if(value == NULL) {
+ /* "get", generate */
+ char buf[16];
+
+ rseed = rseed * 1103515245 + 12345;
+ sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+ /* set without recursion */
+ setvar(vrandom.text, buf, VNOFUNC);
+ vrandom.flags &= ~VNOFUNC;
+ } else {
+ /* set/reset */
+ rseed = strtoul(value, (char **)NULL, 10);
+ }
+}
+#endif
+
+
#ifdef CONFIG_ASH_GETOPTS
static int
getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
}
void
-flushout(FILE *dest)
+flusherr(void)
{
INTOFF;
- fflush(dest);
+ fflush(stderr);
INTON;
}
}
-/*
- * Version of write which resumes after a signal is caught.
- */
-
-static void
-xwrite(int fd, const void *p, size_t n)
-{
- ssize_t i;
-
- do {
- i = bb_full_write(fd, p, n);
- } while (i < 0 && errno == EINTR);
-}
-
/* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */
if (redir->type == NHERE) {
len = strlen(redir->nhere.doc->narg.text);
if (len <= PIPESIZE) {
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
goto out;
}
}
#endif
signal(SIGPIPE, SIG_DFL);
if (redir->type == NHERE)
- xwrite(pip[1], redir->nhere.doc->narg.text, len);
+ bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
else
expandhere(redir->nhere.doc, pip[1]);
_exit(0);
savestatus = exitstatus;
q = gotsig;
- while (pendingsigs = 0, barrier(), (p = memchr(q, 1, NSIG - 1))) {
+ while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) {
*p = 0;
p = trap[p - q + 1];
if (!p)
evalstring(p);
}
flushall();
+ setjobctl(0);
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
if (iflag && rootshell) {
const char *hp = lookupvar("HISTFILE");
static struct var **findvar(struct var **, const char *);
/*
- * Initialize the varable symbol tables and import the environment
+ * Initialize the variable symbol tables and import the environment
*/
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
vp = *findvar(vpp, s);
if (vp) {
- if (vp->flags & VREADONLY) {
+ if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+ const char *n;
+
if (flags & VNOSAVE)
free(s);
- error("%.*s: is read only", strchrnul(s, '=') - s, s);
+ n = vp->text;
+ error("%.*s: is read only", strchrnul(n, '=') - n, n);
}
if (flags & VNOSET)
{
struct var *v;
- if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
- return strchrnul(v->text, '=') + 1;
+ if ((v = *findvar(hashvar(name), name))) {
+#ifdef DYNAMIC_VAR
+ /*
+ * Dynamic variables are implemented roughly the same way they are
+ * in bash. Namely, they're "special" so long as they aren't unset.
+ * As soon as they're unset, they're no longer dynamic, and dynamic
+ * lookup will no longer happen at that point. -- PFM.
+ */
+ if((v->flags & VDYNAMIC))
+ (*v->func)(NULL);
+#endif
+ if(!(v->flags & VUNSET))
+ return strchrnul(v->text, '=') + 1;
}
+
return NULL;
}
retval = 1;
if (flags & VREADONLY)
goto out;
+#ifdef DYNAMIC_VAR
+ vp->flags &= ~VDYNAMIC;
+#endif
if (flags & VUNSET)
goto ok;
if ((flags & VSTRFIXED) == 0) {
}
#ifdef CONFIG_ASH_MATH_SUPPORT
-static int
+static arith_t
dash_arith(const char *s)
{
- long result;
+ arith_t result;
int errcode = 0;
INTOFF;
letcmd(int argc, char **argv)
{
char **ap;
- long i;
+ arith_t i;
ap = argv + 1;
if(!*ap)
/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
/*
- * Miscelaneous builtins.
+ * Miscellaneous builtins.
*/
#undef rflag
/* This is my infix parser/evaluator. It is optimized for size, intended
* as a replacement for yacc-based parsers. However, it may well be faster
- * than a comparable parser writen in yacc. The supported operators are
+ * than a comparable parser written in yacc. The supported operators are
* listed in #defines below. Parens, order of operations, and error handling
- * are supported. This code is threadsafe. The exact expression format should
+ * are supported. This code is thread safe. The exact expression format should
* be that which POSIX specifies for shells. */
/* The code uses a simple two-stack algorithm. See
* http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
- * for a detailed explaination of the infix-to-postfix algorithm on which
+ * for a detailed explanation of the infix-to-postfix algorithm on which
* this is based (this code differs in that it applies operators immediately
* to the stack instead of adding them to a queue to end up with an
* expression). */
* parens and then checking that all binary ops and right parens are
* preceded by a valid expression (NUM_TOKEN).
*
- * Note: It may be desireable to replace Aaron's test for whitespace with
+ * Note: It may be desirable to replace Aaron's test for whitespace with
* ctype's isspace() if it is used by another busybox applet or if additional
* whitespace chars should be considered. Look below the "#include"s for a
* precompiler test.
* - realize comma separated - expr, expr
* - realise ++expr --expr expr++ expr--
* - realise expr ? expr : expr (but, second expr calculate always)
- * - allow hexdecimal and octal numbers
+ * - allow hexadecimal and octal numbers
* - was restored loses XOR operator
* - remove one goto label, added three ;-)
* - protect $((num num)) as true zero expr (Manuel`s error)
typedef unsigned char operator;
/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
- * precedence, and 3 high bits are an ID unique accross operators of that
+ * precedence, and 3 high bits are an ID unique across operators of that
* precedence. The ID portion is so that multiple operators can have the
* same precedence, ensuring that the leftmost one is evaluated first.
* Consider * and /. */
typedef struct ARITCH_VAR_NUM {
- long val;
- long contidional_second_val;
+ arith_t val;
+ arith_t contidional_second_val;
char contidional_second_val_initialized;
char *var; /* if NULL then is regular number,
- else is varable name */
+ else is variable name */
} v_n_t;
static inline int
arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
{
- long numptr_val;
v_n_t *numptr_m1;
- long rez;
+ arith_t numptr_val, rez;
int ret_arith_lookup_val;
if (NUMPTR == numstack) goto err; /* There is no operator that can work
if(numptr_val < 0)
return -3; /* exponent less than 0 */
else {
- long c = 1;
+ arith_t c = 1;
if(numptr_val)
while(numptr_val--)
goto err;
}
/* save to shell variable */
- sprintf(buf, "%ld", rez);
+ snprintf(buf, sizeof(buf), "%lld", (long long) rez);
setvar(numptr_m1->var, buf, 0);
/* after saving, make previous value for v++ or v-- */
if(op == TOK_POST_INC)
#define endexpression &op_tokens[sizeof(op_tokens)-7]
-extern long arith (const char *expr, int *perrcode)
+static arith_t arith (const char *expr, int *perrcode)
{
register char arithval; /* Current character under analysis */
operator lasttok, op;
/* 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 excersize to
+ * 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;
goto prologue;
}
if((p = endofname(expr)) != expr) {
- int var_name_size = (p-expr) + 1; /* trailing zero */
+ size_t var_name_size = (p-expr) + 1; /* trailing zero */
numstackptr->var = alloca(var_name_size);
safe_strncpy(numstackptr->var, expr, var_name_size);
continue;
} else if (is_digit(arithval)) {
numstackptr->var = NULL;
- numstackptr->val = strtol(expr, (char **) &expr, 0);
+ numstackptr->val = strtoll(expr, (char **) &expr, 0);
goto num;
}
for(p = op_tokens; ; p++) {