* 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; \
/* next character in input buffer */
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 __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 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 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 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 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());
}
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;
}
char *start;
char *endname;
int metaflag;
- struct stat64 statb;
+ struct stat statb;
DIR *dirp;
struct dirent *dp;
int atend;
p++;
*enddir++ = *p;
} while (*p++);
- if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
+ if (metaflag == 0 || lstat(expdir, &statb) >= 0)
addfname(expdir);
return;
}
*/
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 */
- 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;
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
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
const char *p;
char s[2];
+ if (!n)
+ return;
switch (n->type) {
default:
#if DEBUG
int quoted = 0;
static const char *const vstype[16] = {
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);
state3:
state = 4;
if (minusc)
- evalstring(minusc, 0);
+ evalstring(minusc);
if (sflag || minusc == NULL) {
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
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);
}
/* 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)
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)
* more letters, underscores, and digits).
*/
-char *
+static char *
endofname(const char *name)
{
char *p;
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];
}
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;
}
}
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");
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
#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) {
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;
-
- if (val == RLIM_INFINITY)
- out1fmt("unlimited\n");
- else
- {
- val /= l->factor;
- out1fmt("%lld\n", (long long) val);
- }
+ printlim(how, &limit, l);
}
return 0;
}
/* 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++) {