* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
+ * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
+ * was re-ported from NetBSD and debianized.
+ *
+ *
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * This version of ash is adapted from the source in Debian's ash 0.3.8-5
- * package.
- * Maintainer Herbert Xu <herbert@debian.org> (C) 1997-2002
+ * Original BSD copyright notice is retained at the end of this file.
+ */
+
+/*
+ * 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> to be used in busybox
+ * 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,
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
*
- * Original copyright notice is retained at the end of this file.
*/
+
/*
* The follow should be set to reflect the type of system you have:
* JOBS -> 1 if you have Berkeley job control, 0 otherwise.
#include <signal.h>
#include <stdint.h>
#include <sysexits.h>
-
+#include <time.h>
#include <fnmatch.h>
-#include <glob.h>
-
#include "busybox.h"
#ifdef CONFIG_ASH_JOB_CONTROL
#define JOBS 1
#else
-#undif JOBS
+#undef JOBS
#endif
#if JOBS
* 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; \
static int parselleft; /* copy of parsefile->lleft */
/* next character in input buffer */
-static char *parsenextc; /* copy of parsefile->nextc */
+static char *parsenextc; /* copy of parsefile->nextc */
+
+struct strpush {
+ struct strpush *prev; /* preceding string on stack */
+ char *prevstring;
+ int prevnleft;
+#ifdef CONFIG_ASH_ALIAS
+ struct alias *ap; /* if push was associated with an alias */
+#endif
+ char *string; /* remember the string since it may change */
+};
+
+struct parsefile {
+ 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 */
+ 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 char basebuf[IBUFSIZ]; /* buffer for top level input file */
static struct parsefile *parsefile = &basepf; /* current input file */
#define TRACEV(param)
#endif
-#if defined(__GNUC__) && __GNUC__ < 3
-#define va_copy __va_copy
-#endif
-
#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define __builtin_expect(x, expected_value) (x)
#endif
-#define likely(x) __builtin_expect((x),1)
-#define unlikely(x) __builtin_expect((x),0)
+#define xlikely(x) __builtin_expect((x),1)
+
#define TEOF 0
#define TNL 1
-static void evalstring(char *, int);
+static void evalstring(char *);
union node; /* BLETCH for ansi C */
static void evaltree(union node *, int);
static void evalbackcmd(union node *, struct backcmd *);
static int evalcmd(int, char **);
static int execcmd(int, char **);
static int exitcmd(int, char **);
-#ifdef CONFIG_ASH_MATH_SUPPORT
-static int expcmd(int, char **);
-#endif
static int exportcmd(int, char **);
static int falsecmd(int, char **);
#ifdef JOBS
#ifdef JOBS
static int jobscmd(int, char **);
#endif
+#ifdef CONFIG_ASH_MATH_SUPPORT
+static int letcmd(int, char **);
+#endif
static int localcmd(int, char **);
static int pwdcmd(int, char **);
static int readcmd(int, char **);
{ BUILTIN_SPEC_REG "eval", evalcmd },
{ BUILTIN_SPEC_REG "exec", execcmd },
{ BUILTIN_SPEC_REG "exit", exitcmd },
-#ifdef CONFIG_ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "exp", expcmd },
-#endif
{ BUILTIN_SPEC_REG_ASSG "export", exportcmd },
{ BUILTIN_REGULAR "false", falsecmd },
#ifdef JOBS
{ BUILTIN_REGULAR "kill", killcmd },
#endif
#ifdef CONFIG_ASH_MATH_SUPPORT
- { BUILTIN_NOSPEC "let", expcmd },
+ { BUILTIN_NOSPEC "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
{ BUILTIN_NOSPEC "pwd", pwdcmd },
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
-/* From arith.y */
-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/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
+static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
-#endif
+#define defifs (defifsvar + 4)
+#else
static const char defifs[] = " \t\n";
+#endif
+
static struct var varinit[] = {
#ifdef IFS_BROKEN
#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 int loopnest; /* current loop nesting level */
-struct strpush {
- struct strpush *prev; /* preceding string on stack */
- char *prevstring;
- int prevnleft;
-#ifdef CONFIG_ASH_ALIAS
- struct alias *ap; /* if push was associated with an alias */
-#endif
- char *string; /* remember the string since it may change */
-};
-
-struct parsefile {
- 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 */
- char *buf; /* input buffer */
- struct strpush *strpush; /* for pushing strings at this level */
- struct strpush basestrpush; /* so pushing one is fast */
-};
-
/*
* The parsefile structure pointed to by the global variable parsefile
* contains information about the current file being read.
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. */
-#define outerr(f) ferror(f)
-#define out2c(c) outcslow((c), stderr)
static void out1str(const char *p)
{
static void out2str(const char *p)
{
outstr(p, stderr);
-}
-
-static void out1c(char c)
-{
- char s[2];
-
- s[0] = c;
- s[1] = 0;
- outstr(s, stdout);
+ flusherr();
}
/*
/* flags passed to redirect */
#define REDIR_PUSH 01 /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03 /* set preverrout */
union node;
static void redirect(union node *, int);
dest = bltinlookup(homestr);
else if (dest[0] == '-' && dest[1] == '\0') {
dest = bltinlookup("OLDPWD");
+ if ( !dest ) goto out;
flags |= CD_PRINT;
goto step7;
}
static int evalbltin(const struct builtincmd *, int, char **);
static int evalfun(struct funcnode *, int, char **, int);
static void prehash(union node *);
-static int eprintlist(struct strlist *, int);
static int bltincmd(int, char **);
*/
/*
- * The eval commmand.
+ * The eval command.
*/
static int
STPUTC('\0', concat);
p = grabstackstr(concat);
}
- evalstring(p, EV_TESTED);
+ evalstring(p);
}
return exitstatus;
}
*/
static void
-evalstring(char *s, int flag)
+evalstring(char *s)
{
union node *n;
struct stackmark smark;
setinputstring(s);
while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, flag);
+ evaltree(n, 0);
popstackmark(&smark);
if (evalskip)
break;
default:
#ifdef DEBUG
out1fmt("Node type = %d\n", n->type);
- flushout(stdout);
+ fflush(stdout);
break;
#endif
case NNOT:
struct arglist varlist;
char **argv;
int argc;
- struct strlist *sp;
+ const struct strlist *sp;
struct cmdentry cmdentry;
struct job *jp;
char *lastarg;
if (iflag && funcnest == 0 && argc > 0)
lastarg = nargv[-1];
+ preverrout_fd = 2;
expredir(cmd->ncmd.redirect);
- status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH);
+ status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
path = vpath.text;
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
/* Print the command if xflag is set. */
if (xflag) {
- int sep;
+ int n;
+ const char *p = " %s";
- out2str(ps4val());
- sep = 0;
- sep = eprintlist(varlist.list, sep);
- eprintlist(arglist.list, sep);
- out2c('\n');
- flushall();
+ p++;
+ dprintf(preverrout_fd, p, ps4val());
+
+ sp = varlist.list;
+ for(n = 0; n < 2; n++) {
+ while (sp) {
+ dprintf(preverrout_fd, p, sp->text);
+ sp = sp->next;
+ if(*p == '%') {
+ p--;
+ }
+ }
+ sp = arglist.list;
+ }
+ 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;
}
exitstatus = (*cmd->builtin)(argc, argv);
flushall();
cmddone:
- exitstatus |= outerr(stdout);
+ exitstatus |= ferror(stdout);
commandname = savecmdname;
exsig = 0;
handler = savehandler;
}
+static inline int
+goodname(const char *p)
+{
+ return !*endofname(p);
+}
+
/*
* Search for a command. This is called before we fork so that the
* location of the command will be available in the parent as well as
- * the child.
+ * the child. The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
*/
static void
struct cmdentry entry;
if (n->type == NCMD && n->ncmd.args)
- find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+ if (goodname(n->ncmd.args->narg.text))
+ find_command(n->ncmd.args->narg.text, &entry, 0,
+ pathval());
}
}
-static int
-eprintlist(struct strlist *sp, int sep)
-{
- while (sp) {
- const char *p;
-
- p = " %s" + (1 - sep);
- sep |= 1;
- fprintf(stderr, p, sp->text);
- sp = sp->next;
- }
-
- return sep;
-}
/* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */
/*
static void tryexec(char *, char **, char **);
-static void printentry(struct tblentry *);
static void clearcmdentry(int);
static struct tblentry *cmdlookup(const char *, int);
static void delete_cmd_entry(void);
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;
for (ap = argv; *ap; ap++)
;
ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
- *ap++ = cmd = "/bin/sh";
+ ap[1] = cmd;
+ *ap = cmd = (char *)DEFAULT_SHELL;
+ ap += 2;
+ argv++;
while ((*ap++ = *argv++))
;
argv = new;
}
-
/*** Command hashing code ***/
+static void
+printentry(struct tblentry *cmdp)
+{
+ int idx;
+ const char *path;
+ char *name;
+
+ idx = cmdp->param.index;
+ path = pathval();
+ do {
+ name = padvance(&path, cmdp->cmdname);
+ stunalloc(name);
+ } while (--idx >= 0);
+ out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+}
+
static int
hashcmd(int argc, char **argv)
}
-static void
-printentry(struct tblentry *cmdp)
-{
- int idx;
- const char *path;
- char *name;
-
- idx = cmdp->param.index;
- path = pathval();
- do {
- name = padvance(&path, cmdp->cmdname);
- stunalloc(name);
- } while (--idx >= 0);
- out1str(name);
- out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
-}
-
-
-
/*
* Resolve a command name. If you change this routine, you may have to
* change the shellexec routine as well.
}
out:
- out1c('\n');
+ outstr("\n", stdout);
return 0;
}
static void expbackq(union node *, int, int);
static const char *subevalvar(char *, char *, int, int, int, int, int);
static char *evalvar(char *, int);
-static int varisset(char *, int);
static void strtodest(const char *, int, int);
static void memtodest(const char *p, size_t len, int syntax, int quotes);
-static void varvalue(char *, int, int);
+static ssize_t varvalue(char *, int, int);
static void recordregion(int, int, int);
static void removerecordregions(int);
static void ifsbreakup(char *, struct arglist *);
static void ifsfree(void);
static void expandmeta(struct strlist *, int);
-static void addglob(const glob_t *);
-static void addfname(char *);
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);
#define pmatch(a, b) !fnmatch((a), (b), 0)
/*
- * Prepare a pattern for a glob(3) call.
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
*
* Returns an stalloced string.
*/
{
herefd = fd;
expandarg(arg, (struct arglist *)NULL, 0);
- xwrite(fd, stackblock(), expdest - (char *)stackblock());
+ bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
}
}
-
/*
* Perform variable and command substitution. If EXP_FULL is set, output CTLESC
* characters to allow for further processing. Otherwise treat
static char *
-scanleft(
- char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero
-) {
+scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
char *loc;
char *loc2;
char c;
static char *
-scanright(
- char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero
-) {
+scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
+ int zero)
+{
int esc = 0;
char *loc;
char *loc2;
char *var;
int patloc;
int c;
- int set;
int startloc;
- size_t varlen;
+ ssize_t varlen;
int easy;
int quotes;
int quoted;
quoted = varflags & VSQUOTE;
var = p;
easy = (!quoted || (*var == '@' && shellparam.nparam));
- varlen = 0;
startloc = expdest - (char *)stackblock();
p = strchr(p, '=') + 1;
- if (!is_name(*var)) {
- set = varisset(var, varflags & VSNUL);
- set--;
- if (subtype == VSPLUS)
- goto vsplus;
- if (++set) {
- varvalue(var, quoted, flag);
- if (subtype == VSLENGTH) {
- varlen =
- expdest - (char *)stackblock() -
- startloc;
- STADJUST(-varlen, expdest);
- goto vslen;
- }
- }
- } else {
- const char *val;
again:
- /* jump here after setting a variable with ${var=text} */
- val = lookupvar(var);
- set = !val || ((varflags & VSNUL) && !*val);
- if (subtype == VSPLUS)
- goto vsplus;
- if (--set) {
- varlen = strlen(val);
- if (subtype == VSLENGTH)
- goto vslen;
- memtodest(
- val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
- quotes
- );
- }
- }
+ varlen = varvalue(var, varflags, flag);
+ if (varflags & VSNUL)
+ varlen--;
+ if (subtype == VSPLUS) {
+ varlen = -1 - varlen;
+ goto vsplus;
+ }
if (subtype == VSMINUS) {
vsplus:
- if (!set) {
+ if (varlen < 0) {
argstr(
p, flag | EXP_TILDE |
(quoted ? EXP_QWORD : EXP_WORD)
}
if (subtype == VSASSIGN || subtype == VSQUESTION) {
- if (!set) {
+ if (varlen < 0) {
if (subevalvar(p, var, 0, subtype, startloc,
varflags, 0)) {
varflags &= ~VSNUL;
goto end;
}
- if (!set && uflag)
+ if (varlen < 0 && uflag)
varunset(p, var, 0, 0);
if (subtype == VSLENGTH) {
-vslen:
- cvtnum(varlen);
+ cvtnum(varlen > 0 ? varlen : 0);
goto record;
}
}
#endif
- if (set) {
+ if (varlen >= 0) {
/*
* Terminate the string and start recording the pattern
* right after it
if ((c = *p++) == CTLESC)
p++;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (set)
+ if (varlen >= 0)
argbackq = argbackq->next;
} else if (c == CTLVAR) {
if ((*p++ & VSTYPE) != VSNORMAL)
}
-
-/*
- * Test whether a specialized variable is set.
- */
-
-static int
-varisset(char *name, int nulok)
-{
- if (*name == '!')
- return backgndpid != 0;
- else if (*name == '@' || *name == '*') {
- if (*shellparam.p == NULL)
- return 0;
-
- if (nulok) {
- char **av;
-
- for (av = shellparam.p; *av; av++)
- if (**av != '\0')
- return 1;
- return 0;
- }
- } else if (is_digit(*name)) {
- char *ap;
- int num = atoi(name);
-
- if (num > shellparam.nparam)
- return 0;
-
- if (num == 0)
- ap = arg0;
- else
- ap = shellparam.p[num - 1];
-
- if (nulok && (ap == NULL || *ap == '\0'))
- return 0;
- }
- return 1;
-}
-
-
-
/*
* Put a string on the stack.
*/
}
-
/*
* Add the value of a specialized variable to the stack string.
*/
-static void
-varvalue(char *name, int quoted, int flags)
+static ssize_t
+varvalue(char *name, int varflags, int flags)
{
int num;
char *p;
int i;
- int sep;
+ int sep = 0;
int sepq = 0;
+ ssize_t len = 0;
char **ap;
int syntax;
- int allow_split = flags & EXP_FULL;
+ int quoted = varflags & VSQUOTE;
+ int subtype = varflags & VSTYPE;
int quotes = flags & (EXP_FULL | EXP_CASE);
+ if (quoted && (flags & EXP_FULL))
+ sep = 1 << CHAR_BIT;
+
syntax = quoted ? DQSYNTAX : BASESYNTAX;
switch (*name) {
case '$':
goto numvar;
case '!':
num = backgndpid;
+ if (num == 0)
+ return -1;
numvar:
- cvtnum(num);
+ len = cvtnum(num);
break;
case '-':
- for (i = 0 ; i < NOPTS ; i++) {
- if (optlist[i])
- STPUTC(optletters(i), expdest);
+ p = makestrspace(NOPTS, expdest);
+ for (i = NOPTS - 1; i >= 0; i--) {
+ if (optlist[i]) {
+ USTPUTC(optletters(i), p);
+ len++;
+ }
}
+ expdest = p;
break;
case '@':
- if (allow_split && quoted) {
- sep = 1 << CHAR_BIT;
+ if (sep)
goto param;
- }
/* fall through */
case '*':
sep = ifsset() ? ifsval()[0] : ' ';
- if (quotes) {
- sepq = (SIT(sep, syntax) == CCTL) || (SIT(sep, syntax) == CBACK);
- }
+ if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
+ sepq = 1;
param:
- for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
- strtodest(p, syntax, quotes);
- if (*ap && sep) {
- p = expdest;
+ if (!(ap = shellparam.p))
+ return -1;
+ while ((p = *ap++)) {
+ size_t partlen;
+
+ partlen = strlen(p);
+
+ len += partlen;
+ if (len > partlen && sep) {
+ char *q;
+
+ len++;
+ if (subtype == VSPLUS || subtype == VSLENGTH) {
+ continue;
+ }
+ q = expdest;
if (sepq)
- STPUTC(CTLESC, p);
- STPUTC(sep, p);
- expdest = p;
+ STPUTC(CTLESC, q);
+ STPUTC(sep, q);
+ expdest = q;
}
+
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, partlen, syntax, quotes);
}
- break;
+ return len;
case '0':
- strtodest(arg0, syntax, quotes);
- break;
- default:
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
num = atoi(name);
- if (num > 0 && num <= shellparam.nparam) {
- strtodest(shellparam.p[num - 1], syntax, quotes);
- }
- break;
+ if (num < 0 || num > shellparam.nparam)
+ return -1;
+ p = num ? shellparam.p[num - 1] : arg0;
+ goto value;
+ default:
+ p = lookupvar(name);
+value:
+ if (!p)
+ return -1;
+
+ len = strlen(p);
+ if (!(subtype == VSPLUS || subtype == VSLENGTH))
+ memtodest(p, len, syntax, quotes);
+ return len;
}
-}
+ if (subtype == VSPLUS || subtype == VSLENGTH)
+ STADJUST(-len, expdest);
+ return len;
+}
/*
}
-
/*
* Break the argument string into pieces based upon IFS and add the
* strings to the argument list. The regions of the string to be
INTON;
}
+static void expmeta(char *, char *);
+static struct strlist *expsort(struct strlist *);
+static struct strlist *msort(struct strlist *, int);
+static char *expdir;
-/*
- * Expand shell metacharacters. At this point, the only control characters
- * should be escapes. The results are stored in the list exparg.
- */
static void
-expandmeta(str, flag)
- struct strlist *str;
- int flag;
+expandmeta(struct strlist *str, int flag)
{
+ static const char metachars[] = {
+ '*', '?', '[', 0
+ };
/* TODO - EXP_REDIR */
while (str) {
- const char *p;
- glob_t pglob;
- int i;
+ struct strlist **savelastp;
+ struct strlist *sp;
+ char *p;
if (fflag)
goto nometa;
+ if (!strpbrk(str->text, metachars))
+ goto nometa;
+ savelastp = exparg.lastp;
+
INTOFF;
p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
- i = glob(p, GLOB_NOMAGIC, 0, &pglob);
+ {
+ int i = strlen(str->text);
+ expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+ }
+
+ expmeta(expdir, p);
+ ckfree(expdir);
if (p != str->text)
ckfree(p);
- switch (i) {
- case 0:
- if (!(pglob.gl_flags & GLOB_MAGCHAR))
- goto nometa2;
- addglob(&pglob);
- globfree(&pglob);
- INTON;
- break;
- case GLOB_NOMATCH:
-nometa2:
- globfree(&pglob);
- INTON;
+ INTON;
+ if (exparg.lastp == savelastp) {
+ /*
+ * no matches
+ */
nometa:
*exparg.lastp = str;
rmescapes(str->text);
exparg.lastp = &str->next;
- break;
- default: /* GLOB_NOSPACE */
- error(bb_msg_memory_exhausted);
+ } else {
+ *exparg.lastp = NULL;
+ *savelastp = sp = expsort(*savelastp);
+ while (sp->next != NULL)
+ sp = sp->next;
+ exparg.lastp = &sp->next;
}
str = str->next;
}
}
-
-/*
- * Add the result of glob(3) to the list.
- */
-
-static void
-addglob(pglob)
- const glob_t *pglob;
-{
- char **p = pglob->gl_pathv;
-
- do {
- addfname(*p);
- } while (*++p);
-}
-
-
/*
* Add a file name to the list.
*/
static void
-addfname(char *name)
+addfname(const char *name)
{
struct strlist *sp;
/*
- * Returns true if the pattern matches the string.
- */
-
-static inline int
-patmatch(char *pattern, const char *string)
-{
- return pmatch(preglob(pattern, 0, 0), string);
-}
-
-
-/*
- * Remove any CTLESC characters from a string.
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
*/
-static char *
-_rmescapes(char *str, int flag)
+static void
+expmeta(char *enddir, char *name)
{
- char *p, *q, *r;
- static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
- unsigned inquotes;
- int notescaped;
- int globbing;
-
- p = strpbrk(str, qchars);
+ char *p;
+ const char *cp;
+ char *start;
+ char *endname;
+ int metaflag;
+ struct stat statb;
+ DIR *dirp;
+ struct dirent *dp;
+ int atend;
+ int matchdot;
+
+ metaflag = 0;
+ start = name;
+ for (p = name; *p; p++) {
+ if (*p == '*' || *p == '?')
+ metaflag = 1;
+ else if (*p == '[') {
+ char *q = p + 1;
+ if (*q == '!')
+ q++;
+ for (;;) {
+ if (*q == '\\')
+ q++;
+ if (*q == '/' || *q == '\0')
+ break;
+ if (*++q == ']') {
+ metaflag = 1;
+ break;
+ }
+ }
+ } else if (*p == '\\')
+ p++;
+ else if (*p == '/') {
+ if (metaflag)
+ goto out;
+ start = p + 1;
+ }
+ }
+out:
+ if (metaflag == 0) { /* we've reached the end of the file name */
+ if (enddir != expdir)
+ metaflag++;
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p;
+ } while (*p++);
+ if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+ addfname(expdir);
+ return;
+ }
+ endname = p;
+ if (name < start) {
+ p = name;
+ do {
+ if (*p == '\\')
+ p++;
+ *enddir++ = *p++;
+ } while (p < start);
+ }
+ if (enddir == expdir) {
+ cp = ".";
+ } else if (enddir == expdir + 1 && *expdir == '/') {
+ cp = "/";
+ } else {
+ cp = expdir;
+ enddir[-1] = '\0';
+ }
+ if ((dirp = opendir(cp)) == NULL)
+ return;
+ if (enddir != expdir)
+ enddir[-1] = '/';
+ if (*endname == 0) {
+ atend = 1;
+ } else {
+ atend = 0;
+ *endname++ = '\0';
+ }
+ matchdot = 0;
+ p = start;
+ if (*p == '\\')
+ p++;
+ if (*p == '.')
+ matchdot++;
+ while (! intpending && (dp = readdir(dirp)) != NULL) {
+ if (dp->d_name[0] == '.' && ! matchdot)
+ continue;
+ if (pmatch(start, dp->d_name)) {
+ if (atend) {
+ scopy(dp->d_name, enddir);
+ addfname(expdir);
+ } else {
+ for (p = enddir, cp = dp->d_name;
+ (*p++ = *cp++) != '\0';)
+ continue;
+ p[-1] = '/';
+ expmeta(p, endname);
+ }
+ }
+ }
+ closedir(dirp);
+ if (! atend)
+ endname[-1] = '/';
+}
+
+/*
+ * Sort the results of file name expansion. It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+
+static struct strlist *
+expsort(struct strlist *str)
+{
+ int len;
+ struct strlist *sp;
+
+ len = 0;
+ for (sp = str ; sp ; sp = sp->next)
+ len++;
+ return msort(str, len);
+}
+
+
+static struct strlist *
+msort(struct strlist *list, int len)
+{
+ struct strlist *p, *q = NULL;
+ struct strlist **lpp;
+ int half;
+ int n;
+
+ if (len <= 1)
+ return list;
+ half = len >> 1;
+ p = list;
+ for (n = half ; --n >= 0 ; ) {
+ q = p;
+ p = p->next;
+ }
+ q->next = NULL; /* terminate first half of list */
+ q = msort(list, half); /* sort first half of list */
+ p = msort(p, len - half); /* sort second half */
+ lpp = &list;
+ for (;;) {
+#ifdef CONFIG_LOCALE_SUPPORT
+ if (strcoll(p->text, q->text) < 0)
+#else
+ if (strcmp(p->text, q->text) < 0)
+#endif
+ {
+ *lpp = p;
+ lpp = &p->next;
+ if ((p = *lpp) == NULL) {
+ *lpp = q;
+ break;
+ }
+ } else {
+ *lpp = q;
+ lpp = &q->next;
+ if ((q = *lpp) == NULL) {
+ *lpp = p;
+ break;
+ }
+ }
+ }
+ return list;
+}
+
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+
+static inline int
+patmatch(char *pattern, const char *string)
+{
+ return pmatch(preglob(pattern, 0, 0), string);
+}
+
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+
+static char *
+_rmescapes(char *str, int flag)
+{
+ char *p, *q, *r;
+ static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
+ unsigned inquotes;
+ int notescaped;
+ int globbing;
+
+ p = strpbrk(str, qchars);
if (!p) {
return str;
}
}
-
/*
* See if a pattern matches in a case statement.
*/
*/
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;
}
}
error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
-/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
+/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
/*
- * This file implements the input routines used by the parser.
+ * This implements the input routines used by the parser.
*/
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
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 */
- raise(SIGINT);
+ if(trap[SIGINT]) {
+ buf[0] = '\n';
+ buf[1] = 0;
+ raise(SIGINT);
+ return 1;
+ }
goto retry;
}
- if(nr < 0) {
+ if(nr < 0 && errno == 0) {
/* Ctrl+D presend */
nr = 0;
}
if (vflag) {
out2str(parsenextc);
- flushout(stderr);
}
*q = savec;
}
-
/*
* To handle the "." command, a stack of input files is used. Pushfile
* adds a new entry to the stack and popfile restores the previous level.
}
-
/*
* Close the file(s) that the shell is reading commands from. Called
* after a fork is done.
parsefile->fd = 0;
}
}
-/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
+/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
/* mode flags for set_curjob */
#define CUR_DELETE 2
}
static int
-killcmd(argc, argv)
- int argc;
- char **argv;
+killcmd(int argc, char **argv)
{
int signo = -1;
int list = 0;
int st;
col = 0;
- st = WEXITSTATUS(status);
if (!WIFEXITED(status)) {
- st = WSTOPSIG(status);
#if JOBS
- if (!WIFSTOPPED(status))
- st = WTERMSIG(status);
+ if (WIFSTOPPED(status))
+ st = WSTOPSIG(status);
+ else
#endif
+ st = WTERMSIG(status);
if (sigonly) {
if (st == SIGINT || st == SIGPIPE)
goto out;
+#if JOBS
if (WIFSTOPPED(status))
goto out;
+#endif
}
st &= 0x7f;
- col = fmtstr(s, 32, u_signal_names(NULL, &st, 0));
+ col = fmtstr(s, 32, strsignal(st));
if (WCOREDUMP(status)) {
col += fmtstr(s + col, 16, " (core dumped)");
}
} else if (!sigonly) {
+ st = WEXITSTATUS(status);
if (st)
col = fmtstr(s, 16, "Done(%d)", st);
else
col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
start:
- fprintf(
- out, "%s%*c%s",
+ fprintf(out, "%s%*c%s",
s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
);
if (!(mode & SHOW_PID)) {
}
-
/*
* Convert a job name to a job structure.
*/
}
-
/*
* Return a new job structure.
* Called with interrupts off.
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
}
-
/*
* return 1 if there are stopped jobs, otherwise 0
*/
+
int
stoppedjobs(void)
{
const char *p;
char s[2];
+ if (!n)
+ return;
switch (n->type) {
default:
#if DEBUG
}
}
-
static void
cmdputs(const char *s)
{
int quoted = 0;
static const char *const vstype[16] = {
nullstr, "}", "-", "+", "?", "=",
- "#", "##", "%", "%%"
+ "%", "%%", "#", "##", nullstr
};
nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
exitshell();
- if (e == EXINT ) {
+ if (e == EXINT) {
outcslow('\n', stderr);
}
popstackmark(&smark);
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);
state3:
state = 4;
if (minusc)
- evalstring(minusc, 0);
+ evalstring(minusc);
if (sflag || minusc == NULL) {
-state4: /* XXX ??? - why isn't this before the "if" statement */
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
if ( iflag ) {
const char *hp = lookupvar("HISTFILE");
load_history ( hp );
}
#endif
+state4: /* XXX ??? - why isn't this before the "if" statement */
cmdloop(1);
}
#if PROFILE
int numeof = 0;
TRACE(("cmdloop(%d) called\n", top));
- setstackmark(&smark);
for (;;) {
+ setstackmark(&smark);
if (pendingsigs)
dotrap();
#if JOBS
evaltree(n, 0);
}
popstackmark(&smark);
- setstackmark(&smark);
- if (evalskip == SKIPFILE) {
+ if (evalskip) {
evalskip = 0;
break;
}
}
- popstackmark(&smark);
}
-
/*
* Read /etc/profile or .profile. Return on error.
*/
}
-
/*
* Read a file containing shell functions.
*/
/* 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;
/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
/*
- * Like malloc, but returns an error when out of space.
+ * Same for malloc, realloc, but returns an error when out of space.
*/
static pointer
-ckmalloc(size_t nbytes)
+ckrealloc(pointer p, size_t nbytes)
{
- pointer p;
-
- p = malloc(nbytes);
+ p = realloc(p, nbytes);
if (p == NULL)
error(bb_msg_memory_exhausted);
return p;
}
-
-/*
- * Same for realloc.
- */
-
static pointer
-ckrealloc(pointer p, size_t nbytes)
+ckmalloc(size_t nbytes)
{
- p = realloc(p, nbytes);
- if (p == NULL)
- error(bb_msg_memory_exhausted);
- return p;
+ return ckrealloc(NULL, nbytes);
}
-
/*
* Make a copy of a string in safe storage.
*/
}
-
void
setstackmark(struct stackmark *mark)
{
{
size_t len = stackblocksize();
if (herefd >= 0 && len >= 1024) {
- xwrite(herefd, stackblock(), len);
+ bb_full_write(herefd, stackblock(), len);
return stackblock();
}
growstackblock();
}
-
/*
* Check for a valid number. This should be elsewhere.
*/
}
-
static void
sizenodelist(struct nodelist *lp)
{
}
-
static union node *
copynode(union node *n)
{
}
-
static char *
nodesavestr(char *s)
{
}
-
/*
* Free a parse tree.
*/
}
-
static void
setoption(int flag, int val)
{
#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)
char c = '?';
int done = 0;
int err = 0;
- char s[10];
- char **optnext = optfirst + *param_optind - 1;
+ char s[12];
+ char **optnext;
- if (*param_optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
- strlen(*(optnext - 1)) < *optoff)
+ if(*param_optind < 1)
+ return 1;
+ optnext = optfirst + *param_optind - 1;
+
+ if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
p = NULL;
else
- p = *(optnext - 1) + *optoff;
+ p = optnext[-1] + *optoff;
if (p == NULL || *p == '\0') {
/* Current word is done, advance */
- if (optnext == NULL)
- return 1;
p = *optnext;
if (p == NULL || *p != '-' || *++p == '\0') {
atend:
return c;
}
-/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
-
+/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
void
outstr(const char *p, FILE *file)
INTON;
}
-
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 $ */
static void setprompt(int);
-static inline int
-goodname(const char *p)
-{
- return !*endofname(p);
-}
static inline int
isassignment(const char *p)
* have parseword (readtoken1?) handle both words and redirection.]
*/
-#define RETURN(token) return lasttoken = token
+#define NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
-static int
-xxreadtoken(void)
+/* singles must be first! */
+static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
+
+static const char xxreadtoken_tokens[] = {
+ TNL, TLP, TRP, /* only single occurrence allowed */
+ TBACKGND, TPIPE, TSEMI, /* if single occurrence */
+ TEOF, /* corresponds to trailing nul */
+ 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()
{
int c;
needprompt = 0;
}
startlinno = plinno;
- for (;;) { /* until token or start of word found */
+ for (;;) { /* until token or start of word found */
c = pgetc_macro();
- switch (c) {
- case ' ': case '\t':
+
+ if ((c != ' ') && (c != '\t')
#ifdef CONFIG_ASH_ALIAS
- case PEOA:
+ && (c != PEOA)
#endif
- continue;
- case '#':
- while ((c = pgetc()) != '\n' && c != PEOF);
+ ) {
+ if (c == '#') {
+ while ((c = pgetc()) != '\n' && c != PEOF);
+ 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;
+
+ if (c != PEOF) {
+ if (c == '\n') {
+ plinno++;
+ needprompt = doprompt;
+ }
+
+ p = strchr(xxreadtoken_chars, c);
+ if (p == NULL) {
+ READTOKEN1:
+ return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
+ }
+
+ if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+ if (pgetc() == *p) { /* double occurrence? */
+ p += xxreadtoken_doubles + 1;
+ } else {
+ pungetc();
+ }
+ }
+ }
+
+ return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ }
+ }
+ }
+}
+
+
+#else
+#define RETURN(token) return lasttoken = token
+
+static int
+xxreadtoken(void)
+{
+ int c;
+
+ if (tokpushback) {
+ tokpushback = 0;
+ return lasttoken;
+ }
+ if (needprompt) {
+ setprompt(2);
+ needprompt = 0;
+ }
+ startlinno = plinno;
+ for (;;) { /* until token or start of word found */
+ c = pgetc_macro();
+ switch (c) {
+ case ' ': case '\t':
+#ifdef CONFIG_ASH_ALIAS
+ case PEOA:
+#endif
+ continue;
+ case '#':
+ while ((c = pgetc()) != '\n' && c != PEOF);
pungetc();
continue;
case '\\':
return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
#undef RETURN
}
-
+#endif /* NEW_xxreadtoken */
/*
* more letters, underscores, and digits).
*/
-char *
+static char *
endofname(const char *name)
- {
+{
char *p;
p = (char *) name;
static const char *const *findkwd(const char *s)
{
return bsearch(s, tokname_array + KWDOFFSET,
- (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
+ (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
sizeof(const char *), pstrcmp);
}
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);
dupredirect(n, newfd);
} while ((n = n->nfile.next));
INTON;
+ if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+ preverrout_fd = sv->renamed[2];
}
-
-
-
-
/*
* Undo the effects of the last redirection.
*/
}
-
/*
* Copy a file descriptor to be >= to. Returns -1
* if the source file descriptor is closed, EMPTY if there are no unused
}
-
static void
shcmd(union node *cmd, FILE *fp)
{
}
-
/*
* Clear traps on a fork.
*/
}
-
/*
* Set the signal handler for the specified signal. The routine figures
* out what it should be set to.
}
-
/*
* Signal handler.
*/
}
-
/*
* Called to execute a trap. Perhaps we should avoid entering new trap
* handlers while we are executing a trap handler.
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)
continue;
- evalstring(p, 0);
+ evalstring(p);
exitstatus = savestatus;
}
}
-
/*
* Controls whether the shell is interactive or not.
*/
-
void
setinteractive(int on)
{
struct jmploc loc;
char *p;
int status;
+ int jmp;
+ jmp = setjmp(loc.loc);
status = exitstatus;
TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
- if (setjmp(loc.loc)) {
+ if (jmp)
goto out;
- }
handler = &loc;
if ((p = trap[0]) != NULL && *p != '\0') {
trap[0] = NULL;
- evalstring(p, 0);
+ evalstring(p);
}
flushall();
+ setjobctl(0);
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
if (iflag && rootshell) {
const char *hp = lookupvar("HISTFILE");
}
#endif
out:
- out1c('\n');
_exit(status);
/* NOTREACHED */
}
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
*/
}
-
/*
* Same as setvar except that the variable and value are passed in
* the first argument as name=value. Since the first argument will
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)
}
-
/*
* Process a linked list of variable assignments.
*/
{
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;
}
-
/*
* Search the environment of a builtin command.
*/
}
-
/*
* Generate a list of variables satisfying the given conditions.
*/
}
-
/*
* POSIX requires that 'set' (but not export or readonly) output the
* variables in lexicographic order - by the locale's collating order (sigh).
}
-
-
/*
* Called after a function returns.
* Interrupts must be off.
retval = 1;
if (flags & VREADONLY)
goto out;
+#ifdef DYNAMIC_VAR
+ vp->flags &= ~VDYNAMIC;
+#endif
if (flags & VUNSET)
goto ok;
if ((flags & VSTRFIXED) == 0) {
}
/* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */
-/*
- * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
- * This code for the times builtin.
- */
-
#include <sys/times.h>
-int timescmd(int ac, char **av) {
+static const unsigned char timescmd_str[] = {
+ ' ', offsetof(struct tms, tms_utime),
+ '\n', offsetof(struct tms, tms_stime),
+ ' ', offsetof(struct tms, tms_cutime),
+ '\n', offsetof(struct tms, tms_cstime),
+ 0
+};
+
+static int timescmd(int ac, char **av)
+{
+ long int clk_tck, s, t;
+ const unsigned char *p;
struct tms buf;
- long int clk_tck = sysconf(_SC_CLK_TCK);
+ clk_tck = sysconf(_SC_CLK_TCK);
times(&buf);
- out1fmt("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
- (int) (buf.tms_utime / clk_tck / 60),
- ((double) buf.tms_utime) / clk_tck,
- (int) (buf.tms_stime / clk_tck / 60),
- ((double) buf.tms_stime) / clk_tck,
- (int) (buf.tms_cutime / clk_tck / 60),
- ((double) buf.tms_cutime) / clk_tck,
- (int) (buf.tms_cstime / clk_tck / 60),
- ((double) buf.tms_cstime) / clk_tck);
+
+ p = timescmd_str;
+ do {
+ t = *(clock_t *)(((char *) &buf) + p[1]);
+ s = t / clk_tck;
+ out1fmt("%ldm%ld.%.3lds%c",
+ s/60, s%60,
+ ((t - s * clk_tck) * 1000) / clk_tck,
+ p[0]);
+ } while (*(p += 2));
+
return 0;
}
#ifdef CONFIG_ASH_MATH_SUPPORT
-static int
+static arith_t
dash_arith(const char *s)
{
- long result = 0;
+ arith_t result;
int errcode = 0;
INTOFF;
result = arith(s, &errcode);
if (errcode < 0) {
- if (errcode == -2)
+ if (errcode == -3)
+ error("exponent less than 0");
+ else if (errcode == -2)
error("divide by zero");
+ else if (errcode == -5)
+ error("expression recursion loop detected");
else
synerror(s);
}
/*
- * The exp(1) builtin.
+ * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
+ * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ *
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
*/
+
static int
-expcmd(int argc, char **argv)
+letcmd(int argc, char **argv)
{
- const char *p;
- char *concat;
char **ap;
- long i;
+ arith_t i;
- if (argc > 1) {
- p = argv[1];
- if (argc > 2) {
- /*
- * concatenate arguments
- */
- STARTSTACKSTR(concat);
- ap = argv + 2;
- for (;;) {
- while (*p)
- STPUTC(*p++, concat);
- if ((p = *ap++) == NULL)
- break;
- STPUTC(' ', concat);
- }
- STPUTC('\0', concat);
- p = grabstackstr(concat);
- }
- } else
- p = nullstr;
-
- i = dash_arith(p);
+ ap = argv + 1;
+ if(!*ap)
+ error("expression expected");
+ for (ap = argv + 1; *ap; ap++) {
+ i = dash_arith(*ap);
+ }
- out1fmt("%ld\n", i);
- return (! i);
+ return (!i);
}
#endif /* CONFIG_ASH_MATH_SUPPORT */
/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
/*
- * Miscelaneous builtins.
+ * Miscellaneous builtins.
*/
#undef rflag
#ifdef __GLIBC__
-#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
typedef enum __rlimit_resource rlim_t;
#endif
#endif
}
if (prompt && isatty(0)) {
out2str(prompt);
- flushall();
}
if (*(ap = argptr) == NULL)
error("arg count");
{ "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
#endif
#ifdef RLIMIT_NPROC
- { "process(processes)", RLIMIT_NPROC, 1, 'p' },
+ { "process", RLIMIT_NPROC, 1, 'p' },
#endif
#ifdef RLIMIT_NOFILE
- { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
+ { "nofiles", RLIMIT_NOFILE, 1, 'n' },
#endif
-#ifdef RLIMIT_VMEM
- { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
+#ifdef RLIMIT_AS
+ { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
#endif
-#ifdef RLIMIT_SWAP
- { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' },
+#ifdef RLIMIT_LOCKS
+ { "locks", RLIMIT_LOCKS, 1, 'w' },
#endif
{ (char *) 0, 0, 0, '\0' }
};
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void printlim(enum limtype how, const struct rlimit *limit,
+ const struct limits *l)
+{
+ rlim_t val;
+
+ val = limit->rlim_max;
+ if (how & SOFT)
+ val = limit->rlim_cur;
+
+ if (val == RLIM_INFINITY)
+ out1fmt("unlimited\n");
+ else {
+ val /= l->factor;
+ out1fmt("%lld\n", (long long) val);
+ }
+}
+
int
ulimitcmd(int argc, char **argv)
{
int c;
rlim_t val = 0;
- enum { SOFT = 0x1, HARD = 0x2 }
- how = SOFT | HARD;
+ enum limtype how = SOFT | HARD;
const struct limits *l;
int set, all = 0;
int optc, what;
struct rlimit limit;
what = 'f';
- while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
+ while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+ "t"
+#endif
+#ifdef RLIMIT_FSIZE
+ "f"
+#endif
+#ifdef RLIMIT_DATA
+ "d"
+#endif
+#ifdef RLIMIT_STACK
+ "s"
+#endif
+#ifdef RLIMIT_CORE
+ "c"
+#endif
+#ifdef RLIMIT_RSS
+ "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+ "l"
+#endif
+#ifdef RLIMIT_NPROC
+ "p"
+#endif
+#ifdef RLIMIT_NOFILE
+ "n"
+#endif
+#ifdef RLIMIT_AS
+ "v"
+#endif
+#ifdef RLIMIT_LOCKS
+ "w"
+#endif
+ )) != '\0')
switch (optc) {
case 'H':
how = HARD;
what = optc;
}
- for (l = limits; l->name && l->option != what; l++)
+ for (l = limits; l->option != what; l++)
;
- if (!l->name)
- error("internal error (%c)", what);
set = *argptr ? 1 : 0;
if (set) {
if (all || argptr[1])
error("too many arguments");
- if (strcmp(p, "unlimited") == 0)
+ if (strncmp(p, "unlimited\n", 9) == 0)
val = RLIM_INFINITY;
else {
val = (rlim_t) 0;
if (all) {
for (l = limits; l->name; l++) {
getrlimit(l->cmd, &limit);
- if (how & SOFT)
- val = limit.rlim_cur;
- else if (how & HARD)
- val = limit.rlim_max;
-
out1fmt("%-20s ", l->name);
- if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
- else
- {
- val /= l->factor;
- out1fmt("%lld\n", (long long) val);
- }
+ printlim(how, &limit, l);
}
return 0;
}
if (setrlimit(l->cmd, &limit) < 0)
error("error setting limit (%m)");
} else {
- if (how & SOFT)
- val = limit.rlim_cur;
- else if (how & HARD)
- val = limit.rlim_max;
+ printlim(how, &limit, l);
+ }
+ return 0;
+}
- if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
- else
- {
- val /= l->factor;
- out1fmt("%lld\n", (long long) val);
+
+#ifdef CONFIG_ASH_MATH_SUPPORT
+
+/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* 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 written in yacc. The supported operators are
+ * listed in #defines below. Parens, order of operations, and error handling
+ * 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 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). */
+
+/* To use the routine, call it with an expression string and error return
+ * pointer */
+
+/*
+ * Aug 24, 2001 Manuel Novoa III
+ *
+ * Reduced the generated code size by about 30% (i386) and fixed several bugs.
+ *
+ * 1) In arith_apply():
+ * a) Cached values of *numptr and &(numptr[-1]).
+ * b) Removed redundant test for zero denominator.
+ *
+ * 2) In arith():
+ * a) Eliminated redundant code for processing operator tokens by moving
+ * to a table-based implementation. Also folded handling of parens
+ * into the table.
+ * b) Combined all 3 loops which called arith_apply to reduce generated
+ * code size at the cost of speed.
+ *
+ * 3) The following expressions were treated as valid by the original code:
+ * 1() , 0! , 1 ( *3 ) .
+ * These bugs have been fixed by internally enclosing the expression in
+ * parens and then checking that all binary ops and right parens are
+ * preceded by a valid expression (NUM_TOKEN).
+ *
+ * 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.
+ */
+
+/*
+ * Aug 26, 2001 Manuel Novoa III
+ *
+ * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
+ *
+ * Merge in Aaron's comments previously posted to the busybox list,
+ * modified slightly to take account of my changes to the code.
+ *
+ */
+
+/*
+ * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * - allow access to variable,
+ * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
+ * - realize assign syntax (VAR=expr, +=, *= etc)
+ * - realize exponentiation (** operator)
+ * - realize comma separated - expr, expr
+ * - realise ++expr --expr expr++ expr--
+ * - realise expr ? expr : expr (but, second expr calculate always)
+ * - 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)
+ * - always use special isspace(), see comment from bash ;-)
+ */
+
+
+#define arith_isspace(arithval) \
+ (arithval == ' ' || arithval == '\n' || arithval == '\t')
+
+
+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 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 /. */
+
+#define tok_decl(prec,id) (((id)<<5)|(prec))
+#define PREC(op) ((op) & 0x1F)
+
+#define TOK_LPAREN tok_decl(0,0)
+
+#define TOK_COMMA tok_decl(1,0)
+
+#define TOK_ASSIGN tok_decl(2,0)
+#define TOK_AND_ASSIGN tok_decl(2,1)
+#define TOK_OR_ASSIGN tok_decl(2,2)
+#define TOK_XOR_ASSIGN tok_decl(2,3)
+#define TOK_PLUS_ASSIGN tok_decl(2,4)
+#define TOK_MINUS_ASSIGN tok_decl(2,5)
+#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
+#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
+
+#define TOK_MUL_ASSIGN tok_decl(3,0)
+#define TOK_DIV_ASSIGN tok_decl(3,1)
+#define TOK_REM_ASSIGN tok_decl(3,2)
+
+/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
+#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
+
+/* conditional is right associativity too */
+#define TOK_CONDITIONAL tok_decl(4,0)
+#define TOK_CONDITIONAL_SEP tok_decl(4,1)
+
+#define TOK_OR tok_decl(5,0)
+
+#define TOK_AND tok_decl(6,0)
+
+#define TOK_BOR tok_decl(7,0)
+
+#define TOK_BXOR tok_decl(8,0)
+
+#define TOK_BAND tok_decl(9,0)
+
+#define TOK_EQ tok_decl(10,0)
+#define TOK_NE tok_decl(10,1)
+
+#define TOK_LT tok_decl(11,0)
+#define TOK_GT tok_decl(11,1)
+#define TOK_GE tok_decl(11,2)
+#define TOK_LE tok_decl(11,3)
+
+#define TOK_LSHIFT tok_decl(12,0)
+#define TOK_RSHIFT tok_decl(12,1)
+
+#define TOK_ADD tok_decl(13,0)
+#define TOK_SUB tok_decl(13,1)
+
+#define TOK_MUL tok_decl(14,0)
+#define TOK_DIV tok_decl(14,1)
+#define TOK_REM tok_decl(14,2)
+
+/* exponent is right associativity */
+#define TOK_EXPONENT tok_decl(15,1)
+
+/* For now unary operators. */
+#define UNARYPREC 16
+#define TOK_BNOT tok_decl(UNARYPREC,0)
+#define TOK_NOT tok_decl(UNARYPREC,1)
+
+#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
+
+#define PREC_PRE (UNARYPREC+2)
+
+#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
+
+#define PREC_POST (UNARYPREC+3)
+
+#define TOK_POST_INC tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC tok_decl(PREC_POST, 1)
+
+#define SPEC_PREC (UNARYPREC+4)
+
+#define TOK_NUM tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
+
+#define NUMPTR (*numstackptr)
+
+static inline int tok_have_assign(operator op)
+{
+ operator prec = PREC(op);
+
+ convert_prec_is_assing(prec);
+ return (prec == PREC(TOK_ASSIGN) ||
+ prec == PREC_PRE || prec == PREC_POST);
+}
+
+static inline int is_right_associativity(operator prec)
+{
+ return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
+ prec == PREC(TOK_CONDITIONAL));
+}
+
+
+typedef struct ARITCH_VAR_NUM {
+ arith_t val;
+ arith_t contidional_second_val;
+ char contidional_second_val_initialized;
+ char *var; /* if NULL then is regular number,
+ else is variable name */
+} v_n_t;
+
+
+typedef struct CHK_VAR_RECURSIVE_LOOPED {
+ const char *var;
+ struct CHK_VAR_RECURSIVE_LOOPED *next;
+} chk_var_recursive_looped_t;
+
+static chk_var_recursive_looped_t *prev_chk_var_recursive;
+
+
+static int arith_lookup_val(v_n_t *t)
+{
+ if(t->var) {
+ const char * p = lookupvar(t->var);
+
+ if(p) {
+ int errcode;
+
+ /* recursive try as expression */
+ chk_var_recursive_looped_t *cur;
+ chk_var_recursive_looped_t cur_save;
+
+ for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
+ if(strcmp(cur->var, t->var) == 0) {
+ /* expression recursion loop detected */
+ return -5;
}
+ }
+ /* save current lookuped var name */
+ cur = prev_chk_var_recursive;
+ cur_save.var = t->var;
+ cur_save.next = cur;
+ prev_chk_var_recursive = &cur_save;
+
+ t->val = arith (p, &errcode);
+ /* restore previous ptr after recursiving */
+ prev_chk_var_recursive = cur;
+ return errcode;
+ } else {
+ /* allow undefined var as 0 */
+ t->val = 0;
}
+ }
+ return 0;
+}
+
+/* "applying" a token means performing it on the top elements on the integer
+ * stack. For a unary operator it will only change the top element, but a
+ * binary operator will pop two arguments and push a result */
+static inline int
+arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
+{
+ v_n_t *numptr_m1;
+ arith_t numptr_val, rez;
+ int ret_arith_lookup_val;
+
+ if (NUMPTR == numstack) goto err; /* There is no operator that can work
+ without arguments */
+ numptr_m1 = NUMPTR - 1;
+
+ /* check operand is var with noninteger value */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if(ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+
+ rez = numptr_m1->val;
+ if (op == TOK_UMINUS)
+ rez *= -1;
+ else if (op == TOK_NOT)
+ rez = !rez;
+ else if (op == TOK_BNOT)
+ rez = ~rez;
+ else if (op == TOK_POST_INC || op == TOK_PRE_INC)
+ rez++;
+ else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
+ rez--;
+ else if (op != TOK_UPLUS) {
+ /* Binary operators */
+
+ /* check and binary operators need two arguments */
+ if (numptr_m1 == numstack) goto err;
+
+ /* ... and they pop one */
+ --NUMPTR;
+ numptr_val = rez;
+ if (op == TOK_CONDITIONAL) {
+ if(! numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 ? expr2)) without ": expr" */
+ goto err;
+ }
+ rez = numptr_m1->contidional_second_val;
+ } else if(numptr_m1->contidional_second_val_initialized) {
+ /* protect $((expr1 : expr2)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1 = NUMPTR - 1;
+ if(op != TOK_ASSIGN) {
+ /* check operand is var with noninteger value for not '=' */
+ ret_arith_lookup_val = arith_lookup_val(numptr_m1);
+ if(ret_arith_lookup_val)
+ return ret_arith_lookup_val;
+ }
+ if (op == TOK_CONDITIONAL) {
+ numptr_m1->contidional_second_val = rez;
+ }
+ rez = numptr_m1->val;
+ if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+ rez |= numptr_val;
+ else if (op == TOK_OR)
+ rez = numptr_val || rez;
+ else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
+ rez &= numptr_val;
+ else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
+ rez ^= numptr_val;
+ else if (op == TOK_AND)
+ rez = rez && numptr_val;
+ else if (op == TOK_EQ)
+ rez = (rez == numptr_val);
+ else if (op == TOK_NE)
+ rez = (rez != numptr_val);
+ else if (op == TOK_GE)
+ rez = (rez >= numptr_val);
+ else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
+ rez >>= numptr_val;
+ else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
+ rez <<= numptr_val;
+ else if (op == TOK_GT)
+ rez = (rez > numptr_val);
+ else if (op == TOK_LT)
+ rez = (rez < numptr_val);
+ else if (op == TOK_LE)
+ rez = (rez <= numptr_val);
+ else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
+ rez *= numptr_val;
+ else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
+ rez += numptr_val;
+ else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
+ rez -= numptr_val;
+ else if (op == TOK_ASSIGN || op == TOK_COMMA)
+ rez = numptr_val;
+ else if (op == TOK_CONDITIONAL_SEP) {
+ if (numptr_m1 == numstack) {
+ /* protect $((expr : expr)) without "expr ? " */
+ goto err;
+ }
+ numptr_m1->contidional_second_val_initialized = op;
+ numptr_m1->contidional_second_val = numptr_val;
+ }
+ else if (op == TOK_CONDITIONAL) {
+ rez = rez ?
+ numptr_val : numptr_m1->contidional_second_val;
+ }
+ else if(op == TOK_EXPONENT) {
+ if(numptr_val < 0)
+ return -3; /* exponent less than 0 */
+ else {
+ arith_t c = 1;
+
+ if(numptr_val)
+ while(numptr_val--)
+ c *= rez;
+ rez = c;
+ }
+ }
+ else if(numptr_val==0) /* zero divisor check */
+ return -2;
+ else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
+ rez /= numptr_val;
+ else if (op == TOK_REM || op == TOK_REM_ASSIGN)
+ rez %= numptr_val;
+ }
+ if(tok_have_assign(op)) {
+ char buf[32];
+
+ if(numptr_m1->var == NULL) {
+ /* Hmm, 1=2 ? */
+ goto err;
+ }
+ /* save to shell variable */
+ 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)
+ rez--;
+ else if(op == TOK_POST_DEC)
+ rez++;
+ }
+ numptr_m1->val = rez;
+ /* protect geting var value, is number now */
+ numptr_m1->var = NULL;
return 0;
+err: return(-1);
+}
+
+/* longest must first */
+static const char op_tokens[] = {
+ '<','<','=',0, TOK_LSHIFT_ASSIGN,
+ '>','>','=',0, TOK_RSHIFT_ASSIGN,
+ '<','<', 0, TOK_LSHIFT,
+ '>','>', 0, TOK_RSHIFT,
+ '|','|', 0, TOK_OR,
+ '&','&', 0, TOK_AND,
+ '!','=', 0, TOK_NE,
+ '<','=', 0, TOK_LE,
+ '>','=', 0, TOK_GE,
+ '=','=', 0, TOK_EQ,
+ '|','=', 0, TOK_OR_ASSIGN,
+ '&','=', 0, TOK_AND_ASSIGN,
+ '*','=', 0, TOK_MUL_ASSIGN,
+ '/','=', 0, TOK_DIV_ASSIGN,
+ '%','=', 0, TOK_REM_ASSIGN,
+ '+','=', 0, TOK_PLUS_ASSIGN,
+ '-','=', 0, TOK_MINUS_ASSIGN,
+ '-','-', 0, TOK_POST_DEC,
+ '^','=', 0, TOK_XOR_ASSIGN,
+ '+','+', 0, TOK_POST_INC,
+ '*','*', 0, TOK_EXPONENT,
+ '!', 0, TOK_NOT,
+ '<', 0, TOK_LT,
+ '>', 0, TOK_GT,
+ '=', 0, TOK_ASSIGN,
+ '|', 0, TOK_BOR,
+ '&', 0, TOK_BAND,
+ '*', 0, TOK_MUL,
+ '/', 0, TOK_DIV,
+ '%', 0, TOK_REM,
+ '+', 0, TOK_ADD,
+ '-', 0, TOK_SUB,
+ '^', 0, TOK_BXOR,
+ /* uniq */
+ '~', 0, TOK_BNOT,
+ ',', 0, TOK_COMMA,
+ '?', 0, TOK_CONDITIONAL,
+ ':', 0, TOK_CONDITIONAL_SEP,
+ ')', 0, TOK_RPAREN,
+ '(', 0, TOK_LPAREN,
+ 0
+};
+/* ptr to ")" */
+#define endexpression &op_tokens[sizeof(op_tokens)-7]
+
+
+static arith_t arith (const char *expr, int *perrcode)
+{
+ register char arithval; /* Current character under analysis */
+ operator lasttok, op;
+ operator prec;
+
+ const char *p = endexpression;
+ int errcode;
+
+ size_t 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;
+ /* Stack of operator tokens */
+ operator *stack = alloca((datasizes) * sizeof(operator)),
+ *stackptr = stack;
+
+ *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
+ *perrcode = errcode = 0;
+
+ while(1) {
+ if ((arithval = *expr) == 0) {
+ if (p == endexpression) {
+ /* Null expression. */
+ return 0;
+ }
+
+ /* This is only reached after all tokens have been extracted from the
+ * input stream. If there are still tokens on the operator stack, they
+ * are to be applied in order. At the end, there should be a final
+ * result on the integer stack */
+
+ if (expr != endexpression + 1) {
+ /* If we haven't done so already, */
+ /* append a closing right paren */
+ expr = endexpression;
+ /* and let the loop process it. */
+ continue;
+ }
+ /* At this point, we're done with the expression. */
+ if (numstackptr != numstack+1) {
+ /* ... but if there isn't, it's bad */
+ err:
+ return (*perrcode = -1);
+ }
+ if(numstack->var) {
+ /* expression is $((var)) only, lookup now */
+ errcode = arith_lookup_val(numstack);
+ }
+ ret:
+ *perrcode = errcode;
+ return numstack->val;
+ } else {
+ /* Continue processing the expression. */
+ if (arith_isspace(arithval)) {
+ /* Skip whitespace */
+ goto prologue;
+ }
+ if((p = endofname(expr)) != expr) {
+ size_t var_name_size = (p-expr) + 1; /* trailing zero */
+
+ numstackptr->var = alloca(var_name_size);
+ safe_strncpy(numstackptr->var, expr, var_name_size);
+ expr = p;
+ num:
+ numstackptr->contidional_second_val_initialized = 0;
+ numstackptr++;
+ lasttok = TOK_NUM;
+ continue;
+ } else if (is_digit(arithval)) {
+ numstackptr->var = NULL;
+ numstackptr->val = strtoll(expr, (char **) &expr, 0);
+ goto num;
+ }
+ for(p = op_tokens; ; p++) {
+ const char *o;
+
+ if(*p == 0) {
+ /* strange operator not found */
+ goto err;
+ }
+ for(o = expr; *p && *o == *p; p++)
+ o++;
+ if(! *p) {
+ /* found */
+ expr = o - 1;
+ break;
+ }
+ /* skip tail uncompared token */
+ while(*p)
+ p++;
+ /* skip zero delim */
+ p++;
+ }
+ op = p[1];
+
+ /* post grammar: a++ reduce to num */
+ if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
+ lasttok = TOK_NUM;
+
+ /* Plus and minus are binary (not unary) _only_ if the last
+ * token was as number, or a right paren (which pretends to be
+ * a number, since it evaluates to one). Think about it.
+ * It makes sense. */
+ if (lasttok != TOK_NUM) {
+ switch(op) {
+ case TOK_ADD:
+ op = TOK_UPLUS;
+ break;
+ case TOK_SUB:
+ op = TOK_UMINUS;
+ break;
+ case TOK_POST_INC:
+ op = TOK_PRE_INC;
+ break;
+ case TOK_POST_DEC:
+ op = TOK_PRE_DEC;
+ break;
+ }
+ }
+ /* We don't want a unary operator to cause recursive descent on the
+ * stack, because there can be many in a row and it could cause an
+ * operator to be evaluated before its argument is pushed onto the
+ * integer stack. */
+ /* But for binary operators, "apply" everything on the operator
+ * stack until we find an operator with a lesser priority than the
+ * one we have just extracted. */
+ /* Left paren is given the lowest priority so it will never be
+ * "applied" in this way.
+ * if associativity is right and priority eq, applied also skip
+ */
+ prec = PREC(op);
+ if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
+ /* not left paren or unary */
+ if (lasttok != TOK_NUM) {
+ /* binary op must be preceded by a num */
+ goto err;
+ }
+ while (stackptr != stack) {
+ if (op == TOK_RPAREN) {
+ /* The algorithm employed here is simple: while we don't
+ * hit an open paren nor the bottom of the stack, pop
+ * tokens and apply them */
+ if (stackptr[-1] == TOK_LPAREN) {
+ --stackptr;
+ /* Any operator directly after a */
+ lasttok = TOK_NUM;
+ /* close paren should consider itself binary */
+ goto prologue;
+ }
+ } else {
+ operator prev_prec = PREC(stackptr[-1]);
+
+ convert_prec_is_assing(prec);
+ convert_prec_is_assing(prev_prec);
+ if (prev_prec < prec)
+ break;
+ /* check right assoc */
+ if(prev_prec == prec && is_right_associativity(prec))
+ break;
+ }
+ errcode = arith_apply(*--stackptr, numstack, &numstackptr);
+ if(errcode) goto ret;
+ }
+ if (op == TOK_RPAREN) {
+ goto err;
+ }
+ }
+
+ /* Push this operator to the stack and remember it. */
+ *stackptr++ = lasttok = op;
+
+ prologue:
+ ++expr;
+ }
+ }
}
+#endif /* CONFIG_ASH_MATH_SUPPORT */
+
#ifdef DEBUG
const char *bb_applet_name = "debug stuff usage";