#include <sys/times.h>
#include "busybox.h"
+extern char **environ;
+
/*#define MSHDEBUG 1*/
/* PROTOTYPES */
static int newfile(char *s);
-static char *findeq(char *cp);
static char *cclass(char *p, int sub);
-static void initarea(void);
-extern int msh_main(int argc, char **argv);
struct brkcon {
static char line[LINELIM];
static char *elinep;
+#if ENABLE_FEATURE_EDITING
+static char *current_prompt;
+static line_input_t *line_input_state;
+#endif
+
+static int areanum; /* current allocation area */
+
/*
* other functions
static builtin_func_ptr inbuilt(char *s);
static char *rexecve(char *c, char **v, char **envp);
-static char *space(int n);
-static char *strsave(char *s, int a);
static char *evalstr(char *cp, int f);
static char *putn(int n);
static char *unquote(char *as);
-static struct var *lookup(char *n);
static int rlookup(char *n);
static struct wdblock *glob(char *cp, struct wdblock *wb);
static int my_getc(int ec);
static int gmatch(char *s, char *p);
-/*
- * error handling
- */
-static void leave(void); /* abort shell (or fail in subshell) */
-static void fail(void); /* fail but return to process next command */
-static void warn(const char *s);
-static void sig(int i); /* default signal handler */
-
-
-
/* -------- area stuff -------- */
#define REGSIZE sizeof(struct region)
};
-
/* -------- grammar stuff -------- */
typedef union {
char *cp;
struct op *o;
} YYSTYPE;
-#define WORD 256
-#define LOGAND 257
-#define LOGOR 258
-#define BREAK 259
-#define IF 260
-#define THEN 261
-#define ELSE 262
-#define ELIF 263
-#define FI 264
-#define CASE 265
-#define ESAC 266
-#define FOR 267
-#define WHILE 268
-#define UNTIL 269
-#define DO 270
-#define DONE 271
-#define IN 272
+#define WORD 256
+#define LOGAND 257
+#define LOGOR 258
+#define BREAK 259
+#define IF 260
+#define THEN 261
+#define ELSE 262
+#define ELIF 263
+#define FI 264
+#define CASE 265
+#define ESAC 266
+#define FOR 267
+#define WHILE 268
+#define UNTIL 269
+#define DO 270
+#define DONE 271
+#define IN 272
/* Added for "." file expansion */
-#define DOT 273
+#define DOT 273
#define YYERRCODE 300
/* flags to yylex */
-#define CONTIN 01 /* skip new lines to complete command */
+#define CONTIN 01 /* skip new lines to complete command */
-#define SYNTAXERR zzerr()
+#define SYNTAXERR zzerr()
static struct op *pipeline(int cf);
static struct op *andor(void);
#define GETCELL 04 /* name & value space was got with getcell */
static int yyparse(void);
-static struct var *lookup(char *n);
-static void setval(struct var *vp, char *val);
-static void nameval(struct var *vp, char *val, char *name);
-static void export(struct var *vp);
-static void ronly(struct var *vp);
-static int isassign(char *s);
-static int checkname(char *cp);
-static int assign(char *s, int cf);
-static void putvlist(int f, int out);
-static int eqname(char *n1, char *n2);
static int execute(struct op *t, int *pin, int *pout, int act);
static struct wdblock *newword(int nw);
static char **getwords(struct wdblock *wb);
-/* -------- area.h -------- */
-
-/*
- * storage allocation
- */
-static char *getcell(unsigned nbytes);
-static void garbage(void);
-static void setarea(char *cp, int a);
-static int getarea(char *cp);
-static void freearea(int a);
-static void freecell(char *cp);
-static int areanum; /* current allocation area */
-
-#define NEW(type) (type *)getcell(sizeof(type))
-#define DELETE(obj) freecell((char *)obj)
-
-
/* -------- misc stuff -------- */
static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp);
static struct op *scantree(struct op *);
static struct op *dowholefile(int, int);
-/* Globals */
-extern char **environ; /* environment pointer */
+/* Globals */
static char **dolv;
static int dolc;
static int exstat;
#endif /* MSHDEBUG */
-#if ENABLE_FEATURE_EDITING
-static char *current_prompt;
-#endif
+/* fail but return to process next command */
+static void fail(void)
+{
+ longjmp(failpt, 1);
+ /* NOTREACHED */
+}
-/* -------- sh.c -------- */
-/*
- * shell
- */
+/* abort shell (or fail in subshell) */
+static void leave(void) ATTRIBUTE_NORETURN;
+static void leave(void)
+{
+ DBGPRINTF(("LEAVE: leave called!\n"));
+ if (execflg)
+ fail();
+ scraphere();
+ freehere(1);
+ runtrap(0);
+ _exit(exstat);
+ /* NOTREACHED */
+}
-#if ENABLE_FEATURE_EDITING
-static line_input_t *line_input_state;
-#endif
+static void warn(const char *s)
+{
+ if (*s) {
+ prs(s);
+ exstat = -1;
+ }
+ prs("\n");
+ if (flag['e'])
+ leave();
+}
-int msh_main(int argc, char **argv)
+static void err(const char *s)
{
- int f;
- char *s;
- int cflag;
- char *name, **ap;
- int (*iof) (struct ioarg *);
+ warn(s);
+ if (flag['n'])
+ return;
+ if (!interactive)
+ leave();
+ if (e.errpt)
+ longjmp(e.errpt, 1);
+ closeall();
+ e.iop = e.iobase = iostack;
+}
-#if ENABLE_FEATURE_EDITING
- line_input_state = new_line_input_t(FOR_SHELL);
-#endif
+/* -------- area.c -------- */
- DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
+/*
+ * All memory between (char *)areabot and (char *)(areatop+1) is
+ * exclusively administered by the area management routines.
+ * It is assumed that sbrk() and brk() manipulate the high end.
+ */
- initarea();
- ap = environ;
- if (ap != NULL) {
- while (*ap)
- assign(*ap++, !COPYV);
- for (ap = environ; *ap;)
- export(lookup(*ap++));
- }
- closeall();
- areanum = 1;
+#define sbrk(X) ({ \
+ void * __q = (void *)-1; \
+ if (brkaddr + (int)(X) < brktop) { \
+ __q = brkaddr; \
+ brkaddr += (int)(X); \
+ } \
+ __q; \
+})
- shell = lookup("SHELL");
- if (shell->value == null)
- setval(shell, (char *)DEFAULT_SHELL);
- export(shell);
+static void initarea(void)
+{
+ brkaddr = xmalloc(AREASIZE);
+ brktop = brkaddr + AREASIZE;
- homedir = lookup("HOME");
- if (homedir->value == null)
- setval(homedir, "/");
- export(homedir);
+ while ((long) sbrk(0) & ALIGN)
+ sbrk(1);
+ areabot = (struct region *) sbrk(REGSIZE);
- setval(lookup("$"), putn(getpid()));
+ areabot->next = areabot;
+ areabot->area = BUSY;
+ areatop = areabot;
+ areanxt = areabot;
+}
- path = lookup("PATH");
- if (path->value == null) {
- if (geteuid() == 0)
- setval(path, "/sbin:/bin:/usr/sbin:/usr/bin");
- else
- setval(path, "/bin:/usr/bin");
- }
- export(path);
+static char *getcell(unsigned nbytes)
+{
+ int nregio;
+ struct region *p, *q;
+ int i;
- ifs = lookup("IFS");
- if (ifs->value == null)
- setval(ifs, " \t\n");
+ if (nbytes == 0) {
+ puts("getcell(0)");
+ abort();
+ }
+ /* silly and defeats the algorithm */
+ /*
+ * round upwards and add administration area
+ */
+ nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
+ p = areanxt;
+ for (;;) {
+ if (p->area > areanum) {
+ /*
+ * merge free cells
+ */
+ while ((q = p->next)->area > areanum && q != areanxt)
+ p->next = q->next;
+ /*
+ * exit loop if cell big enough
+ */
+ if (q >= p + nregio)
+ goto found;
+ }
+ p = p->next;
+ if (p == areanxt)
+ break;
+ }
+ i = nregio >= GROWBY ? nregio : GROWBY;
+ p = (struct region *) sbrk(i * REGSIZE);
+ if (p == (struct region *) -1)
+ return NULL;
+ p--;
+ if (p != areatop) {
+ puts("not contig");
+ abort(); /* allocated areas are contiguous */
+ }
+ q = p + i;
+ p->next = q;
+ p->area = FREE;
+ q->next = areabot;
+ q->area = BUSY;
+ areatop = q;
+ found:
+ /*
+ * we found a FREE area big enough, pointed to by 'p', and up to 'q'
+ */
+ areanxt = p + nregio;
+ if (areanxt < q) {
+ /*
+ * split into requested area and rest
+ */
+ if (areanxt + 1 > q) {
+ puts("OOM");
+ abort(); /* insufficient space left for admin */
+ }
+ areanxt->next = q;
+ areanxt->area = FREE;
+ p->next = areanxt;
+ }
+ p->area = areanum;
+ return (char *) (p + 1);
+}
-#ifdef MSHDEBUG
- mshdbg_var = lookup("MSHDEBUG");
- if (mshdbg_var->value == null)
- setval(mshdbg_var, "0");
-#endif
+static void freecell(char *cp)
+{
+ struct region *p;
- prompt = lookup("PS1");
-#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
- if (prompt->value == null)
-#endif
- setval(prompt, DEFAULT_USER_PROMPT);
- if (geteuid() == 0) {
- setval(prompt, DEFAULT_ROOT_PROMPT);
- prompt->status &= ~EXPORT;
+ p = (struct region *) cp;
+ if (p != NULL) {
+ p--;
+ if (p < areanxt)
+ areanxt = p;
+ p->area = FREE;
}
- cprompt = lookup("PS2");
-#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
- if (cprompt->value == null)
-#endif
- setval(cprompt, "> ");
+}
+#define DELETE(obj) freecell((char *)obj)
- iof = filechar;
- cflag = 0;
- name = *argv++;
- if (--argc >= 1) {
- if (argv[0][0] == '-' && argv[0][1] != '\0') {
- for (s = argv[0] + 1; *s; s++)
- switch (*s) {
- case 'c':
- prompt->status &= ~EXPORT;
- cprompt->status &= ~EXPORT;
- setval(prompt, "");
- setval(cprompt, "");
- cflag = 1;
- if (--argc > 0)
- PUSHIO(aword, *++argv, iof = nlchar);
- break;
+static void freearea(int a)
+{
+ struct region *p, *top;
- case 'q':
- qflag = SIG_DFL;
- break;
+ top = areatop;
+ for (p = areabot; p != top; p = p->next)
+ if (p->area >= a)
+ p->area = FREE;
+}
- case 's':
- /* standard input */
- break;
+static void setarea(char *cp, int a)
+{
+ struct region *p;
- case 't':
- prompt->status &= ~EXPORT;
- setval(prompt, "");
- iof = linechar;
- break;
+ p = (struct region *) cp;
+ if (p != NULL)
+ (p - 1)->area = a;
+}
- case 'i':
- interactive++;
- default:
- if (*s >= 'a' && *s <= 'z')
- flag[(int) *s]++;
- }
- } else {
- argv--;
- argc++;
- }
+static int getarea(char *cp)
+{
+ return ((struct region *) cp - 1)->area;
+}
- if (iof == filechar && --argc > 0) {
- setval(prompt, "");
- setval(cprompt, "");
- prompt->status &= ~EXPORT;
- cprompt->status &= ~EXPORT;
+static void garbage(void)
+{
+ struct region *p, *q, *top;
-/* Shell is non-interactive, activate printf-based debug */
-#ifdef MSHDEBUG
- mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
- if (mshdbg < 0)
- mshdbg = 0;
+ top = areatop;
+ for (p = areabot; p != top; p = p->next) {
+ if (p->area > areanum) {
+ while ((q = p->next)->area > areanum)
+ p->next = q->next;
+ areanxt = p;
+ }
+ }
+#ifdef SHRINKBY
+ if (areatop >= q + SHRINKBY && q->area > areanum) {
+ brk((char *) (q + 1));
+ q->next = areabot;
+ q->area = BUSY;
+ areatop = q;
+ }
#endif
- DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
+}
- name = *++argv;
- if (newfile(name))
- exit(1); /* Exit on error */
- }
+static char *space(int n)
+{
+ char *cp;
+
+ cp = getcell(n);
+ if (cp == '\0')
+ err("out of string space");
+ return cp;
+}
+
+static char *strsave(const char *s, int a)
+{
+ char *cp;
+
+ cp = space(strlen(s) + 1);
+ if (cp != NULL) {
+ setarea(cp, a);
+ strcpy(cp, s);
+ return cp;
}
+ return "";
+}
- setdash();
+/* -------- var.c -------- */
- /* This won't be true if PUSHIO has been called, say from newfile() above */
- if (e.iop < iostack) {
- PUSHIO(afile, 0, iof);
- if (isatty(0) && isatty(1) && !cflag) {
- interactive++;
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-#ifdef MSHDEBUG
- printf("\n\n%s Built-in shell (msh with debug)\n", BB_BANNER);
-#else
- printf("\n\n%s Built-in shell (msh)\n", BB_BANNER);
-#endif
- printf("Enter 'help' for a list of built-in commands.\n\n");
-#endif
- }
+static int eqname(const char *n1, const char *n2)
+{
+ for (; *n1 != '=' && *n1 != '\0'; n1++)
+ if (*n2++ != *n1)
+ return 0;
+ return *n2 == '\0' || *n2 == '=';
+}
+
+static char *findeq(const char *cp)
+{
+ while (*cp != '\0' && *cp != '=')
+ cp++;
+ return cp;
+}
+
+/*
+ * Find the given name in the dictionary
+ * and return its value. If the name was
+ * not previously there, enter it now and
+ * return a null value.
+ */
+static struct var *lookup(const char *n)
+{
+ struct var *vp;
+ char *cp;
+ int c;
+ static struct var dummy;
+
+ if (isdigit(*n)) {
+ dummy.name = n;
+ for (c = 0; isdigit(*n) && c < 1000; n++)
+ c = c * 10 + *n - '0';
+ dummy.status = RONLY;
+ dummy.value = (c <= dolc ? dolv[c] : null);
+ return &dummy;
}
+ for (vp = vlist; vp; vp = vp->next)
+ if (eqname(vp->name, n))
+ return vp;
+ cp = findeq(n);
+ vp = (struct var *) space(sizeof(*vp));
+ if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
+ dummy.name = dummy.value = "";
+ return &dummy;
+ }
+ for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++);
+ if (*cp == '\0')
+ *cp = '=';
+ *++cp = '\0';
+ setarea((char *) vp, 0);
+ setarea((char *) vp->name, 0);
+ vp->value = null;
+ vp->next = vlist;
+ vp->status = GETCELL;
+ vlist = vp;
+ return vp;
+}
- signal(SIGQUIT, qflag);
- if (name && name[0] == '-') {
- interactive++;
- f = open(".profile", 0);
- if (f >= 0)
- next(remap(f));
- f = open("/etc/profile", 0);
- if (f >= 0)
- next(remap(f));
+/*
+ * if name is not NULL, it must be
+ * a prefix of the space `val',
+ * and end with `='.
+ * this is all so that exporting
+ * values is reasonably painless.
+ */
+static void nameval(struct var *vp, const char *val, const char *name)
+{
+ const char *cp;
+ char *xp;
+ int fl;
+
+ if (vp->status & RONLY) {
+ xp = vp->name;
+ while (*xp && *xp != '=')
+ putc(*xp++, stderr);
+ err(" is read-only");
+ return;
}
- if (interactive)
- signal(SIGTERM, sig);
+ fl = 0;
+ if (name == NULL) {
+ xp = space(strlen(vp->name) + strlen(val) + 2);
+ if (xp == NULL)
+ return;
+ /* make string: name=value */
+ setarea(xp, 0);
+ name = xp;
+ cp = vp->name;
+ while ((*xp = *cp++) != '\0' && *xp != '=')
+ xp++;
+ *xp++ = '=';
+ strcpy(xp, val);
+ val = xp;
+ fl = GETCELL;
+ }
+ if (vp->status & GETCELL)
+ freecell(vp->name); /* form new string `name=value' */
+ vp->name = name;
+ vp->value = val;
+ vp->status |= fl;
+}
- if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- signal(SIGINT, onintr);
- dolv = argv;
- dolc = argc;
- dolv[0] = name;
- if (dolc > 1) {
- for (ap = ++argv; --argc > 0;) {
- *ap = *argv++;
- if (assign(*ap, !COPYV)) {
- dolc--; /* keyword */
- } else {
- ap++;
- }
+/*
+ * give variable at `vp' the value `val'.
+ */
+static void setval(struct var *vp, const char *val)
+{
+ nameval(vp, val, NULL);
+}
+
+static void export(struct var *vp)
+{
+ vp->status |= EXPORT;
+}
+
+static void ronly(struct var *vp)
+{
+ if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
+ vp->status |= RONLY;
+}
+
+static int isassign(const char *s)
+{
+ unsigned char c;
+ DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
+
+ /* no isalpha() - we shouldn't use locale */
+ c = *s;
+ if (c != '_'
+ && (unsigned)((c|0x20) - 'a') > 25 /* not letter */
+ ) {
+ return 0;
+ }
+ while (1) {
+ c = *++s;
+ if (c == '\0')
+ return 0;
+ if (c == '=')
+ return 1;
+ c |= 0x20; /* lowercase letters, doesn't affect numbers */
+ if (c != '_'
+ && (unsigned)(c - '0') > 9 /* not number */
+ && (unsigned)(c - 'a') > 25 /* not letter */
+ ) {
+ return 0;
}
}
- setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
+}
- DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop %p, iostack %p\n", interactive, e.iop, iostack));
+static int assign(const char *s, int cf)
+{
+ char *cp;
+ struct var *vp;
- for (;;) {
- if (interactive && e.iop <= iostack) {
-#if ENABLE_FEATURE_EDITING
- current_prompt = prompt->value;
-#else
- prs(prompt->value);
-#endif
+ DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
+
+ if (!isalpha(*s) && *s != '_')
+ return 0;
+ for (cp = s; *cp != '='; cp++)
+ if (*cp == '\0' || (!isalnum(*cp) && *cp != '_'))
+ return 0;
+ vp = lookup(s);
+ nameval(vp, ++cp, cf == COPYV ? NULL : s);
+ if (cf != COPYV)
+ vp->status &= ~GETCELL;
+ return 1;
+}
+
+static int checkname(char *cp)
+{
+ DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
+
+ if (!isalpha(*cp++) && *(cp - 1) != '_')
+ return 0;
+ while (*cp)
+ if (!isalnum(*cp++) && *(cp - 1) != '_')
+ return 0;
+ return 1;
+}
+
+static void putvlist(int f, int out)
+{
+ struct var *vp;
+
+ for (vp = vlist; vp; vp = vp->next)
+ if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
+ if (vp->status & EXPORT)
+ write(out, "export ", 7);
+ if (vp->status & RONLY)
+ write(out, "readonly ", 9);
+ write(out, vp->name, (int) (findeq(vp->name) - vp->name));
+ write(out, "\n", 1);
}
- onecommand();
- /* Ensure that getenv("PATH") stays current */
- setenv("PATH", path->value, 1);
- }
+}
+
+
+/*
+ * trap handling
+ */
+static void sig(int i)
+{
+ trapset = i;
+ signal(i, sig);
+}
+
+static void runtrap(int i)
+{
+ char *trapstr;
- DBGPRINTF(("MSH_MAIN: returning.\n"));
+ trapstr = trap[i];
+ if (trapstr == NULL)
+ return;
+
+ if (i == 0)
+ trap[i] = NULL;
+
+ RUN(aword, trapstr, nlchar);
}
+
static void setdash(void)
{
char *cp;
setjmp(failpt); /* Bruce Evans' fix */
failpt = m1;
if (setjmp(failpt) || yyparse() || intr) {
-
DBGPRINTF(("ONECOMMAND: this is not good.\n"));
while (e.oenv)
}
}
-static void fail(void)
-{
- longjmp(failpt, 1);
- /* NOTREACHED */
-}
-
-static void leave(void)
-{
- DBGPRINTF(("LEAVE: leave called!\n"));
-
- if (execflg)
- fail();
- scraphere();
- freehere(1);
- runtrap(0);
- _exit(exstat);
- /* NOTREACHED */
-}
-
-static void warn(const char *s)
-{
- if (*s) {
- prs(s);
- exstat = -1;
- }
- prs("\n");
- if (flag['e'])
- leave();
-}
-
-static void err(const char *s)
-{
- warn(s);
- if (flag['n'])
- return;
- if (!interactive)
- leave();
- if (e.errpt)
- longjmp(e.errpt, 1);
- closeall();
- e.iop = e.iobase = iostack;
-}
-
static int newenv(int f)
{
struct env *ep;
e.oenv = ep;
e.errpt = errpt;
- return 0;
-}
-
-static void quitenv(void)
-{
- struct env *ep;
- int fd;
-
- DBGPRINTF(("QUITENV: e.oenv=%p\n", e.oenv));
-
- ep = e.oenv;
- if (ep != NULL) {
- fd = e.iofd;
- e = *ep;
- /* should close `'d files */
- DELETE(ep);
- while (--fd >= e.iofd)
- close(fd);
- }
-}
-
-/*
- * Is character c in s?
- */
-static int any(int c, const char *s)
-{
- while (*s)
- if (*s++ == c)
- return 1;
- return 0;
-}
-
-/*
- * Is any character from s1 in s2?
- */
-static int anys(const char *s1, const char *s2)
-{
- while (*s1)
- if (any(*s1++, s2))
- return 1;
- return 0;
-}
-
-static char *putn(int n)
-{
- return itoa(n);
-}
-
-static void next(int f)
-{
- PUSHIO(afile, f, filechar);
-}
-
-static void onintr(int s) /* ANSI C requires a parameter */
-{
- signal(SIGINT, onintr);
- intr = 1;
- if (interactive) {
- if (inparse) {
- prs("\n");
- fail();
- }
- } else if (heedint) {
- execflg = 0;
- leave();
- }
-}
-
-static char *space(int n)
-{
- char *cp;
-
- cp = getcell(n);
- if (cp == '\0')
- err("out of string space");
- return cp;
-}
-
-static char *strsave(char *s, int a)
-{
- char *cp;
-
- cp = space(strlen(s) + 1);
- if (cp != NULL) {
- setarea(cp, a);
- strcpy(cp, s);
- return cp;
- }
- return "";
-}
-
-/*
- * trap handling
- */
-static void sig(int i)
-{
- trapset = i;
- signal(i, sig);
-}
-
-static void runtrap(int i)
-{
- char *trapstr;
-
- trapstr = trap[i];
- if (trapstr == NULL)
- return;
-
- if (i == 0)
- trap[i] = NULL;
-
- RUN(aword, trapstr, nlchar);
-}
-
-/* -------- var.c -------- */
-
-/*
- * Find the given name in the dictionary
- * and return its value. If the name was
- * not previously there, enter it now and
- * return a null value.
- */
-static struct var *lookup(char *n)
-{
- struct var *vp;
- char *cp;
- int c;
- static struct var dummy;
-
- if (isdigit(*n)) {
- dummy.name = n;
- for (c = 0; isdigit(*n) && c < 1000; n++)
- c = c * 10 + *n - '0';
- dummy.status = RONLY;
- dummy.value = c <= dolc ? dolv[c] : null;
- return &dummy;
- }
- for (vp = vlist; vp; vp = vp->next)
- if (eqname(vp->name, n))
- return vp;
- cp = findeq(n);
- vp = (struct var *) space(sizeof(*vp));
- if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
- dummy.name = dummy.value = "";
- return &dummy;
- }
- for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++);
- if (*cp == 0)
- *cp = '=';
- *++cp = '\0';
- setarea((char *) vp, 0);
- setarea((char *) vp->name, 0);
- vp->value = null;
- vp->next = vlist;
- vp->status = GETCELL;
- vlist = vp;
- return vp;
-}
-
-/*
- * give variable at `vp' the value `val'.
- */
-static void setval(struct var *vp, char *val)
-{
- nameval(vp, val, (char *) NULL);
-}
-
-/*
- * if name is not NULL, it must be
- * a prefix of the space `val',
- * and end with `='.
- * this is all so that exporting
- * values is reasonably painless.
- */
-static void nameval(struct var *vp, char *val, char *name)
-{
- char *cp, *xp;
- char *nv;
- int fl;
-
- if (vp->status & RONLY) {
- for (xp = vp->name; *xp && *xp != '=';)
- putc(*xp++, stderr);
- err(" is read-only");
- return;
- }
- fl = 0;
- if (name == NULL) {
- xp = space(strlen(vp->name) + strlen(val) + 2);
- if (xp == NULL)
- return;
- /* make string: name=value */
- setarea((char *) xp, 0);
- name = xp;
- for (cp = vp->name; (*xp = *cp++) && *xp != '='; xp++);
- if (*xp++ == '\0')
- xp[-1] = '=';
- nv = xp;
- for (cp = val; (*xp++ = *cp++) != '\0';);
- val = nv;
- fl = GETCELL;
- }
- if (vp->status & GETCELL)
- freecell(vp->name); /* form new string `name=value' */
- vp->name = name;
- vp->value = val;
- vp->status |= fl;
-}
-
-static void export(struct var *vp)
-{
- vp->status |= EXPORT;
-}
-
-static void ronly(struct var *vp)
-{
- if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
- vp->status |= RONLY;
+ return 0;
}
-static int isassign(char *s)
+static void quitenv(void)
{
- DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
+ struct env *ep;
+ int fd;
- if (!isalpha((int) *s) && *s != '_')
- return 0;
- for (; *s != '='; s++)
- if (*s == '\0' || (!isalnum(*s) && *s != '_'))
- return 0;
+ DBGPRINTF(("QUITENV: e.oenv=%p\n", e.oenv));
- return 1;
+ ep = e.oenv;
+ if (ep != NULL) {
+ fd = e.iofd;
+ e = *ep;
+ /* should close `'d files */
+ DELETE(ep);
+ while (--fd >= e.iofd)
+ close(fd);
+ }
}
-static int assign(char *s, int cf)
+/*
+ * Is character c in s?
+ */
+static int any(int c, const char *s)
{
- char *cp;
- struct var *vp;
-
- DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
-
- if (!isalpha(*s) && *s != '_')
- return 0;
- for (cp = s; *cp != '='; cp++)
- if (*cp == '\0' || (!isalnum(*cp) && *cp != '_'))
- return 0;
- vp = lookup(s);
- nameval(vp, ++cp, cf == COPYV ? (char *) NULL : s);
- if (cf != COPYV)
- vp->status &= ~GETCELL;
- return 1;
+ while (*s)
+ if (*s++ == c)
+ return 1;
+ return 0;
}
-static int checkname(char *cp)
+/*
+ * Is any character from s1 in s2?
+ */
+static int anys(const char *s1, const char *s2)
{
- DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
-
- if (!isalpha(*cp++) && *(cp - 1) != '_')
- return 0;
- while (*cp)
- if (!isalnum(*cp++) && *(cp - 1) != '_')
- return 0;
- return 1;
+ while (*s1)
+ if (any(*s1++, s2))
+ return 1;
+ return 0;
}
-static void putvlist(int f, int out)
+static char *putn(int n)
{
- struct var *vp;
-
- for (vp = vlist; vp; vp = vp->next)
- if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
- if (vp->status & EXPORT)
- write(out, "export ", 7);
- if (vp->status & RONLY)
- write(out, "readonly ", 9);
- write(out, vp->name, (int) (findeq(vp->name) - vp->name));
- write(out, "\n", 1);
- }
+ return itoa(n);
}
-static int eqname(char *n1, char *n2)
+static void next(int f)
{
- for (; *n1 != '=' && *n1 != '\0'; n1++)
- if (*n2++ != *n1)
- return 0;
- return *n2 == '\0' || *n2 == '=';
+ PUSHIO(afile, f, filechar);
}
-static char *findeq(char *cp)
+static void onintr(int s) /* ANSI C requires a parameter */
{
- while (*cp != '\0' && *cp != '=')
- cp++;
- return cp;
+ signal(SIGINT, onintr);
+ intr = 1;
+ if (interactive) {
+ if (inparse) {
+ prs("\n");
+ fail();
+ }
+ } else if (heedint) {
+ execflg = 0;
+ leave();
+ }
}
/* -------- gmatch.c -------- */
} while (*s++ != '\0');
return 0;
- default:
- if (sc != (pc & ~QUOTE))
- return 0;
- }
- }
- return *s == '\0';
-}
-
-static char *cclass(char *p, int sub)
-{
- int c, d, not, found;
-
- not = (*p == NOT);
- if (not != 0)
- p++;
- found = not;
- do {
- if (*p == '\0')
- return NULL;
- c = *p & CMASK;
- if (p[1] == '-' && p[2] != ']') {
- d = p[2] & CMASK;
- p++;
- } else
- d = c;
- if (c == sub || (c <= sub && sub <= d))
- found = !not;
- } while (*++p != ']');
- return found ? p + 1 : NULL;
-}
-
-
-/* -------- area.c -------- */
-
-/*
- * All memory between (char *)areabot and (char *)(areatop+1) is
- * exclusively administered by the area management routines.
- * It is assumed that sbrk() and brk() manipulate the high end.
- */
-
-#define sbrk(X) ({ \
- void * __q = (void *)-1; \
- if (brkaddr + (int)(X) < brktop) { \
- __q = brkaddr; \
- brkaddr += (int)(X); \
- } \
- __q; \
-})
-
-static void initarea(void)
-{
- brkaddr = xmalloc(AREASIZE);
- brktop = brkaddr + AREASIZE;
-
- while ((long) sbrk(0) & ALIGN)
- sbrk(1);
- areabot = (struct region *) sbrk(REGSIZE);
-
- areabot->next = areabot;
- areabot->area = BUSY;
- areatop = areabot;
- areanxt = areabot;
-}
-
-char *getcell(unsigned nbytes)
-{
- int nregio;
- struct region *p, *q;
- int i;
-
- if (nbytes == 0) {
- puts("getcell(0)");
- abort();
- }
- /* silly and defeats the algorithm */
- /*
- * round upwards and add administration area
- */
- nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
- for (p = areanxt;;) {
- if (p->area > areanum) {
- /*
- * merge free cells
- */
- while ((q = p->next)->area > areanum && q != areanxt)
- p->next = q->next;
- /*
- * exit loop if cell big enough
- */
- if (q >= p + nregio)
- goto found;
- }
- p = p->next;
- if (p == areanxt)
- break;
- }
- i = nregio >= GROWBY ? nregio : GROWBY;
- p = (struct region *) sbrk(i * REGSIZE);
- if (p == (struct region *) -1)
- return NULL;
- p--;
- if (p != areatop) {
- puts("not contig");
- abort(); /* allocated areas are contiguous */
- }
- q = p + i;
- p->next = q;
- p->area = FREE;
- q->next = areabot;
- q->area = BUSY;
- areatop = q;
- found:
- /*
- * we found a FREE area big enough, pointed to by 'p', and up to 'q'
- */
- areanxt = p + nregio;
- if (areanxt < q) {
- /*
- * split into requested area and rest
- */
- if (areanxt + 1 > q) {
- puts("OOM");
- abort(); /* insufficient space left for admin */
- }
- areanxt->next = q;
- areanxt->area = FREE;
- p->next = areanxt;
- }
- p->area = areanum;
- return (char *) (p + 1);
-}
-
-static void freecell(char *cp)
-{
- struct region *p;
-
- p = (struct region *) cp;
- if (p != NULL) {
- p--;
- if (p < areanxt)
- areanxt = p;
- p->area = FREE;
- }
-}
-
-static void freearea(int a)
-{
- struct region *p, *top;
-
- top = areatop;
- for (p = areabot; p != top; p = p->next)
- if (p->area >= a)
- p->area = FREE;
-}
-
-static void setarea(char *cp, int a)
-{
- struct region *p;
-
- p = (struct region *) cp;
- if (p != NULL)
- (p - 1)->area = a;
-}
-
-int getarea(char *cp)
-{
- return ((struct region *) cp - 1)->area;
+ default:
+ if (sc != (pc & ~QUOTE))
+ return 0;
+ }
+ }
+ return *s == '\0';
}
-static void garbage(void)
+static char *cclass(char *p, int sub)
{
- struct region *p, *q, *top;
+ int c, d, not, found;
- top = areatop;
- for (p = areabot; p != top; p = p->next) {
- if (p->area > areanum) {
- while ((q = p->next)->area > areanum)
- p->next = q->next;
- areanxt = p;
- }
- }
-#ifdef SHRINKBY
- if (areatop >= q + SHRINKBY && q->area > areanum) {
- brk((char *) (q + 1));
- q->next = areabot;
- q->area = BUSY;
- areatop = q;
- }
-#endif
+ not = (*p == NOT);
+ if (not != 0)
+ p++;
+ found = not;
+ do {
+ if (*p == '\0')
+ return NULL;
+ c = *p & CMASK;
+ if (p[1] == '-' && p[2] != ']') {
+ d = p[2] & CMASK;
+ p++;
+ } else
+ d = c;
+ if (c == sub || (c <= sub && sub <= d))
+ found = !not;
+ } while (*++p != ']');
+ return found ? p + 1 : NULL;
}
+
/* -------- csyn.c -------- */
/*
* shell: syntax (C version)
break;
case TCOM:
- {
- rv = forkexec(t, pin, pout, act, wp);
- }
+ rv = forkexec(t, pin, pout, act, wp);
break;
case TPIPE:
return -1;
}
- if (newpid > 0) { /* Parent */
-
+ if (newpid > 0) { /* Parent */
/* Restore values */
pin = hpin;
pout = hpout;
intr = hintr;
brklist = hbrklist;
execflg = hexecflg;
-
/* moved up
if (i == -1)
return rv;
*/
-
if (pin != NULL)
closepipe(pin);
} else
setsig(n, SIG_IGN);
} else {
- if (interactive)
+ if (interactive) {
if (n == SIGINT)
setsig(n, onintr);
else
setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
- else
+ } else
setsig(n, SIG_DFL);
}
}
}
+/* -------- sh.c -------- */
+/*
+ * shell
+ */
+
+int msh_main(int argc, char **argv)
+{
+ int f;
+ char *s;
+ int cflag;
+ char *name, **ap;
+ int (*iof) (struct ioarg *);
+
+#if ENABLE_FEATURE_EDITING
+ line_input_state = new_line_input_t(FOR_SHELL);
+#endif
+
+ DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
+
+ initarea();
+ ap = environ;
+ if (ap != NULL) {
+ while (*ap)
+ assign(*ap++, !COPYV);
+ for (ap = environ; *ap;)
+ export(lookup(*ap++));
+ }
+ closeall();
+ areanum = 1;
+
+ shell = lookup("SHELL");
+ if (shell->value == null)
+ setval(shell, (char *)DEFAULT_SHELL);
+ export(shell);
+
+ homedir = lookup("HOME");
+ if (homedir->value == null)
+ setval(homedir, "/");
+ export(homedir);
+
+ setval(lookup("$"), putn(getpid()));
+
+ path = lookup("PATH");
+ if (path->value == null) {
+ if (geteuid() == 0)
+ setval(path, "/sbin:/bin:/usr/sbin:/usr/bin");
+ else
+ setval(path, "/bin:/usr/bin");
+ }
+ export(path);
+
+ ifs = lookup("IFS");
+ if (ifs->value == null)
+ setval(ifs, " \t\n");
+
+#ifdef MSHDEBUG
+ mshdbg_var = lookup("MSHDEBUG");
+ if (mshdbg_var->value == null)
+ setval(mshdbg_var, "0");
+#endif
+
+ prompt = lookup("PS1");
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ if (prompt->value == null)
+#endif
+ setval(prompt, DEFAULT_USER_PROMPT);
+ if (geteuid() == 0) {
+ setval(prompt, DEFAULT_ROOT_PROMPT);
+ prompt->status &= ~EXPORT;
+ }
+ cprompt = lookup("PS2");
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ if (cprompt->value == null)
+#endif
+ setval(cprompt, "> ");
+
+ iof = filechar;
+ cflag = 0;
+ name = *argv++;
+ if (--argc >= 1) {
+ if (argv[0][0] == '-' && argv[0][1] != '\0') {
+ for (s = argv[0] + 1; *s; s++)
+ switch (*s) {
+ case 'c':
+ prompt->status &= ~EXPORT;
+ cprompt->status &= ~EXPORT;
+ setval(prompt, "");
+ setval(cprompt, "");
+ cflag = 1;
+ if (--argc > 0)
+ PUSHIO(aword, *++argv, iof = nlchar);
+ break;
+
+ case 'q':
+ qflag = SIG_DFL;
+ break;
+
+ case 's':
+ /* standard input */
+ break;
+
+ case 't':
+ prompt->status &= ~EXPORT;
+ setval(prompt, "");
+ iof = linechar;
+ break;
+
+ case 'i':
+ interactive++;
+ default:
+ if (*s >= 'a' && *s <= 'z')
+ flag[(int) *s]++;
+ }
+ } else {
+ argv--;
+ argc++;
+ }
+
+ if (iof == filechar && --argc > 0) {
+ setval(prompt, "");
+ setval(cprompt, "");
+ prompt->status &= ~EXPORT;
+ cprompt->status &= ~EXPORT;
+
+/* Shell is non-interactive, activate printf-based debug */
+#ifdef MSHDEBUG
+ mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
+ if (mshdbg < 0)
+ mshdbg = 0;
+#endif
+ DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
+
+ name = *++argv;
+ if (newfile(name))
+ exit(1); /* Exit on error */
+ }
+ }
+
+ setdash();
+
+ /* This won't be true if PUSHIO has been called, say from newfile() above */
+ if (e.iop < iostack) {
+ PUSHIO(afile, 0, iof);
+ if (isatty(0) && isatty(1) && !cflag) {
+ interactive++;
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+#ifdef MSHDEBUG
+ printf("\n\n%s Built-in shell (msh with debug)\n", BB_BANNER);
+#else
+ printf("\n\n%s Built-in shell (msh)\n", BB_BANNER);
+#endif
+ printf("Enter 'help' for a list of built-in commands.\n\n");
+#endif
+ }
+ }
+
+ signal(SIGQUIT, qflag);
+ if (name && name[0] == '-') {
+ interactive++;
+ f = open(".profile", 0);
+ if (f >= 0)
+ next(remap(f));
+ f = open("/etc/profile", 0);
+ if (f >= 0)
+ next(remap(f));
+ }
+ if (interactive)
+ signal(SIGTERM, sig);
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+ dolv = argv;
+ dolc = argc;
+ dolv[0] = name;
+ if (dolc > 1) {
+ for (ap = ++argv; --argc > 0;) {
+ *ap = *argv++;
+ if (assign(*ap, !COPYV)) {
+ dolc--; /* keyword */
+ } else {
+ ap++;
+ }
+ }
+ }
+ setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
+
+ DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop %p, iostack %p\n", interactive, e.iop, iostack));
+
+ for (;;) {
+ if (interactive && e.iop <= iostack) {
+#if ENABLE_FEATURE_EDITING
+ current_prompt = prompt->value;
+#else
+ prs(prompt->value);
+#endif
+ }
+ onecommand();
+ /* Ensure that getenv("PATH") stays current */
+ setenv("PATH", path->value, 1);
+ }
+
+ DBGPRINTF(("MSH_MAIN: returning.\n"));
+}
+
+
/*
* Copyright (c) 1987,1997, Prentice Hall
* All rights reserved.