From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- release/src/router/busybox/shell/msh.c | 5920 +++++++++++++++++--------------- 1 file changed, 3151 insertions(+), 2769 deletions(-) (limited to 'release/src/router/busybox/shell/msh.c') diff --git a/release/src/router/busybox/shell/msh.c b/release/src/router/busybox/shell/msh.c index a142c451..dffacf02 100644 --- a/release/src/router/busybox/shell/msh.c +++ b/release/src/router/busybox/shell/msh.c @@ -10,43 +10,128 @@ * Robert Schwebel * Erik Andersen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Original copyright notice is retained at the end of this file. + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include +#include + +#ifdef STANDALONE +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# define bb_dev_null "/dev/null" +# define DEFAULT_SHELL "/proc/self/exe" +# define bb_banner "busybox standalone" +# define ENABLE_FEATURE_SH_STANDALONE 0 +# define bb_msg_memory_exhausted "memory exhausted" +# define xmalloc(size) malloc(size) +# define msh_main(argc,argv) main(argc,argv) +# define safe_read(fd,buf,count) read(fd,buf,count) +# define nonblock_safe_read(fd,buf,count) read(fd,buf,count) +# define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1]) +# define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1]) +# define NORETURN __attribute__ ((__noreturn__)) +static int find_applet_by_name(const char *applet) +{ + return -1; +} +static char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) +{ + unsigned i, out, res; + assert(sizeof(unsigned) == 4); + if (buflen) { + out = 0; + for (i = 1000000000; i; i /= 10) { + res = n / i; + if (res || out || i == 1) { + if (!--buflen) break; + out++; + n -= res*i; + *buf++ = '0' + res; + } + } + } + return buf; +} +static char *itoa_to_buf(int n, char *buf, unsigned buflen) +{ + if (buflen && n < 0) { + n = -n; + *buf++ = '-'; + buflen--; + } + return utoa_to_buf((unsigned)n, buf, buflen); +} +static char local_buf[12]; +static char *itoa(int n) +{ + *(itoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0'; + return local_buf; +} +#else +# include "busybox.h" /* for applet_names */ +#endif + +//#define MSHDEBUG 4 + +#ifdef MSHDEBUG +static int mshdbg = MSHDEBUG; + +#define DBGPRINTF(x) if (mshdbg > 0) printf x +#define DBGPRINTF0(x) if (mshdbg > 0) printf x +#define DBGPRINTF1(x) if (mshdbg > 1) printf x +#define DBGPRINTF2(x) if (mshdbg > 2) printf x +#define DBGPRINTF3(x) if (mshdbg > 3) printf x +#define DBGPRINTF4(x) if (mshdbg > 4) printf x +#define DBGPRINTF5(x) if (mshdbg > 5) printf x +#define DBGPRINTF6(x) if (mshdbg > 6) printf x +#define DBGPRINTF7(x) if (mshdbg > 7) printf x +#define DBGPRINTF8(x) if (mshdbg > 8) printf x +#define DBGPRINTF9(x) if (mshdbg > 9) printf x + +static int mshdbg_rc = 0; + +#define RCPRINTF(x) if (mshdbg_rc) printf x + +#else -#include "cmdedit.h" -#include "busybox.h" +#define DBGPRINTF(x) +#define DBGPRINTF0(x) ((void)0) +#define DBGPRINTF1(x) ((void)0) +#define DBGPRINTF2(x) ((void)0) +#define DBGPRINTF3(x) ((void)0) +#define DBGPRINTF4(x) ((void)0) +#define DBGPRINTF5(x) ((void)0) +#define DBGPRINTF6(x) ((void)0) +#define DBGPRINTF7(x) ((void)0) +#define DBGPRINTF8(x) ((void)0) +#define DBGPRINTF9(x) ((void)0) + +#define RCPRINTF(x) ((void)0) + +#endif /* MSHDEBUG */ + + +#if ENABLE_FEATURE_EDITING_FANCY_PROMPT +# define DEFAULT_ROOT_PROMPT "\\u:\\w> " +# define DEFAULT_USER_PROMPT "\\u:\\w$ " +#else +# define DEFAULT_ROOT_PROMPT "# " +# define DEFAULT_USER_PROMPT "$ " +#endif /* -------- sh.h -------- */ @@ -54,508 +139,406 @@ * shell */ -#define LINELIM 2100 -#define NPUSH 8 /* limit to input nesting */ +#define LINELIM 2100 +#define NPUSH 8 /* limit to input nesting */ #undef NOFILE -#define NOFILE 20 /* Number of open files */ -#define NUFILE 10 /* Number of user-accessible files */ -#define FDBASE 10 /* First file usable by Shell */ +#define NOFILE 20 /* Number of open files */ +#define NUFILE 10 /* Number of user-accessible files */ +#define FDBASE 10 /* First file usable by Shell */ /* * values returned by wait */ -#define WAITSIG(s) ((s)&0177) -#define WAITVAL(s) (((s)>>8)&0377) -#define WAITCORE(s) (((s)&0200)!=0) +#define WAITSIG(s) ((s) & 0177) +#define WAITVAL(s) (((s) >> 8) & 0377) +#define WAITCORE(s) (((s) & 0200) != 0) /* - * library and system defintions + * library and system definitions */ -typedef void xint; /* base type of jmp_buf, for not broken compilers */ +typedef void xint; /* base type of jmp_buf, for not broken compilers */ /* * shell components */ - -#define QUOTE 0200 - #define NOBLOCK ((struct op *)NULL) #define NOWORD ((char *)NULL) #define NOWORDS ((char **)NULL) #define NOPIPE ((int *)NULL) +/* + * redirection + */ +struct ioword { + smallint io_flag; /* action (below) */ + int io_fd; /* fd affected */ + char *io_name; /* file name */ +}; + +#define IOREAD 1 /* < */ +#define IOHERE 2 /* << (here file) */ +#define IOWRITE 4 /* > */ +#define IOCAT 8 /* >> */ +#define IOXHERE 16 /* ${}, ` in << */ +#define IODUP 32 /* >&digit */ +#define IOCLOSE 64 /* >&- */ + +#define IODEFAULT (-1) /* "default" IO fd */ + + /* * Description of a command or an operation on commands. * Might eventually use a union. */ struct op { - int type; /* operation type, see below */ - char **words; /* arguments to a command */ - struct ioword **ioact; /* IO actions (eg, < > >>) */ + smallint op_type; /* operation type, see Txxxx below */ + char **op_words; /* arguments to a command */ + struct ioword **ioact; /* IO actions (eg, < > >>) */ struct op *left; struct op *right; - char *str; /* identifier for case and for */ + char *str; /* identifier for case and for */ }; -#define TCOM 1 /* command */ -#define TPAREN 2 /* (c-list) */ -#define TPIPE 3 /* a | b */ -#define TLIST 4 /* a [&;] b */ -#define TOR 5 /* || */ -#define TAND 6 /* && */ -#define TFOR 7 -#define TDO 8 -#define TCASE 9 -#define TIF 10 -#define TWHILE 11 -#define TUNTIL 12 -#define TELIF 13 -#define TPAT 14 /* pattern in case */ -#define TBRACE 15 /* {c-list} */ -#define TASYNC 16 /* c & */ +#define TCOM 1 /* command */ +#define TPAREN 2 /* (c-list) */ +#define TPIPE 3 /* a | b */ +#define TLIST 4 /* a [&;] b */ +#define TOR 5 /* || */ +#define TAND 6 /* && */ +#define TFOR 7 +#define TDO 8 +#define TCASE 9 +#define TIF 10 +#define TWHILE 11 +#define TUNTIL 12 +#define TELIF 13 +#define TPAT 14 /* pattern in case */ +#define TBRACE 15 /* {c-list} */ +#define TASYNC 16 /* c & */ +/* Added to support "." file expansion */ +#define TDOT 17 + +/* Strings for names to make debug easier */ +#ifdef MSHDEBUG +static const char *const T_CMD_NAMES[] = { + "PLACEHOLDER", + "TCOM", + "TPAREN", + "TPIPE", + "TLIST", + "TOR", + "TAND", + "TFOR", + "TDO", + "TCASE", + "TIF", + "TWHILE", + "TUNTIL", + "TELIF", + "TPAT", + "TBRACE", + "TASYNC", + "TDOT", +}; +#endif -/* - * actions determining the environment of a process - */ -#define BIT(i) (1<<(i)) -#define FEXEC BIT(0) /* execute without forking */ +#define AREASIZE (90000) /* * flags to control evaluation of words */ -#define DOSUB 1 /* interpret $, `, and quotes */ -#define DOBLANK 2 /* perform blank interpretation */ -#define DOGLOB 4 /* interpret [?* */ -#define DOKEY 8 /* move words with `=' to 2nd arg. list */ -#define DOTRIM 16 /* trim resulting string */ - -#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) - -static char **dolv; -static int dolc; -static int exstat; -static char gflg; -static int interactive; /* Is this an interactive shell */ -static int execflg; -static int multiline; /* \n changed to ; */ -static struct op *outtree; /* result from parser */ - -static xint *failpt; -static xint *errpt; -static struct brkcon *brklist; -static int isbreak; -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 { - jmp_buf brkpt; - struct brkcon *nextlev; -} ; - -/* - * redirection - */ -struct ioword { - short io_unit; /* unit affected */ - short io_flag; /* action (below) */ - char *io_name; /* file name */ -}; -#define IOREAD 1 /* < */ -#define IOHERE 2 /* << (here file) */ -#define IOWRITE 4 /* > */ -#define IOCAT 8 /* >> */ -#define IOXHERE 16 /* ${}, ` in << */ -#define IODUP 32 /* >&digit */ -#define IOCLOSE 64 /* >&- */ +#define DOSUB 1 /* interpret $, `, and quotes */ +#define DOBLANK 2 /* perform blank interpretation */ +#define DOGLOB 4 /* interpret [?* */ +#define DOKEY 8 /* move words with `=' to 2nd arg. list */ +#define DOTRIM 16 /* trim resulting string */ -#define IODEFAULT (-1) /* token for default IO unit */ +#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) -static struct wdblock *wdlist; -static struct wdblock *iolist; -/* - * parsing & execution environment - */ -static struct env { - char *linep; - struct io *iobase; - struct io *iop; - xint *errpt; - int iofd; - struct env *oenv; -} e; +struct brkcon { + jmp_buf brkpt; + struct brkcon *nextlev; +}; -/* - * flags: - * -e: quit on error - * -k: look for name=value everywhere on command line - * -n: no execution - * -t: exit after reading and executing one command - * -v: echo as read - * -x: trace - * -u: unset variables net diagnostic - */ -static char *flag; -static char *null; /* null value for variable */ -static int intr; /* interrupt pending */ +static smallint trapset; /* trap pending (signal number) */ -static char *trap[_NSIG+1]; -static char ourtrap[_NSIG+1]; -static int trapset; /* trap pending */ +static smallint yynerrs; /* yacc (flag) */ -static int heedint; /* heed interrupt signals */ +/* moved to G: static char line[LINELIM]; */ -static int yynerrs; /* yacc */ +#if ENABLE_FEATURE_EDITING +static char *current_prompt; +static line_input_t *line_input_state; +#endif -static char line[LINELIM]; -static char *elinep; /* * other functions */ -static int(*inbuilt(char *s))(struct op *); - - -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 *itoa (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 subgetc (int ec, int quoted ); -static char **makenv (void); -static char **eval (char **ap, int f ); -static int setstatus (int s ); -static int waitfor (int lastpid, int canintr ); - -static void onintr (int s ); /* SIGINT handler */ - -static int newenv (int f ); -static void quitenv (void); -static void err (char *s ); -static int anys (char *s1, char *s2 ); -static int any (int c, char *s ); -static void next (int f ); -static void setdash (void); -static void onecommand (void); -static void runtrap (int i ); -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 (char *s ); -static void sig (int i ); /* default signal handler */ - +static const char *rexecve(char *c, char **v, char **envp); +static char *evalstr(char *cp, int f); +static char *putn(int n); +static char *unquote(char *as); +static int rlookup(char *n); +static struct wdblock *glob(char *cp, struct wdblock *wb); +static int my_getc(int ec); +static int subgetc(char ec, int quoted); +static char **makenv(int all, struct wdblock *wb); +static char **eval(char **ap, int f); +static int setstatus(int s); +static int waitfor(int lastpid, int canintr); + +static void onintr(int s); /* SIGINT handler */ + +static int newenv(int f); +static void quitenv(void); +static void next(int f); +static void setdash(void); +static void onecommand(void); +static void runtrap(int i); /* -------- area stuff -------- */ -#define REGSIZE sizeof(struct region) -#define GROWBY 256 -//#define SHRINKBY 64 -#undef SHRINKBY -#define FREE 32767 -#define BUSY 0 -#define ALIGN (sizeof(int)-1) +#define REGSIZE sizeof(struct region) +#define GROWBY (256) +/* #define SHRINKBY (64) */ +#undef SHRINKBY +#define FREE (32767) +#define BUSY (0) +#define ALIGN (sizeof(int)-1) struct region { - struct region *next; - int area; + struct region *next; + int area; }; - /* -------- grammar stuff -------- */ typedef union { - char *cp; - char **wp; - int i; - struct op *o; + char *cp; + char **wp; + int i; + 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 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() -static struct op *pipeline(int cf ); +static struct op *pipeline(int cf); static struct op *andor(void); static struct op *c_list(void); -static int synio(int cf ); -static void musthave (int c, int cf ); +static int synio(int cf); +static void musthave(int c, int cf); static struct op *simple(void); -static struct op *nested(int type, int mark ); -static struct op *command(int cf ); -static struct op *dogroup(int onlydone ); +static struct op *nested(int type, int mark); +static struct op *command(int cf); +static struct op *dogroup(int onlydone); static struct op *thenpart(void); static struct op *elsepart(void); static struct op *caselist(void); static struct op *casepart(void); static char **pattern(void); static char **wordlist(void); -static struct op *list(struct op *t1, struct op *t2 ); -static struct op *block(int type, struct op *t1, struct op *t2, char **wp ); +static struct op *list(struct op *t1, struct op *t2); +static struct op *block(int type, struct op *t1, struct op *t2, char **wp); static struct op *newtp(void); -static struct op *namelist(struct op *t ); +static struct op *namelist(struct op *t); static char **copyw(void); -static void word(char *cp ); +static void word(char *cp); static struct ioword **copyio(void); -static struct ioword *io (int u, int f, char *cp ); -static void zzerr(void); -static void yyerror(char *s ); -static int yylex(int cf ); -static int collect(int c, int c1 ); -static int dual(int c ); -static void diag(int ec ); -static char *tree(unsigned size ); +static struct ioword *io(int u, int f, char *cp); +static int yylex(int cf); +static int collect(int c, int c1); +static int dual(int c); +static void diag(int ec); +static char *tree(unsigned size); /* -------- var.h -------- */ -struct var { - char *value; - char *name; - struct var *next; - char status; +struct var { + char *value; + char *name; + struct var *next; + char status; }; -#define COPYV 1 /* flag to setval, suggesting copy */ -#define RONLY 01 /* variable is read-only */ -#define EXPORT 02 /* variable is to be exported */ -#define GETCELL 04 /* name & value space was got with getcell */ - -static struct var *vlist; /* dictionary */ - -static struct var *homedir; /* home directory */ -static struct var *prompt; /* main prompt */ -static struct var *cprompt; /* continuation prompt */ -static struct var *path; /* search path for commands */ -static struct var *shell; /* shell to interpret command files */ -static struct var *ifs; /* field separators */ - -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 ); + +#define COPYV 1 /* flag to setval, suggesting copy */ +#define RONLY 01 /* variable is read-only */ +#define EXPORT 02 /* variable is to be exported */ +#define GETCELL 04 /* name & value space was got with getcell */ + +static int yyparse(void); + /* -------- io.h -------- */ /* io buffer */ struct iobuf { - unsigned id; /* buffer id */ - char buf[512]; /* buffer */ - char *bufp; /* pointer into buffer */ - char *ebufp; /* pointer to end of buffer */ + unsigned id; /* buffer id */ + char buf[512]; /* buffer */ + char *bufp; /* pointer into buffer */ + char *ebufp; /* pointer to end of buffer */ }; /* possible arguments to an IO function */ struct ioarg { - char *aword; - char **awordlist; - int afile; /* file descriptor */ - unsigned afid; /* buffer id */ - long afpos; /* file position */ - struct iobuf *afbuf; /* buffer for this file */ + const char *aword; + char **awordlist; + int afile; /* file descriptor */ + unsigned afid; /* buffer id */ + off_t afpos; /* file position */ + struct iobuf *afbuf; /* buffer for this file */ }; -//static struct ioarg ioargstack[NPUSH]; -#define AFID_NOBUF (~0) -#define AFID_ID 0 /* an input generator's state */ -struct io { - int (*iofn)(struct ioarg *, struct io *); - struct ioarg *argp; - int peekc; - char prev; /* previous character read by readc() */ - char nlcount; /* for `'s */ - char xchar; /* for `'s */ - char task; /* reason for pushed IO */ +struct io { + int (*iofn) (struct ioarg *, struct io *); + struct ioarg *argp; + int peekc; + char prev; /* previous character read by readc() */ + char nlcount; /* for `'s */ + char xchar; /* for `'s */ + char task; /* reason for pushed IO */ }; -//static struct io iostack[NPUSH]; +/* ->task: */ #define XOTHER 0 /* none of the below */ #define XDOLL 1 /* expanding ${} */ #define XGRAVE 2 /* expanding `'s */ #define XIO 3 /* file IO */ -/* in substitution */ -#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL) /* * input generators for IO structure */ -static int nlchar (struct ioarg *ap ); -static int strchar (struct ioarg *ap ); -static int qstrchar (struct ioarg *ap ); -static int filechar (struct ioarg *ap ); -static int herechar (struct ioarg *ap ); -static int linechar (struct ioarg *ap ); -static int gravechar (struct ioarg *ap, struct io *iop ); -static int qgravechar (struct ioarg *ap, struct io *iop ); -static int dolchar (struct ioarg *ap ); -static int wdchar (struct ioarg *ap ); -static void scraphere (void); -static void freehere (int area ); -static void gethere (void); -static void markhere (char *s, struct ioword *iop ); -static int herein (char *hname, int xdoll ); -static int run (struct ioarg *argp, int (*f)(struct ioarg *)); +static int nlchar(struct ioarg *ap); +static int strchar(struct ioarg *ap); +static int qstrchar(struct ioarg *ap); +static int filechar(struct ioarg *ap); +static int herechar(struct ioarg *ap); +static int linechar(struct ioarg *ap); +static int gravechar(struct ioarg *ap, struct io *iop); +static int qgravechar(struct ioarg *ap, struct io *iop); +static int dolchar(struct ioarg *ap); +static int wdchar(struct ioarg *ap); +static void scraphere(void); +static void freehere(int area); +static void gethere(void); +static void markhere(char *s, struct ioword *iop); +static int herein(char *hname, int xdoll); +static int run(struct ioarg *argp, int (*f) (struct ioarg *)); + + +static int eofc(void); +static int readc(void); +static void unget(int c); +static void ioecho(char c); -/* - * IO functions - */ -static int eofc (void); -static int readc (void); -static void unget (int c ); -static void ioecho (int c ); -static void prs (char *s ); -static void prn (unsigned u ); -static void closef (int i ); -static void closeall (void); /* * IO control */ -static void pushio (struct ioarg *argp, int (*f)(struct ioarg *)); -static int remap (int fd ); -static int openpipe (int *pv ); -static void closepipe (int *pv ); -static struct io *setbase (struct io *ip ); - -static struct ioarg temparg; /* temporary for PUSHIO */ -#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(&temparg,(gen))) -#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen))) +static void pushio(struct ioarg *argp, int (*f) (struct ioarg *)); +#define PUSHIO(what,arg,gen) ((temparg.what = (arg)), pushio(&temparg,(gen))) +static int remap(int fd); +static int openpipe(int *pv); +static void closepipe(int *pv); +static struct io *setbase(struct io *ip); /* -------- word.h -------- */ -#define NSTART 16 /* default number of words to allow for initially */ +#define NSTART 16 /* default number of words to allow for initially */ -struct wdblock { - short w_bsize; - short w_nword; +struct wdblock { + short w_bsize; + short w_nword; /* bounds are arbitrary */ - char *w_words[1]; + char *w_words[1]; }; -static struct wdblock *addword (char *wd, struct wdblock *wb ); -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) - +static struct wdblock *addword(char *wd, struct wdblock *wb); +static struct wdblock *newword(int nw); +static char **getwords(struct wdblock *wb); /* -------- misc stuff -------- */ -static int forkexec (struct op *t, int *pin, int *pout, int act, char **wp, int *pforked ); -static int iosetup (struct ioword *iop, int pipein, int pipeout ); -static void echo(char **wp ); -static struct op **find1case (struct op *t, char *w ); -static struct op *findcase (struct op *t, char *w ); -static void brkset(struct brkcon *bc ); -static int dolabel(struct op *t ); -static int dohelp(struct op *t ); -static int dochdir(struct op *t ); -static int doshift(struct op *t ); -static int dologin(struct op *t ); -static int doumask(struct op *t ); -static int doexec(struct op *t ); -static int dodot(struct op *t ); -static int dowait(struct op *t ); -static int doread(struct op *t ); -static int doeval(struct op *t ); -static int dotrap(struct op *t ); -static int getsig(char *s ); -static void setsig (int n, sighandler_t f); -static int getn(char *as ); -static int dobreak(struct op *t ); -static int docontinue(struct op *t ); -static int brkcontin (char *cp, int val ); -static int doexit(struct op *t ); -static int doexport(struct op *t ); -static int doreadonly(struct op *t ); -static void rdexp (char **wp, void (*f)(struct var *), int key); -static void badid(char *s ); -static int doset(struct op *t ); -static void varput (char *s, int out ); -static int dotimes(struct op *t ); -static int expand (char *cp, struct wdblock **wbp, int f ); -static char *blank(int f ); -static int dollar(int quoted ); -static int grave(int quoted ); -static void globname (char *we, char *pp ); -static char *generate (char *start1, char *end1, char *middle, char *end ); -static int anyspcl(struct wdblock *wb ); -static int xstrcmp (char *p1, char *p2 ); -static void glob0 (char *a0, unsigned int a1, int a2, int (*a3)(char *, char *)); -static void glob1 (char *base, char *lim ); -static void glob2 (char *i, char *j ); -static void glob3 (char *i, char *j, char *k ); -static void readhere (char **name, char *s, int ec ); -static void pushio (struct ioarg *argp, int (*f)(struct ioarg *)); -static int xxchar(struct ioarg *ap ); - -struct here { - char *h_tag; - int h_dosub; - struct ioword *h_iop; - struct here *h_next; +static int dolabel(struct op *t, char **args); +static int dohelp(struct op *t, char **args); +static int dochdir(struct op *t, char **args); +static int doshift(struct op *t, char **args); +static int dologin(struct op *t, char **args); +static int doumask(struct op *t, char **args); +static int doexec(struct op *t, char **args); +static int dodot(struct op *t, char **args); +static int dowait(struct op *t, char **args); +static int doread(struct op *t, char **args); +static int doeval(struct op *t, char **args); +static int dotrap(struct op *t, char **args); +static int dobreak(struct op *t, char **args); +static int doexit(struct op *t, char **args); +static int doexport(struct op *t, char **args); +static int doreadonly(struct op *t, char **args); +static int doset(struct op *t, char **args); +static int dotimes(struct op *t, char **args); +static int docontinue(struct op *t, char **args); + +static int forkexec(struct op *t, int *pin, int *pout, int no_fork, char **wp); +static int execute(struct op *t, int *pin, int *pout, int no_fork); +static int iosetup(struct ioword *iop, int pipein, int pipeout); +static void brkset(struct brkcon *bc); +static int getsig(char *s); +static void setsig(int n, sighandler_t f); +static int getn(char *as); +static int brkcontin(char *cp, int val); +static void rdexp(char **wp, void (*f) (struct var *), int key); +static void badid(char *s); +static void varput(char *s, int out); +static int expand(const char *cp, struct wdblock **wbp, int f); +static char *blank(int f); +static int dollar(int quoted); +static int grave(int quoted); +static void globname(char *we, char *pp); +static char *generate(char *start1, char *end1, char *middle, char *end); +static int anyspcl(struct wdblock *wb); +static void readhere(char **name, char *s, int ec); +static int xxchar(struct ioarg *ap); + +struct here { + char *h_tag; + char h_dosub; + struct ioword *h_iop; + struct here *h_next; }; -static char *signame[] = { +static const char *const signame[] = { "Signal 0", "Hangup", - (char *)NULL, /* interrupt */ + NULL, /* interrupt */ "Quit", "Illegal instruction", "Trace/BPT trap", @@ -566,637 +549,534 @@ static char *signame[] = { "SIGUSR1", "SIGSEGV", "SIGUSR2", - (char *)NULL, /* broken pipe */ + NULL, /* broken pipe */ "Alarm clock", - "Terminated", + "Terminated" }; -#define NSIGNAL (sizeof(signame)/sizeof(signame[0])) -struct res { - char *r_name; - int r_val; -}; -static struct res restab[] = { - {"for", FOR}, - {"case", CASE}, - {"esac", ESAC}, - {"while", WHILE}, - {"do", DO}, - {"done", DONE}, - {"if", IF}, - {"in", IN}, - {"then", THEN}, - {"else", ELSE}, - {"elif", ELIF}, - {"until", UNTIL}, - {"fi", FI}, - - {";;", BREAK}, - {"||", LOGOR}, - {"&&", LOGAND}, - {"{", '{'}, - {"}", '}'}, - {0, 0}, -}; +typedef int (*builtin_func_ptr)(struct op *, char **); struct builtincmd { const char *name; - int (*builtinfunc)(struct op *t); + builtin_func_ptr builtinfunc; }; -static const struct builtincmd builtincmds[] = { - {".", dodot}, - {":", dolabel}, - {"break", dobreak}, - {"cd", dochdir}, - {"continue",docontinue}, - {"eval", doeval}, - {"exec", doexec}, - {"exit", doexit}, - {"export", doexport}, - {"help", dohelp}, - {"login", dologin}, - {"newgrp", dologin}, - {"read", doread}, - {"readonly",doreadonly}, - {"set", doset}, - {"shift", doshift}, - {"times", dotimes}, - {"trap", dotrap}, - {"umask", doumask}, - {"wait", dowait}, - {0,0} + +static const struct builtincmd builtincmds[] = { + { "." , dodot }, + { ":" , dolabel }, + { "break" , dobreak }, + { "cd" , dochdir }, + { "continue", docontinue }, + { "eval" , doeval }, + { "exec" , doexec }, + { "exit" , doexit }, + { "export" , doexport }, + { "help" , dohelp }, + { "login" , dologin }, + { "newgrp" , dologin }, + { "read" , doread }, + { "readonly", doreadonly }, + { "set" , doset }, + { "shift" , doshift }, + { "times" , dotimes }, + { "trap" , dotrap }, + { "umask" , doumask }, + { "wait" , dowait }, + { NULL , NULL }, }; +static struct op *dowholefile(int /*, int*/); + + /* Globals */ -extern char **environ; /* environment pointer */ -static char **dolv; -static int dolc; -static int exstat; -static char gflg; -static int interactive; /* Is this an interactive shell */ -static int execflg; -static int multiline; /* \n changed to ; */ -static struct op *outtree; /* result from parser */ -static xint *failpt; -static xint *errpt; -static struct brkcon *brklist; -static int isbreak; -static struct wdblock *wdlist; -static struct wdblock *iolist; -static char *trap[_NSIG+1]; -static char ourtrap[_NSIG+1]; -static int trapset; /* trap pending */ -static int yynerrs; /* yacc */ -static char line[LINELIM]; -static struct var *vlist; /* dictionary */ -static struct var *homedir; /* home directory */ -static struct var *prompt; /* main prompt */ -static struct var *cprompt; /* continuation prompt */ -static struct var *path; /* search path for commands */ -static struct var *shell; /* shell to interpret command files */ -static struct var *ifs; /* field separators */ -static struct ioarg ioargstack[NPUSH]; -static struct io iostack[NPUSH]; -static int areanum; /* current allocation area */ -static int intr; -static int inparse; -static char flags['z'-'a'+1]; -static char *flag = flags-'a'; -static char *elinep = line+sizeof(line)-5; -static char *null = ""; -static int heedint =1; -static struct env e ={line, iostack, iostack-1, (xint *)NULL, FDBASE, (struct env *)NULL}; -static void (*qflag)(int) = SIG_IGN; -static char shellname[] = "/bin/sh"; -static int startl; -static int peeksym; -static int nlseen; -static int iounit = IODEFAULT; -static YYSTYPE yylval; -static struct iobuf sharedbuf = {AFID_NOBUF}; -static struct iobuf mainbuf = {AFID_NOBUF}; -static unsigned bufid = AFID_ID; /* buffer id counter */ -static struct ioarg temparg = {0, 0, 0, AFID_NOBUF, 0}; -static struct here *inhere; /* list of hear docs while parsing */ -static struct here *acthere; /* list of active here documents */ -static struct region *areabot; /* bottom of area */ -static struct region *areatop; /* top of area */ -static struct region *areanxt; /* starting point of scan */ -static void * brktop; -static void * brkaddr; - - -#ifdef CONFIG_FEATURE_COMMAND_EDITING -static char * current_prompt; +static char **dolv; +static int dolc; +static uint8_t exstat; +static smallint gflg; /* (seems to be a parse error indicator) */ +static smallint interactive; /* Is this an interactive shell */ +static smallint execflg; +static smallint isbreak; /* "break" statement was seen */ +static int multiline; /* '\n' changed to ';' (counter) */ +static struct op *outtree; /* result from parser */ +static xint *failpt; +static xint *errpt; +static struct brkcon *brklist; +static struct wdblock *wdlist; +static struct wdblock *iolist; + +#ifdef MSHDEBUG +static struct var *mshdbg_var; #endif +static struct var *vlist; /* dictionary */ +static struct var *homedir; /* home directory */ +static struct var *prompt; /* main prompt */ +static struct var *cprompt; /* continuation prompt */ +static struct var *path; /* search path for commands */ +static struct var *shell; /* shell to interpret command files */ +static struct var *ifs; /* field separators */ + +static int areanum; /* current allocation area */ +static smallint intr; /* interrupt pending (bool) */ +static smallint heedint = 1; /* heed interrupt signals (bool) */ +static int inparse; +static char *null = (char*)""; /* null value for variable */ +static void (*qflag)(int) = SIG_IGN; +static int startl; +static int peeksym; +static int nlseen; +static int iounit = IODEFAULT; +static YYSTYPE yylval; +static char *elinep; /* done in main(): = line + sizeof(line) - 5 */ + +static struct here *inhere; /* list of hear docs while parsing */ +static struct here *acthere; /* list of active here documents */ +static struct region *areabot; /* bottom of area */ +static struct region *areatop; /* top of area */ +static struct region *areanxt; /* starting point of scan */ +static void *brktop; +static void *brkaddr; + +#define AFID_NOBUF (~0) +#define AFID_ID 0 + -/* -------- sh.c -------- */ /* - * shell + * parsing & execution environment */ +struct env { + char *linep; + struct io *iobase; + struct io *iop; + xint *errpt; /* void * */ + int iofd; + struct env *oenv; +}; -extern int msh_main(int argc, char **argv) -{ - register int f; - register char *s; - int cflag; - char *name, **ap; - int (*iof)(struct ioarg *); +struct globals { + struct env global_env; + struct ioarg temparg; // = { .afid = AFID_NOBUF }; /* temporary for PUSHIO */ + unsigned bufid; // = AFID_ID; /* buffer id counter */ + char ourtrap[_NSIG + 1]; + char *trap[_NSIG + 1]; + struct iobuf sharedbuf; /* in main(): set to { AFID_NOBUF } */ + struct iobuf mainbuf; /* in main(): set to { AFID_NOBUF } */ + struct ioarg ioargstack[NPUSH]; + /* + * flags: + * -e: quit on error + * -k: look for name=value everywhere on command line + * -n: no execution + * -t: exit after reading and executing one command + * -v: echo as read + * -x: trace + * -u: unset variables net diagnostic + */ + char flags['z' - 'a' + 1]; + char filechar_cmdbuf[BUFSIZ]; + char line[LINELIM]; + char child_cmd[LINELIM]; - initarea(); - if ((ap = environ) != NULL) { - while (*ap) - assign(*ap++, !COPYV); - for (ap = environ; *ap;) - export(lookup(*ap++)); - } - closeall(); - areanum = 1; + struct io iostack[NPUSH]; - shell = lookup("SHELL"); - if (shell->value == null) - setval(shell, shellname); - export(shell); + char grave__var_name[LINELIM]; + char grave__alt_value[LINELIM]; +}; - homedir = lookup("HOME"); - if (homedir->value == null) - setval(homedir, "/"); - export(homedir); +#define G (*ptr_to_globals) +#define global_env (G.global_env ) +#define temparg (G.temparg ) +#define bufid (G.bufid ) +#define ourtrap (G.ourtrap ) +#define trap (G.trap ) +#define sharedbuf (G.sharedbuf ) +#define mainbuf (G.mainbuf ) +#define ioargstack (G.ioargstack ) +/* this looks weird, but is OK ... we index FLAG with 'a'...'z' */ +#define FLAG (G.flags - 'a' ) +#define filechar_cmdbuf (G.filechar_cmdbuf) +#define line (G.line ) +#define child_cmd (G.child_cmd ) +#define iostack (G.iostack ) +#define INIT_G() do { \ + SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ + global_env.linep = line; \ + global_env.iobase = iostack; \ + global_env.iop = iostack - 1; \ + global_env.iofd = FDBASE; \ + temparg.afid = AFID_NOBUF; \ + bufid = AFID_ID; \ +} while (0) - 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); +/* in substitution */ +#define INSUB() (global_env.iop->task == XGRAVE || global_env.iop->task == XDOLL) - ifs = lookup("IFS"); - if (ifs->value == null) - setval(ifs, " \t\n"); +#define RUN(what, arg, gen) ((temparg.what = (arg)), run(&temparg, (gen))) - prompt = lookup("PS1"); -#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT - if (prompt->value == null) -#endif - setval(prompt, "$ "); - if (geteuid() == 0) { - setval(prompt, "# "); - prompt->status &= ~EXPORT; +#ifdef MSHDEBUG +static void print_tree(struct op *head) +{ + if (head == NULL) { + DBGPRINTF(("PRINT_TREE: no tree\n")); + return; } - cprompt = lookup("PS2"); -#ifdef CONFIG_FEATURE_SH_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; + DBGPRINTF(("NODE: %p, left %p, right %p\n", head, head->left, + head->right)); - case 's': - /* standard input */ - break; + if (head->left) + print_tree(head->left); - 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; - if (newfile(name = *++argv)) - exit(1); - } - } - setdash(); - if (e.iop < iostack) { - PUSHIO(afile, 0, iof); - if (isatty(0) && isatty(1) && !cflag) { - interactive++; -#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET - printf( "\n\n" BB_BANNER " Built-in shell (msh)\n"); - printf( "Enter 'help' for a list of built-in commands.\n\n"); -#endif - } - } - signal(SIGQUIT, qflag); - if (name && name[0] == '-') { - interactive++; - if ((f = open(".profile", 0)) >= 0) - next(remap(f)); - if ((f = open("/etc/profile", 0)) >= 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;) { - if (assign(*ap = *argv++, !COPYV)) { - dolc--; /* keyword */ - } else { - ap++; - } - } - } - setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc)); + if (head->right) + print_tree(head->right); +} +#endif /* MSHDEBUG */ - for (;;) { - if (interactive && e.iop <= iostack) { -#ifdef CONFIG_FEATURE_COMMAND_EDITING - current_prompt=prompt->value; -#else - prs(prompt->value); -#endif - } - onecommand(); - /* Ensure that getenv("PATH") stays current */ - setenv("PATH", path->value, 1); - } + +/* + * IO functions + */ +static void prs(const char *s) +{ + if (*s) + xwrite_str(STDERR_FILENO, s); } -static void -setdash() +static void prn(unsigned u) { - register char *cp; - register int c; - char m['z'-'a'+1]; + prs(itoa(u)); +} - cp = m; - for (c='a'; c<='z'; c++) - if (flag[(int)c]) - *cp++ = c; - *cp = 0; - setval(lookup("-"), m); +static void echo(char **wp) +{ + int i; + + prs("+"); + for (i = 0; wp[i]; i++) { + if (i) + prs(" "); + prs(wp[i]); + } + prs("\n"); } -static int -newfile(s) -register char *s; +static void closef(int i) { - register int f; + if (i > 2) + close(i); +} - if (strcmp(s, "-") != 0) { - f = open(s, 0); - if (f < 0) { - prs(s); - err(": cannot open"); - return(1); - } - } else - f = 0; - next(remap(f)); - return(0); +static void closeall(void) +{ + int u; + + for (u = NUFILE; u < NOFILE;) + close(u++); } -static void -onecommand() + +/* fail but return to process next command */ +static void fail(void) NORETURN; +static void fail(void) { - register int i; - jmp_buf m1; + longjmp(failpt, 1); + /* NOTREACHED */ +} - while (e.oenv) - quitenv(); - areanum = 1; - freehere(areanum); - freearea(areanum); - garbage(); - wdlist = 0; - iolist = 0; - e.errpt = 0; - e.linep = line; - yynerrs = 0; - multiline = 0; - inparse = 1; - intr = 0; - execflg = 0; - setjmp(failpt = m1); /* Bruce Evans' fix */ - if (setjmp(failpt = m1) || yyparse() || intr) { - while (e.oenv) - quitenv(); - scraphere(); - if (!interactive && intr) - leave(); - inparse = 0; - intr = 0; - return; - } - inparse = 0; - brklist = 0; - intr = 0; - execflg = 0; - if (!flag['n']) - execute(outtree, NOPIPE, NOPIPE, 0); - if (!interactive && intr) { - execflg = 0; - leave(); - } - if ((i = trapset) != 0) { - trapset = 0; - runtrap(i); - } -} - -static void -fail() +/* abort shell (or fail in subshell) */ +static void leave(void) NORETURN; +static void leave(void) { - longjmp(failpt, 1); - /* NOTREACHED */ -} + DBGPRINTF(("LEAVE: leave called!\n")); -static void -leave() -{ if (execflg) fail(); scraphere(); freehere(1); runtrap(0); - exit(exstat); + _exit(exstat); /* NOTREACHED */ } -static void -warn(s) -register char *s; +static void warn(const char *s) { - if(*s) { + if (*s) { prs(s); - exstat = -1; + if (!exstat) + exstat = 255; } prs("\n"); - if (flag['e']) + if (FLAG['e']) leave(); } -static void -err(s) -char *s; +static void err(const char *s) { warn(s); - if (flag['n']) + if (FLAG['n']) return; if (!interactive) leave(); - if (e.errpt) - longjmp(e.errpt, 1); + if (global_env.errpt) + longjmp(global_env.errpt, 1); closeall(); - e.iop = e.iobase = iostack; + global_env.iop = global_env.iobase = iostack; } -static int -newenv(f) -int f; + +/* -------- 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) { - register struct env *ep; + brkaddr = xmalloc(AREASIZE); + brktop = brkaddr + AREASIZE; - if (f) { - quitenv(); - return(1); - } - ep = (struct env *) space(sizeof(*ep)); - if (ep == NULL) { - while (e.oenv) - quitenv(); - fail(); - } - *ep = e; - e.oenv = ep; - e.errpt = errpt; - return(0); + while ((long) sbrk(0) & ALIGN) + sbrk(1); + areabot = (struct region *) sbrk(REGSIZE); + + areabot->next = areabot; + areabot->area = BUSY; + areatop = areabot; + areanxt = areabot; } -static void -quitenv() +static char *getcell(unsigned nbytes) { - register struct env *ep; - register int fd; + int nregio; + struct region *p, *q; + int i; - if ((ep = e.oenv) != NULL) { - fd = e.iofd; - e = *ep; - /* should close `'d files */ - DELETE(ep); - while (--fd >= e.iofd) - close(fd); + 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); } -/* - * Is any character from s1 in s2? - */ -static int -anys(s1, s2) -register char *s1, *s2; +static void freecell(char *cp) { - while (*s1) - if (any(*s1++, s2)) - return(1); - return(0); -} + struct region *p; -/* - * Is character c in s? - */ -static int -any(c, s) -register int c; -register char *s; -{ - while (*s) - if (*s++ == c) - return(1); - return(0); + p = (struct region *) cp; + if (p != NULL) { + p--; + if (p < areanxt) + areanxt = p; + p->area = FREE; + } } +#define DELETE(obj) freecell((char *)obj) -static char * -putn(n) -register int n; +static void freearea(int a) { - return(itoa(n)); + struct region *p, *top; + + top = areatop; + for (p = areabot; p != top; p = p->next) + if (p->area >= a) + p->area = FREE; } -static char * -itoa(n) -register int n; +static void setarea(char *cp, int a) { - static char s[20]; - snprintf(s, sizeof(s), "%u", n); - return(s); + struct region *p; + + p = (struct region *) cp; + if (p != NULL) + (p - 1)->area = a; } -static void -next(int f) +static int getarea(char *cp) { - PUSHIO(afile, f, filechar); + return ((struct region *) cp - 1)->area; } -static void -onintr(s) -int s; /* ANSI C requires a parameter */ +static void garbage(void) { - signal(SIGINT, onintr); - intr = 1; - if (interactive) { - if (inparse) { - prs("\n"); - fail(); + struct region *p, *q, *top; + + 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; } } - else if (heedint) { - execflg = 0; - leave(); +#ifdef SHRINKBY + if (areatop >= q + SHRINKBY && q->area > areanum) { + brk((char *) (q + 1)); + q->next = areabot; + q->area = BUSY; + areatop = q; } +#endif } -static char * -space(n) -int n; +static void *get_space(int n) { - register char *cp; + char *cp; - if ((cp = getcell(n)) == 0) + cp = getcell(n); + if (cp == NULL) err("out of string space"); - return(cp); + return cp; } -static char * -strsave(s, a) -register char *s; -int a; +static char *strsave(const char *s, int a) { - register char *cp, *xp; + char *cp; - if ((cp = space(strlen(s)+1)) != NULL) { - setarea((char *)cp, a); - for (xp = cp; (*xp++ = *s++) != '\0';) - ; - return(cp); + cp = get_space(strlen(s) + 1); + if (cp == NULL) { +// FIXME: I highly doubt this is good. + return (char*)""; } - return(""); + setarea(cp, a); + strcpy(cp, s); + return cp; } -/* - * trap handling - */ -static void -sig(i) -register int i; + +/* -------- var.c -------- */ + +static int eqname(const char *n1, const char *n2) { - trapset = i; - signal(i, sig); + for (; *n1 != '=' && *n1 != '\0'; n1++) + if (*n2++ != *n1) + return 0; + return *n2 == '\0' || *n2 == '='; } -static void runtrap(i) -int i; +static const char *findeq(const char *cp) { - char *trapstr; - - if ((trapstr = trap[i]) == NULL) - return; - if (i == 0) - trap[i] = 0; - RUN(aword, trapstr, nlchar); + while (*cp != '\0' && *cp != '=') + cp++; + return cp; } -/* -------- 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(n) -register char *n; +static struct var *lookup(const char *n) { - register struct var *vp; - register char *cp; - register int c; +// FIXME: dirty hack static struct var dummy; + struct var *vp; + const char *cp; + char *xp; + int c; + if (isdigit(*n)) { - dummy.name = n; + dummy.name = (char*)n; for (c = 0; isdigit(*n) && c < 1000; n++) - c = c*10 + *n-'0'; + c = c * 10 + *n - '0'; dummy.status = RONLY; - dummy.value = c <= dolc? dolv[c]: null; - return(&dummy); + dummy.value = (c <= dolc ? dolv[c] : null); + return &dummy; } + for (vp = vlist; vp; vp = vp->next) if (eqname(vp->name, n)) - return(vp); + 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 = get_space(sizeof(*vp)); + if (vp == 0 || (vp->name = get_space((int) (cp - n) + 2)) == NULL) { + dummy.name = dummy.value = (char*)""; + return &dummy; + } + + xp = vp->name; + while ((*xp = *n++) != '\0' && *xp != '=') + xp++; + *xp++ = '='; + *xp = '\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(vp, val) -struct var *vp; -char *val; -{ - nameval(vp, val, (char *)NULL); + return vp; } /* @@ -1206,494 +1086,631 @@ char *val; * this is all so that exporting * values is reasonably painless. */ -static void -nameval(vp, val, name) -register struct var *vp; -char *val, *name; +static void nameval(struct var *vp, const char *val, const char *name) { - register char *cp, *xp; - char *nv; + const char *cp; + char *xp; int fl; if (vp->status & RONLY) { - for (xp = vp->name; *xp && *xp != '=';) - putc(*xp++, stderr); + xp = vp->name; + while (*xp && *xp != '=') + fputc(*xp++, stderr); err(" is read-only"); return; } fl = 0; if (name == NULL) { - xp = space(strlen(vp->name)+strlen(val)+2); - if (xp == 0) + xp = get_space(strlen(vp->name) + strlen(val) + 2); + if (xp == NULL) return; - /* make string: name=value */ - setarea((char *)xp, 0); + /* make string: name=value */ + setarea(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; + 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; + freecell(vp->name); /* form new string `name=value' */ + vp->name = (char*)name; + vp->value = (char*)val; vp->status |= fl; } -static void -export(vp) -struct var *vp; +/* + * 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(vp) -struct var *vp; +static void ronly(struct var *vp) { if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */ vp->status |= RONLY; } -static int -isassign(s) -register char *s; -{ - if (!isalpha((int)*s) && *s != '_') - return(0); - for (; *s != '='; s++) - if (*s == 0 || (!isalnum(*s) && *s != '_')) - return(0); - return(1); +static int isassign(const char *s) +{ + unsigned char c; + DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s)); + + c = *s; + /* no isalpha() - we shouldn't use locale */ + /* c | 0x20 - lowercase (Latin) letters */ + if (c != '_' && (unsigned)((c|0x20) - 'a') > 25) + /* not letter */ + return 0; + + while (1) { + c = *++s; + if (c == '=') + return 1; + if (c == '\0') + return 0; + if (c != '_' + && (unsigned)(c - '0') > 9 /* not number */ + && (unsigned)((c|0x20) - 'a') > 25 /* not letter */ + ) { + return 0; + } + } } -static int -assign(s, cf) -register char *s; -int cf; +static int assign(const char *s, int cf) { - register char *cp; + const char *cp; struct var *vp; + DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf)); + if (!isalpha(*s) && *s != '_') - return(0); + return 0; for (cp = s; *cp != '='; cp++) - if (*cp == 0 || (!isalnum(*cp) && *cp != '_')) - return(0); + if (*cp == '\0' || (!isalnum(*cp) && *cp != '_')) + return 0; vp = lookup(s); - nameval(vp, ++cp, cf == COPYV? (char *)NULL: s); + nameval(vp, ++cp, cf == COPYV ? NULL : s); if (cf != COPYV) vp->status &= ~GETCELL; - return(1); + return 1; } -static int -checkname(cp) -register char *cp; +static int checkname(char *cp) { - if (!isalpha(*cp++) && *(cp-1) != '_') - return(0); + 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); + if (!isalnum(*cp++) && *(cp - 1) != '_') + return 0; + return 1; } -static void -putvlist(f, out) -register int f, out; +static void putvlist(int f, int out) { - register struct var *vp; + struct var *vp; - for (vp = vlist; vp; vp = vp->next) + 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, vp->name, (int) (findeq(vp->name) - vp->name)); write(out, "\n", 1); } + } } -static int -eqname(n1, n2) -register char *n1, *n2; + +/* + * trap handling + */ +static void sig(int i) { - for (; *n1 != '=' && *n1 != 0; n1++) - if (*n2++ != *n1) - return(0); - return(*n2 == 0 || *n2 == '='); + trapset = i; + signal(i, sig); } -static char * -findeq(cp) -register char *cp; +static void runtrap(int i) { - while (*cp != '\0' && *cp != '=') - cp++; - return(cp); -} + char *trapstr; -/* -------- gmatch.c -------- */ -/* - * int gmatch(string, pattern) - * char *string, *pattern; - * - * Match a pattern as in sh(1). - */ + trapstr = trap[i]; + if (trapstr == NULL) + return; -#define CMASK 0377 -#define QUOTE 0200 -#define QMASK (CMASK&~QUOTE) -#define NOT '!' /* might use ^ */ + if (i == 0) + trap[i] = NULL; + + RUN(aword, trapstr, nlchar); +} -static int -gmatch(s, p) -register char *s, *p; + +static void setdash(void) { - register int sc, pc; + char *cp; + int c; + char m['z' - 'a' + 1]; - if (s == NULL || p == NULL) - return(0); - while ((pc = *p++ & CMASK) != '\0') { - sc = *s++ & QMASK; - switch (pc) { - case '[': - if ((p = cclass(p, sc)) == NULL) - return(0); - break; + cp = m; + for (c = 'a'; c <= 'z'; c++) + if (FLAG[c]) + *cp++ = c; + *cp = '\0'; + setval(lookup("-"), m); +} - case '?': - if (sc == 0) - return(0); - break; +static int newfile(char *s) +{ + int f; - case '*': - s--; - do { - if (*p == '\0' || gmatch(s, p)) - return(1); - } while (*s++ != '\0'); - return(0); + DBGPRINTF7(("NEWFILE: opening %s\n", s)); - default: - if (sc != (pc&~QUOTE)) - return(0); + f = 0; + if (NOT_LONE_DASH(s)) { + DBGPRINTF(("NEWFILE: s is %s\n", s)); + f = open(s, O_RDONLY); + if (f < 0) { + prs(s); + err(": can't open"); + return 1; } } - return(*s == 0); + + next(remap(f)); + return 0; } -static char * -cclass(p, sub) -register char *p; -register int sub; + +#ifdef UNUSED +struct op *scantree(struct op *head) { - register int c, d, not, found; + struct op *dotnode; - if ((not = *p == NOT) != 0) - p++; - found = not; - do { - if (*p == '\0') - return((char *)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: (char *)NULL); -} + if (head == NULL) + return NULL; + if (head->left != NULL) { + dotnode = scantree(head->left); + if (dotnode) + return dotnode; + } -/* -------- area.c -------- */ + if (head->right != NULL) { + dotnode = scantree(head->right); + if (dotnode) + return dotnode; + } -/* - * 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. - */ + if (head->op_words == NULL) + return NULL; + + DBGPRINTF5(("SCANTREE: checking node %p\n", head)); + + if ((head->op_type != TDOT) && LONE_CHAR(head->op_words[0], '.')) { + DBGPRINTF5(("SCANTREE: dot found in node %p\n", head)); + return head; + } + + return NULL; +} +#endif -#define sbrk(X) ({ void * __q = (void *)-1; if (brkaddr + (int)(X) < brktop) { __q = brkaddr; brkaddr+=(int)(X); } __q;}) -static void -initarea() +static void onecommand(void) { - brkaddr = malloc(65000); - brktop = brkaddr + 65000; + int i; + jmp_buf m1; - while ((int)sbrk(0) & ALIGN) - sbrk(1); - areabot = (struct region *)sbrk(REGSIZE); + DBGPRINTF(("ONECOMMAND: enter, outtree=%p\n", outtree)); - areabot->next = areabot; - areabot->area = BUSY; - areatop = areabot; - areanxt = areabot; + while (global_env.oenv) + quitenv(); + + areanum = 1; + freehere(areanum); + freearea(areanum); + garbage(); + wdlist = NULL; + iolist = NULL; + global_env.errpt = NULL; + global_env.linep = line; + yynerrs = 0; + multiline = 0; + inparse = 1; + intr = 0; + execflg = 0; + + failpt = m1; + setjmp(failpt); /* Bruce Evans' fix */ + failpt = m1; + if (setjmp(failpt) || yyparse() || intr) { + DBGPRINTF(("ONECOMMAND: this is not good.\n")); + + while (global_env.oenv) + quitenv(); + scraphere(); + if (!interactive && intr) + leave(); + inparse = 0; + intr = 0; + return; + } + + inparse = 0; + brklist = 0; + intr = 0; + execflg = 0; + + if (!FLAG['n']) { + DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=%p\n", + outtree)); + execute(outtree, NOPIPE, NOPIPE, /* no_fork: */ 0); + } + + if (!interactive && intr) { + execflg = 0; + leave(); + } + + i = trapset; + if (i != 0) { + trapset = 0; + runtrap(i); + } } -char * -getcell(nbytes) -unsigned nbytes; +static int newenv(int f) { - register int nregio; - register struct region *p, *q; - register int i; + struct env *ep; - 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((char *)NULL); - p--; - if (p != areatop) { - puts("not contig"); - abort(); /* allocated areas are contiguous */ + DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f)); + + if (f) { + quitenv(); + return 1; } - 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; + + ep = get_space(sizeof(*ep)); + if (ep == NULL) { + while (global_env.oenv) + quitenv(); + fail(); } - p->area = areanum; - return((char *)(p+1)); + *ep = global_env; + global_env.oenv = ep; + global_env.errpt = errpt; + + return 0; } -static void -freecell(cp) -char *cp; +static void quitenv(void) { - register struct region *p; + struct env *ep; + int fd; - if ((p = (struct region *)cp) != NULL) { - p--; - if (p < areanxt) - areanxt = p; - p->area = FREE; + DBGPRINTF(("QUITENV: global_env.oenv=%p\n", global_env.oenv)); + + ep = global_env.oenv; + if (ep != NULL) { + fd = global_env.iofd; + global_env = *ep; + /* should close `'d files */ + DELETE(ep); + while (--fd >= global_env.iofd) + close(fd); } } -static void -freearea(a) -register int a; +/* + * Is character c in s? + */ +static int any(int c, const char *s) { - register struct region *p, *top; + while (*s) + if (*s++ == c) + return 1; + return 0; +} - top = areatop; - for (p = areabot; p != top; p = p->next) - if (p->area >= a) - p->area = FREE; +/* + * 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 UNUSED_PARAM) /* ANSI C requires a parameter */ +{ + signal(SIGINT, onintr); + intr = 1; + if (interactive) { + if (inparse) { + prs("\n"); + fail(); + } + } else if (heedint) { + execflg = 0; + leave(); + } } -static void -setarea(cp,a) -char *cp; -int a; + +/* -------- gmatch.c -------- */ +/* + * int gmatch(string, pattern) + * char *string, *pattern; + * + * Match a pattern as in sh(1). + */ + +#define CMASK 0377 +#define QUOTE 0200 +#define QMASK (CMASK & ~QUOTE) +#define NOT '!' /* might use ^ */ + +static const char *cclass(const char *p, int sub) { - register struct region *p; + int c, d, not, found; - if ((p = (struct region *)cp) != NULL) - (p-1)->area = a; + 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; } -int -getarea(cp) -char *cp; -{ - return ((struct region*)cp-1)->area; -} +static int gmatch(const char *s, const char *p) +{ + int sc, pc; + + if (s == NULL || p == NULL) + return 0; + + while ((pc = *p++ & CMASK) != '\0') { + sc = *s++ & QMASK; + switch (pc) { + case '[': + p = cclass(p, sc); + if (p == NULL) + return 0; + break; + + case '?': + if (sc == 0) + return 0; + break; -static void -garbage() -{ - register struct region *p, *q, *top; + case '*': + s--; + do { + if (*p == '\0' || gmatch(s, p)) + return 1; + } while (*s++ != '\0'); + return 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; + default: + if (sc != (pc & ~QUOTE)) + return 0; } } -#ifdef SHRINKBY - if (areatop >= q + SHRINKBY && q->area > areanum) { - brk((char *)(q+1)); - q->next = areabot; - q->area = BUSY; - areatop = q; - } -#endif + return *s == '\0'; } + /* -------- csyn.c -------- */ /* * shell: syntax (C version) */ +static void yyerror(const char *s) NORETURN; +static void yyerror(const char *s) +{ + yynerrs = 1; + if (interactive && global_env.iop <= iostack) { + multiline = 0; + while (eofc() == 0 && yylex(0) != '\n') + continue; + } + err(s); + fail(); +} + +static void zzerr(void) NORETURN; +static void zzerr(void) +{ + yyerror("syntax error"); +} -int -yyparse() +int yyparse(void) { - startl = 1; + DBGPRINTF7(("YYPARSE: enter...\n")); + + startl = 1; peeksym = 0; yynerrs = 0; outtree = c_list(); musthave('\n', 0); - return(yynerrs!=0); + return yynerrs; /* 0/1 */ } -static struct op * -pipeline(cf) -int cf; +static struct op *pipeline(int cf) { - register struct op *t, *p; - register int c; + struct op *t, *p; + int c; + + DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf)); t = command(cf); + + DBGPRINTF9(("PIPELINE: t=%p\n", t)); + if (t != NULL) { while ((c = yylex(0)) == '|') { - if ((p = command(CONTIN)) == NULL) - SYNTAXERR; - if (t->type != TPAREN && t->type != TCOM) { + p = command(CONTIN); + if (p == NULL) { + DBGPRINTF8(("PIPELINE: error!\n")); + zzerr(); + } + + if (t->op_type != TPAREN && t->op_type != TCOM) { /* shell statement */ t = block(TPAREN, t, NOBLOCK, NOWORDS); } + t = block(TPIPE, t, p, NOWORDS); } peeksym = c; } - return(t); + + DBGPRINTF7(("PIPELINE: returning t=%p\n", t)); + return t; } -static struct op * -andor() +static struct op *andor(void) { - register struct op *t, *p; - register int c; + struct op *t, *p; + int c; + + DBGPRINTF7(("ANDOR: enter...\n")); t = pipeline(0); + + DBGPRINTF9(("ANDOR: t=%p\n", t)); + if (t != NULL) { while ((c = yylex(0)) == LOGAND || c == LOGOR) { - if ((p = pipeline(CONTIN)) == NULL) - SYNTAXERR; - t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); + p = pipeline(CONTIN); + if (p == NULL) { + DBGPRINTF8(("ANDOR: error!\n")); + zzerr(); + } + + t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS); } + peeksym = c; } - return(t); + + DBGPRINTF7(("ANDOR: returning t=%p\n", t)); + return t; } -static struct op * -c_list() +static struct op *c_list(void) { - register struct op *t, *p; - register int c; + struct op *t, *p; + int c; + + DBGPRINTF7(("C_LIST: enter...\n")); t = andor(); + if (t != NULL) { - if((peeksym = yylex(0)) == '&') + peeksym = yylex(0); + if (peeksym == '&') t = block(TASYNC, t, NOBLOCK, NOWORDS); - while ((c = yylex(0)) == ';' || c == '&' || (multiline && c == '\n')) { - if ((p = andor()) == NULL) - return(t); - if((peeksym = yylex(0)) == '&') + + while ((c = yylex(0)) == ';' || c == '&' + || (multiline && c == '\n') + ) { + p = andor(); + if (p== NULL) + return t; + + peeksym = yylex(0); + if (peeksym == '&') p = block(TASYNC, p, NOBLOCK, NOWORDS); + t = list(t, p); - } + } /* WHILE */ + peeksym = c; } - return(t); + /* IF */ + DBGPRINTF7(("C_LIST: returning t=%p\n", t)); + return t; } - -static int -synio(cf) -int cf; +static int synio(int cf) { - register struct ioword *iop; - register int i; - register int c; + struct ioword *iop; + int i; + int c; - if ((c = yylex(cf)) != '<' && c != '>') { + DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf)); + + c = yylex(cf); + if (c != '<' && c != '>') { peeksym = c; - return(0); + return 0; } + i = yylval.i; musthave(WORD, 0); iop = io(iounit, i, yylval.cp); iounit = IODEFAULT; + if (i & IOHERE) markhere(yylval.cp, iop); - return(1); + + DBGPRINTF7(("SYNIO: returning 1\n")); + return 1; } -static void -musthave(c, cf) -int c, cf; +static void musthave(int c, int cf) { - if ((peeksym = yylex(cf)) != c) - SYNTAXERR; + peeksym = yylex(cf); + if (peeksym != c) { + DBGPRINTF7(("MUSTHAVE: error!\n")); + zzerr(); + } + peeksym = 0; } -static struct op * -simple() +static struct op *simple(void) { - register struct op *t; + struct op *t; t = NULL; for (;;) { @@ -1706,53 +1723,59 @@ simple() case WORD: if (t == NULL) { t = newtp(); - t->type = TCOM; + t->op_type = TCOM; } peeksym = 0; word(yylval.cp); break; default: - return(t); + return t; } } } -static struct op * -nested(type, mark) -int type, mark; +static struct op *nested(int type, int mark) { - register struct op *t; + struct op *t; + + DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark)); multiline++; t = c_list(); musthave(mark, 0); multiline--; - return(block(type, t, NOBLOCK, NOWORDS)); + return block(type, t, NOBLOCK, NOWORDS); } -static struct op * -command(cf) -int cf; +static struct op *command(int cf) { - register struct op *t; + struct op *t; struct wdblock *iosave; - register int c; + int c; + + DBGPRINTF(("COMMAND: enter, cf=%d\n", cf)); iosave = iolist; iolist = NULL; + if (multiline) cf |= CONTIN; + while (synio(cf)) cf = 0; - switch (c = yylex(cf)) { + + c = yylex(cf); + + switch (c) { default: peeksym = c; - if ((t = simple()) == NULL) { + t = simple(); + if (t == NULL) { if (iolist == NULL) - return((struct op *)NULL); + return NULL; t = newtp(); - t->type = TCOM; + t->op_type = TCOM; } break; @@ -1766,13 +1789,14 @@ int cf; case FOR: t = newtp(); - t->type = TFOR; + t->op_type = TFOR; musthave(WORD, 0); startl = 1; t->str = yylval.cp; multiline++; - t->words = wordlist(); - if ((c = yylex(0)) != '\n' && c != ';') + t->op_words = wordlist(); + c = yylex(0); + if (c != '\n' && c != ';') peeksym = c; t->left = dogroup(0); multiline--; @@ -1782,23 +1806,25 @@ int cf; case UNTIL: multiline++; t = newtp(); - t->type = c == WHILE? TWHILE: TUNTIL; + t->op_type = (c == WHILE ? TWHILE : TUNTIL); t->left = c_list(); t->right = dogroup(1); - t->words = NULL; + /* t->op_words = NULL; - newtp() did this */ multiline--; break; case CASE: t = newtp(); - t->type = TCASE; + t->op_type = TCASE; musthave(WORD, 0); t->str = yylval.cp; startl++; multiline++; musthave(IN, CONTIN); startl++; + t->left = caselist(); + musthave(ESAC, 0); multiline--; break; @@ -1806,331 +1832,386 @@ int cf; case IF: multiline++; t = newtp(); - t->type = TIF; + t->op_type = TIF; t->left = c_list(); t->right = thenpart(); musthave(FI, 0); multiline--; break; + + case DOT: + t = newtp(); + t->op_type = TDOT; + + musthave(WORD, 0); /* gets name of file */ + DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp)); + + word(yylval.cp); /* add word to wdlist */ + word(NOWORD); /* terminate wdlist */ + t->op_words = copyw(); /* dup wdlist */ + break; + } + while (synio(0)) - ; + continue; + t = namelist(t); iolist = iosave; - return(t); + + DBGPRINTF(("COMMAND: returning %p\n", t)); + + return t; } -static struct op * -dogroup(onlydone) -int onlydone; +static struct op *dowholefile(int type /*, int mark*/) { - register int c; - register struct op *mylist; + struct op *t; + + DBGPRINTF(("DOWHOLEFILE: enter, type=%d\n", type /*, mark*/)); + + multiline++; + t = c_list(); + multiline--; + t = block(type, t, NOBLOCK, NOWORDS); + DBGPRINTF(("DOWHOLEFILE: return t=%p\n", t)); + return t; +} + +static struct op *dogroup(int onlydone) +{ + int c; + struct op *mylist; c = yylex(CONTIN); if (c == DONE && onlydone) - return((struct op *)NULL); + return NULL; if (c != DO) - SYNTAXERR; + zzerr(); mylist = c_list(); musthave(DONE, 0); - return(mylist); + return mylist; } -static struct op * -thenpart() +static struct op *thenpart(void) { - register int c; - register struct op *t; + int c; + struct op *t; - if ((c = yylex(0)) != THEN) { + c = yylex(0); + if (c != THEN) { peeksym = c; - return((struct op *)NULL); + return NULL; } t = newtp(); - t->type = 0; + /*t->op_type = 0; - newtp() did this */ t->left = c_list(); if (t->left == NULL) - SYNTAXERR; + zzerr(); t->right = elsepart(); - return(t); + return t; } -static struct op * -elsepart() +static struct op *elsepart(void) { - register int c; - register struct op *t; + int c; + struct op *t; switch (c = yylex(0)) { case ELSE: - if ((t = c_list()) == NULL) - SYNTAXERR; - return(t); + t = c_list(); + if (t == NULL) + zzerr(); + return t; case ELIF: t = newtp(); - t->type = TELIF; + t->op_type = TELIF; t->left = c_list(); t->right = thenpart(); - return(t); + return t; default: peeksym = c; - return((struct op *)NULL); + return NULL; } } -static struct op * -caselist() +static struct op *caselist(void) { - register struct op *t; + struct op *t; t = NULL; - while ((peeksym = yylex(CONTIN)) != ESAC) + while ((peeksym = yylex(CONTIN)) != ESAC) { + DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym)); t = list(t, casepart()); - return(t); + } + + DBGPRINTF(("CASELIST, returning t=%p\n", t)); + return t; } -static struct op * -casepart() +static struct op *casepart(void) { - register struct op *t; + struct op *t; + + DBGPRINTF7(("CASEPART: enter...\n")); t = newtp(); - t->type = TPAT; - t->words = pattern(); + t->op_type = TPAT; + t->op_words = pattern(); musthave(')', 0); t->left = c_list(); - if ((peeksym = yylex(CONTIN)) != ESAC) + peeksym = yylex(CONTIN); + if (peeksym != ESAC) musthave(BREAK, CONTIN); - return(t); + + DBGPRINTF7(("CASEPART: made newtp(TPAT, t=%p)\n", t)); + + return t; } -static char ** -pattern() +static char **pattern(void) { - register int c, cf; + int c, cf; cf = CONTIN; do { musthave(WORD, cf); word(yylval.cp); cf = 0; - } while ((c = yylex(0)) == '|'); + c = yylex(0); + } while (c == '|'); peeksym = c; word(NOWORD); - return(copyw()); + + return copyw(); } -static char ** -wordlist() +static char **wordlist(void) { - register int c; + int c; - if ((c = yylex(0)) != IN) { + c = yylex(0); + if (c != IN) { peeksym = c; - return((char **)NULL); + return NULL; } startl = 0; while ((c = yylex(0)) == WORD) word(yylval.cp); word(NOWORD); peeksym = c; - return(copyw()); + return copyw(); } /* * supporting functions */ -static struct op * -list(t1, t2) -register struct op *t1, *t2; +static struct op *list(struct op *t1, struct op *t2) { + DBGPRINTF7(("LIST: enter, t1=%p, t2=%p\n", t1, t2)); + if (t1 == NULL) - return(t2); + return t2; if (t2 == NULL) - return(t1); - return(block(TLIST, t1, t2, NOWORDS)); + return t1; + + return block(TLIST, t1, t2, NOWORDS); } -static struct op * -block(type, t1, t2, wp) -int type; -struct op *t1, *t2; -char **wp; +static struct op *block(int type, struct op *t1, struct op *t2, char **wp) { - register struct op *t; + struct op *t; + + DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type])); t = newtp(); - t->type = type; + t->op_type = type; t->left = t1; t->right = t2; - t->words = wp; - return(t); + t->op_words = wp; + + DBGPRINTF7(("BLOCK: inserted %p between %p and %p\n", t, t1, t2)); + + return t; +} + +/* See if given string is a shell multiline (FOR, IF, etc) */ +static int rlookup(char *n) +{ + struct res { + char r_name[6]; + int16_t r_val; + }; + static const struct res restab[] = { + { "for" , FOR }, + { "case" , CASE }, + { "esac" , ESAC }, + { "while", WHILE }, + { "do" , DO }, + { "done" , DONE }, + { "if" , IF }, + { "in" , IN }, + { "then" , THEN }, + { "else" , ELSE }, + { "elif" , ELIF }, + { "until", UNTIL }, + { "fi" , FI }, + { ";;" , BREAK }, + { "||" , LOGOR }, + { "&&" , LOGAND }, + { "{" , '{' }, + { "}" , '}' }, + { "." , DOT }, + { }, + }; + + const struct res *rp; + + DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n)); + + for (rp = restab; rp->r_name[0]; rp++) + if (strcmp(rp->r_name, n) == 0) { + DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val)); + return rp->r_val; /* Return numeric code for shell multiline */ + } + + DBGPRINTF7(("RLOOKUP: NO match, returning 0\n")); + return 0; /* Not a shell multiline */ } -static int -rlookup(n) -register char *n; +static struct op *newtp(void) { - register struct res *rp; + struct op *t; - for (rp = restab; rp->r_name; rp++) - if (strcmp(rp->r_name, n) == 0) - return(rp->r_val); - return(0); -} + t = (struct op *) tree(sizeof(*t)); + memset(t, 0, sizeof(*t)); -static struct op * -newtp() -{ - register struct op *t; + DBGPRINTF3(("NEWTP: allocated %p\n", t)); - t = (struct op *)tree(sizeof(*t)); - t->type = 0; - t->words = NULL; - t->ioact = NULL; - t->left = NULL; - t->right = NULL; - t->str = NULL; - return(t); + return t; } -static struct op * -namelist(t) -register struct op *t; +static struct op *namelist(struct op *t) { + DBGPRINTF7(("NAMELIST: enter, t=%p, type %s, iolist=%p\n", t, + T_CMD_NAMES[t->op_type], iolist)); + if (iolist) { - iolist = addword((char *)NULL, iolist); + iolist = addword((char *) NULL, iolist); t->ioact = copyio(); } else t->ioact = NULL; - if (t->type != TCOM) { - if (t->type != TPAREN && t->ioact != NULL) { + + if (t->op_type != TCOM) { + if (t->op_type != TPAREN && t->ioact != NULL) { t = block(TPAREN, t, NOBLOCK, NOWORDS); t->ioact = t->left->ioact; t->left->ioact = NULL; } - return(t); + return t; } + word(NOWORD); - t->words = copyw(); - return(t); + t->op_words = copyw(); + + return t; } -static char ** -copyw() +static char **copyw(void) { - register char **wd; + char **wd; wd = getwords(wdlist); - wdlist = 0; - return(wd); + wdlist = NULL; + return wd; } -static void -word(cp) -char *cp; +static void word(char *cp) { wdlist = addword(cp, wdlist); } -static struct ioword ** -copyio() +static struct ioword **copyio(void) { - register struct ioword **iop; + struct ioword **iop; iop = (struct ioword **) getwords(iolist); - iolist = 0; - return(iop); + iolist = NULL; + return iop; } -static struct ioword * -io(u, f, cp) -int u; -int f; -char *cp; +static struct ioword *io(int u, int f, char *cp) { - register struct ioword *iop; + struct ioword *iop; iop = (struct ioword *) tree(sizeof(*iop)); - iop->io_unit = u; + iop->io_fd = u; iop->io_flag = f; iop->io_name = cp; - iolist = addword((char *)iop, iolist); - return(iop); + iolist = addword((char *) iop, iolist); + return iop; } -static void -zzerr() +static int yylex(int cf) { - yyerror("syntax error"); -} - -static void -yyerror(s) -char *s; -{ - yynerrs++; - if (interactive && e.iop <= iostack) { - multiline = 0; - while (eofc() == 0 && yylex(0) != '\n') - ; - } - err(s); - fail(); -} - -static int -yylex(cf) -int cf; -{ - register int c, c1; + int c, c1; int atstart; - if ((c = peeksym) > 0) { + c = peeksym; + if (c > 0) { peeksym = 0; if (c == '\n') startl = 1; - return(c); + return c; } + nlseen = 0; - e.linep = line; atstart = startl; startl = 0; yylval.i = 0; + global_env.linep = line; + +/* MALAMO */ + line[LINELIM - 1] = '\0'; + + loop: + while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */ + continue; -loop: - while ((c = my_getc(0)) == ' ' || c == '\t') - ; switch (c) { default: if (any(c, "0123456789")) { - unget(c1 = my_getc(0)); + c1 = my_getc(0); + unget(c1); if (c1 == '<' || c1 == '>') { iounit = c - '0'; goto loop; } - *e.linep++ = c; + *global_env.linep++ = c; c = c1; } break; - case '#': - while ((c = my_getc(0)) != 0 && c != '\n') - ; + case '#': /* Comment, skip to next newline or End-of-string */ + while ((c = my_getc(0)) != '\0' && c != '\n') + continue; unget(c); goto loop; case 0: - return(c); + DBGPRINTF5(("YYLEX: return 0, c=%d\n", c)); + return c; case '$': - *e.linep++ = c; - if ((c = my_getc(0)) == '{') { - if ((c = collect(c, '}')) != '\0') - return(c); + DBGPRINTF9(("YYLEX: found $\n")); + *global_env.linep++ = c; + c = my_getc(0); + if (c == '{') { + c = collect(c, '}'); + if (c != '\0') + return c; goto pack; } break; @@ -2138,227 +2219,325 @@ loop: case '`': case '\'': case '"': - if ((c = collect(c, c)) != '\0') - return(c); + c = collect(c, c); + if (c != '\0') + return c; goto pack; case '|': case '&': case ';': - if ((c1 = dual(c)) != '\0') { - startl = 1; - return(c1); - } startl = 1; - return(c); + /* If more chars process them, else return NULL char */ + c1 = dual(c); + if (c1 != '\0') + return c1; + return c; + case '^': startl = 1; - return('|'); + return '|'; case '>': case '<': diag(c); - return(c); + return c; case '\n': nlseen++; gethere(); startl = 1; if (multiline || cf & CONTIN) { - if (interactive && e.iop <= iostack) { -#ifdef CONFIG_FEATURE_COMMAND_EDITING - current_prompt=cprompt->value; + if (interactive && global_env.iop <= iostack) { +#if ENABLE_FEATURE_EDITING + current_prompt = cprompt->value; #else - prs(cprompt->value); + prs(cprompt->value); #endif } if (cf & CONTIN) goto loop; } - return(c); + return c; case '(': case ')': startl = 1; - return(c); + return c; } unget(c); -pack: - while ((c = my_getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) - if (e.linep >= elinep) + pack: + while ((c = my_getc(0)) != '\0' && !any(c, "`$ '\"\t;&<>()|^\n")) { + if (global_env.linep >= elinep) err("word too long"); else - *e.linep++ = c; + *global_env.linep++ = c; + }; + unget(c); - if(any(c, "\"'`$")) + + if (any(c, "\"'`$")) goto loop; - *e.linep++ = '\0'; - if (atstart && (c = rlookup(line))!=0) { - startl = 1; - return(c); + + *global_env.linep++ = '\0'; + + if (atstart) { + c = rlookup(line); + if (c != 0) { + startl = 1; + return c; + } } + yylval.cp = strsave(line, areanum); - return(WORD); + return WORD; } -static int -collect(c, c1) -register int c, c1; + +static int collect(int c, int c1) { char s[2]; - *e.linep++ = c; + DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1)); + + *global_env.linep++ = c; while ((c = my_getc(c1)) != c1) { if (c == 0) { unget(c); s[0] = c1; s[1] = 0; - prs("no closing "); yyerror(s); - return(YYERRCODE); + prs("no closing "); + yyerror(s); + return YYERRCODE; } - if (interactive && c == '\n' && e.iop <= iostack) { -#ifdef CONFIG_FEATURE_COMMAND_EDITING - current_prompt=cprompt->value; + if (interactive && c == '\n' && global_env.iop <= iostack) { +#if ENABLE_FEATURE_EDITING + current_prompt = cprompt->value; #else - prs(cprompt->value); + prs(cprompt->value); #endif } - *e.linep++ = c; + *global_env.linep++ = c; } - *e.linep++ = c; - return(0); + + *global_env.linep++ = c; + + DBGPRINTF8(("COLLECT: return 0, line is %s\n", line)); + + return 0; } -static int -dual(c) -register int c; +/* "multiline commands" helper func */ +/* see if next 2 chars form a shell multiline */ +static int dual(int c) { char s[3]; - register char *cp = s; + char *cp = s; - *cp++ = c; - *cp++ = my_getc(0); - *cp = 0; - if ((c = rlookup(s)) == 0) - unget(*--cp); - return(c); + DBGPRINTF8(("DUAL: enter, c=%d\n", c)); + + *cp++ = c; /* c is the given "peek" char */ + *cp++ = my_getc(0); /* get next char of input */ + *cp = '\0'; /* add EOS marker */ + + c = rlookup(s); /* see if 2 chars form a shell multiline */ + if (c == 0) + unget(*--cp); /* String is not a shell multiline, put peek char back */ + + return c; /* String is multiline, return numeric multiline (restab) code */ } -static void -diag(ec) -register int ec; +static void diag(int ec) { - register int c; + int c; + + DBGPRINTF8(("DIAG: enter, ec=%d\n", ec)); c = my_getc(0); if (c == '>' || c == '<') { if (c != ec) zzerr(); - yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE; + yylval.i = (ec == '>' ? IOWRITE | IOCAT : IOHERE); c = my_getc(0); } else - yylval.i = ec == '>'? IOWRITE: IOREAD; + yylval.i = (ec == '>' ? IOWRITE : IOREAD); if (c != '&' || yylval.i == IOHERE) unget(c); else yylval.i |= IODUP; } -static char * -tree(size) -unsigned size; +static char *tree(unsigned size) { - register char *t; + char *t; - if ((t = getcell(size)) == NULL) { + t = getcell(size); + if (t == NULL) { + DBGPRINTF2(("TREE: getcell(%d) failed!\n", size)); prs("command line too complicated\n"); fail(); /* NOTREACHED */ } - return(t); + return t; } + /* VARARGS1 */ /* ARGSUSED */ /* -------- exec.c -------- */ +static struct op **find1case(struct op *t, const char *w) +{ + struct op *t1; + struct op **tp; + char **wp; + char *cp; + + if (t == NULL) { + DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n")); + return NULL; + } + + DBGPRINTF3(("FIND1CASE: enter, t->op_type=%d (%s)\n", t->op_type, + T_CMD_NAMES[t->op_type])); + + if (t->op_type == TLIST) { + tp = find1case(t->left, w); + if (tp != NULL) { + DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=%p\n", tp)); + return tp; + } + t1 = t->right; /* TPAT */ + } else + t1 = t; + + for (wp = t1->op_words; *wp;) { + cp = evalstr(*wp++, DOSUB); + if (cp && gmatch(w, cp)) { + DBGPRINTF3(("FIND1CASE: returning &t1->left= %p.\n", + &t1->left)); + return &t1->left; + } + } + + DBGPRINTF(("FIND1CASE: returning NULL\n")); + return NULL; +} + +static struct op *findcase(struct op *t, const char *w) +{ + struct op **tp; + + tp = find1case(t, w); + return tp != NULL ? *tp : NULL; +} + /* * execute tree */ - -static int -execute(t, pin, pout, act) -register struct op *t; -int *pin, *pout; -int act; +static int execute(struct op *t, int *pin, int *pout, int no_fork) { - register struct op *t1; + struct op *t1; volatile int i, rv, a; - char *cp, **wp, **wp2; + const char *cp; + char **wp, **wp2; struct var *vp; + struct op *outtree_save; struct brkcon bc; #if __GNUC__ /* Avoid longjmp clobbering */ (void) ℘ -#endif +#endif + + if (t == NULL) { + DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n")); + return 0; + } + DBGPRINTF(("EXECUTE: t=%p, t->op_type=%d (%s), t->op_words is %s\n", t, + t->op_type, T_CMD_NAMES[t->op_type], + ((t->op_words == NULL) ? "NULL" : t->op_words[0]))); - if (t == NULL) - return(0); rv = 0; a = areanum++; - wp = (wp2 = t->words) != NULL - ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY) - : NULL; + wp2 = t->op_words; + wp = (wp2 != NULL) + ? eval(wp2, t->op_type == TCOM ? DOALL : DOALL & ~DOKEY) + : NULL; + + switch (t->op_type) { + case TDOT: + DBGPRINTF3(("EXECUTE: TDOT\n")); + + outtree_save = outtree; + + newfile(evalstr(t->op_words[0], DOALL)); + + t->left = dowholefile(TLIST /*, 0*/); + t->right = NULL; + + outtree = outtree_save; + + if (t->left) + rv = execute(t->left, pin, pout, /* no_fork: */ 0); + if (t->right) + rv = execute(t->right, pin, pout, /* no_fork: */ 0); + break; - switch(t->type) { case TPAREN: - rv = execute(t->left, pin, pout, 0); + rv = execute(t->left, pin, pout, /* no_fork: */ 0); break; - + case TCOM: - { - int child; - rv = forkexec(t, pin, pout, act, wp, &child); - if (child) { - exstat = rv; - leave(); - } - } + rv = forkexec(t, pin, pout, no_fork, wp); break; case TPIPE: { - int pv[2]; - if ((rv = openpipe(pv)) < 0) - break; - pv[0] = remap(pv[0]); - pv[1] = remap(pv[1]); - (void) execute(t->left, pin, pv, 0); - rv = execute(t->right, pv, pout, 0); + int pv[2]; + + rv = openpipe(pv); + if (rv < 0) + break; + pv[0] = remap(pv[0]); + pv[1] = remap(pv[1]); + (void) execute(t->left, pin, pv, /* no_fork: */ 0); + rv = execute(t->right, pv, pout, /* no_fork: */ 0); } break; case TLIST: - (void) execute(t->left, pin, pout, 0); - rv = execute(t->right, pin, pout, 0); + (void) execute(t->left, pin, pout, /* no_fork: */ 0); + rv = execute(t->right, pin, pout, /* no_fork: */ 0); break; case TASYNC: - { - int hinteractive = interactive; - - i = vfork(); - if (i != 0) { + { + smallint hinteractive = interactive; + + DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n")); + + i = vfork(); + if (i == 0) { /* child */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + if (interactive) + signal(SIGTERM, SIG_DFL); + interactive = 0; + if (pin == NULL) { + close(0); + xopen(bb_dev_null, O_RDONLY); + } + _exit(execute(t->left, pin, pout, /* no_fork: */ 1)); + } interactive = hinteractive; if (i != -1) { setval(lookup("!"), putn(i)); - if (pin != NULL) - closepipe(pin); + closepipe(pin); if (interactive) { prs(putn(i)); prs("\n"); @@ -2366,46 +2545,40 @@ int act; } else rv = -1; setstatus(rv); - } else { - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - if (interactive) - signal(SIGTERM, SIG_DFL); - interactive = 0; - if (pin == NULL) { - close(0); - open("/dev/null", 0); - } - exit(execute(t->left, pin, pout, FEXEC)); } - } break; case TOR: case TAND: - rv = execute(t->left, pin, pout, 0); - if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND)) - rv = execute(t1, pin, pout, 0); + rv = execute(t->left, pin, pout, /* no_fork: */ 0); + t1 = t->right; + if (t1 != NULL && (rv == 0) == (t->op_type == TAND)) + rv = execute(t1, pin, pout, /* no_fork: */ 0); break; case TFOR: if (wp == NULL) { - wp = dolv+1; - if ((i = dolc) < 0) + wp = dolv + 1; + i = dolc; + if (i < 0) i = 0; } else { i = -1; while (*wp++ != NULL) - ; + continue; } vp = lookup(t->str); while (setjmp(bc.brkpt)) if (isbreak) goto broken; + /* Restore areanum value. It may be incremented by execute() + * below, and then "continue" may jump back to setjmp above */ + areanum = a + 1; + freearea(areanum + 1); brkset(&bc); for (t1 = t->left; i-- && *wp != NULL;) { setval(vp, *wp++); - rv = execute(t1, pin, pout, 0); + rv = execute(t1, pin, pout, /* no_fork: */ 0); } brklist = brklist->nextlev; break; @@ -2415,45 +2588,67 @@ int act; while (setjmp(bc.brkpt)) if (isbreak) goto broken; + /* Restore areanum value. It may be incremented by execute() + * below, and then "continue" may jump back to setjmp above */ + areanum = a + 1; + freearea(areanum + 1); brkset(&bc); t1 = t->left; - while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE)) - rv = execute(t->right, pin, pout, 0); + while ((execute(t1, pin, pout, /* no_fork: */ 0) == 0) == (t->op_type == TWHILE)) + rv = execute(t->right, pin, pout, /* no_fork: */ 0); brklist = brklist->nextlev; break; case TIF: case TELIF: - if (t->right != NULL) { - rv = !execute(t->left, pin, pout, 0) ? - execute(t->right->left, pin, pout, 0): - execute(t->right->right, pin, pout, 0); + if (t->right != NULL) { + rv = !execute(t->left, pin, pout, /* no_fork: */ 0) ? + execute(t->right->left, pin, pout, /* no_fork: */ 0) : + execute(t->right->right, pin, pout, /* no_fork: */ 0); } break; case TCASE: - if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0) + cp = evalstr(t->str, DOSUB | DOTRIM); + if (cp == NULL) cp = ""; - if ((t1 = findcase(t->left, cp)) != NULL) - rv = execute(t1, pin, pout, 0); + + DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n", + ((t->str == NULL) ? "NULL" : t->str), + ((cp == NULL) ? "NULL" : cp))); + + t1 = findcase(t->left, cp); + if (t1 != NULL) { + DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=%p, t1=%p)...\n", t, t1)); + rv = execute(t1, pin, pout, /* no_fork: */ 0); + DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=%p, t1=%p)...\n", t, t1)); + } break; case TBRACE: /* - if (iopp = t->ioact) + iopp = t->ioact; + if (i) while (*iopp) if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) { rv = -1; break; } */ - if (rv >= 0 && (t1 = t->left)) - rv = execute(t1, pin, pout, 0); + if (rv >= 0) { + t1 = t->left; + if (t1) { + rv = execute(t1, pin, pout, /* no_fork: */ 0); + } + } break; - } -broken: - t->words = wp2; + }; + + broken: +// Restoring op_words is most likely not needed now: see comment in forkexec() +// (also take a look at exec builtin (doexec) - it touches t->op_words) + t->op_words = wp2; isbreak = 0; freehere(areanum); freearea(areanum); @@ -2462,98 +2657,137 @@ broken: closeall(); fail(); } - if ((i = trapset) != 0) { + + i = trapset; + if (i != 0) { trapset = 0; runtrap(i); } - return(rv); + + DBGPRINTF(("EXECUTE: returning from t=%p, rv=%d\n", t, rv)); + return rv; } -static int -forkexec( register struct op *t, int *pin, int *pout, int act, char **wp, int *pforked) +static builtin_func_ptr inbuilt(const char *s) { - int i, rv; - int (*shcom)(struct op *) = NULL; - register int f; - char *cp = NULL; + const struct builtincmd *bp; + + for (bp = builtincmds; bp->name; bp++) + if (strcmp(bp->name, s) == 0) + return bp->builtinfunc; + return NULL; +} + +static int forkexec(struct op *t, int *pin, int *pout, int no_fork, char **wp) +{ + pid_t newpid; + int i; + builtin_func_ptr bltin = NULL; + const char *bltin_name = NULL; + const char *cp; struct ioword **iopp; int resetsig; char **owp; + int forked; int *hpin = pin; int *hpout = pout; - int hforked; char *hwp; - int hinteractive; - int hintr; - struct brkcon * hbrklist; - int hexecflg; + smallint hinteractive; + smallint hintr; + smallint hexecflg; + struct brkcon *hbrklist; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &pin; (void) &pout; (void) ℘ - (void) &shcom; + (void) &bltin; (void) &cp; (void) &resetsig; (void) &owp; -#endif +#endif + DBGPRINTF(("FORKEXEC: t=%p, pin %p, pout %p, no_fork %d\n", t, pin, + pout, no_fork)); + DBGPRINTF7(("FORKEXEC: t->op_words is %s\n", + ((t->op_words == NULL) ? "NULL" : t->op_words[0]))); owp = wp; resetsig = 0; - *pforked = 0; - rv = -1; /* system-detected error */ - if (t->type == TCOM) { - while ((cp = *wp++) != NULL) - ; + if (t->op_type == TCOM) { + while (*wp++ != NULL) + continue; cp = *wp; /* strip all initial assignments */ - /* not correct wrt PATH=yyy command etc */ - if (flag['x']) - echo (cp ? wp: owp); - if (cp == NULL && t->ioact == NULL) { - while ((cp = *owp++) != NULL && assign(cp, COPYV)) - ; - return(setstatus(0)); + /* FIXME: not correct wrt PATH=yyy command etc */ + if (FLAG['x']) { + DBGPRINTF9(("FORKEXEC: echo'ing, cp=%p, wp=%p, owp=%p\n", + cp, wp, owp)); + echo(cp ? wp : owp); + } + + if (cp == NULL) { + if (t->ioact == NULL) { + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + continue; + DBGPRINTF(("FORKEXEC: returning setstatus(0)\n")); + return setstatus(0); + } + } else { /* cp != NULL */ + bltin_name = cp; + bltin = inbuilt(cp); } - else if (cp != NULL) - shcom = inbuilt(cp); } - t->words = wp; - f = act; - if (shcom == NULL && (f & FEXEC) == 0) { + forked = 0; + // We were pointing t->op_words to temporary (expanded) arg list: + // t->op_words = wp; + // and restored it later (in execute()), but "break" + // longjmps away (at "Run builtin" below), leaving t->op_words clobbered! + // See http://bugs.busybox.net/view.php?id=846. + // Now we do not touch t->op_words, but separately pass wp as param list + // to builtins + DBGPRINTF(("FORKEXEC: bltin %p, no_fork %d, owp %p\n", bltin, + no_fork, owp)); + /* Don't fork if it is a lone builtin (not in pipe) + * OR we are told to _not_ fork */ + if ((!bltin || pin || pout) /* not lone bltin AND */ + && !no_fork /* not told to avoid fork */ + ) { + /* Save values in case child alters them after vfork */ hpin = pin; hpout = pout; - hforked = *pforked; hwp = *wp; hinteractive = interactive; hintr = intr; hbrklist = brklist; hexecflg = execflg; - - i = vfork(); - if (i != 0) { - /* who wrote this crappy non vfork safe shit? */ + + DBGPRINTF3(("FORKEXEC: calling vfork()...\n")); + newpid = vfork(); + if (newpid == -1) { + DBGPRINTF(("FORKEXEC: ERROR, can't vfork()!\n")); + return -1; + } + + if (newpid > 0) { /* Parent */ + /* Restore values */ pin = hpin; pout = hpout; - *pforked = hforked; *wp = hwp; interactive = hinteractive; intr = hintr; brklist = hbrklist; execflg = hexecflg; - *pforked = 0; - if (i == -1) - return(rv); - if (pin != NULL) - closepipe(pin); - return(pout==NULL? setstatus(waitfor(i,0)): 0); + closepipe(pin); + return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0); } + /* Child */ + DBGPRINTF(("FORKEXEC: child process, bltin=%p (%s)\n", bltin, bltin_name)); if (interactive) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); @@ -2561,191 +2795,176 @@ forkexec( register struct op *t, int *pin, int *pout, int act, char **wp, int *p } interactive = 0; intr = 0; - (*pforked)++; + forked = 1; brklist = 0; execflg = 0; - } - if (owp != NULL) + } + + if (owp) while ((cp = *owp++) != NULL && assign(cp, COPYV)) - if (shcom == NULL) + if (!bltin) export(lookup(cp)); -#ifdef COMPIPE - if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) { - err("piping to/from shell builtins not yet done"); - return(-1); + + if (pin) { /* NB: close _first_, then move fds! */ + close(pin[1]); + xmove_fd(pin[0], 0); } -#endif - if (pin != NULL) { - dup2(pin[0], 0); - closepipe(pin); + if (pout) { + close(pout[0]); + xmove_fd(pout[1], 1); } - if (pout != NULL) { - dup2(pout[1], 1); - closepipe(pout); + + iopp = t->ioact; + if (iopp) { + if (bltin && bltin != doexec) { + prs(bltin_name); + err(": can't redirect shell command"); + if (forked) + _exit(-1); + return -1; + } + while (*iopp) { + if (iosetup(*iopp++, pin != NULL, pout != NULL)) { + /* system-detected error */ + if (forked) + _exit(-1); + return -1; + } + } } - if ((iopp = t->ioact) != NULL) { - if (shcom != NULL && shcom != doexec) { - prs(cp); - err(": cannot redirect shell command"); - return(-1); + + if (bltin) { + if (forked || pin || pout) { + /* Builtin in pipe: disallowed */ + /* TODO: allow "exec"? */ + prs(bltin_name); + err(": can't run builtin as part of pipe"); + if (forked) + _exit(-1); + return -1; } - while (*iopp) - if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) - return(rv); + /* Run builtin */ + i = setstatus(bltin(t, wp)); + if (forked) + _exit(i); + DBGPRINTF(("FORKEXEC: returning i=%d\n", i)); + return i; } - if (shcom) - return(setstatus((*shcom)(t))); + /* should use FIOCEXCL */ - for (i=FDBASE; itype == TPAREN) - exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); + + if (t->op_type == TPAREN) + _exit(execute(t->left, NOPIPE, NOPIPE, /* no_fork: */ 1)); if (wp[0] == NULL) - exit(0); + _exit(EXIT_SUCCESS); - cp = rexecve(wp[0], wp, makenv()); - prs(wp[0]); prs(": "); warn(cp); + cp = rexecve(wp[0], wp, makenv(0, NULL)); + prs(wp[0]); + prs(": "); + err(cp); if (!execflg) trap[0] = NULL; + + DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", getpid())); + leave(); /* NOTREACHED */ - exit(1); + return 0; } /* * 0< 1> are ignored as required * within pipelines. */ -static int -iosetup(iop, pipein, pipeout) -register struct ioword *iop; -int pipein, pipeout; -{ - register int u = -1; - char *cp=NULL, *msg; - - if (iop->io_unit == IODEFAULT) /* take default */ - iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1; - if (pipein && iop->io_unit == 0) - return(0); - if (pipeout && iop->io_unit == 1) - return(0); - msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create"; +static int iosetup(struct ioword *iop, int pipein, int pipeout) +{ + int u = -1; + char *cp = NULL; + const char *msg; + + DBGPRINTF(("IOSETUP: iop %p, pipein %i, pipeout %i\n", iop, + pipein, pipeout)); + + if (iop->io_fd == IODEFAULT) /* take default */ + iop->io_fd = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1; + + if (pipein && iop->io_fd == 0) + return 0; + + if (pipeout && iop->io_fd == 1) + return 0; + + msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create"; if ((iop->io_flag & IOHERE) == 0) { - cp = iop->io_name; - if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL) - return(1); + cp = iop->io_name; /* huh?? */ + cp = evalstr(cp, DOSUB | DOTRIM); + if (cp == NULL) + return 1; } + if (iop->io_flag & IODUP) { if (cp[1] || (!isdigit(*cp) && *cp != '-')) { prs(cp); err(": illegal >& argument"); - return(1); + return 1; } if (*cp == '-') iop->io_flag = IOCLOSE; - iop->io_flag &= ~(IOREAD|IOWRITE); + iop->io_flag &= ~(IOREAD | IOWRITE); } + switch (iop->io_flag) { case IOREAD: - u = open(cp, 0); + u = open(cp, O_RDONLY); break; case IOHERE: - case IOHERE|IOXHERE: - u = herein(iop->io_name, iop->io_flag&IOXHERE); - cp = "here file"; + case IOHERE | IOXHERE: + u = herein(iop->io_name, iop->io_flag & IOXHERE); + cp = (char*)"here file"; break; - case IOWRITE|IOCAT: - if ((u = open(cp, 1)) >= 0) { - lseek(u, (long)0, 2); + case IOWRITE | IOCAT: + u = open(cp, O_WRONLY); + if (u >= 0) { + lseek(u, (long) 0, SEEK_END); break; } + /* fall through to creation if >>file doesn't exist */ + case IOWRITE: u = creat(cp, 0666); break; case IODUP: - u = dup2(*cp-'0', iop->io_unit); + u = dup2(*cp - '0', iop->io_fd); break; case IOCLOSE: - close(iop->io_unit); - return(0); + close(iop->io_fd); + return 0; } + if (u < 0) { prs(cp); - prs(": cannot "); + prs(": can't "); warn(msg); - return(1); - } else { - if (u != iop->io_unit) { - dup2(u, iop->io_unit); - close(u); - } - } - return(0); -} - -static void -echo(wp) -register char **wp; -{ - register int i; - - prs("+"); - for (i=0; wp[i]; i++) { - if (i) - prs(" "); - prs(wp[i]); + return 1; } - prs("\n"); -} - -static struct op ** -find1case(t, w) -struct op *t; -char *w; -{ - register struct op *t1; - struct op **tp; - register char **wp, *cp; - - if (t == NULL) - return((struct op **)NULL); - if (t->type == TLIST) { - if ((tp = find1case(t->left, w)) != NULL) - return(tp); - t1 = t->right; /* TPAT */ - } else - t1 = t; - for (wp = t1->words; *wp;) - if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) - return(&t1->left); - return((struct op **)NULL); -} - -static struct op * -findcase(t, w) -struct op *t; -char *w; -{ - register struct op **tp; - - return((tp = find1case(t, w)) != NULL? *tp: (struct op *)NULL); + xmove_fd(u, iop->io_fd); + return 0; } /* * Enter a new loop level (marked for break/continue). */ -static void -brkset(bc) -struct brkcon *bc; +static void brkset(struct brkcon *bc) { bc->nextlev = brklist; brklist = bc; @@ -2758,14 +2977,11 @@ struct brkcon *bc; * Ignore interrupt signals while waiting * unless `canintr' is true. */ -static int -waitfor(lastpid, canintr) -register int lastpid; -int canintr; +static int waitfor(int lastpid, int canintr) { - register int pid, rv; + int pid, rv; int s; - int oheedint = heedint; + smallint oheedint = heedint; heedint = 0; rv = 0; @@ -2775,8 +2991,9 @@ int canintr; if (errno != EINTR || canintr) break; } else { - if ((rv = WAITSIG(s)) != 0) { - if (rv < NSIGNAL) { + rv = WAITSIG(s); + if (rv != 0) { + if (rv < ARRAY_SIZE(signame)) { if (signame[rv] != NULL) { if (pid != lastpid) { prn(pid); @@ -2789,13 +3006,15 @@ int canintr; prn(pid); prs(": "); } - prs("Signal "); prn(rv); prs(" "); + prs("Signal "); + prn(rv); + prs(" "); } if (WAITCORE(s)) prs(" - core dumped"); - if (rv >= NSIGNAL || signame[rv]) + if (rv >= ARRAY_SIZE(signame) || signame[rv]) prs("\n"); - rv = -1; + rv |= 0x80; } else rv = WAITVAL(s); } @@ -2806,20 +3025,19 @@ int canintr; if (canintr) intr = 0; } else { - if (exstat == 0) exstat = rv; + if (exstat == 0) + exstat = rv; onintr(0); } } - return(rv); + return rv; } -static int -setstatus(s) -register int s; +static int setstatus(int s) { exstat = s; setval(lookup("?"), putn(s)); - return(s); + return s; } /* @@ -2827,74 +3045,77 @@ register int s; * If getenv("PATH") were kept up-to-date, * execvp might be used. */ -static char * -rexecve(c, v, envp) -char *c, **v, **envp; +static const char *rexecve(char *c, char **v, char **envp) { - register int i; - register char *sp, *tp; - int eacces = 0, asis = 0; - -#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL + const char *sp; + char *tp; + int asis = 0; char *name = c; -#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN - name = bb_get_last_path_component(name); -#endif - optind = 1; - if (find_applet_by_name(name)) { - /* We have to exec here since we vforked. Running - * run_applet_by_name() won't work and bad things - * will happen. */ - execve("/proc/self/exe", v, envp); - execve("busybox", v, envp); + + if (ENABLE_FEATURE_SH_STANDALONE) { + if (find_applet_by_name(name) >= 0) { + /* We have to exec here since we vforked. Running + * run_applet_and_exit() won't work and bad things + * will happen. */ + execve(bb_busybox_exec_path, v, envp); + } } -#endif - sp = any('/', c)? "": path->value; - asis = *sp == '\0'; + DBGPRINTF(("REXECVE: c=%p, v=%p, envp=%p\n", c, v, envp)); + + sp = any('/', c) ? "" : path->value; + asis = (*sp == '\0'); while (asis || *sp != '\0') { asis = 0; - tp = e.linep; - for (; *sp != '\0'; tp++) - if ((*tp = *sp++) == ':') { - asis = *sp == '\0'; + tp = global_env.linep; + for (; *sp != '\0'; tp++) { + *tp = *sp++; + if (*tp == ':') { + asis = (*sp == '\0'); break; } - if (tp != e.linep) + } + if (tp != global_env.linep) *tp++ = '/'; - for (i = 0; (*tp++ = c[i++]) != '\0';) - ; + strcpy(tp, c); + + DBGPRINTF3(("REXECVE: global_env.linep is %s\n", global_env.linep)); + + execve(global_env.linep, v, envp); - execve(e.linep, v, envp); switch (errno) { case ENOEXEC: - *v = e.linep; - tp = *--v; - *v = e.linep; - execve(shellname, v, envp); + /* File is executable but file format isnt recognized */ + /* Run it as a shell script */ + /* (execve above didnt do it itself, unlike execvp) */ + *v = global_env.linep; + v--; + tp = *v; + *v = (char*)DEFAULT_SHELL; + execve(DEFAULT_SHELL, v, envp); *v = tp; - return("no Shell"); + return "no shell"; case ENOMEM: - return((char*)bb_msg_memory_exhausted); + return (char *) bb_msg_memory_exhausted; case E2BIG: - return("argument list too long"); - - case EACCES: - eacces++; - break; + return "argument list too long"; } } - return(errno==ENOENT ? "not found" : "cannot execute"); + if (errno == ENOENT) { + exstat = 127; /* standards require this */ + return "not found"; + } + exstat = 126; /* mimic bash */ + return "can't execute"; } /* * Run the command produced by generator `f' * applied to stream `arg'. */ -static int -run(struct ioarg *argp, int (*f)(struct ioarg *)) +static int run(struct ioarg *argp, int (*f) (struct ioarg *)) { struct op *otree; struct wdblock *swdlist; @@ -2908,28 +3129,38 @@ run(struct ioarg *argp, int (*f)(struct ioarg *)) (void) &rv; #endif + DBGPRINTF(("RUN: enter, areanum %d, outtree %p, failpt %p\n", + areanum, outtree, failpt)); + areanum++; swdlist = wdlist; siolist = iolist; otree = outtree; ofail = failpt; rv = -1; - if (newenv(setjmp(errpt = ev)) == 0) { - wdlist = 0; - iolist = 0; + + errpt = ev; + if (newenv(setjmp(errpt)) == 0) { + wdlist = NULL; + iolist = NULL; pushio(argp, f); - e.iobase = e.iop; + global_env.iobase = global_env.iop; yynerrs = 0; - if (setjmp(failpt = rt) == 0 && yyparse() == 0) - rv = execute(outtree, NOPIPE, NOPIPE, 0); + failpt = rt; + if (setjmp(failpt) == 0 && yyparse() == 0) + rv = execute(outtree, NOPIPE, NOPIPE, /* no_fork: */ 0); quitenv(); + } else { + DBGPRINTF(("RUN: error from newenv()!\n")); } + wdlist = swdlist; iolist = siolist; failpt = ofail; outtree = otree; freearea(areanum--); - return(rv); + + return rv; } /* -------- do.c -------- */ @@ -2938,287 +3169,307 @@ run(struct ioarg *argp, int (*f)(struct ioarg *)) * built-in commands: doX */ -static int dohelp(struct op *t ) +static int dohelp(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM) { int col; const struct builtincmd *x; - printf("\nBuilt-in commands:\n"); - printf("-------------------\n"); + printf("\n" + "Built-in commands:\n" + "------------------\n"); - for (col=0, x = builtincmds; x->builtinfunc != NULL; x++) { - if (!x->name) - continue; - col += printf("%s%s", ((col == 0) ? "\t" : " "), x->name); + col = 0; + x = builtincmds; + while (x->name) { + col += printf("%c%s", ((col == 0) ? '\t' : ' '), x->name); if (col > 60) { - printf("\n"); + bb_putchar('\n'); col = 0; } + x++; } -#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL +#if ENABLE_FEATURE_SH_STANDALONE { - int i; - const struct BB_applet *applet; - extern const struct BB_applet applets[]; - extern const size_t NUM_APPLETS; + const char *applet = applet_names; - for (i=0, applet = applets; i < NUM_APPLETS; applet++, i++) { - if (!applet->name) - continue; - - col += printf("%s%s", ((col == 0) ? "\t" : " "), - applet->name); + while (*applet) { + col += printf("%c%s", ((col == 0) ? '\t' : ' '), applet); if (col > 60) { - printf("\n"); + bb_putchar('\n'); col = 0; } + applet += strlen(applet) + 1; } } #endif - printf("\n\n"); + puts("\n"); return EXIT_SUCCESS; } - - -static int dolabel(struct op *t ) +static int dolabel(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM) { - return(0); + return 0; } -static int -dochdir(t) -register struct op *t; +static int dochdir(struct op *t UNUSED_PARAM, char **args) { - register char *cp, *er; + const char *cp, *er; - if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL) + cp = args[1]; + if (cp == NULL) { + cp = homedir->value; + if (cp != NULL) + goto do_cd; er = ": no home directory"; - else if(chdir(cp) < 0) + } else { + do_cd: + if (chdir(cp) >= 0) + return 0; er = ": bad directory"; - else - return(0); - prs(cp != NULL? cp: "cd"); + } + prs(cp != NULL ? cp : "cd"); err(er); - return(1); + return 1; } -static int -doshift(t) -register struct op *t; +static int doshift(struct op *t UNUSED_PARAM, char **args) { - register int n; + int n; - n = t->words[1]? getn(t->words[1]): 1; - if(dolc < n) { + n = args[1] ? getn(args[1]) : 1; + if (dolc < n) { err("nothing to shift"); - return(1); + return 1; } dolv[n] = dolv[0]; dolv += n; dolc -= n; setval(lookup("#"), putn(dolc)); - return(0); + return 0; } /* * execute login and newgrp directly */ -static int -dologin(t) -struct op *t; +static int dologin(struct op *t UNUSED_PARAM, char **args) { - register char *cp; + const char *cp; if (interactive) { signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); } - cp = rexecve(t->words[0], t->words, makenv()); - prs(t->words[0]); prs(": "); err(cp); - return(1); + cp = rexecve(args[0], args, makenv(0, NULL)); + prs(args[0]); + prs(": "); + err(cp); + return 1; } -static int -doumask(t) -register struct op *t; +static int doumask(struct op *t UNUSED_PARAM, char **args) { - register int i, n; - register char *cp; + int i; + char *cp; - if ((cp = t->words[1]) == NULL) { + cp = args[1]; + if (cp == NULL) { i = umask(0); umask(i); - for (n=3*4; (n-=3) >= 0;) - putc('0'+((i>>n)&07), stderr); - putc('\n', stderr); + printf("%04o\n", i); } else { - for (n=0; *cp>='0' && *cp<='9'; cp++) - n = n*8 + (*cp-'0'); - umask(n); + i = bb_strtou(cp, NULL, 8); + if (errno) { + err("umask: bad octal number"); + return 1; + } + umask(i); } - return(0); + return 0; } -static int -doexec(t) -register struct op *t; +static int doexec(struct op *t, char **args) { - register int i; jmp_buf ex; xint *ofail; + char **sv_words; t->ioact = NULL; - for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++) - ; - if (i == 0) - return(1); + if (!args[1]) + return 1; + execflg = 1; ofail = failpt; - if (setjmp(failpt = ex) == 0) - execute(t, NOPIPE, NOPIPE, FEXEC); + failpt = ex; + + sv_words = t->op_words; + t->op_words = args + 1; +// TODO: test what will happen with "exec break" - +// will it leave t->op_words pointing to garbage? +// (see http://bugs.busybox.net/view.php?id=846) + if (setjmp(failpt) == 0) + execute(t, NOPIPE, NOPIPE, /* no_fork: */ 1); + t->op_words = sv_words; + failpt = ofail; execflg = 0; - return(1); + + return 1; } -static int -dodot(t) -struct op *t; +static int dodot(struct op *t UNUSED_PARAM, char **args) { - register int i; - register char *sp, *tp; + int i; + const char *sp; + char *tp; char *cp; + int maltmp; + + DBGPRINTF(("DODOT: enter, t=%p, tleft %p, tright %p, global_env.linep is %s\n", + t, t->left, t->right, ((global_env.linep == NULL) ? "NULL" : global_env.linep))); + + cp = args[1]; + if (cp == NULL) { + DBGPRINTF(("DODOT: bad args, ret 0\n")); + return 0; + } + DBGPRINTF(("DODOT: cp is %s\n", cp)); + + sp = any('/', cp) ? ":" : path->value; + + DBGPRINTF(("DODOT: sp is %s, global_env.linep is %s\n", + ((sp == NULL) ? "NULL" : sp), + ((global_env.linep == NULL) ? "NULL" : global_env.linep))); - if ((cp = t->words[1]) == NULL) - return(0); - sp = any('/', cp)? ":": path->value; while (*sp) { - tp = e.linep; + tp = global_env.linep; while (*sp && (*tp = *sp++) != ':') tp++; - if (tp != e.linep) + if (tp != global_env.linep) *tp++ = '/'; - for (i = 0; (*tp++ = cp[i++]) != '\0';) - ; - if ((i = open(e.linep, 0)) >= 0) { + strcpy(tp, cp); + + /* Original code */ + i = open(global_env.linep, O_RDONLY); + if (i >= 0) { exstat = 0; - next(remap(i)); - return(exstat); + maltmp = remap(i); + DBGPRINTF(("DODOT: remap=%d, exstat=%d, global_env.iofd %d, i %d, global_env.linep is %s\n", + maltmp, exstat, global_env.iofd, i, global_env.linep)); + + next(maltmp); /* Basically a PUSHIO */ + + DBGPRINTF(("DODOT: returning exstat=%d\n", exstat)); + + return exstat; } - } + } /* while */ + prs(cp); err(": not found"); - return(-1); + + return -1; } -static int -dowait(t) -struct op *t; +static int dowait(struct op *t UNUSED_PARAM, char **args) { - register int i; - register char *cp; + int i; + char *cp; - if ((cp = t->words[1]) != NULL) { + cp = args[1]; + if (cp != NULL) { i = getn(cp); if (i == 0) - return(0); + return 0; } else i = -1; setstatus(waitfor(i, 1)); - return(0); + return 0; } -static int -doread(t) -struct op *t; +static int doread(struct op *t UNUSED_PARAM, char **args) { - register char *cp, **wp; - register int nb = 0; - register int nl = 0; + char *cp, **wp; + int nb = 0; + int nl = 0; - if (t->words[1] == NULL) { + if (args[1] == NULL) { err("Usage: read name ..."); - return(1); + return 1; } - for (wp = t->words+1; *wp; wp++) { - for (cp = e.linep; !nl && cp < elinep-1; cp++) - if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) || - (nl = (*cp == '\n')) || - (wp[1] && any(*cp, ifs->value))) + for (wp = args + 1; *wp; wp++) { + for (cp = global_env.linep; !nl && cp < elinep - 1; cp++) { + nb = nonblock_safe_read(STDIN_FILENO, cp, sizeof(*cp)); + if (nb != sizeof(*cp)) + break; + nl = (*cp == '\n'); + if (nl || (wp[1] && any(*cp, ifs->value))) break; - *cp = 0; + } + *cp = '\0'; if (nb <= 0) break; - setval(lookup(*wp), e.linep); + setval(lookup(*wp), global_env.linep); } - return(nb <= 0); + return nb <= 0; } -static int -doeval(t) -register struct op *t; +static int doeval(struct op *t UNUSED_PARAM, char **args) { - return(RUN(awordlist, t->words+1, wdchar)); + return RUN(awordlist, args + 1, wdchar); } -static int -dotrap(t) -register struct op *t; +static int dotrap(struct op *t UNUSED_PARAM, char **args) { - register int n, i; - register int resetsig; + int n, i; + int resetsig; - if (t->words[1] == NULL) { - for (i=0; i<=_NSIG; i++) + if (args[1] == NULL) { + for (i = 0; i <= _NSIG; i++) if (trap[i]) { prn(i); prs(": "); prs(trap[i]); prs("\n"); } - return(0); + return 0; } - resetsig = isdigit(*t->words[1]); - for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) { - n = getsig(t->words[i]); + resetsig = isdigit(args[1][0]); + for (i = resetsig ? 1 : 2; args[i] != NULL; ++i) { + n = getsig(args[i]); freecell(trap[n]); trap[n] = 0; if (!resetsig) { - if (*t->words[1] != '\0') { - trap[n] = strsave(t->words[1], 0); + if (args[1][0] != '\0') { + trap[n] = strsave(args[1], 0); setsig(n, sig); } 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 + setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL); + } else setsig(n, SIG_DFL); } } - return(0); + return 0; } -static int -getsig(s) -char *s; +static int getsig(char *s) { - register int n; + int n; - if ((n = getn(s)) < 0 || n > _NSIG) { + n = getn(s); + if (n < 0 || n > _NSIG) { err("trap: bad signal number"); n = 0; } - return(n); + return n; } -static void -setsig( register int n, sighandler_t f) +static void setsig(int n, sighandler_t f) { if (n == 0) return; @@ -3228,12 +3479,10 @@ setsig( register int n, sighandler_t f) } } -static int -getn(as) -char *as; +static int getn(char *as) { - register char *s; - register int n, m; + char *s; + int n, m; s = as; m = 1; @@ -3242,96 +3491,92 @@ char *as; s++; } for (n = 0; isdigit(*s); s++) - n = (n*10) + (*s-'0'); + n = (n * 10) + (*s - '0'); if (*s) { prs(as); err(": bad number"); } - return(n*m); + return n * m; } -static int -dobreak(t) -struct op *t; +static int dobreak(struct op *t UNUSED_PARAM, char **args) { - return(brkcontin(t->words[1], 1)); + return brkcontin(args[1], 1); } -static int -docontinue(t) -struct op *t; +static int docontinue(struct op *t UNUSED_PARAM, char **args) { - return(brkcontin(t->words[1], 0)); + return brkcontin(args[1], 0); } -static int -brkcontin(cp, val) -register char *cp; -int val; +static int brkcontin(char *cp, int val) { - register struct brkcon *bc; - register int nl; + struct brkcon *bc; + int nl; - nl = cp == NULL? 1: getn(cp); + nl = cp == NULL ? 1 : getn(cp); if (nl <= 0) nl = 999; do { - if ((bc = brklist) == NULL) + bc = brklist; + if (bc == NULL) break; brklist = bc->nextlev; } while (--nl); if (nl) { err("bad break/continue level"); - return(1); + return 1; } - isbreak = val; + isbreak = (val != 0); longjmp(bc->brkpt, 1); /* NOTREACHED */ } -static int -doexit(t) -struct op *t; +static int doexit(struct op *t UNUSED_PARAM, char **args) { - register char *cp; + char *cp; execflg = 0; - if ((cp = t->words[1]) != NULL) + cp = args[1]; + if (cp != NULL) setstatus(getn(cp)); + + DBGPRINTF(("DOEXIT: calling leave(), t=%p\n", t)); + leave(); /* NOTREACHED */ - return(0); + return 0; } -static int -doexport(t) -struct op *t; +static int doexport(struct op *t UNUSED_PARAM, char **args) { - rdexp(t->words+1, export, EXPORT); - return(0); + rdexp(args + 1, export, EXPORT); + return 0; } -static int -doreadonly(t) -struct op *t; +static int doreadonly(struct op *t UNUSED_PARAM, char **args) { - rdexp(t->words+1, ronly, RONLY); - return(0); + rdexp(args + 1, ronly, RONLY); + return 0; } -static void rdexp (char **wp, void (*f)(struct var *), int key) +static void rdexp(char **wp, void (*f) (struct var *), int key) { + DBGPRINTF6(("RDEXP: enter, wp=%p, func=%p, key=%d\n", wp, f, key)); + DBGPRINTF6(("RDEXP: *wp=%s\n", *wp)); + if (*wp != NULL) { for (; *wp != NULL; wp++) { if (isassign(*wp)) { char *cp; + assign(*wp, COPYV); for (cp = *wp; *cp != '='; cp++) - ; + continue; *cp = '\0'; } if (checkname(*wp)) - (*f)(lookup(*wp)); + (*f) (lookup(*wp)); else badid(*wp); } @@ -3339,68 +3584,62 @@ static void rdexp (char **wp, void (*f)(struct var *), int key) putvlist(key, 1); } -static void -badid(s) -register char *s; +static void badid(char *s) { prs(s); err(": bad identifier"); } -static int -doset(t) -register struct op *t; +static int doset(struct op *t UNUSED_PARAM, char **args) { - register struct var *vp; - register char *cp; - register int n; + struct var *vp; + char *cp; + int n; - if ((cp = t->words[1]) == NULL) { + cp = args[1]; + if (cp == NULL) { for (vp = vlist; vp; vp = vp->next) - varput(vp->name, 1); - return(0); + varput(vp->name, STDOUT_FILENO); + return 0; } if (*cp == '-') { - /* bad: t->words++; */ - for(n = 0; (t->words[n]=t->words[n+1]) != NULL; n++) - ; + args++; if (*++cp == 0) - flag['x'] = flag['v'] = 0; - else - for (; *cp; cp++) + FLAG['x'] = FLAG['v'] = 0; + else { + for (; *cp; cp++) { switch (*cp) { case 'e': if (!interactive) - flag['e']++; + FLAG['e']++; break; default: - if (*cp>='a' && *cp<='z') - flag[(int)*cp]++; + if (*cp >= 'a' && *cp <= 'z') + FLAG[(int) *cp]++; break; } + } + } setdash(); } - if (t->words[1]) { - t->words[0] = dolv[0]; - for (n=1; t->words[n]; n++) - setarea((char *)t->words[n], 0); - dolc = n-1; - dolv = t->words; + if (args[1]) { + args[0] = dolv[0]; + for (n = 1; args[n]; n++) + setarea((char *) args[n], 0); + dolc = n - 1; + dolv = args; setval(lookup("#"), putn(dolc)); - setarea((char *)(dolv-1), 0); + setarea((char *) (dolv - 1), 0); } - return(0); + return 0; } -static void -varput(s, out) -register char *s; -int out; +static void varput(char *s, int out) { if (isalnum(*s) || *s == '_') { - write(out, s, strlen(s)); - write(out, "\n", 1); + xwrite_str(out, s); + xwrite(out, "\n", 1); } } @@ -3409,36 +3648,44 @@ int out; * Copyright (c) 1999 Herbert Xu * This file contains code for the times builtin. */ -static int dotimes(struct op *t ) +static void times_fmt(char *buf, clock_t val, unsigned clk_tck) { - struct tms buf; - long int clk_tck = sysconf(_SC_CLK_TCK); - - times(&buf); - printf("%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); - return 0; + unsigned min, sec; + if (sizeof(val) > sizeof(int)) + sec = ((unsigned long)val) / clk_tck; + else + sec = ((unsigned)val) / clk_tck; + min = sec / 60; +#if ENABLE_DESKTOP + sprintf(buf, "%um%u.%03us", min, (sec - min * 60), + /* msec: */ ((unsigned)(val - (clock_t)sec * clk_tck)) * 1000 / clk_tck + ); +#else + sprintf(buf, "%um%us", min, (sec - min * 60)); +#endif } - -static int(*inbuilt(char *s))(struct op *) +static int dotimes(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM) { - const struct builtincmd *bp; + struct tms buf; + unsigned clk_tck = sysconf(_SC_CLK_TCK); + /* How much do we need for "NmN.NNNs" ? */ + enum { TIMEBUF_SIZE = sizeof(int)*3 + sizeof(int)*3 + 6 }; + char u[TIMEBUF_SIZE], s[TIMEBUF_SIZE]; + char cu[TIMEBUF_SIZE], cs[TIMEBUF_SIZE]; - for (bp = builtincmds; bp->name != NULL; bp++) - if (strcmp(bp->name, s) == 0) - return(bp->builtinfunc); + times(&buf); + + times_fmt(u, buf.tms_utime, clk_tck); + times_fmt(s, buf.tms_stime, clk_tck); + times_fmt(cu, buf.tms_cutime, clk_tck); + times_fmt(cs, buf.tms_cstime, clk_tck); - return(NULL); + printf("%s %s\n%s %s\n", u, s, cu, cs); + return 0; } + /* -------- eval.c -------- */ /* @@ -3449,7 +3696,7 @@ static int(*inbuilt(char *s))(struct op *) * glob */ -static char ** eval( char **ap, int f) +static char **eval(char **ap, int f) { struct wdblock *wb; char **wp; @@ -3461,129 +3708,146 @@ static char ** eval( char **ap, int f) (void) ℘ (void) ≈ #endif + + DBGPRINTF4(("EVAL: enter, f=%d\n", f)); + wp = NULL; wb = NULL; wf = NULL; - if (newenv(setjmp(errpt = ev)) == 0) { + errpt = ev; + if (newenv(setjmp(errpt)) == 0) { while (*ap && isassign(*ap)) expand(*ap++, &wb, f & ~DOGLOB); - if (flag['k']) { + if (FLAG['k']) { for (wf = ap; *wf; wf++) { if (isassign(*wf)) expand(*wf, &wb, f & ~DOGLOB); } } - for (wb = addword((char *)0, wb); *ap; ap++) { - if (!flag['k'] || !isassign(*ap)) + for (wb = addword((char *) NULL, wb); *ap; ap++) { + if (!FLAG['k'] || !isassign(*ap)) expand(*ap, &wb, f & ~DOKEY); } - wb = addword((char *)0, wb); + wb = addword((char *) 0, wb); wp = getwords(wb); quitenv(); } else gflg = 1; - return(gflg? (char **)NULL: wp); + + return gflg ? (char **) NULL : wp; } + /* * Make the exported environment from the exported * names in the dictionary. Keyword assignments * will already have been done. */ -static char ** -makenv() - +static char **makenv(int all, struct wdblock *wb) { - register struct wdblock *wb; - register struct var *vp; + struct var *vp; + + DBGPRINTF5(("MAKENV: enter, all=%d\n", all)); - wb = NULL; for (vp = vlist; vp; vp = vp->next) - if (vp->status & EXPORT) + if (all || vp->status & EXPORT) wb = addword(vp->name, wb); - wb = addword((char *)0, wb); - return(getwords(wb)); -} - -static char * -evalstr(cp, f) -register char *cp; -int f; -{ - struct wdblock *wb; - - wb = NULL; - if (expand(cp, &wb, f)) { - if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL) - cp = ""; - DELETE(wb); - } else - cp = NULL; - return(cp); + wb = addword((char *) 0, wb); + return getwords(wb); } -static int -expand( char *cp, register struct wdblock **wbp, int f) +static int expand(const char *cp, struct wdblock **wbp, int f) { jmp_buf ev; + char *xp; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &cp; #endif + + DBGPRINTF3(("EXPAND: enter, f=%d\n", f)); + gflg = 0; + if (cp == NULL) - return(0); - if (!anys("$`'\"", cp) && - !anys(ifs->value, cp) && - ((f&DOGLOB)==0 || !anys("[*?", cp))) { - cp = strsave(cp, areanum); + return 0; + + if (!anys("$`'\"", cp) && !anys(ifs->value, cp) + && ((f & DOGLOB) == 0 || !anys("[*?", cp)) + ) { + xp = strsave(cp, areanum); if (f & DOTRIM) - unquote(cp); - *wbp = addword(cp, *wbp); - return(1); + unquote(xp); + *wbp = addword(xp, *wbp); + return 1; } - if (newenv(setjmp(errpt = ev)) == 0) { + errpt = ev; + if (newenv(setjmp(errpt)) == 0) { PUSHIO(aword, cp, strchar); - e.iobase = e.iop; - while ((cp = blank(f)) && gflg == 0) { - e.linep = cp; - cp = strsave(cp, areanum); - if ((f&DOGLOB) == 0) { + global_env.iobase = global_env.iop; + while ((xp = blank(f)) && gflg == 0) { + global_env.linep = xp; + xp = strsave(xp, areanum); + if ((f & DOGLOB) == 0) { if (f & DOTRIM) - unquote(cp); - *wbp = addword(cp, *wbp); + unquote(xp); + *wbp = addword(xp, *wbp); } else - *wbp = glob(cp, *wbp); + *wbp = glob(xp, *wbp); } quitenv(); } else gflg = 1; - return(gflg == 0); + return gflg == 0; +} + +static char *evalstr(char *cp, int f) +{ + struct wdblock *wb; + + DBGPRINTF6(("EVALSTR: enter, cp=%p, f=%d\n", cp, f)); + + wb = NULL; + if (expand(cp, &wb, f)) { + if (wb == NULL || wb->w_nword == 0 + || (cp = wb->w_words[0]) == NULL + ) { +// TODO: I suspect that +// char *evalstr(char *cp, int f) is actually +// const char *evalstr(const char *cp, int f)! + cp = (char*)""; + } + DELETE(wb); + } else + cp = NULL; + return cp; } + /* * Blank interpretation and quoting */ -static char * -blank(f) -int f; +static char *blank(int f) { - register int c, c1; - register char *sp; + int c, c1; + char *sp; int scanequals, foundequals; - sp = e.linep; + DBGPRINTF3(("BLANK: enter, f=%d\n", f)); + + sp = global_env.linep; scanequals = f & DOKEY; foundequals = 0; -loop: - switch (c = subgetc('"', foundequals)) { + loop: + c = subgetc('"', foundequals); + switch (c) { case 0: - if (sp == e.linep) - return(0); - *e.linep++ = 0; - return(sp); + if (sp == global_env.linep) + return 0; + *global_env.linep++ = 0; + return sp; default: if (f & DOBLANK && any(c, ifs->value)) @@ -3600,7 +3864,7 @@ loop: break; if (c == '\'' || !any(c, "$`\"")) c |= QUOTE; - *e.linep++ = c; + *global_env.linep++ = c; } c = 0; } @@ -3610,9 +3874,9 @@ loop: for (;;) { c = subgetc('"', foundequals); if (c == 0 || - f & (DOBLANK && any(c, ifs->value)) || - (!INSUB() && any(c, "\"'"))) { - scanequals = 0; + f & (DOBLANK && any(c, ifs->value)) || + (!INSUB() && any(c, "\"'"))) { + scanequals = 0; unget(c); if (any(c, "\"'")) goto loop; @@ -3621,91 +3885,93 @@ loop: if (scanequals) { if (c == '=') { foundequals = 1; - scanequals = 0; - } - else if (!isalnum(c) && c != '_') + scanequals = 0; + } else if (!isalnum(c) && c != '_') scanequals = 0; } - *e.linep++ = c; + *global_env.linep++ = c; } - *e.linep++ = 0; - return(sp); + *global_env.linep++ = 0; + return sp; } /* * Get characters, substituting for ` and $ */ -static int -subgetc(ec, quoted) -register char ec; -int quoted; +static int subgetc(char ec, int quoted) { - register char c; + char c; + + DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted)); -again: + again: c = my_getc(ec); if (!INSUB() && ec != '\'') { if (c == '`') { if (grave(quoted) == 0) - return(0); - e.iop->task = XGRAVE; + return 0; + global_env.iop->task = XGRAVE; goto again; } - if (c == '$' && (c = dollar(quoted)) == 0) { - e.iop->task = XDOLL; - goto again; + if (c == '$') { + c = dollar(quoted); + if (c == 0) { + global_env.iop->task = XDOLL; + goto again; + } } } - return(c); + return c; } /* * Prepare to generate the string returned by ${} substitution. */ -static int -dollar(quoted) -int quoted; +static int dollar(int quoted) { int otask; struct io *oiop; char *dolp; - register char *s, c, *cp=NULL; + char *s, c, *cp = NULL; struct var *vp; + DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted)); + c = readc(); - s = e.linep; + s = global_env.linep; if (c != '{') { - *e.linep++ = c; + *global_env.linep++ = c; if (isalpha(c) || c == '_') { - while ((c = readc())!=0 && (isalnum(c) || c == '_')) - if (e.linep < elinep) - *e.linep++ = c; + while ((c = readc()) != 0 && (isalnum(c) || c == '_')) + if (global_env.linep < elinep) + *global_env.linep++ = c; unget(c); } c = 0; } else { - oiop = e.iop; - otask = e.iop->task; - e.iop->task = XOTHER; - while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n') - if (e.linep < elinep) - *e.linep++ = c; - if (oiop == e.iop) - e.iop->task = otask; + oiop = global_env.iop; + otask = global_env.iop->task; + + global_env.iop->task = XOTHER; + while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n') + if (global_env.linep < elinep) + *global_env.linep++ = c; + if (oiop == global_env.iop) + global_env.iop->task = otask; if (c != '}') { err("unclosed ${"); - gflg++; - return(c); + gflg = 1; + return c; } } - if (e.linep >= elinep) { + if (global_env.linep >= elinep) { err("string in ${} too long"); - gflg++; - e.linep -= 10; + gflg = 1; + global_env.linep -= 10; } - *e.linep = 0; + *global_env.linep = 0; if (*s) - for (cp = s+1; *cp; cp++) + for (cp = s + 1; *cp; cp++) if (any(*cp, "=-+?")) { c = *cp; *cp++ = 0; @@ -3715,21 +3981,22 @@ int quoted; if (dolc > 1) { /* currently this does not distinguish $* and $@ */ /* should check dollar */ - e.linep = s; - PUSHIO(awordlist, dolv+1, dolchar); - return(0); - } else { /* trap the nasty ${=} */ + global_env.linep = s; + PUSHIO(awordlist, dolv + 1, dolchar); + return 0; + } else { /* trap the nasty ${=} */ s[0] = '1'; - s[1] = 0; + s[1] = '\0'; } } vp = lookup(s); - if ((dolp = vp->value) == null) { + dolp = vp->value; + if (dolp == null) { switch (c) { case '=': if (isdigit(*s)) { - err("cannot use ${...=...} with $n"); - gflg++; + err("can't use ${...=...} with $n"); + gflg = 1; break; } setval(vp, cp); @@ -3746,54 +4013,55 @@ int quoted; err(s); } else err(cp); - gflg++; + gflg = 1; break; } } else if (c == '+') dolp = strsave(cp, areanum); - if (flag['u'] && dolp == null) { + if (FLAG['u'] && dolp == null) { prs("unset variable: "); err(s); - gflg++; + gflg = 1; } - e.linep = s; + global_env.linep = s; PUSHIO(aword, dolp, quoted ? qstrchar : strchar); - return(0); + return 0; } /* * Run the command in `...` and read its output. */ -static int -grave(quoted) -int quoted; +static int grave(int quoted) { - char *cp; - register int i; + /* moved to G: static char child_cmd[LINELIM]; */ + + const char *cp; + int i; int j; int pf[2]; - static char child_cmd[LINELIM]; - char *src; + const char *src; char *dest; int count; int ignore; int ignore_once; char *argument_list[4]; + struct wdblock *wb = NULL; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &cp; #endif - - for (cp = e.iop->argp->aword; *cp != '`'; cp++) + + for (cp = global_env.iop->argp->aword; *cp != '`'; cp++) { if (*cp == 0) { err("no closing `"); - return(0); + return 0; } + } /* string copy with dollar expansion */ - src = e.iop->argp->aword; + src = global_env.iop->argp->aword; dest = child_cmd; count = 0; ignore = 0; @@ -3805,8 +4073,12 @@ int quoted; ignore_once = 1; if (*src == '$' && !ignore && !ignore_once) { struct var *vp; + /* moved to G to reduce stack usage char var_name[LINELIM]; char alt_value[LINELIM]; + */ +#define var_name (G.grave__var_name) +#define alt_value (G.grave__alt_value) int var_index = 0; int alt_index = 0; char operator = 0; @@ -3820,7 +4092,7 @@ int quoted; } var_name[var_index++] = *src++; - while (isalnum(*src)) + while (isalnum(*src) || *src=='_') var_name[var_index++] = *src++; var_name[var_index] = 0; @@ -3832,13 +4104,13 @@ int quoted; case '=': case '+': case '?': - operator = *src; + operator = * src; break; default: err("unclosed ${\n"); - return(0); + return 0; } - if (operator) { + if (operator) { src++; while (*src && (*src != '}')) { alt_value[alt_index++] = *src++; @@ -3846,29 +4118,56 @@ int quoted; alt_value[alt_index] = 0; if (*src != '}') { err("unclosed ${\n"); - return(0); + return 0; } } src++; } - vp = lookup(var_name); - if (vp->value != null) - value = (operator == '+')? alt_value : vp->value; - else if (operator == '?') { - err(alt_value); - return(0); - } else if (alt_index && (operator != '+')) { - value = alt_value; - if (operator == '=') - setval(vp, value); - } else - continue; + if (isalpha(*var_name)) { + /* let subshell handle it instead */ + + char *namep = var_name; + + *dest++ = '$'; + if (braces) + *dest++ = '{'; + while (*namep) + *dest++ = *namep++; + if (operator) { + char *altp = alt_value; + *dest++ = operator; + while (*altp) + *dest++ = *altp++; + } + if (braces) + *dest++ = '}'; - while (*value && (count < LINELIM)) { - *dest++ = *value++; - count++; + wb = addword(lookup(var_name)->name, wb); + } else { + /* expand */ + + vp = lookup(var_name); + if (vp->value != null) + value = (operator == '+') ? + alt_value : vp->value; + else if (operator == '?') { + err(alt_value); + return 0; + } else if (alt_index && (operator != '+')) { + value = alt_value; + if (operator == '=') + setval(vp, value); + } else + continue; + + while (*value && (count < LINELIM)) { + *dest++ = *value++; + count++; + } } +#undef var_name +#undef alt_value } else { *dest++ = *src++; count++; @@ -3876,52 +4175,65 @@ int quoted; } } *dest = '\0'; - + if (openpipe(pf) < 0) - return(0); + return 0; + while ((i = vfork()) == -1 && errno == EAGAIN) - ; + continue; + + DBGPRINTF3(("GRAVE: i is %p\n", io)); + if (i < 0) { closepipe(pf); - err((char*)bb_msg_memory_exhausted); - return(0); + err((char *) bb_msg_memory_exhausted); + return 0; } if (i != 0) { - waitpid(i, NULL, 0); - e.iop->argp->aword = ++cp; + waitpid(i, NULL, 0); // safe_waitpid? + global_env.iop->argp->aword = ++cp; close(pf[1]); - PUSHIO(afile, remap(pf[0]), (int(*)(struct ioarg *))((quoted)? qgravechar: gravechar)); - return(1); + PUSHIO(afile, remap(pf[0]), + (int (*)(struct ioarg *)) ((quoted) ? qgravechar : gravechar)); + return 1; } /* allow trapped signals */ /* XXX - Maybe this signal stuff should go as well? */ - for (j=0; j<=_NSIG; j++) + for (j = 0; j <= _NSIG; j++) if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN) signal(j, SIG_DFL); - - dup2(pf[1], 1); - closepipe(pf); - argument_list[0] = shellname; - argument_list[1] = "-c"; + /* Testcase where below checks are needed: + * close stdout & run this script: + * files=`ls` + * echo "$files" >zz + */ + xmove_fd(pf[1], 1); + if (pf[0] != 1) + close(pf[0]); + + argument_list[0] = (char *) DEFAULT_SHELL; + argument_list[1] = (char *) "-c"; argument_list[2] = child_cmd; - argument_list[3] = 0; + argument_list[3] = NULL; - prs(rexecve(argument_list[0], argument_list, makenv())); - _exit(1); + cp = rexecve(argument_list[0], argument_list, makenv(1, wb)); + prs(argument_list[0]); + prs(": "); + err(cp); + _exit(EXIT_FAILURE); } -static char * -unquote(as) -register char *as; +static char *unquote(char *as) { - register char *s; + char *s; - if ((s = as) != NULL) + s = as; + if (s != NULL) while (*s) *s++ &= ~QUOTE; - return(as); + return as; } /* -------- glob.c -------- */ @@ -3934,19 +4246,16 @@ register char *as; #define BLKSIZ 512 #define NDENT ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent)) -static struct wdblock *cl, *nl; -static char spcl[] = "[?*"; +static struct wdblock *cl, *nl; +static const char spcl[] ALIGN1= "[?*"; -static struct wdblock * -glob(cp, wb) -char *cp; -struct wdblock *wb; +static struct wdblock *glob(char *cp, struct wdblock *wb) { - register int i; - register char *pp; + int i; + char *pp; if (cp == 0) - return(wb); + return wb; i = 0; for (pp = cp; *pp; pp++) if (any(*pp, spcl)) @@ -3954,9 +4263,9 @@ struct wdblock *wb; else if (!any(*pp & ~QUOTE, spcl)) *pp &= ~QUOTE; if (i != 0) { - for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) { - nl = newword(cl->w_nword*2); - for(i=0; iw_nword; i++) { /* for each argument */ + for (cl = addword(scopy(cp), NULL); anyspcl(cl); cl = nl) { + nl = newword(cl->w_nword * 2); + for (i = 0; i < cl->w_nword; i++) { /* for each argument */ for (pp = cl->w_words[i]; *pp; pp++) if (any(*pp, spcl)) { globname(cl->w_words[i], pp); @@ -3965,45 +4274,44 @@ struct wdblock *wb; if (*pp == '\0') nl = addword(scopy(cl->w_words[i]), nl); } - for(i=0; iw_nword; i++) + for (i = 0; i < cl->w_nword; i++) DELETE(cl->w_words[i]); DELETE(cl); } - for(i=0; iw_nword; i++) - unquote(cl->w_words[i]); - glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp); if (cl->w_nword) { - for (i=0; iw_nword; i++) + for (i = 0; i < cl->w_nword; i++) + unquote(cl->w_words[i]); + qsort_string_vector(cl->w_words, cl->w_nword); + for (i = 0; i < cl->w_nword; i++) wb = addword(cl->w_words[i], wb); DELETE(cl); - return(wb); + return wb; } } wb = addword(unquote(cp), wb); - return(wb); + return wb; } -static void -globname(we, pp) -char *we; -register char *pp; +static void globname(char *we, char *pp) { - register char *np, *cp; + char *np, *cp; char *name, *gp, *dp; int k; DIR *dirp; struct dirent *de; - char dname[NAME_MAX+1]; + char dname[NAME_MAX + 1]; struct stat dbuf; for (np = we; np != pp; pp--) if (pp[-1] == '/') break; - for (dp = cp = space((int)(pp-np)+3); np < pp;) + dp = cp = get_space((int) (pp - np) + 3); + while (np < pp) *cp++ = *np++; *cp++ = '.'; *cp = '\0'; - for (gp = cp = space(strlen(pp)+1); *np && *np != '/';) + gp = cp = get_space(strlen(pp) + 1); + while (*np && *np != '/') *cp++ = *np++; *cp = '\0'; dirp = opendir(dp); @@ -4013,29 +4321,27 @@ register char *pp; return; } dname[NAME_MAX] = '\0'; - while ((de=readdir(dirp))!=NULL) { + while ((de = readdir(dirp)) != NULL) { /* XXX Hmmm... What this could be? (abial) */ - /* - if (ent[j].d_ino == 0) - continue; - */ + /* if (ent[j].d_ino == 0) continue; + */ strncpy(dname, de->d_name, NAME_MAX); - if (dname[0] == '.') - if (*gp != '.') + if (dname[0] == '.') + if (*gp != '.') + continue; + for (k = 0; k < NAME_MAX; k++) + if (any(dname[k], spcl)) + dname[k] |= QUOTE; + if (gmatch(dname, gp)) { + name = generate(we, pp, dname, np); + if (*np && !anys(np, spcl)) { + if (stat(name, &dbuf)) { + DELETE(name); continue; - for(k=0; kw_words; - for (i=0; iw_nword; i++) + for (i = 0; i < wb->w_nword; i++) if (anys(spcl, *wd++)) - return(1); - return(0); + return 1; + return 0; } -static int -xstrcmp(p1, p2) -char *p1, *p2; -{ - return(strcmp(*(char **)p1, *(char **)p2)); -} /* -------- word.c -------- */ -static struct wdblock * -newword(nw) -register int nw; +static struct wdblock *newword(int nw) { - register struct wdblock *wb; + struct wdblock *wb; - wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *)); + wb = get_space(sizeof(*wb) + nw * sizeof(char *)); wb->w_bsize = nw; wb->w_nword = 0; - return(wb); + return wb; } -static struct wdblock * -addword(wd, wb) -char *wd; -register struct wdblock *wb; +static struct wdblock *addword(char *wd, struct wdblock *wb) { - register struct wdblock *wb2; - register int nw; + struct wdblock *wb2; + int nw; if (wb == NULL) wb = newword(NSTART); - if ((nw = wb->w_nword) >= wb->w_bsize) { + nw = wb->w_nword; + if (nw >= wb->w_bsize) { wb2 = newword(nw * 2); - memcpy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *)); + memcpy((char *) wb2->w_words, (char *) wb->w_words, + nw * sizeof(char *)); wb2->w_nword = nw; DELETE(wb); wb = wb2; } wb->w_words[wb->w_nword++] = wd; - return(wb); + return wb; } -static -char ** -getwords(wb) -register struct wdblock *wb; + +static char **getwords(struct wdblock *wb) { - register char **wd; - register int nb; + char **wd; + int nb; if (wb == NULL) - return((char **)NULL); + return NULL; if (wb->w_nword == 0) { DELETE(wb); - return((char **)NULL); - } - wd = (char **) space(nb = sizeof(*wd) * wb->w_nword); - memcpy((char *)wd, (char *)wb->w_words, nb); - DELETE(wb); /* perhaps should done by caller */ - return(wd); -} - -int (*func)(char *, char *); -int globv; - -static void -glob0(a0, a1, a2, a3) -char *a0; -unsigned a1; -int a2; -int (*a3) (char *, char *); -{ - func = a3; - globv = a2; - glob1(a0, a0 + a1 * a2); -} - -static void -glob1(base, lim) -char *base, *lim; -{ - register char *i, *j; - int v2; - char *lptr, *hptr; - int c; - unsigned n; - - - v2 = globv; - -top: - if ((n=(int)(lim-base)) <= v2) - return; - n = v2 * (n / (2*v2)); - hptr = lptr = base+n; - i = base; - j = lim-v2; - for(;;) { - if (i < lptr) { - if ((c = (*func)(i, lptr)) == 0) { - glob2(i, lptr -= v2); - continue; - } - if (c < 0) { - i += v2; - continue; - } - } - -begin: - if (j > hptr) { - if ((c = (*func)(hptr, j)) == 0) { - glob2(hptr += v2, j); - goto begin; - } - if (c > 0) { - if (i == lptr) { - glob3(i, hptr += v2, j); - i = lptr += v2; - goto begin; - } - glob2(i, j); - j -= v2; - i += v2; - continue; - } - j -= v2; - goto begin; - } - - - if (i == lptr) { - if (lptr-base >= lim-hptr) { - glob1(hptr+v2, lim); - lim = lptr; - } else { - glob1(base, lptr); - base = hptr+v2; - } - goto top; - } - - - glob3(j, lptr -= v2, i); - j = hptr -= v2; + return NULL; } + nb = sizeof(*wd) * wb->w_nword; + wd = get_space(nb); + memcpy(wd, wb->w_words, nb); + DELETE(wb); /* perhaps should done by caller */ + return wd; } -static void -glob2(i, j) -char *i, *j; -{ - register char *index1, *index2, c; - int m; - - m = globv; - index1 = i; - index2 = j; - do { - c = *index1; - *index1++ = *index2; - *index2++ = c; - } while(--m); -} - -static void -glob3(i, j, k) -char *i, *j, *k; -{ - register char *index1, *index2, *index3; - int c; - int m; - - m = globv; - index1 = i; - index2 = j; - index3 = k; - do { - c = *index1; - *index1++ = *index3; - *index3++ = *index2; - *index2++ = c; - } while(--m); -} /* -------- io.c -------- */ @@ -4271,146 +4439,174 @@ char *i, *j, *k; * shell IO */ -static int my_getc( int ec) +static int my_getc(int ec) { - register int c; + int c; - if(e.linep > elinep) { - while((c=readc()) != '\n' && c) - ; + if (global_env.linep > elinep) { + while ((c = readc()) != '\n' && c) + continue; err("input line too long"); - gflg++; - return(c); + gflg = 1; + return c; } c = readc(); - if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) { - if(c == '\\') { + if ((ec != '\'') && (ec != '`') && (global_env.iop->task != XGRAVE)) { + if (c == '\\') { c = readc(); if (c == '\n' && ec != '\"') - return(my_getc(ec)); + return my_getc(ec); c |= QUOTE; } } - return(c); + return c; } -static void -unget(c) -int c; +static void unget(int c) { - if (e.iop >= e.iobase) - e.iop->peekc = c; + if (global_env.iop >= global_env.iobase) + global_env.iop->peekc = c; } -static int -eofc() - +static int eofc(void) { - return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0); + return global_env.iop < global_env.iobase || (global_env.iop->peekc == 0 && global_env.iop->prev == 0); } -static int -readc() +static int readc(void) { - register int c; + int c; + + RCPRINTF(("READC: global_env.iop %p, global_env.iobase %p\n", global_env.iop, global_env.iobase)); - for (; e.iop >= e.iobase; e.iop--) - if ((c = e.iop->peekc) != '\0') { - e.iop->peekc = 0; - return(c); + for (; global_env.iop >= global_env.iobase; global_env.iop--) { + RCPRINTF(("READC: global_env.iop %p, peekc 0x%x\n", global_env.iop, global_env.iop->peekc)); + c = global_env.iop->peekc; + if (c != '\0') { + global_env.iop->peekc = 0; + return c; } - else { - if (e.iop->prev != 0) { - if ((c = (*e.iop->iofn)(e.iop->argp, e.iop)) != '\0') { - if (c == -1) { - e.iop++; - continue; - } - if (e.iop == iostack) - ioecho(c); - return(e.iop->prev = c); - } - else if (e.iop->task == XIO && e.iop->prev != '\n') { - e.iop->prev = 0; - if (e.iop == iostack) + if (global_env.iop->prev != 0) { + c = (*global_env.iop->iofn)(global_env.iop->argp, global_env.iop); + if (c != '\0') { + if (c == -1) { + global_env.iop++; + continue; + } + if (global_env.iop == iostack) + ioecho(c); + global_env.iop->prev = c; + return c; + } + if (global_env.iop->task == XIO && global_env.iop->prev != '\n') { + global_env.iop->prev = 0; + if (global_env.iop == iostack) ioecho('\n'); - return '\n'; - } - } - if (e.iop->task == XIO) { - if (multiline) - return e.iop->prev = 0; - if (interactive && e.iop == iostack+1) { -#ifdef CONFIG_FEATURE_COMMAND_EDITING - current_prompt=prompt->value; + return '\n'; + } + } + if (global_env.iop->task == XIO) { + if (multiline) { + global_env.iop->prev = 0; + return 0; + } + if (interactive && global_env.iop == iostack + 1) { +#if ENABLE_FEATURE_EDITING + current_prompt = prompt->value; #else - prs(prompt->value); + prs(prompt->value); #endif } - } } - if (e.iop >= iostack) - return(0); + } /* FOR */ + + if (global_env.iop >= iostack) { + RCPRINTF(("READC: return 0, global_env.iop %p\n", global_env.iop)); + return 0; + } + + DBGPRINTF(("READC: leave()...\n")); leave(); /* NOTREACHED */ - return(0); + return 0; } -static void -ioecho(c) -char c; +static void ioecho(char c) { - if (flag['v']) - write(2, &c, sizeof c); + if (FLAG['v']) + write(STDERR_FILENO, &c, sizeof c); } -static void -pushio(struct ioarg *argp, int (*fn)(struct ioarg *)) +static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *)) { - if (++e.iop >= &iostack[NPUSH]) { - e.iop--; + DBGPRINTF(("PUSHIO: argp %p, argp->afid 0x%x, global_env.iop %p\n", argp, + argp->afid, global_env.iop)); + + /* Set env ptr for io source to next array spot and check for array overflow */ + if (++global_env.iop >= &iostack[NPUSH]) { + global_env.iop--; err("Shell input nested too deeply"); - gflg++; + gflg = 1; return; } - e.iop->iofn = (int (*)(struct ioarg *, struct io *))fn; + + /* We did not overflow the NPUSH array spots so setup data structs */ + + global_env.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn; /* Store data source func ptr */ if (argp->afid != AFID_NOBUF) - e.iop->argp = argp; + global_env.iop->argp = argp; else { - e.iop->argp = ioargstack + (e.iop - iostack); - *e.iop->argp = *argp; - e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf; - if (isatty(e.iop->argp->afile) == 0 && - (e.iop == &iostack[0] || - lseek(e.iop->argp->afile, 0L, 1) != -1)) { - if (++bufid == AFID_NOBUF) - bufid = AFID_ID; - e.iop->argp->afid = bufid; - } - } - - e.iop->prev = ~'\n'; - e.iop->peekc = 0; - e.iop->xchar = 0; - e.iop->nlcount = 0; + + global_env.iop->argp = ioargstack + (global_env.iop - iostack); /* MAL - index into stack */ + *global_env.iop->argp = *argp; /* copy data from temp area into stack spot */ + + /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */ + + if (global_env.iop == &iostack[0]) + global_env.iop->argp->afbuf = &mainbuf; + else + global_env.iop->argp->afbuf = &sharedbuf; + + /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */ + /* This line appears to be active when running scripts from command line */ + if ((isatty(global_env.iop->argp->afile) == 0) + && (global_env.iop == &iostack[0] + || lseek(global_env.iop->argp->afile, 0L, SEEK_CUR) != -1)) { + if (++bufid == AFID_NOBUF) /* counter rollover check, AFID_NOBUF = 11111111 */ + bufid = AFID_ID; /* AFID_ID = 0 */ + + global_env.iop->argp->afid = bufid; /* assign buffer id */ + } + + DBGPRINTF(("PUSHIO: iostack %p, global_env.iop %p, afbuf %p\n", + iostack, global_env.iop, global_env.iop->argp->afbuf)); + DBGPRINTF(("PUSHIO: mbuf %p, sbuf %p, bid %d, global_env.iop %p\n", + &mainbuf, &sharedbuf, bufid, global_env.iop)); + + } + + global_env.iop->prev = ~'\n'; + global_env.iop->peekc = 0; + global_env.iop->xchar = 0; + global_env.iop->nlcount = 0; + if (fn == filechar || fn == linechar) - e.iop->task = XIO; - else if (fn == (int(*)(struct ioarg *))gravechar || fn == (int(*)(struct ioarg *))qgravechar) - e.iop->task = XGRAVE; + global_env.iop->task = XIO; + else if (fn == (int (*)(struct ioarg *)) gravechar + || fn == (int (*)(struct ioarg *)) qgravechar) + global_env.iop->task = XGRAVE; else - e.iop->task = XOTHER; + global_env.iop->task = XOTHER; } -static struct io * -setbase(ip) -struct io *ip; +static struct io *setbase(struct io *ip) { - register struct io *xp; + struct io *xp; - xp = e.iobase; - e.iobase = ip; - return(xp); + xp = global_env.iobase; + global_env.iobase = ip; + return xp; } /* @@ -4418,199 +4614,188 @@ struct io *ip; */ /* - * Produce the characters of a string, then a newline, then EOF. + * Produce the characters of a string, then a newline, then NUL. */ -static int -nlchar(ap) -register struct ioarg *ap; +static int nlchar(struct ioarg *ap) { - register int c; + char c; if (ap->aword == NULL) - return(0); - if ((c = *ap->aword++) == 0) { + return '\0'; + c = *ap->aword++; + if (c == '\0') { ap->aword = NULL; - return('\n'); + return '\n'; } - return(c); + return c; } /* * Given a list of words, produce the characters * in them, with a space after each word. */ -static int -wdchar(ap) -register struct ioarg *ap; +static int wdchar(struct ioarg *ap) { - register char c; - register char **wl; + char c; + char **wl; - if ((wl = ap->awordlist) == NULL) - return(0); + wl = ap->awordlist; + if (wl == NULL) + return 0; if (*wl != NULL) { - if ((c = *(*wl)++) != 0) - return(c & 0177); + c = *(*wl)++; + if (c != 0) + return c & 0177; ap->awordlist++; - return(' '); + return ' '; } ap->awordlist = NULL; - return('\n'); + return '\n'; } /* * Return the characters of a list of words, * producing a space between them. */ -static int -dolchar(ap) -register struct ioarg *ap; +static int dolchar(struct ioarg *ap) { - register char *wp; + char *wp; - if ((wp = *ap->awordlist++) != NULL) { - PUSHIO(aword, wp, *ap->awordlist == NULL? strchar: xxchar); - return(-1); + wp = *ap->awordlist++; + if (wp != NULL) { + PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar); + return -1; } - return(0); + return 0; } -static int -xxchar(ap) -register struct ioarg *ap; +static int xxchar(struct ioarg *ap) { - register int c; + int c; if (ap->aword == NULL) - return(0); - if ((c = *ap->aword++) == '\0') { + return 0; + c = *ap->aword++; + if (c == '\0') { ap->aword = NULL; - return(' '); + return ' '; } - return(c); + return c; } /* * Produce the characters from a single word (string). */ -static int -strchar(ap) -register struct ioarg *ap; +static int strchar(struct ioarg *ap) { - register int c; - - if (ap->aword == NULL || (c = *ap->aword++) == 0) - return(0); - return(c); + if (ap->aword == NULL) + return 0; + return *ap->aword++; } /* * Produce quoted characters from a single word (string). */ -static int -qstrchar(ap) -register struct ioarg *ap; +static int qstrchar(struct ioarg *ap) { - register int c; + int c; - if (ap->aword == NULL || (c = *ap->aword++) == 0) - return(0); - return(c|QUOTE); + if (ap->aword == NULL) + return 0; + c = *ap->aword++; + if (c) + c |= QUOTE; + return c; } /* * Return the characters from a file. */ -static int -filechar(ap) -register struct ioarg *ap; +static int filechar(struct ioarg *ap) { - register int i; + int i; char c; struct iobuf *bp = ap->afbuf; if (ap->afid != AFID_NOBUF) { - if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) { - if (i) - lseek(ap->afile, ap->afpos, 0); - i = safe_read(ap->afile, bp->buf, sizeof(bp->buf)); - if (i <= 0) { - closef(ap->afile); - return 0; - } - bp->id = ap->afid; - bp->ebufp = (bp->bufp = bp->buf) + i; - } - ap->afpos++; - return *bp->bufp++ & 0177; - } - -#ifdef CONFIG_FEATURE_COMMAND_EDITING + i = (ap->afid != bp->id); + if (i || bp->bufp == bp->ebufp) { + if (i) + lseek(ap->afile, ap->afpos, SEEK_SET); + + i = nonblock_safe_read(ap->afile, bp->buf, sizeof(bp->buf)); + if (i <= 0) { + closef(ap->afile); + return 0; + } + + bp->id = ap->afid; + bp->bufp = bp->buf; + bp->ebufp = bp->bufp + i; + } + + ap->afpos++; + return *bp->bufp++ & 0177; + } +#if ENABLE_FEATURE_EDITING if (interactive && isatty(ap->afile)) { - static char mycommand[BUFSIZ]; - static int position = 0, size = 0; - - while (size == 0 || position >= size) { - cmdedit_read_input(current_prompt, mycommand); - size = strlen(mycommand); - position = 0; - } - c = mycommand[position]; - position++; - return(c); - } else -#endif - { - i = safe_read(ap->afile, &c, sizeof(c)); - return(i == sizeof(c)? c&0177: (closef(ap->afile), 0)); + /* moved to G: static char filechar_cmdbuf[BUFSIZ]; */ + static int position = 0, size = 0; + + while (size == 0 || position >= size) { + size = read_line_input(current_prompt, filechar_cmdbuf, BUFSIZ, line_input_state); + if (size < 0) /* Error/EOF */ + exit(EXIT_SUCCESS); + position = 0; + /* if Ctrl-C, size == 0 and loop will repeat */ + } + c = filechar_cmdbuf[position]; + position++; + return c; } +#endif + i = nonblock_safe_read(ap->afile, &c, sizeof(c)); + return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0); } /* * Return the characters from a here temp file. */ -static int -herechar(ap) -register struct ioarg *ap; +static int herechar(struct ioarg *ap) { char c; - - if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) { + if (nonblock_safe_read(ap->afile, &c, sizeof(c)) != sizeof(c)) { close(ap->afile); - c = 0; + c = '\0'; } - return (c); - + return c; } /* * Return the characters produced by a process (`...`). * Quote them if required, and remove any trailing newline characters. */ -static int -gravechar(ap, iop) -struct ioarg *ap; -struct io *iop; +static int gravechar(struct ioarg *ap, struct io *iop) { - register int c; + int c; - if ((c = qgravechar(ap, iop)&~QUOTE) == '\n') + c = qgravechar(ap, iop) & ~QUOTE; + if (c == '\n') c = ' '; - return(c); + return c; } -static int -qgravechar(ap, iop) -register struct ioarg *ap; -struct io *iop; +static int qgravechar(struct ioarg *ap, struct io *iop) { - register int c; + int c; + + DBGPRINTF3(("QGRAVECHAR: enter, ap=%p, iop=%p\n", ap, iop)); if (iop->xchar) { if (iop->nlcount) { iop->nlcount--; - return('\n'|QUOTE); + return '\n' | QUOTE; } c = iop->xchar; iop->xchar = 0; @@ -4620,200 +4805,176 @@ struct io *iop; iop->nlcount++; iop->xchar = c; if (c == 0) - return(c); + return c; iop->nlcount--; c = '\n'; } - return(c!=0? c|QUOTE: 0); + return c != 0 ? c | QUOTE : 0; } /* * Return a single command (usually the first line) from a file. */ -static int -linechar(ap) -register struct ioarg *ap; +static int linechar(struct ioarg *ap) { - register int c; + int c; - if ((c = filechar(ap)) == '\n') { + c = filechar(ap); + if (c == '\n') { if (!multiline) { closef(ap->afile); - ap->afile = -1; /* illegal value */ + ap->afile = -1; /* illegal value */ } } - return(c); -} - -static void -prs(s) -register char *s; -{ - if (*s) - write(2, s, strlen(s)); -} - -static void -prn(u) -unsigned u; -{ - prs(itoa(u)); -} - -static void -closef(i) -register int i; -{ - if (i > 2) - close(i); -} - -static void -closeall() -{ - register int u; - - for (u=NUFILE; u= 0 && fd < e.iofd); - for (i=0; i= 0 && fd < global_env.iofd); + + for (i = 0; i < NOFILE; i++) if (map[i]) close(i); + if (fd < 0) err("too many files open in shell"); } - return(fd); + + return fd; } -static int -openpipe(pv) -register int *pv; +static int openpipe(int *pv) { - register int i; + int i; - if ((i = pipe(pv)) < 0) + i = pipe(pv); + if (i < 0) err("can't create pipe - try again"); - return(i); + return i; } -static void -closepipe(pv) -register int *pv; +static void closepipe(int *pv) { if (pv != NULL) { - close(*pv++); - close(*pv); + close(pv[0]); + close(pv[1]); } } + /* -------- here.c -------- */ /* * here documents */ -static void -markhere(s, iop) -register char *s; -struct ioword *iop; +static void markhere(char *s, struct ioword *iop) { - register struct here *h, *lh; + struct here *h, *lh; + + DBGPRINTF7(("MARKHERE: enter, s=%p\n", s)); - h = (struct here *) space(sizeof(struct here)); - if (h == 0) + h = get_space(sizeof(struct here)); + if (h == NULL) return; + h->h_tag = evalstr(s, DOSUB); if (h->h_tag == 0) return; + h->h_iop = iop; iop->io_name = 0; h->h_next = NULL; if (inhere == 0) inhere = h; - else - for (lh = inhere; lh!=NULL; lh = lh->h_next) + else { + for (lh = inhere; lh != NULL; lh = lh->h_next) { if (lh->h_next == 0) { lh->h_next = h; break; } - iop->io_flag |= IOHERE|IOXHERE; - for (s = h->h_tag; *s; s++) + } + } + iop->io_flag |= IOHERE | IOXHERE; + for (s = h->h_tag; *s; s++) { if (*s & QUOTE) { - iop->io_flag &= ~ IOXHERE; - *s &= ~ QUOTE; + iop->io_flag &= ~IOXHERE; + *s &= ~QUOTE; } - h->h_dosub = iop->io_flag & IOXHERE; + } + h->h_dosub = ((iop->io_flag & IOXHERE) ? '\0' : '\''); } -static void -gethere() +static void gethere(void) { - register struct here *h, *hp; + struct here *h, *hp; + + DBGPRINTF7(("GETHERE: enter...\n")); /* Scan here files first leaving inhere list in place */ for (hp = h = inhere; h != NULL; hp = h, h = h->h_next) - readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub? 0: '\''); + readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub /* NUL or ' */); /* Make inhere list active - keep list intact for scraphere */ if (hp != NULL) { - hp->h_next = acthere; - acthere = inhere; - inhere = NULL; + hp->h_next = acthere; + acthere = inhere; + inhere = NULL; } } -static void -readhere(name, s, ec) -char **name; -register char *s; -int ec; +static void readhere(char **name, char *s, int ec) { int tf; char tname[30] = ".msh_XXXXXX"; - register int c; + int c; jmp_buf ev; - char myline [LINELIM+1]; + char myline[LINELIM + 1]; char *thenext; + DBGPRINTF7(("READHERE: enter, name=%p, s=%p\n", name, s)); + tf = mkstemp(tname); if (tf < 0) return; + *name = strsave(tname, areanum); - if (newenv(setjmp(errpt = ev)) != 0) + errpt = ev; + if (newenv(setjmp(errpt)) != 0) unlink(tname); else { - pushio(e.iop->argp, (int(*)(struct ioarg *))e.iop->iofn); - e.iobase = e.iop; + pushio(global_env.iop->argp, (int (*)(struct ioarg *)) global_env.iop->iofn); + global_env.iobase = global_env.iop; for (;;) { - if (interactive && e.iop <= iostack) { -#ifdef CONFIG_FEATURE_COMMAND_EDITING - current_prompt=cprompt->value; + if (interactive && global_env.iop <= iostack) { +#if ENABLE_FEATURE_EDITING + current_prompt = cprompt->value; #else - prs(cprompt->value); + prs(cprompt->value); #endif } thenext = myline; while ((c = my_getc(ec)) != '\n' && c) { if (ec == '\'') - c &= ~ QUOTE; + c &= ~QUOTE; if (thenext >= &myline[LINELIM]) { c = 0; break; @@ -4824,10 +4985,12 @@ int ec; if (strcmp(s, myline) == 0 || c == 0) break; *thenext++ = '\n'; - write (tf, myline, (int)(thenext-myline)); + write(tf, myline, (int) (thenext - myline)); } if (c == 0) { - prs("here document `"); prs(s); err("' unclosed"); + prs("here document `"); + prs(s); + err("' unclosed"); } quitenv(); } @@ -4838,70 +5001,73 @@ int ec; * open here temp file. * if unquoted here, expand here temp file into second temp file. */ -static int -herein(hname, xdoll) -char *hname; -int xdoll; +static int herein(char *hname, int xdoll) { - register int hf; + int hf; int tf; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &tf; #endif - if (hname == 0) - return(-1); - hf = open(hname, 0); + if (hname == NULL) + return -1; + + DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll)); + + hf = open(hname, O_RDONLY); if (hf < 0) - return (-1); + return -1; + if (xdoll) { char c; char tname[30] = ".msh_XXXXXX"; jmp_buf ev; - + tf = mkstemp(tname); if (tf < 0) - return (-1); - if (newenv(setjmp(errpt = ev)) == 0) { + return -1; + errpt = ev; + if (newenv(setjmp(errpt)) == 0) { PUSHIO(afile, hf, herechar); - setbase(e.iop); + setbase(global_env.iop); while ((c = subgetc(0, 0)) != 0) { - c &= ~ QUOTE; + c &= ~QUOTE; write(tf, &c, sizeof c); } quitenv(); } else unlink(tname); close(tf); - tf = open(tname, 0); + tf = open(tname, O_RDONLY); unlink(tname); - return (tf); - } else - return (hf); + return tf; + } + return hf; } -static void -scraphere() +static void scraphere(void) { - register struct here *h; + struct here *h; + + DBGPRINTF7(("SCRAPHERE: enter...\n")); for (h = inhere; h != NULL; h = h->h_next) { if (h->h_iop && h->h_iop->io_name) - unlink(h->h_iop->io_name); + unlink(h->h_iop->io_name); } inhere = NULL; } /* unlink here temp files before a freearea(area) */ -static void -freehere(area) -int area; +static void freehere(int area) { - register struct here *h, *hl; + struct here *h, *hl; + + DBGPRINTF6(("FREEHERE: enter, area=%d\n", area)); hl = NULL; - for (h = acthere; h != NULL; h = h->h_next) + for (h = acthere; h != NULL; h = h->h_next) { if (getarea((char *) h) >= area) { if (h->h_iop->io_name != NULL) unlink(h->h_iop->io_name); @@ -4909,33 +5075,250 @@ int area; acthere = h->h_next; else hl->h_next = h->h_next; - } else + } else { hl = h; + } + } } +/* -------- sh.c -------- */ +/* + * shell + */ + +int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int msh_main(int argc, char **argv) +{ + int f; + char *s; + int cflag; + char *name, **ap; + int (*iof) (struct ioarg *); + + INIT_G(); + + sharedbuf.id = AFID_NOBUF; + mainbuf.id = AFID_NOBUF; + elinep = line + sizeof(line) - 5; + +#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) { + /* Can be merged with same string elsewhere in bbox */ + if (geteuid() == 0) + setval(path, bb_default_root_path); + else + setval(path, bb_default_path); + } + 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 = 1; + 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 = mshdbg_var->value[0] - '0'; + if (mshdbg < 0) + mshdbg = 0; +#endif + DBGPRINTF(("MSH_MAIN: calling newfile()\n")); + + name = *++argv; + if (newfile(name)) + exit(EXIT_FAILURE); /* Exit on error */ + } + } + + setdash(); + + /* This won't be true if PUSHIO has been called, say from newfile() above */ + if (global_env.iop < iostack) { + PUSHIO(afile, 0, iof); + if (isatty(0) && isatty(1) && !cflag) { + interactive = 1; +#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 = 1; + f = open(".profile", O_RDONLY); + if (f >= 0) + next(remap(f)); + f = open("/etc/profile", O_RDONLY); + if (f >= 0) + next(remap(f)); + } + if (interactive) + signal(SIGTERM, sig); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + +/* Handle "msh SCRIPT VAR=val params..." */ +/* Disabled: bash does not do it! */ +#if 0 + argv++; + /* skip leading args of the form VAR=val */ + while (*argv && assign(*argv, !COPYV)) { + argc--; + argv++; + } + argv--; +#endif + dolv = argv; + dolc = argc; + dolv[0] = name; + + setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc)); + + DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, global_env.iop %p, iostack %p\n", interactive, global_env.iop, iostack)); + + for (;;) { + if (interactive && global_env.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. - * + * * Redistribution and use of the MINIX operating system in source and * binary forms, with or without modification, are permitted provided * that the following conditions are met: - * + * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. - * + * * Neither the name of Prentice Hall nor the names of the software * authors or contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF @@ -4950,4 +5333,3 @@ int area; * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ - -- cgit v1.2.3-54-g00ecf