X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fash.c;h=2a71a2cf2a3f47cb3a0dbe204fa439f5bb786486;hb=81c3a1d0b2be02c1d675e6dceb500ce6b3da3282;hp=4113ce8e20982f94175cb3c8f06452f1eb01a555;hpb=7d75a96b15327050a294b1f54981781ce6e551e2;p=oweals%2Fbusybox.git diff --git a/shell/ash.c b/shell/ash.c index 4113ce8e2..2a71a2cf2 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8,7 +8,6 @@ * Copyright (c) 1997-2005 Herbert Xu * was re-ported from NetBSD and debianized. * - * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * @@ -28,7 +27,6 @@ * used in busybox and size optimizations, * rewrote arith (see notes to this), added locale support, * rewrote dynamic variables. - * */ /* @@ -42,36 +40,57 @@ * a quit signal will generate a core dump. */ #define DEBUG 0 -#define IFS_BROKEN #define PROFILE 0 -#if ENABLE_ASH_JOB_CONTROL -#define JOBS 1 -#else -#define JOBS 0 -#endif + +#define IFS_BROKEN + +#define JOBS ENABLE_ASH_JOB_CONTROL #if DEBUG +#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif -#include "busybox.h" /* for struct bb_applet */ +#endif + +#include "busybox.h" /* for applet_names */ +//TODO: pull in some .h and find out do we have SINGLE_APPLET_MAIN? +//#include "applet_tables.h" doesn't work #include #include #include -#if JOBS || ENABLE_ASH_READ_NCHARS -#include + +#if defined SINGLE_APPLET_MAIN +/* STANDALONE does not make sense, and won't compile */ +#undef CONFIG_FEATURE_SH_STANDALONE +#undef ENABLE_FEATURE_SH_STANDALONE +#undef USE_FEATURE_SH_STANDALONE +#undef SKIP_FEATURE_SH_STANDALONE(...) +#define ENABLE_FEATURE_SH_STANDALONE 0 +#define USE_FEATURE_SH_STANDALONE(...) +#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__ +#endif + +#ifndef PIPE_BUF +#define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif -extern char **environ; #if defined(__uClinux__) -#error "Do not even bother, ash will not run on uClinux" +#error "Do not even bother, ash will not run on NOMMU machine" #endif +/* ============ Hash table sizes. Configurable. */ + +#define VTABSIZE 39 +#define ATABSIZE 39 +#define CMDTABLESIZE 31 /* should be prime */ + + /* ============ Misc helpers */ #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) -/* C99 say: "char" declaration may be signed or unsigned default */ +/* C99 says: "char" declaration may be signed or unsigned by default */ #define signed_char2int(sc) ((int)((signed char)sc)) @@ -103,54 +122,13 @@ static const char *const optletters_optnames[] = { enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; -static char optlist[NOPTS] ALIGN1; - -#define eflag optlist[0] -#define fflag optlist[1] -#define Iflag optlist[2] -#define iflag optlist[3] -#define mflag optlist[4] -#define nflag optlist[5] -#define sflag optlist[6] -#define xflag optlist[7] -#define vflag optlist[8] -#define Cflag optlist[9] -#define aflag optlist[10] -#define bflag optlist[11] -#define uflag optlist[12] -#define viflag optlist[13] -#if DEBUG -#define nolog optlist[14] -#define debug optlist[15] -#endif - /* ============ Misc data */ -static char nullstr[1] ALIGN1; /* zero length string */ static const char homestr[] ALIGN1 = "HOME"; static const char snlfmt[] ALIGN1 = "%s\n"; static const char illnum[] ALIGN1 = "Illegal number: %s"; -static char *minusc; /* argument to -c option */ - -/* pid of main shell */ -static int rootpid; -/* shell level: 0 for the main shell, 1 for its children, and so on */ -static int shlvl; -#define rootshell (!shlvl) -/* trap handler commands */ -static char *trap[NSIG]; -static smallint isloginsh; -/* current value of signal */ -static char sigmode[NSIG - 1]; -/* indicates specified signal received */ -static char gotsig[NSIG - 1]; -static char *arg0; /* value of $0 */ - - -/* ============ Interrupts / exceptions */ - /* * We enclose jmp_buf in a structure so that we can declare pointers to * jump locations. The global variable handler contains the location to @@ -163,52 +141,147 @@ static char *arg0; /* value of $0 */ struct jmploc { jmp_buf loc; }; -static struct jmploc *exception_handler; -static int exception; -/* exceptions */ + +struct globals_misc { + /* pid of main shell */ + int rootpid; + /* shell level: 0 for the main shell, 1 for its children, and so on */ + int shlvl; +#define rootshell (!shlvl) + char *minusc; /* argument to -c option */ + + char *curdir; // = nullstr; /* current working directory */ + char *physdir; // = nullstr; /* physical working directory */ + + char *arg0; /* value of $0 */ + + struct jmploc *exception_handler; + +// disabled by vda: cannot understand how it was supposed to work - +// cannot fix bugs. That's why you have to explain your non-trivial designs! +// /* do we generate EXSIG events */ +// int exsig; /* counter */ + volatile int suppressint; /* counter */ + volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */ + /* last pending signal */ + volatile /*sig_atomic_t*/ smallint pendingsig; + smallint exception; /* kind of exception (0..5) */ + /* exceptions */ #define EXINT 0 /* SIGINT received */ #define EXERROR 1 /* a generic error */ #define EXSHELLPROC 2 /* execute a shell procedure */ #define EXEXEC 3 /* command execution failed */ #define EXEXIT 4 /* exit the shell */ #define EXSIG 5 /* trapped signal in wait(1) */ -static volatile int suppressint; -static volatile sig_atomic_t intpending; -/* do we generate EXSIG events */ -static int exsig; -/* last pending signal */ -static volatile sig_atomic_t pendingsig; -/* - * Sigmode records the current value of the signal handlers for the various - * modes. A value of zero means that the current handler is not known. - * S_HARD_IGN indicates that the signal was ignored on entry to the shell, - */ + smallint isloginsh; + char nullstr[1]; /* zero length string */ -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#define S_CATCH 2 /* signal is caught */ -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ + char optlist[NOPTS]; +#define eflag optlist[0] +#define fflag optlist[1] +#define Iflag optlist[2] +#define iflag optlist[3] +#define mflag optlist[4] +#define nflag optlist[5] +#define sflag optlist[6] +#define xflag optlist[7] +#define vflag optlist[8] +#define Cflag optlist[9] +#define aflag optlist[10] +#define bflag optlist[11] +#define uflag optlist[12] +#define viflag optlist[13] +#if DEBUG +#define nolog optlist[14] +#define debug optlist[15] +#endif + + /* trap handler commands */ + /* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell. + */ + char sigmode[NSIG - 1]; +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ #define S_HARD_IGN 4 /* signal is ignored permenantly */ -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + /* indicates specified signal received */ + char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ + char *trap[NSIG]; + + /* Rarely referenced stuff */ +#if ENABLE_ASH_RANDOM_SUPPORT + /* Random number generators */ + int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */ + uint32_t random_LCG; /* LCG (fast but weak) */ +#endif + pid_t backgndpid; /* pid of last background process */ + smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ +}; +extern struct globals_misc *const ash_ptr_to_globals_misc; +#define G_misc (*ash_ptr_to_globals_misc) +#define rootpid (G_misc.rootpid ) +#define shlvl (G_misc.shlvl ) +#define minusc (G_misc.minusc ) +#define curdir (G_misc.curdir ) +#define physdir (G_misc.physdir ) +#define arg0 (G_misc.arg0 ) +#define exception_handler (G_misc.exception_handler) +#define exception (G_misc.exception ) +#define suppressint (G_misc.suppressint ) +#define intpending (G_misc.intpending ) +//#define exsig (G_misc.exsig ) +#define pendingsig (G_misc.pendingsig ) +#define isloginsh (G_misc.isloginsh ) +#define nullstr (G_misc.nullstr ) +#define optlist (G_misc.optlist ) +#define sigmode (G_misc.sigmode ) +#define gotsig (G_misc.gotsig ) +#define trap (G_misc.trap ) +#define random_galois_LFSR (G_misc.random_galois_LFSR) +#define random_LCG (G_misc.random_LCG ) +#define backgndpid (G_misc.backgndpid ) +#define job_warning (G_misc.job_warning) +#define INIT_G_misc() do { \ + (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ + barrier(); \ + curdir = nullstr; \ + physdir = nullstr; \ +} while (0) + + +/* ============ Utility functions */ +static int isdigit_str9(const char *str) +{ + int maxlen = 9 + 1; /* max 9 digits: 999999999 */ + while (--maxlen && isdigit(*str)) + str++; + return (*str == '\0'); +} + + +/* ============ Interrupts / exceptions */ /* * These macros allow the user to suspend the handling of interrupt signals - * over a period of time. This is similar to SIGHOLD to or sigblock, but + * over a period of time. This is similar to SIGHOLD or to sigblock, but * much more efficient and portable. (But hacking the kernel is so much * more fun than worrying about efficiency and portability. :-)) */ -#define INT_OFF \ - do { \ - suppressint++; \ - xbarrier(); \ - } while (0) +#define INT_OFF do { \ + suppressint++; \ + xbarrier(); \ +} while (0) /* * Called to raise an exception. Since C doesn't include exceptions, we * just do a longjmp to the exception handler. The type of exception is * stored in the global variable "exception". */ -static void raise_exception(int) ATTRIBUTE_NORETURN; +static void raise_exception(int) NORETURN; static void raise_exception(int e) { @@ -228,23 +301,22 @@ raise_exception(int e) * are held using the INT_OFF macro. (The test for iflag is just * defensive programming.) */ -static void raise_interrupt(void) ATTRIBUTE_NORETURN; +static void raise_interrupt(void) NORETURN; static void raise_interrupt(void) { int i; - sigset_t mask; intpending = 0; - /* Signal is not automatically re-enabled after it is raised, - * do it ourself */ - sigemptyset(&mask); - sigprocmask(SIG_SETMASK, &mask, 0); + /* Signal is not automatically unmasked after it is raised, + * do it ourself - unmask all signals */ + sigprocmask_allsigs(SIG_UNBLOCK); /* pendingsig = 0; - now done in onsig() */ i = EXSIG; if (gotsig[SIGINT - 1] && !trap[SIGINT]) { if (!(rootshell && iflag)) { + /* Kill ourself with SIGINT */ signal(SIGINT, SIG_DFL); raise(SIGINT); } @@ -272,42 +344,30 @@ force_int_on(void) } #define FORCE_INT_ON force_int_on() #else -#define INT_ON \ - do { \ - xbarrier(); \ - if (--suppressint == 0 && intpending) \ - raise_interrupt(); \ - } while (0) -#define FORCE_INT_ON \ - do { \ - xbarrier(); \ - suppressint = 0; \ - if (intpending) \ - raise_interrupt(); \ - } while (0) +#define INT_ON do { \ + xbarrier(); \ + if (--suppressint == 0 && intpending) \ + raise_interrupt(); \ +} while (0) +#define FORCE_INT_ON do { \ + xbarrier(); \ + suppressint = 0; \ + if (intpending) \ + raise_interrupt(); \ +} while (0) #endif /* ASH_OPTIMIZE_FOR_SIZE */ #define SAVE_INT(v) ((v) = suppressint) -#define RESTORE_INT(v) \ - do { \ - xbarrier(); \ - suppressint = (v); \ - if (suppressint == 0 && intpending) \ - raise_interrupt(); \ - } while (0) - -#define EXSIGON \ - do { \ - exsig++; \ - xbarrier(); \ - if (pendingsig) \ - raise_exception(EXSIG); \ - } while (0) -/* EXSIG is turned off by evalbltin(). */ +#define RESTORE_INT(v) do { \ + xbarrier(); \ + suppressint = (v); \ + if (suppressint == 0 && intpending) \ + raise_interrupt(); \ +} while (0) /* - * Ignore a signal. Only one usage site - in forkchild() + * Ignore a signal. Avoids unnecessary system calls. */ static void ignoresig(int signo) @@ -327,10 +387,10 @@ onsig(int signo) gotsig[signo - 1] = 1; pendingsig = signo; - if (exsig || (signo == SIGINT && !trap[SIGINT])) { + if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { if (!suppressint) { pendingsig = 0; - raise_interrupt(); + raise_interrupt(); /* does not return */ } intpending = 1; } @@ -436,139 +496,154 @@ out2str(const char *p) #define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ /* values of VSTYPE field */ -#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ -#define VSMINUS 0x2 /* ${var-text} */ -#define VSPLUS 0x3 /* ${var+text} */ -#define VSQUESTION 0x4 /* ${var?message} */ -#define VSASSIGN 0x5 /* ${var=text} */ -#define VSTRIMRIGHT 0x6 /* ${var%pattern} */ -#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ -#define VSTRIMLEFT 0x8 /* ${var#pattern} */ -#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ -#define VSLENGTH 0xa /* ${#var} */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMRIGHT 0x6 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */ +#define VSTRIMLEFT 0x8 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ +#define VSLENGTH 0xa /* ${#var} */ +#if ENABLE_ASH_BASH_COMPAT +#define VSSUBSTR 0xc /* ${var:position:length} */ +#define VSREPLACE 0xd /* ${var/pattern/replacement} */ +#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */ +#endif static const char dolatstr[] ALIGN1 = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; -#define NCMD 0 -#define NPIPE 1 -#define NREDIR 2 -#define NBACKGND 3 +#define NCMD 0 +#define NPIPE 1 +#define NREDIR 2 +#define NBACKGND 3 #define NSUBSHELL 4 -#define NAND 5 -#define NOR 6 -#define NSEMI 7 -#define NIF 8 -#define NWHILE 9 -#define NUNTIL 10 -#define NFOR 11 -#define NCASE 12 -#define NCLIST 13 -#define NDEFUN 14 -#define NARG 15 -#define NTO 16 -#define NCLOBBER 17 -#define NFROM 18 -#define NFROMTO 19 -#define NAPPEND 20 -#define NTOFD 21 -#define NFROMFD 22 -#define NHERE 23 -#define NXHERE 24 -#define NNOT 25 +#define NAND 5 +#define NOR 6 +#define NSEMI 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#if ENABLE_ASH_BASH_COMPAT +#define NTO2 17 +#endif +#define NCLOBBER 18 +#define NFROM 19 +#define NFROMTO 20 +#define NAPPEND 21 +#define NTOFD 22 +#define NFROMFD 23 +#define NHERE 24 +#define NXHERE 25 +#define NNOT 26 +#define N_NUMBER 27 union node; struct ncmd { - int type; + smallint type; /* Nxxxx */ union node *assign; union node *args; union node *redirect; }; struct npipe { - int type; - int backgnd; + smallint type; + smallint pipe_backgnd; struct nodelist *cmdlist; }; struct nredir { - int type; + smallint type; union node *n; union node *redirect; }; struct nbinary { - int type; + smallint type; union node *ch1; union node *ch2; }; struct nif { - int type; + smallint type; union node *test; union node *ifpart; union node *elsepart; }; struct nfor { - int type; + smallint type; union node *args; union node *body; char *var; }; struct ncase { - int type; + smallint type; union node *expr; union node *cases; }; struct nclist { - int type; + smallint type; union node *next; union node *pattern; union node *body; }; struct narg { - int type; + smallint type; union node *next; char *text; struct nodelist *backquote; }; +/* nfile and ndup layout must match! + * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight + * that it is actually NTO2 (>&file), and change its type. + */ struct nfile { - int type; + smallint type; union node *next; int fd; + int _unused_dupfd; union node *fname; char *expfname; }; struct ndup { - int type; + smallint type; union node *next; int fd; int dupfd; union node *vname; + char *_unused_expfname; }; struct nhere { - int type; + smallint type; union node *next; int fd; union node *doc; }; struct nnot { - int type; + smallint type; union node *com; }; union node { - int type; + smallint type; struct ncmd ncmd; struct npipe npipe; struct nredir nredir; @@ -855,8 +930,11 @@ shcmd(union node *cmd, FILE *fp) case NTO: s = ">>"+1; dftfd = 1; break; case NCLOBBER: s = ">|"; dftfd = 1; break; case NAPPEND: s = ">>"; dftfd = 1; break; +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NTOFD: s = ">&"; dftfd = 1; break; - case NFROM: s = "<"; break; + case NFROM: s = "<"; break; case NFROMFD: s = "<&"; break; case NFROMTO: s = "<>"; break; default: s = "*error*"; break; @@ -909,7 +987,7 @@ shtree(union node *n, int ind, char *pfx, FILE *fp) if (lp->next) fputs(" | ", fp); } - if (n->npipe.backgnd) + if (n->npipe.pipe_backgnd) fputs(" &", fp); if (ind >= 0) putc('\n', fp); @@ -950,14 +1028,12 @@ struct strlist { char *text; }; -#if ENABLE_ASH_ALIAS struct alias; -#endif struct strpush { struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; + char *prev_string; + int prev_left_in_line; #if ENABLE_ASH_ALIAS struct alias *ap; /* if push was associated with an alias */ #endif @@ -968,20 +1044,20 @@ struct parsefile { struct parsefile *prev; /* preceding file on stack */ int linno; /* current line */ int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ + int left_in_line; /* number of chars left in this line */ + int left_in_buffer; /* number of chars left in this buffer past the line */ + char *next_to_pgetc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ }; -static struct parsefile basepf; /* top level input file */ -static struct parsefile *parsefile = &basepf; /* current input file */ +static struct parsefile basepf; /* top level input file */ +static struct parsefile *g_parsefile = &basepf; /* current input file */ static int startlinno; /* line # where last token started */ static char *commandname; /* currently executing command */ static struct strlist *cmdenviron; /* environment for builtin command */ -static int exitstatus; /* exit status of last command */ +static uint8_t exitstatus; /* exit status of last command */ /* ============ Message printing */ @@ -993,7 +1069,7 @@ ash_vmsg(const char *msg, va_list ap) if (commandname) { if (strcmp(arg0, commandname)) fprintf(stderr, "%s: ", commandname); - if (!iflag || parsefile->fd) + if (!iflag || g_parsefile->fd) fprintf(stderr, "line %d: ", startlinno); } vfprintf(stderr, msg, ap); @@ -1005,7 +1081,7 @@ ash_vmsg(const char *msg, va_list ap) * is not NULL then error prints an error message using printf style * formatting. It then raises the error exception. */ -static void ash_vmsg_and_raise(int, const char *, va_list) ATTRIBUTE_NORETURN; +static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN; static void ash_vmsg_and_raise(int cond, const char *msg, va_list ap) { @@ -1025,7 +1101,7 @@ ash_vmsg_and_raise(int cond, const char *msg, va_list ap) /* NOTREACHED */ } -static void ash_msg_and_raise_error(const char *, ...) ATTRIBUTE_NORETURN; +static void ash_msg_and_raise_error(const char *, ...) NORETURN; static void ash_msg_and_raise_error(const char *msg, ...) { @@ -1037,7 +1113,7 @@ ash_msg_and_raise_error(const char *msg, ...) va_end(ap); } -static void ash_msg_and_raise(int, const char *, ...) ATTRIBUTE_NORETURN; +static void ash_msg_and_raise(int, const char *, ...) NORETURN; static void ash_msg_and_raise(int cond, const char *msg, ...) { @@ -1090,7 +1166,7 @@ enum { * on many machines. */ SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1, /* Minimum size of a block */ - MINSIZE = SHELL_ALIGN(504), + MINSIZE = SHELL_ALIGN(504), }; struct stack_block { @@ -1105,16 +1181,38 @@ struct stackmark { struct stackmark *marknext; }; -static struct stack_block stackbase; -static struct stack_block *stackp = &stackbase; -static struct stackmark *markp; -static char *stacknxt = stackbase.space; -static size_t stacknleft = MINSIZE; -static char *sstrend = stackbase.space + MINSIZE; -static int herefd = -1; -#define stackblock() ((void *)stacknxt) -#define stackblocksize() stacknleft +struct globals_memstack { + struct stack_block *g_stackp; // = &stackbase; + struct stackmark *markp; + char *g_stacknxt; // = stackbase.space; + char *sstrend; // = stackbase.space + MINSIZE; + size_t g_stacknleft; // = MINSIZE; + int herefd; // = -1; + struct stack_block stackbase; +}; +extern struct globals_memstack *const ash_ptr_to_globals_memstack; +#define G_memstack (*ash_ptr_to_globals_memstack) +#define g_stackp (G_memstack.g_stackp ) +#define markp (G_memstack.markp ) +#define g_stacknxt (G_memstack.g_stacknxt ) +#define sstrend (G_memstack.sstrend ) +#define g_stacknleft (G_memstack.g_stacknleft) +#define herefd (G_memstack.herefd ) +#define stackbase (G_memstack.stackbase ) +#define INIT_G_memstack() do { \ + (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \ + barrier(); \ + g_stackp = &stackbase; \ + g_stacknxt = stackbase.space; \ + g_stacknleft = MINSIZE; \ + sstrend = stackbase.space + MINSIZE; \ + herefd = -1; \ +} while (0) + +#define stackblock() ((void *)g_stacknxt) +#define stackblocksize() g_stacknleft + static void * ckrealloc(void * p, size_t nbytes) @@ -1131,6 +1229,12 @@ ckmalloc(size_t nbytes) return ckrealloc(NULL, nbytes); } +static void * +ckzalloc(size_t nbytes) +{ + return memset(ckmalloc(nbytes), 0, nbytes); +} + /* * Make a copy of a string in safe storage. */ @@ -1158,7 +1262,7 @@ stalloc(size_t nbytes) size_t aligned; aligned = SHELL_ALIGN(nbytes); - if (aligned > stacknleft) { + if (aligned > g_stacknleft) { size_t len; size_t blocksize; struct stack_block *sp; @@ -1171,30 +1275,36 @@ stalloc(size_t nbytes) ash_msg_and_raise_error(bb_msg_memory_exhausted); INT_OFF; sp = ckmalloc(len); - sp->prev = stackp; - stacknxt = sp->space; - stacknleft = blocksize; - sstrend = stacknxt + blocksize; - stackp = sp; + sp->prev = g_stackp; + g_stacknxt = sp->space; + g_stacknleft = blocksize; + sstrend = g_stacknxt + blocksize; + g_stackp = sp; INT_ON; } - p = stacknxt; - stacknxt += aligned; - stacknleft -= aligned; + p = g_stacknxt; + g_stacknxt += aligned; + g_stacknleft -= aligned; return p; } +static void * +stzalloc(size_t nbytes) +{ + return memset(stalloc(nbytes), 0, nbytes); +} + static void stunalloc(void *p) { #if DEBUG - if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) { - write(2, "stunalloc\n", 10); + if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) { + write(STDERR_FILENO, "stunalloc\n", 10); abort(); } #endif - stacknleft += stacknxt - (char *)p; - stacknxt = p; + g_stacknleft += g_stacknxt - (char *)p; + g_stacknxt = p; } /* @@ -1210,9 +1320,9 @@ ststrdup(const char *p) static void setstackmark(struct stackmark *mark) { - mark->stackp = stackp; - mark->stacknxt = stacknxt; - mark->stacknleft = stacknleft; + mark->stackp = g_stackp; + mark->stacknxt = g_stacknxt; + mark->stacknleft = g_stacknleft; mark->marknext = markp; markp = mark; } @@ -1227,13 +1337,13 @@ popstackmark(struct stackmark *mark) INT_OFF; markp = mark->marknext; - while (stackp != mark->stackp) { - sp = stackp; - stackp = sp->prev; + while (g_stackp != mark->stackp) { + sp = g_stackp; + g_stackp = sp->prev; free(sp); } - stacknxt = mark->stacknxt; - stacknleft = mark->stacknleft; + g_stacknxt = mark->stacknxt; + g_stacknleft = mark->stacknleft; sstrend = mark->stacknxt + mark->stacknleft; INT_ON; } @@ -1252,13 +1362,13 @@ growstackblock(void) { size_t newlen; - newlen = stacknleft * 2; - if (newlen < stacknleft) + newlen = g_stacknleft * 2; + if (newlen < g_stacknleft) ash_msg_and_raise_error(bb_msg_memory_exhausted); if (newlen < 128) newlen += 128; - if (stacknxt == stackp->space && stackp != &stackbase) { + if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { struct stack_block *oldstackp; struct stackmark *xmark; struct stack_block *sp; @@ -1266,15 +1376,15 @@ growstackblock(void) size_t grosslen; INT_OFF; - oldstackp = stackp; - sp = stackp; + oldstackp = g_stackp; + sp = g_stackp; prevstackp = sp->prev; grosslen = newlen + sizeof(struct stack_block) - MINSIZE; sp = ckrealloc(sp, grosslen); sp->prev = prevstackp; - stackp = sp; - stacknxt = sp->space; - stacknleft = newlen; + g_stackp = sp; + g_stacknxt = sp->space; + g_stacknleft = newlen; sstrend = sp->space + newlen; /* @@ -1283,20 +1393,20 @@ growstackblock(void) */ xmark = markp; while (xmark != NULL && xmark->stackp == oldstackp) { - xmark->stackp = stackp; - xmark->stacknxt = stacknxt; - xmark->stacknleft = stacknleft; + xmark->stackp = g_stackp; + xmark->stacknxt = g_stacknxt; + xmark->stacknleft = g_stacknleft; xmark = xmark->marknext; } INT_ON; } else { - char *oldspace = stacknxt; - int oldlen = stacknleft; + char *oldspace = g_stacknxt; + size_t oldlen = g_stacknleft; char *p = stalloc(newlen); /* free the space we just allocated */ - stacknxt = memcpy(p, oldspace, oldlen); - stacknleft += newlen; + g_stacknxt = memcpy(p, oldspace, oldlen); + g_stacknleft += newlen; } } @@ -1304,8 +1414,8 @@ static void grabstackblock(size_t len) { len = SHELL_ALIGN(len); - stacknxt += len; - stacknleft -= len; + g_stacknxt += len; + g_stacknleft -= len; } /* @@ -1334,7 +1444,7 @@ growstackstr(void) return stackblock(); } growstackblock(); - return stackblock() + len; + return (char *)stackblock() + len; } /* @@ -1343,7 +1453,7 @@ growstackstr(void) static char * makestrspace(size_t newlen, char *p) { - size_t len = p - stacknxt; + size_t len = p - g_stacknxt; size_t size = stackblocksize(); for (;;) { @@ -1355,14 +1465,14 @@ makestrspace(size_t newlen, char *p) break; growstackblock(); } - return stackblock() + len; + return (char *)stackblock() + len; } static char * stack_nputstr(const char *s, size_t n, char *p) { p = makestrspace(n, p); - p = memcpy(p, s, n) + n; + p = (char *)memcpy(p, s, n) + n; return p; } @@ -1383,27 +1493,25 @@ _STPUTC(int c, char *p) #define STARTSTACKSTR(p) ((p) = stackblock()) #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) -#define CHECKSTRSPACE(n, p) \ - do { \ - char *q = (p); \ - size_t l = (n); \ - size_t m = sstrend - q; \ - if (l > m) \ - (p) = makestrspace(l, q); \ - } while (0) -#define USTPUTC(c, p) (*p++ = (c)) -#define STACKSTRNUL(p) \ - do { \ - if ((p) == sstrend) \ - p = growstackstr(); \ - *p = '\0'; \ - } while (0) -#define STUNPUTC(p) (--p) -#define STTOPC(p) (p[-1]) -#define STADJUST(amount, p) (p += (amount)) +#define CHECKSTRSPACE(n, p) do { \ + char *q = (p); \ + size_t l = (n); \ + size_t m = sstrend - q; \ + if (l > m) \ + (p) = makestrspace(l, q); \ +} while (0) +#define USTPUTC(c, p) (*(p)++ = (c)) +#define STACKSTRNUL(p) do { \ + if ((p) == sstrend) \ + (p) = growstackstr(); \ + *(p) = '\0'; \ +} while (0) +#define STUNPUTC(p) (--(p)) +#define STTOPC(p) ((p)[-1]) +#define STADJUST(amount, p) ((p) += (amount)) #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) -#define ungrabstackstr(s, p) stunalloc((s)) +#define ungrabstackstr(s, p) stunalloc(s) #define stackstrend() ((void *)sstrend) @@ -1417,7 +1525,7 @@ prefix(const char *string, const char *pfx) { while (*pfx) { if (*pfx++ != *string++) - return 0; + return NULL; } return (char *) string; } @@ -1467,7 +1575,7 @@ single_quote(const char *s) q = p = makestrspace(len + 3, p); *q++ = '\''; - q = memcpy(q, s, len) + len; + q = (char *)memcpy(q, s, len) + len; *q++ = '\''; s += len; @@ -1480,7 +1588,7 @@ single_quote(const char *s) q = p = makestrspace(len + 3, p); *q++ = '"'; - q = memcpy(q, s, len) + len; + q = (char *)memcpy(q, s, len) + len; *q++ = '"'; s += len; @@ -1500,14 +1608,14 @@ static char *optionarg; /* set by nextopt (like getopt) */ static char *optptr; /* used by nextopt */ /* - * XXX - should get rid of. have all builtins use getopt(3). the - * library getopt must have the BSD extension static variable "optreset" - * otherwise it can't be used within the shell safely. + * XXX - should get rid of. Have all builtins use getopt(3). + * The library getopt must have the BSD extension static variable + * "optreset", otherwise it can't be used within the shell safely. * - * Standard option processing (a la getopt) for builtin routines. The - * only argument that is passed to nextopt is the option string; the - * other arguments are unnecessary. It return the character, or '\0' on - * end of input. + * Standard option processing (a la getopt) for builtin routines. + * The only argument that is passed to nextopt is the option string; + * the other arguments are unnecessary. It returns the character, + * or '\0' on end of input. */ static int nextopt(const char *optstring) @@ -1518,23 +1626,33 @@ nextopt(const char *optstring) p = optptr; if (p == NULL || *p == '\0') { + /* We ate entire "-param", take next one */ p = *argptr; - if (p == NULL || *p != '-' || *++p == '\0') + if (p == NULL) + return '\0'; + if (*p != '-') + return '\0'; + if (*++p == '\0') /* just "-" ? */ return '\0'; argptr++; - if (LONE_DASH(p)) /* check for "--" */ + if (LONE_DASH(p)) /* "--" ? */ return '\0'; + /* p => next "-param" */ } + /* p => some option char in the middle of a "-param" */ c = *p++; - for (q = optstring; *q != c; ) { + for (q = optstring; *q != c;) { if (*q == '\0') ash_msg_and_raise_error("illegal option -%c", c); if (*++q == ':') q++; } if (*++q == ':') { - if (*p == '\0' && (p = *argptr++) == NULL) - ash_msg_and_raise_error("no arg for -%c option", c); + if (*p == '\0') { + p = *argptr++; + if (p == NULL) + ash_msg_and_raise_error("no arg for -%c option", c); + } optionarg = p; p = NULL; } @@ -1543,88 +1661,39 @@ nextopt(const char *optstring) } -/* ============ Math support definitions */ - -#if ENABLE_ASH_MATH_SUPPORT_64 -typedef int64_t arith_t; -#define arith_t_type long long -#else -typedef long arith_t; -#define arith_t_type long -#endif - -#if ENABLE_ASH_MATH_SUPPORT -static arith_t dash_arith(const char *); -static arith_t arith(const char *expr, int *perrcode); -#endif - -#if ENABLE_ASH_RANDOM_SUPPORT -static unsigned long rseed; -#ifndef DYNAMIC_VAR -#define DYNAMIC_VAR -#endif -#endif - - /* ============ Shell variables */ -/* flags */ -#define VEXPORT 0x01 /* variable is exported */ -#define VREADONLY 0x02 /* variable cannot be modified */ -#define VSTRFIXED 0x04 /* variable struct is statically allocated */ -#define VTEXTFIXED 0x08 /* text is statically allocated */ -#define VSTACK 0x10 /* text is allocated on the stack */ -#define VUNSET 0x20 /* the variable is not set */ -#define VNOFUNC 0x40 /* don't call the callback function */ -#define VNOSET 0x80 /* do not set variable - just readonly test */ -#define VNOSAVE 0x100 /* when text is on the heap before setvareq */ -#ifdef DYNAMIC_VAR -# define VDYNAMIC 0x200 /* dynamic variable */ -#else -# define VDYNAMIC 0 -#endif - -#ifdef IFS_BROKEN -static const char defifsvar[] ALIGN1 = "IFS= \t\n"; -#define defifs (defifsvar + 4) -#else -static const char defifs[] ALIGN1 = " \t\n"; -#endif - +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ struct shparam { int nparam; /* # of positional parameters (without $0) */ - unsigned char malloc; /* if parameter list dynamically allocated */ - char **p; /* parameter list */ #if ENABLE_ASH_GETOPTS int optind; /* next parameter to be processed by getopts */ int optoff; /* used by getopts */ #endif + unsigned char malloced; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ }; -static struct shparam shellparam; /* $@ current positional parameters */ - /* * Free the list of positional parameters. */ static void freeparam(volatile struct shparam *param) { - char **ap; - - if (param->malloc) { - for (ap = param->p; *ap; ap++) - free(*ap); - free(param->p); + if (param->malloced) { + char **ap, **ap1; + ap = ap1 = param->p; + while (*ap) + free(*ap++); + free(ap1); } } #if ENABLE_ASH_GETOPTS -static void -getoptsreset(const char *value) -{ - shellparam.optind = number(value); - shellparam.optoff = -1; -} +static void getoptsreset(const char *value); #endif struct var { @@ -1642,7 +1711,31 @@ struct localvar { const char *text; /* saved text */ }; -/* Forward decls for varinit[] */ +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ +#define VNOSAVE 0x100 /* when text is on the heap before setvareq */ +#if ENABLE_ASH_RANDOM_SUPPORT +# define VDYNAMIC 0x200 /* dynamic variable */ +#else +# define VDYNAMIC 0 +#endif + +#ifdef IFS_BROKEN +static const char defifsvar[] ALIGN1 = "IFS= \t\n"; +#define defifs (defifsvar + 4) +#else +static const char defifs[] ALIGN1 = " \t\n"; +#endif + + +/* Need to be before varinit_data[] */ #if ENABLE_LOCALE_SUPPORT static void change_lc_all(const char *value) @@ -1666,51 +1759,88 @@ static void changepath(const char *); static void change_random(const char *); #endif -static struct var varinit[] = { +static const struct { + int flags; + const char *text; + void (*func)(const char *); +} varinit_data[] = { #ifdef IFS_BROKEN - { NULL, VSTRFIXED|VTEXTFIXED, defifsvar, NULL }, + { VSTRFIXED|VTEXTFIXED , defifsvar , NULL }, #else - { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", NULL }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL }, #endif #if ENABLE_ASH_MAIL - { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, - { NULL, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, #endif - { NULL, VSTRFIXED|VTEXTFIXED, bb_PATH_root_path, changepath }, - { NULL, VSTRFIXED|VTEXTFIXED, "PS1=$ ", NULL }, - { NULL, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL }, - { NULL, VSTRFIXED|VTEXTFIXED, "PS4=+ ", NULL }, + { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath }, + { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL }, + { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, + { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, #if ENABLE_ASH_GETOPTS - { NULL, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, + { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset }, #endif #if ENABLE_ASH_RANDOM_SUPPORT - { NULL, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, + { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, #endif #if ENABLE_LOCALE_SUPPORT - { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, - { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL\0" , change_lc_all }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE\0", change_lc_ctype }, #endif #if ENABLE_FEATURE_EDITING_SAVEHISTORY - { NULL, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, + { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE\0", NULL }, #endif }; -#define vifs varinit[0] +struct redirtab; + +struct globals_var { + struct shparam shellparam; /* $@ current positional parameters */ + struct redirtab *redirlist; + int g_nullredirs; + int preverrout_fd; /* save fd2 before print debug if xflag is set. */ + struct var *vartab[VTABSIZE]; + struct var varinit[ARRAY_SIZE(varinit_data)]; +}; +extern struct globals_var *const ash_ptr_to_globals_var; +#define G_var (*ash_ptr_to_globals_var) +#define shellparam (G_var.shellparam ) +//#define redirlist (G_var.redirlist ) +#define g_nullredirs (G_var.g_nullredirs ) +#define preverrout_fd (G_var.preverrout_fd) +#define vartab (G_var.vartab ) +#define varinit (G_var.varinit ) +#define INIT_G_var() do { \ + unsigned i; \ + (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ + barrier(); \ + for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \ + varinit[i].flags = varinit_data[i].flags; \ + varinit[i].text = varinit_data[i].text; \ + varinit[i].func = varinit_data[i].func; \ + } \ +} while (0) + +#define vifs varinit[0] #if ENABLE_ASH_MAIL -#define vmail (&vifs)[1] -#define vmpath (&vmail)[1] +# define vmail (&vifs)[1] +# define vmpath (&vmail)[1] +# define vpath (&vmpath)[1] #else -#define vmpath vifs +# define vpath (&vifs)[1] #endif -#define vpath (&vmpath)[1] -#define vps1 (&vpath)[1] -#define vps2 (&vps1)[1] -#define vps4 (&vps2)[1] -#define voptind (&vps4)[1] +#define vps1 (&vpath)[1] +#define vps2 (&vps1)[1] +#define vps4 (&vps2)[1] #if ENABLE_ASH_GETOPTS -#define vrandom (&voptind)[1] +# define voptind (&vps4)[1] +# if ENABLE_ASH_RANDOM_SUPPORT +# define vrandom (&voptind)[1] +# endif #else -#define vrandom (&vps4)[1] +# if ENABLE_ASH_RANDOM_SUPPORT +# define vrandom (&vps4)[1] +# endif #endif /* @@ -1720,37 +1850,32 @@ static struct var varinit[] = { */ #define ifsval() (vifs.text + 4) #define ifsset() ((vifs.flags & VUNSET) == 0) -#define mailval() (vmail.text + 5) -#define mpathval() (vmpath.text + 9) +#if ENABLE_ASH_MAIL +# define mailval() (vmail.text + 5) +# define mpathval() (vmpath.text + 9) +# define mpathset() ((vmpath.flags & VUNSET) == 0) +#endif #define pathval() (vpath.text + 5) #define ps1val() (vps1.text + 4) #define ps2val() (vps2.text + 4) #define ps4val() (vps4.text + 4) -#define optindval() (voptind.text + 7) - -#define mpathset() ((vmpath.flags & VUNSET) == 0) - -/* - * The parsefile structure pointed to by the global variable parsefile - * contains information about the current file being read. - */ -struct redirtab { - struct redirtab *next; - int renamed[10]; - int nullredirs; -}; - -static struct redirtab *redirlist; -static int nullredirs; -static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ - -#define VTABSIZE 39 +#if ENABLE_ASH_GETOPTS +# define optindval() (voptind.text + 7) +#endif -static struct var *vartab[VTABSIZE]; #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) +#if ENABLE_ASH_GETOPTS +static void +getoptsreset(const char *value) +{ + shellparam.optind = number(value); + shellparam.optoff = -1; +} +#endif + /* * Return of a legal variable name (a letter or underscore followed by zero or * more letters, underscores, and digits). @@ -1869,7 +1994,7 @@ lookupvar(const char *name) v = *findvar(hashvar(name), name); if (v) { -#ifdef DYNAMIC_VAR +#if ENABLE_ASH_RANDOM_SUPPORT /* * Dynamic variables are implemented roughly the same way they are * in bash. Namely, they're "special" so long as they aren't unset. @@ -1939,9 +2064,9 @@ setvareq(char *s, int flags) if (flags & VNOSET) return; /* not found */ - vp = ckmalloc(sizeof(*vp)); + vp = ckzalloc(sizeof(*vp)); vp->next = *vpp; - vp->func = NULL; + /*vp->func = NULL; - ckzalloc did it */ *vpp = vp; } if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) @@ -1975,10 +2100,10 @@ setvar(const char *name, const char *val, int flags) } INT_OFF; nameeq = ckmalloc(namelen + vallen + 2); - p = memcpy(nameeq, name, namelen) + namelen; + p = (char *)memcpy(nameeq, name, namelen) + namelen; if (val) { *p++ = '='; - p = memcpy(p, val, vallen) + vallen; + p = (char *)memcpy(p, val, vallen) + vallen; } *p = '\0'; setvareq(nameeq, flags | VNOSAVE); @@ -2030,7 +2155,7 @@ unsetvar(const char *s) retval = 1; if (flags & VREADONLY) goto out; -#ifdef DYNAMIC_VAR +#if ENABLE_ASH_RANDOM_SUPPORT vp->flags &= ~VDYNAMIC; #endif if (flags & VUNSET) @@ -2126,7 +2251,8 @@ padvance(const char **path, const char *name) if (*path == NULL) return NULL; start = *path; - for (p = start; *p && *p != ':' && *p != '%'; p++); + for (p = start; *p && *p != ':' && *p != '%'; p++) + continue; len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ while (stackblocksize() < len) growstackblock(); @@ -2140,7 +2266,8 @@ padvance(const char **path, const char *name) pathopt = NULL; if (*p == '%') { pathopt = ++p; - while (*p && *p != ':') p++; + while (*p && *p != ':') + p++; } if (*p == ':') *path = p + 1; @@ -2221,9 +2348,6 @@ setprompt(int whichprompt) static int docd(const char *, int); -static char *curdir = nullstr; /* current working directory */ -static char *physdir = nullstr; /* physical working directory */ - static int cdopt(void) { @@ -2261,7 +2385,7 @@ updatepwd(const char *dir) new = stack_putstr(curdir, new); } new = makestrspace(strlen(dir) + 2, new); - lim = stackblock() + 1; + lim = (char *)stackblock() + 1; if (*dir != '/') { if (new[-1] != '/') USTPUTC('/', new); @@ -2310,7 +2434,7 @@ updatepwd(const char *dir) static char * getpwd(void) { - char *dir = getcwd(0, 0); + char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */ return dir ? dir : nullstr; } @@ -2376,7 +2500,7 @@ docd(const char *dest, int flags) } static int -cdcmd(int argc, char **argv) +cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { const char *dest; const char *path; @@ -2440,7 +2564,7 @@ cdcmd(int argc, char **argv) } static int -pwdcmd(int argc, char **argv) +pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int flags; const char *dir = curdir; @@ -2458,34 +2582,36 @@ pwdcmd(int argc, char **argv) /* ============ ... */ + #define IBUFSIZ COMMON_BUFSIZE -#define basebuf bb_common_bufsiz1 /* buffer for top level input file */ +/* buffer for top level input file */ +#define basebuf bb_common_bufsiz1 /* Syntax classes */ -#define CWORD 0 /* character is nothing special */ -#define CNL 1 /* newline character */ -#define CBACK 2 /* a backslash character */ -#define CSQUOTE 3 /* single quote */ -#define CDQUOTE 4 /* double quote */ +#define CWORD 0 /* character is nothing special */ +#define CNL 1 /* newline character */ +#define CBACK 2 /* a backslash character */ +#define CSQUOTE 3 /* single quote */ +#define CDQUOTE 4 /* double quote */ #define CENDQUOTE 5 /* a terminating quote */ -#define CBQUOTE 6 /* backwards single quote */ -#define CVAR 7 /* a dollar sign */ -#define CENDVAR 8 /* a '}' character */ -#define CLP 9 /* a left paren in arithmetic */ -#define CRP 10 /* a right paren in arithmetic */ +#define CBQUOTE 6 /* backwards single quote */ +#define CVAR 7 /* a dollar sign */ +#define CENDVAR 8 /* a '}' character */ +#define CLP 9 /* a left paren in arithmetic */ +#define CRP 10 /* a right paren in arithmetic */ #define CENDFILE 11 /* end of file */ -#define CCTL 12 /* like CWORD, except it must be escaped */ -#define CSPCL 13 /* these terminate a word */ -#define CIGN 14 /* character should be ignored */ +#define CCTL 12 /* like CWORD, except it must be escaped */ +#define CSPCL 13 /* these terminate a word */ +#define CIGN 14 /* character should be ignored */ #if ENABLE_ASH_ALIAS -#define SYNBASE 130 -#define PEOF -130 -#define PEOA -129 +#define SYNBASE 130 +#define PEOF -130 +#define PEOA -129 #define PEOA_OR_PEOF PEOA #else -#define SYNBASE 129 -#define PEOF -129 +#define SYNBASE 129 +#define PEOF -129 #define PEOA_OR_PEOF PEOF #endif @@ -2577,17 +2703,16 @@ SIT(int c, int syntax) indx = 0; else #endif -#define U_C(c) ((unsigned char)(c)) if ((unsigned char)c >= (unsigned char)(CTLESC) && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) ) { return CCTL; } else { - s = strchr(spec_symbls, c); - if (s == NULL || *s == '\0') + s = strchrnul(spec_symbls, c); + if (*s == '\0') return CWORD; - indx = syntax_index_table[(s - spec_symbls)]; + indx = syntax_index_table[s - spec_symbls]; } return S_I_T[indx][syntax]; } @@ -2903,8 +3028,6 @@ static const char syntax_index_table[258] = { #define ALIASINUSE 1 #define ALIASDEAD 2 -#define ATABSIZE 39 - struct alias { struct alias *next; char *name; @@ -2912,7 +3035,12 @@ struct alias { int flag; }; -static struct alias *atab[ATABSIZE]; + +static struct alias **atab; // [ATABSIZE]; +#define INIT_G_alias() do { \ + atab = xzalloc(ATABSIZE * sizeof(atab[0])); \ +} while (0) + static struct alias ** __lookupalias(const char *name) { @@ -2983,11 +3111,11 @@ setalias(const char *name, const char *val) ap->flag &= ~ALIASDEAD; } else { /* not found */ - ap = ckmalloc(sizeof(struct alias)); + ap = ckzalloc(sizeof(struct alias)); ap->name = ckstrdup(name); ap->val = ckstrdup(val); - ap->flag = 0; - ap->next = 0; + /*ap->flag = 0; - ckzalloc did it */ + /*ap->next = NULL;*/ *app = ap; } INT_ON; @@ -3039,19 +3167,20 @@ printalias(const struct alias *ap) * TODO - sort output */ static int -aliascmd(int argc, char **argv) +aliascmd(int argc UNUSED_PARAM, char **argv) { char *n, *v; int ret = 0; struct alias *ap; - if (argc == 1) { + if (!argv[1]) { int i; - for (i = 0; i < ATABSIZE; i++) + for (i = 0; i < ATABSIZE; i++) { for (ap = atab[i]; ap; ap = ap->next) { printalias(ap); } + } return 0; } while ((n = *++argv) != NULL) { @@ -3073,7 +3202,7 @@ aliascmd(int argc, char **argv) } static int -unaliascmd(int argc, char **argv) +unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int i; @@ -3143,18 +3272,18 @@ struct job { struct job *prev_job; /* previous job */ }; -static pid_t backgndpid; /* pid of last background process */ -static smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ - -static struct job *makejob(union node *, int); +static struct job *makejob(/*union node *,*/ int); +#if !JOBS +#define forkshell(job, node, mode) forkshell(job, mode) +#endif static int forkshell(struct job *, union node *, int); static int waitforjob(struct job *); #if !JOBS -enum { jobctl = 0 }; +enum { doing_jobctl = 0 }; #define setjobctl(on) do {} while (0) #else -static smallint jobctl; /* true if doing job control */ +static smallint doing_jobctl; //references:8 static void setjobctl(int); #endif @@ -3165,84 +3294,90 @@ static void setjobctl(int); static void setsignal(int signo) { - int action; - char *t, tsig; + char *t; + char cur_act, new_act; struct sigaction act; t = trap[signo]; - if (t == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - else - action = S_IGN; - if (rootshell && action == S_DFL) { + new_act = S_DFL; + if (t != NULL) { /* trap for this sig is set */ + new_act = S_CATCH; + if (t[0] == '\0') /* trap is "": ignore this sig */ + new_act = S_IGN; + } + + if (rootshell && new_act == S_DFL) { switch (signo) { case SIGINT: if (iflag || minusc || sflag == 0) - action = S_CATCH; + new_act = S_CATCH; break; case SIGQUIT: #if DEBUG if (debug) break; #endif - /* FALLTHROUGH */ + /* man bash: + * "In all cases, bash ignores SIGQUIT. Non-builtin + * commands run by bash have signal handlers + * set to the values inherited by the shell + * from its parent". */ + new_act = S_IGN; + break; case SIGTERM: if (iflag) - action = S_IGN; + new_act = S_IGN; break; #if JOBS case SIGTSTP: case SIGTTOU: if (mflag) - action = S_IGN; + new_act = S_IGN; break; #endif } } +//TODO: if !rootshell, we reset SIGQUIT to DFL, +//whereas we have to restore it to what shell got on entry +//from the parent. See comment above t = &sigmode[signo - 1]; - tsig = *t; - if (tsig == 0) { - /* - * current setting unknown - */ - if (sigaction(signo, 0, &act) == -1) { - /* - * Pretend it worked; maybe we should give a warning - * here, but other shells don't. We don't alter - * sigmode, so that we retry every time. - */ + cur_act = *t; + if (cur_act == 0) { + /* current setting is not yet known */ + if (sigaction(signo, NULL, &act)) { + /* pretend it worked; maybe we should give a warning, + * but other shells don't. We don't alter sigmode, + * so we retry every time. + * btw, in Linux it never fails. --vda */ return; } if (act.sa_handler == SIG_IGN) { + cur_act = S_HARD_IGN; if (mflag && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) ) { - tsig = S_IGN; /* don't hard ignore these */ - } else - tsig = S_HARD_IGN; - } else { - tsig = S_RESET; /* force to be set */ + cur_act = S_IGN; /* don't hard ignore these */ + } } } - if (tsig == S_HARD_IGN || tsig == action) + if (cur_act == S_HARD_IGN || cur_act == new_act) return; - switch (action) { + + act.sa_handler = SIG_DFL; + switch (new_act) { case S_CATCH: act.sa_handler = onsig; + act.sa_flags = 0; /* matters only if !DFL and !IGN */ + sigfillset(&act.sa_mask); /* ditto */ break; case S_IGN: act.sa_handler = SIG_IGN; break; - default: - act.sa_handler = SIG_DFL; } - *t = action; - act.sa_flags = 0; - sigfillset(&act.sa_mask); - sigaction(signo, &act, 0); + sigaction_set(signo, &act); + + *t = new_act; } /* mode flags for set_curjob */ @@ -3251,22 +3386,22 @@ setsignal(int signo) #define CUR_STOPPED 0 /* mode flags for dowait */ -#define DOWAIT_NORMAL 0 -#define DOWAIT_BLOCK 1 +#define DOWAIT_NONBLOCK WNOHANG +#define DOWAIT_BLOCK 0 #if JOBS /* pgrp of shell on invocation */ -static int initialpgrp; -static int ttyfd = -1; +static int initialpgrp; //references:2 +static int ttyfd = -1; //5 #endif /* array of jobs */ -static struct job *jobtab; +static struct job *jobtab; //5 /* size of array */ -static unsigned njobs; +static unsigned njobs; //4 /* current job */ -static struct job *curjob; +static struct job *curjob; //lots /* number of presumed living untracked jobs */ -static int jobless; +static int jobless; //4 static void set_curjob(struct job *jp, unsigned mode) @@ -3327,6 +3462,9 @@ jobno(const struct job *jp) /* * Convert a job name to a job structure. */ +#if !JOBS +#define getjob(name, getctl) getjob(name) +#endif static struct job * getjob(const char *name, int getctl) { @@ -3368,6 +3506,7 @@ getjob(const char *name, int getctl) } if (is_number(p)) { +// TODO: number() instead? It does error checking... num = atoi(p); if (num < njobs) { jp = jobtab + num - 1; @@ -3433,7 +3572,7 @@ static void xtcsetpgrp(int fd, pid_t pgrp) { if (tcsetpgrp(fd, pgrp)) - ash_msg_and_raise_error("cannot set tty process group (%m)"); + ash_msg_and_raise_error("can't set tty process group (%m)"); } /* @@ -3451,7 +3590,7 @@ setjobctl(int on) int fd; int pgrp; - if (on == jobctl || rootshell == 0) + if (on == doing_jobctl || rootshell == 0) return; if (on) { int ofd; @@ -3461,14 +3600,17 @@ setjobctl(int on) * That sometimes helps to acquire controlling tty. * Obviously, a workaround for bugs when someone * failed to provide a controlling tty to bash! :) */ - fd += 3; - while (!isatty(fd) && --fd >= 0) - ; + fd = 2; + while (!isatty(fd)) + if (--fd < 0) + goto out; } fd = fcntl(fd, F_DUPFD, 10); - close(ofd); + if (ofd >= 0) + close(ofd); if (fd < 0) goto out; + /* fd is a tty at this point */ close_on_exec_on(fd); do { /* while we are in the background */ pgrp = tcgetpgrp(fd); @@ -3495,25 +3637,26 @@ setjobctl(int on) fd = ttyfd; pgrp = initialpgrp; /* was xtcsetpgrp, but this can make exiting ash - * with pty already deleted loop forever */ + * loop forever if pty is already deleted */ tcsetpgrp(fd, pgrp); setpgid(0, pgrp); setsignal(SIGTSTP); setsignal(SIGTTOU); setsignal(SIGTTIN); close: - close(fd); + if (fd >= 0) + close(fd); fd = -1; } ttyfd = fd; - jobctl = on; + doing_jobctl = on; } static int killcmd(int argc, char **argv) { + int i = 1; if (argv[1] && strcmp(argv[1], "-l") != 0) { - int i = 1; do { if (argv[i][0] == '%') { struct job *jp = getjob(argv[i], 0); @@ -3575,7 +3718,7 @@ restartjob(struct job *jp, int mode) } static int -fg_bgcmd(int argc, char **argv) +fg_bgcmd(int argc UNUSED_PARAM, char **argv) { struct job *jp; FILE *out; @@ -3638,53 +3781,8 @@ sprint_status(char *s, int status, int sigonly) return col; } -/* - * Do a wait system call. If job control is compiled in, we accept - * stopped processes. If block is zero, we return a value of zero - * rather than blocking. - * - * System V doesn't have a non-blocking wait system call. It does - * have a SIGCLD signal that is sent to a process when one of it's - * children dies. The obvious way to use SIGCLD would be to install - * a handler for SIGCLD which simply bumped a counter when a SIGCLD - * was received, and have waitproc bump another counter when it got - * the status of a process. Waitproc would then know that a wait - * system call would not block if the two counters were different. - * This approach doesn't work because if a process has children that - * have not been waited for, System V will send it a SIGCLD when it - * installs a signal handler for SIGCLD. What this means is that when - * a child exits, the shell will be sent SIGCLD signals continuously - * until is runs out of stack space, unless it does a wait call before - * restoring the signal handler. The code below takes advantage of - * this (mis)feature by installing a signal handler for SIGCLD and - * then checking to see whether it was called. If there are any - * children to be waited for, it will be. - * - * If neither SYSV nor BSD is defined, we don't implement nonblocking - * waits at all. In this case, the user will not be informed when - * a background process until the next time she runs a real program - * (as opposed to running a builtin command or just typing return), - * and the jobs command may give out of date information. - */ -static int -waitproc(int block, int *status) -{ - int flags = 0; - -#if JOBS - if (jobctl) - flags |= WUNTRACED; -#endif - if (block == 0) - flags |= WNOHANG; - return wait3(status, flags, (struct rusage *)NULL); -} - -/* - * Wait for a process to terminate. - */ static int -dowait(int block, struct job *job) +dowait(int wait_flags, struct job *job) { int pid; int status; @@ -3692,11 +3790,17 @@ dowait(int block, struct job *job) struct job *thisjob; int state; - TRACE(("dowait(%d) called\n", block)); - pid = waitproc(block, &status); - TRACE(("wait returns pid %d, status=%d\n", pid, status)); + TRACE(("dowait(0x%x) called\n", wait_flags)); + + /* Do a wait system call. If job control is compiled in, we accept + * stopped processes. wait_flags may have WNOHANG, preventing blocking. + * NB: _not_ safe_waitpid, we need to detect EINTR */ + pid = waitpid(-1, &status, + (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); + TRACE(("wait returns pid=%d, status=0x%x\n", pid, status)); if (pid <= 0) return pid; + INT_OFF; thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { @@ -3732,7 +3836,6 @@ dowait(int block, struct job *job) #if JOBS if (!WIFSTOPPED(status)) #endif - jobless--; goto out; @@ -3762,13 +3865,22 @@ dowait(int block, struct job *job) len = sprint_status(s, status, 1); if (len) { s[len] = '\n'; - s[len + 1] = 0; + s[len + 1] = '\0'; out2str(s); } } return pid; } +static int +blocking_wait_with_raise_on_sig(struct job *job) +{ + pid_t pid = dowait(DOWAIT_BLOCK, job); + if (pid <= 0 && pendingsig) + raise_exception(EXSIG); + return pid; +} + #if JOBS static void showjob(FILE *out, struct job *jp, int mode) @@ -3848,8 +3960,8 @@ showjobs(FILE *out, int mode) TRACE(("showjobs(%x) called\n", mode)); - /* If not even one one job changed, there is nothing to do */ - while (dowait(DOWAIT_NORMAL, NULL) > 0) + /* Handle all finished jobs */ + while (dowait(DOWAIT_NONBLOCK, NULL) > 0) continue; for (jp = curjob; jp; jp = jp->prev_job) { @@ -3860,7 +3972,7 @@ showjobs(FILE *out, int mode) } static int -jobscmd(int argc, char **argv) +jobscmd(int argc UNUSED_PARAM, char **argv) { int mode, m; @@ -3913,13 +4025,16 @@ getstatus(struct job *job) } static int -waitcmd(int argc, char **argv) +waitcmd(int argc UNUSED_PARAM, char **argv) { struct job *job; int retval; struct job *jp; - EXSIGON; +// exsig++; +// xbarrier(); + if (pendingsig) + raise_exception(EXSIG); nextopt(nullstr); retval = 0; @@ -3930,16 +4045,21 @@ waitcmd(int argc, char **argv) for (;;) { jp = curjob; while (1) { - if (!jp) { - /* no running procs */ - goto out; - } + if (!jp) /* no running procs */ + goto ret; if (jp->state == JOBRUNNING) break; jp->waited = 1; jp = jp->prev_job; } - dowait(DOWAIT_BLOCK, 0); + /* man bash: + * "When bash is waiting for an asynchronous command via + * the wait builtin, the reception of a signal for which a trap + * has been set will cause the wait builtin to return immediately + * with an exit status greater than 128, immediately after which + * the trap is executed." + * Do we do it that way? */ + blocking_wait_with_raise_on_sig(NULL); } } @@ -3948,27 +4068,24 @@ waitcmd(int argc, char **argv) if (**argv != '%') { pid_t pid = number(*argv); job = curjob; - goto start; - do { + while (1) { + if (!job) + goto repeat; if (job->ps[job->nprocs - 1].pid == pid) break; job = job->prev_job; - start: - if (!job) - goto repeat; - } while (1); + } } else job = getjob(*argv, 0); /* loop until process terminated or stopped */ while (job->state == JOBRUNNING) - dowait(DOWAIT_BLOCK, 0); + blocking_wait_with_raise_on_sig(NULL); job->waited = 1; retval = getstatus(job); - repeat: - ; + repeat: ; } while (*++argv); - out: + ret: return retval; } @@ -4020,7 +4137,7 @@ growjobtab(void) * Called with interrupts off. */ static struct job * -makejob(union node *node, int nprocs) +makejob(/*union node *node,*/ int nprocs) { int i; struct job *jp; @@ -4035,7 +4152,7 @@ makejob(union node *node, int nprocs) if (jp->state != JOBDONE || !jp->waited) continue; #if JOBS - if (jobctl) + if (doing_jobctl) continue; #endif freejob(jp); @@ -4045,7 +4162,7 @@ makejob(union node *node, int nprocs) #if JOBS /* jp->jobctl is a bitfield. * "jp->jobctl |= jobctl" likely to give awful code */ - if (jobctl) + if (doing_jobctl) jp->jobctl = 1; #endif jp->prev_job = curjob; @@ -4055,7 +4172,7 @@ makejob(union node *node, int nprocs) if (nprocs > 1) { jp->ps = ckmalloc(nprocs * sizeof(struct procstat)); } - TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + TRACE(("makejob(%d) returns %%%d\n", nprocs, jobno(jp))); return jp; } @@ -4070,20 +4187,22 @@ static char *cmdnextc; static void cmdputs(const char *s) { + static const char vstype[VSTYPE + 1][3] = { + "", "}", "-", "+", "?", "=", + "%", "%%", "#", "##" + USE_ASH_BASH_COMPAT(, ":", "/", "//") + }; + const char *p, *str; char c, cc[2] = " "; char *nextc; int subtype = 0; int quoted = 0; - static const char vstype[VSTYPE + 1][4] = { - "", "}", "-", "+", "?", "=", - "%", "%%", "#", "##" - }; nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); p = s; while ((c = *p++) != 0) { - str = 0; + str = NULL; switch (c) { case CTLESC: c = *p++; @@ -4182,7 +4301,6 @@ cmdtxt(union node *n) union node *np; struct nodelist *lp; const char *p; - char s[2]; if (!n) return; @@ -4302,6 +4420,9 @@ cmdtxt(union node *n) case NAPPEND: p = ">>"; goto redir; +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NTOFD: p = ">&"; goto redir; @@ -4314,14 +4435,11 @@ cmdtxt(union node *n) case NFROMTO: p = "<>"; redir: - s[0] = n->nfile.fd + '0'; - s[1] = '\0'; - cmdputs(s); + cmdputs(utoa(n->nfile.fd)); cmdputs(p); if (n->type == NTOFD || n->type == NFROMFD) { - s[0] = n->ndup.dupfd + '0'; - p = s; - goto dotail2; + cmdputs(utoa(n->ndup.dupfd)); + break; } n = n->nfile.fname; goto donode; @@ -4367,7 +4485,7 @@ clear_traps(void) char **tp; for (tp = trap; tp < &trap[NSIG]; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ INT_OFF; free(*tp); *tp = NULL; @@ -4383,7 +4501,7 @@ static void closescript(void); /* Called after fork(), in child */ static void -forkchild(struct job *jp, union node *n, int mode) +forkchild(struct job *jp, /*union node *n,*/ int mode) { int oldlvl; @@ -4391,11 +4509,15 @@ forkchild(struct job *jp, union node *n, int mode) oldlvl = shlvl; shlvl++; + /* man bash: "Non-builtin commands run by bash have signal handlers + * set to the values inherited by the shell from its parent". + * Do we do it correctly? */ + closescript(); clear_traps(); #if JOBS /* do job control only in root shell */ - jobctl = 0; + doing_jobctl = 0; if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) { pid_t pgrp; @@ -4403,8 +4525,8 @@ forkchild(struct job *jp, union node *n, int mode) pgrp = getpid(); else pgrp = jp->ps[0].pid; - /* This can fail because we are doing it in the parent also */ - (void)setpgid(0, pgrp); + /* this can fail because we are doing it in the parent also */ + setpgid(0, pgrp); if (mode == FORK_FG) xtcsetpgrp(ttyfd, pgrp); setsignal(SIGTSTP); @@ -4412,6 +4534,8 @@ forkchild(struct job *jp, union node *n, int mode) } else #endif if (mode == FORK_BG) { + /* man bash: "When job control is not in effect, + * asynchronous commands ignore SIGINT and SIGQUIT" */ ignoresig(SIGINT); ignoresig(SIGQUIT); if (jp->nprocs == 0) { @@ -4420,10 +4544,18 @@ forkchild(struct job *jp, union node *n, int mode) ash_msg_and_raise_error("can't open %s", bb_dev_null); } } - if (!oldlvl && iflag) { - setsignal(SIGINT); + if (!oldlvl) { + if (iflag) { /* why if iflag only? */ + setsignal(SIGINT); + setsignal(SIGTERM); + } + /* man bash: + * "In all cases, bash ignores SIGQUIT. Non-builtin + * commands run by bash have signal handlers + * set to the values inherited by the shell + * from its parent". + * Take care of the second rule: */ setsignal(SIGQUIT); - setsignal(SIGTERM); } for (jp = curjob; jp; jp = jp->prev_job) freejob(jp); @@ -4431,12 +4563,16 @@ forkchild(struct job *jp, union node *n, int mode) } /* Called after fork(), in parent */ +#if !JOBS +#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) +#endif static void forkparent(struct job *jp, union node *n, int mode, pid_t pid) { TRACE(("In parent shell: child = %d\n", pid)); if (!jp) { - while (jobless && dowait(DOWAIT_NORMAL, 0) > 0); + while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) + continue; jobless++; return; } @@ -4462,7 +4598,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid) ps->status = -1; ps->cmd = nullstr; #if JOBS - if (jobctl && n) + if (doing_jobctl && n) ps->cmd = commandtext(n); #endif } @@ -4479,10 +4615,10 @@ forkshell(struct job *jp, union node *n, int mode) TRACE(("Fork failed, errno=%d", errno)); if (jp) freejob(jp); - ash_msg_and_raise_error("cannot fork"); + ash_msg_and_raise_error("can't fork"); } if (pid == 0) - forkchild(jp, n, mode); + forkchild(jp, /*n,*/ mode); else forkparent(jp, n, mode, pid); return pid; @@ -4491,12 +4627,12 @@ forkshell(struct job *jp, union node *n, int mode) /* * Wait for job to finish. * - * Under job control we have the problem that while a child process is - * running interrupts generated by the user are sent to the child but not - * to the shell. This means that an infinite loop started by an inter- - * active user may be hard to kill. With job control turned off, an - * interactive user may place an interactive program inside a loop. If - * the interactive program catches interrupts, the user doesn't want + * Under job control we have the problem that while a child process + * is running interrupts generated by the user are sent to the child + * but not to the shell. This means that an infinite loop started by + * an interactive user may be hard to kill. With job control turned off, + * an interactive user may place an interactive program inside a loop. + * If the interactive program catches interrupts, the user doesn't want * these interrupts to also abort the loop. The approach we take here * is to have the shell ignore interrupt signals while waiting for a * foreground process to terminate, and then send itself an interrupt @@ -4514,9 +4650,43 @@ waitforjob(struct job *jp) int st; TRACE(("waitforjob(%%%d) called\n", jobno(jp))); + + INT_OFF; while (jp->state == JOBRUNNING) { + /* In non-interactive shells, we _can_ get + * a keyboard signal here and be EINTRed, + * but we just loop back, waiting for command to complete. + * + * man bash: + * "If bash is waiting for a command to complete and receives + * a signal for which a trap has been set, the trap + * will not be executed until the command completes." + * + * Reality is that even if trap is not set, bash + * will not act on the signal until command completes. + * Try this. sleep5intoff.c: + * #include + * #include + * int main() { + * sigset_t set; + * sigemptyset(&set); + * sigaddset(&set, SIGINT); + * sigaddset(&set, SIGQUIT); + * sigprocmask(SIG_BLOCK, &set, NULL); + * sleep(5); + * return 0; + * } + * $ bash -c './sleep5intoff; echo hi' + * ^C^C^C^C <--- pressing ^C once a second + * $ _ + * $ bash -c './sleep5intoff; echo hi' + * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) + * $ _ + */ dowait(DOWAIT_BLOCK, jp); } + INT_ON; + st = getstatus(jp); #if JOBS if (jp->jobctl) { @@ -4529,8 +4699,8 @@ waitforjob(struct job *jp) * intuit from the subprocess exit status whether a SIGINT * occurred, and if so interrupt ourselves. Yuck. - mycroft */ - if (jp->sigint) - raise(SIGINT); + if (jp->sigint) /* TODO: do the same with all signals */ + raise(SIGINT); /* ... by raise(jp->sig) instead? */ } if (jp->state == JOBDONE) #endif @@ -4568,11 +4738,6 @@ stoppedjobs(void) #define EMPTY -2 /* marks an unused slot in redirtab */ #define CLOSED -3 /* marks a slot of previously-closed fd */ -#ifndef PIPE_BUF -# define PIPESIZE 4096 /* amount of buffering in a pipe */ -#else -# define PIPESIZE PIPE_BUF -#endif /* * Open a file in noclobber mode. @@ -4649,25 +4814,24 @@ openhere(union node *redir) ash_msg_and_raise_error("pipe call failed"); if (redir->type == NHERE) { len = strlen(redir->nhere.doc->narg.text); - if (len <= PIPESIZE) { + if (len <= PIPE_BUF) { full_write(pip[1], redir->nhere.doc->narg.text, len); goto out; } } if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + /* child */ close(pip[0]); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif + ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); + ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); + ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); + ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); signal(SIGPIPE, SIG_DFL); if (redir->type == NHERE) full_write(pip[1], redir->nhere.doc->narg.text, len); - else + else /* NXHERE */ expandhere(redir->nhere.doc, pip[1]); - _exit(0); + _exit(EXIT_SUCCESS); } out: close(pip[1]); @@ -4694,6 +4858,9 @@ openredirect(union node *redir) goto ecreate; break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif /* Take care of noclobber mode. */ if (Cflag) { fname = redir->nfile.expfname; @@ -4720,10 +4887,11 @@ openredirect(union node *redir) abort(); #endif /* Fall through to eliminate warning. */ - case NTOFD: - case NFROMFD: - f = -1; - break; +/* Our single caller does this itself */ +// case NTOFD: +// case NFROMFD: +// f = -1; +// break; case NHERE: case NXHERE: f = openhere(redir); @@ -4732,9 +4900,9 @@ openredirect(union node *redir) return f; ecreate: - ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory")); + ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory")); eopen: - ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file")); + ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file")); } /* @@ -4742,36 +4910,85 @@ openredirect(union node *redir) * if the source file descriptor is closed, EMPTY if there are no unused * file descriptors left. */ +/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD). + * old code was doing close(to) prior to copyfd() to achieve the same */ +enum { + COPYFD_EXACT = (int)~(INT_MAX), + COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1), +}; static int copyfd(int from, int to) { int newfd; - newfd = fcntl(from, F_DUPFD, to); + if (to & COPYFD_EXACT) { + to &= ~COPYFD_EXACT; + /*if (from != to)*/ + newfd = dup2(from, to); + } else { + newfd = fcntl(from, F_DUPFD, to); + } if (newfd < 0) { if (errno == EMFILE) return EMPTY; + /* Happens when source fd is not open: try "echo >&99" */ ash_msg_and_raise_error("%d: %m", from); } return newfd; } -static void -dupredirect(union node *redir, int f) +/* Struct def and variable are moved down to the first usage site */ +struct two_fd_t { + int orig, copy; +}; +struct redirtab { + struct redirtab *next; + int nullredirs; + int pair_count; + struct two_fd_t two_fd[0]; +}; +#define redirlist (G_var.redirlist) + +static int need_to_remember(struct redirtab *rp, int fd) { - int fd = redir->nfile.fd; + int i; + + if (!rp) /* remembering was not requested */ + return 0; - if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { - if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ - copyfd(redir->ndup.dupfd, fd); + for (i = 0; i < rp->pair_count; i++) { + if (rp->two_fd[i].orig == fd) { + /* already remembered */ + return 0; } - return; } + return 1; +} - if (f != fd) { - copyfd(f, fd); - close(f); +/* "hidden" fd is a fd used to read scripts, or a copy of such */ +static int is_hidden_fd(struct redirtab *rp, int fd) +{ + int i; + struct parsefile *pf; + + if (fd == -1) + return 0; + pf = g_parsefile; + while (pf) { + if (fd == pf->fd) { + return 1; + } + pf = pf->prev; } + if (!rp) + return 0; + fd |= COPYFD_RESTORE; + for (i = 0; i < rp->pair_count; i++) { + if (rp->two_fd[i].copy == fd) { + return 1; + } + } + return 0; } /* @@ -4787,96 +5004,164 @@ dupredirect(union node *redir, int f) static void redirect(union node *redir, int flags) { - union node *n; struct redirtab *sv; + int sv_pos; int i; int fd; int newfd; + int copied_fd2 = -1; - nullredirs++; + g_nullredirs++; if (!redir) { return; } + sv = NULL; + sv_pos = 0; INT_OFF; if (flags & REDIR_PUSH) { - sv = ckmalloc(sizeof(*sv)); + union node *tmp = redir; + do { + sv_pos++; +#if ENABLE_ASH_BASH_COMPAT + if (redir->nfile.type == NTO2) + sv_pos++; +#endif + tmp = tmp->nfile.next; + } while (tmp); + sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0])); sv->next = redirlist; + sv->pair_count = sv_pos; redirlist = sv; - sv->nullredirs = nullredirs - 1; - for (i = 0; i < 10; i++) - sv->renamed[i] = EMPTY; - nullredirs = 0; + sv->nullredirs = g_nullredirs - 1; + g_nullredirs = 0; + while (sv_pos > 0) { + sv_pos--; + sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY; + } } - n = redir; + do { - fd = n->nfile.fd; - if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) - && n->ndup.dupfd == fd) - continue; /* redirect from/to same file descriptor */ - - newfd = openredirect(n); - if (fd == newfd) { - /* Descriptor wasn't open before redirect. - * Mark it for close in the future */ - if (sv && sv->renamed[fd] == EMPTY) - sv->renamed[fd] = CLOSED; - continue; + fd = redir->nfile.fd; + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + int right_fd = redir->ndup.dupfd; + /* redirect from/to same file descriptor? */ + if (right_fd == fd) + continue; + /* echo >&10 and 10 is a fd opened to the sh script? */ + if (is_hidden_fd(sv, right_fd)) { + errno = EBADF; /* as if it is closed */ + ash_msg_and_raise_error("%d: %m", right_fd); + } + newfd = -1; + } else { + newfd = openredirect(redir); /* always >= 0 */ + if (fd == newfd) { + /* Descriptor wasn't open before redirect. + * Mark it for close in the future */ + if (need_to_remember(sv, fd)) { + goto remember_to_close; + } + continue; + } } - if (sv && sv->renamed[fd] == EMPTY) { +#if ENABLE_ASH_BASH_COMPAT + redirect_more: +#endif + if (need_to_remember(sv, fd)) { + /* Copy old descriptor */ i = fcntl(fd, F_DUPFD, 10); - +/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds + * are closed in popredir() in the child, preventing them from leaking + * into child. (popredir() also cleans up the mess in case of failures) + */ if (i == -1) { i = errno; if (i != EBADF) { - close(newfd); + /* Strange error (e.g. "too many files" EMFILE?) */ + if (newfd >= 0) + close(newfd); errno = i; ash_msg_and_raise_error("%d: %m", fd); /* NOTREACHED */ } - } else { - sv->renamed[fd] = i; + /* EBADF: it is not open - good, remember to close it */ + remember_to_close: + i = CLOSED; + } else { /* fd is open, save its copy */ + /* "exec fd>&-" should not close fds + * which point to script file(s). + * Force them to be restored afterwards */ + if (is_hidden_fd(sv, fd)) + i |= COPYFD_RESTORE; + } + if (fd == 2) + copied_fd2 = i; + sv->two_fd[sv_pos].orig = fd; + sv->two_fd[sv_pos].copy = i; + sv_pos++; + } + if (newfd < 0) { + /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */ + if (redir->ndup.dupfd < 0) { /* "fd>&-" */ close(fd); + } else { + copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT); } - } else { - close(fd); + } else if (fd != newfd) { /* move newfd to fd */ + copyfd(newfd, fd | COPYFD_EXACT); +#if ENABLE_ASH_BASH_COMPAT + if (!(redir->nfile.type == NTO2 && fd == 2)) +#endif + close(newfd); + } +#if ENABLE_ASH_BASH_COMPAT + if (redir->nfile.type == NTO2 && fd == 1) { + /* We already redirected it to fd 1, now copy it to 2 */ + newfd = 1; + fd = 2; + goto redirect_more; } - dupredirect(n, newfd); - } while ((n = n->nfile.next)); +#endif + } while ((redir = redir->nfile.next) != NULL); + INT_ON; - if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0) - preverrout_fd = sv->renamed[2]; + if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) + preverrout_fd = copied_fd2; } /* * Undo the effects of the last redirection. */ static void -popredir(int drop) +popredir(int drop, int restore) { struct redirtab *rp; int i; - if (--nullredirs >= 0) + if (--g_nullredirs >= 0) return; INT_OFF; rp = redirlist; - for (i = 0; i < 10; i++) { - if (rp->renamed[i] == CLOSED) { + for (i = 0; i < rp->pair_count; i++) { + int fd = rp->two_fd[i].orig; + int copy = rp->two_fd[i].copy; + if (copy == CLOSED) { if (!drop) - close(i); + close(fd); continue; } - if (rp->renamed[i] != EMPTY) { - if (!drop) { - close(i); - copyfd(rp->renamed[i], i); + if (copy != EMPTY) { + if (!drop || (restore && (copy & COPYFD_RESTORE))) { + copy &= ~COPYFD_RESTORE; + /*close(fd);*/ + copyfd(copy, fd | COPYFD_EXACT); } - close(rp->renamed[i]); + close(copy); } } redirlist = rp->next; - nullredirs = rp->nullredirs; + g_nullredirs = rp->nullredirs; free(rp); INT_ON; } @@ -4892,10 +5177,10 @@ static void clearredir(int drop) { for (;;) { - nullredirs = 0; + g_nullredirs = 0; if (!redirlist) break; - popredir(drop); + popredir(drop, /*restore:*/ 0); } } @@ -4908,7 +5193,8 @@ redirectsafe(union node *redir, int flags) struct jmploc jmploc; SAVE_INT(saveint); - err = setjmp(jmploc.loc) * 2; + /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ + err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; if (!err) { exception_handler = &jmploc; redirect(redir, flags); @@ -4926,6 +5212,19 @@ redirectsafe(union node *redir, int flags) * We have to deal with backquotes, shell variables, and file metacharacters. */ +#if ENABLE_ASH_MATH_SUPPORT_64 +typedef int64_t arith_t; +#define arith_t_type long long +#else +typedef long arith_t; +#define arith_t_type long +#endif + +#if ENABLE_ASH_MATH_SUPPORT +static arith_t dash_arith(const char *); +static arith_t arith(const char *expr, int *perrcode); +#endif + /* * expandarg flags */ @@ -5035,7 +5334,7 @@ _rmescapes(char *str, int flag) } q = r; if (len > 0) { - q = memcpy(q, str, len) + len; + q = (char *)memcpy(q, str, len) + len; } } inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; @@ -5130,8 +5429,8 @@ recordregion(int start, int end, int nulonly) ifsp = &ifsfirst; } else { INT_OFF; - ifsp = ckmalloc(sizeof(*ifsp)); - ifsp->next = NULL; + ifsp = ckzalloc(sizeof(*ifsp)); + /*ifsp->next = NULL; - ckzalloc did it */ ifslastp->next = ifsp; INT_ON; } @@ -5237,13 +5536,13 @@ exptilde(char *startp, char *p, int flag) */ struct backcmd { /* result of evalbackcmd */ int fd; /* file descriptor to read from */ - char *buf; /* buffer */ int nleft; /* number of chars in buffer */ + char *buf; /* buffer */ struct job *jp; /* job structure for command */ }; /* These forward decls are needed to use "eval" code for backticks handling: */ -static int back_exitstatus; /* exit status of backquoted command */ +static uint8_t back_exitstatus; /* exit status of backquoted command */ #define EV_EXIT 01 /* exit after evaluating tree */ static void evaltree(union node *, int); @@ -5256,9 +5555,8 @@ evalbackcmd(union node *n, struct backcmd *result) result->buf = NULL; result->nleft = 0; result->jp = NULL; - if (n == NULL) { + if (n == NULL) goto out; - } saveherefd = herefd; herefd = -1; @@ -5269,13 +5567,13 @@ evalbackcmd(union node *n, struct backcmd *result) if (pipe(pip) < 0) ash_msg_and_raise_error("pipe call failed"); - jp = makejob(n, 1); + jp = makejob(/*n,*/ 1); if (forkshell(jp, n, FORK_NOJOB) == 0) { FORCE_INT_ON; close(pip[0]); if (pip[1] != 1) { - close(1); - copyfd(pip[1], 1); + /*close(1);*/ + copyfd(pip[1], 1 | COPYFD_EXACT); close(pip[1]); } eflag = 0; @@ -5304,7 +5602,7 @@ expbackq(union node *cmd, int quoted, int quotes) char *p; char *dest; int startloc; - int syntax = quoted? DQSYNTAX : BASESYNTAX; + int syntax = quoted ? DQSYNTAX : BASESYNTAX; struct stackmark smark; INT_OFF; @@ -5324,7 +5622,7 @@ expbackq(union node *cmd, int quoted, int quotes) read: if (in.fd < 0) break; - i = safe_read(in.fd, buf, sizeof(buf)); + i = nonblock_safe_read(in.fd, buf, sizeof(buf)); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; @@ -5365,7 +5663,7 @@ expari(int quotes) int flag; int len; - /* ifsfree(); */ + /* ifsfree(); */ /* * This routine is slightly over-complicated for @@ -5415,15 +5713,19 @@ expari(int quotes) #endif /* argstr needs it */ -static char *evalvar(char *p, int flag); +static char *evalvar(char *p, int flag, struct strlist *var_str_list); /* * Perform variable and command substitution. If EXP_FULL is set, output CTLESC * characters to allow for further processing. Otherwise treat * $@ like $* since no splitting will be performed. + * + * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence + * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it + * for correct expansion of "B=$A" word. */ static void -argstr(char *p, int flag) +argstr(char *p, int flag, struct strlist *var_str_list) { static const char spclchars[] ALIGN1 = { '=', @@ -5525,7 +5827,7 @@ argstr(char *p, int flag) p[5] == CTLQUOTEMARK )) ) { - p = evalvar(p + 1, flag) + 1; + p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; goto start; } inquotes = !inquotes; @@ -5541,7 +5843,7 @@ argstr(char *p, int flag) length++; goto addquote; case CTLVAR: - p = evalvar(p, flag); + p = evalvar(p, flag, var_str_list); goto start; case CTLBACKQ: c = 0; @@ -5562,26 +5864,67 @@ argstr(char *p, int flag) } static char * -scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes, +scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes, int zero) { - char *loc; - char *loc2; +// This commented out code was added by James Simmons +// as part of a larger change when he added support for ${var/a/b}. +// However, it broke # and % operators: +// +//var=ababcdcd +// ok bad +//echo ${var#ab} abcdcd abcdcd +//echo ${var##ab} abcdcd abcdcd +//echo ${var#a*b} abcdcd ababcdcd (!) +//echo ${var##a*b} cdcd cdcd +//echo ${var#?} babcdcd ababcdcd (!) +//echo ${var##?} babcdcd babcdcd +//echo ${var#*} ababcdcd babcdcd (!) +//echo ${var##*} +//echo ${var%cd} ababcd ababcd +//echo ${var%%cd} ababcd abab (!) +//echo ${var%c*d} ababcd ababcd +//echo ${var%%c*d} abab ababcdcd (!) +//echo ${var%?} ababcdc ababcdc +//echo ${var%%?} ababcdc ababcdcd (!) +//echo ${var%*} ababcdcd ababcdcd +//echo ${var%%*} +// +// Commenting it back out helped. Remove it completely if it really +// is not needed. + + char *loc, *loc2; //, *full; char c; loc = startp; loc2 = rmesc; do { - int match; + int match; // = strlen(str); const char *s = loc2; + c = *loc2; if (zero) { *loc2 = '\0'; s = rmesc; } - match = pmatch(str, s); + match = pmatch(str, s); // this line was deleted + +// // chop off end if its '*' +// full = strrchr(str, '*'); +// if (full && full != str) +// match--; +// +// // If str starts with '*' replace with s. +// if ((*str == '*') && strlen(s) >= match) { +// full = xstrdup(s); +// strncpy(full+strlen(s)-match+1, str+1, match-1); +// } else +// full = xstrndup(str, match); +// match = strncmp(s, full, strlen(full)); +// free(full); +// *loc2 = c; - if (match) + if (match) // if (!match) return loc; if (quotes && *loc == CTLESC) loc++; @@ -5625,7 +5968,7 @@ scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes, return 0; } -static void varunset(const char *, const char *, const char *, int) ATTRIBUTE_NORETURN; +static void varunset(const char *, const char *, const char *, int) NORETURN; static void varunset(const char *end, const char *var, const char *umsg, int varflags) { @@ -5638,30 +5981,66 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) if (*end == CTLENDVAR) { if (varflags & VSNUL) tail = " or null"; - } else + } else { msg = umsg; + } } ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); } +#if ENABLE_ASH_BASH_COMPAT +static char * +parse_sub_pattern(char *arg, int inquotes) +{ + char *idx, *repl = NULL; + unsigned char c; + + idx = arg; + while (1) { + c = *arg; + if (!c) + break; + if (c == '/') { + /* Only the first '/' seen is our separator */ + if (!repl) { + repl = idx + 1; + c = '\0'; + } + } + *idx++ = c; + if (!inquotes && c == '\\' && arg[1] == '\\') + arg++; /* skip both \\, not just first one */ + arg++; + } + *idx = c; /* NUL */ + + return repl; +} +#endif /* ENABLE_ASH_BASH_COMPAT */ + static const char * -subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes) +subevalvar(char *p, char *str, int strloc, int subtype, + int startloc, int varflags, int quotes, struct strlist *var_str_list) { + struct nodelist *saveargbackq = argbackq; char *startp; char *loc; - int saveherefd = herefd; - struct nodelist *saveargbackq = argbackq; - int amount; char *rmesc, *rmescend; + USE_ASH_BASH_COMPAT(char *repl = NULL;) + USE_ASH_BASH_COMPAT(char null = '\0';) + USE_ASH_BASH_COMPAT(int pos, len, orig_len;) + int saveherefd = herefd; + int amount, workloc, resetloc; int zero; - char *(*scan)(char *, char *, char *, char *, int , int); + char *(*scan)(char*, char*, char*, char*, int, int); herefd = -1; - argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); + argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0, + var_str_list); STPUTC('\0', expdest); herefd = saveherefd; argbackq = saveargbackq; - startp = stackblock() + startloc; + startp = (char *)stackblock() + startloc; switch (subtype) { case VSASSIGN: @@ -5670,30 +6049,177 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla STADJUST(amount, expdest); return startp; +#if ENABLE_ASH_BASH_COMPAT + case VSSUBSTR: + loc = str = stackblock() + strloc; +// TODO: number() instead? It does error checking... + pos = atoi(loc); + len = str - startp - 1; + + /* *loc != '\0', guaranteed by parser */ + if (quotes) { + char *ptr; + + /* We must adjust the length by the number of escapes we find. */ + for (ptr = startp; ptr < (str - 1); ptr++) { + if (*ptr == CTLESC) { + len--; + ptr++; + } + } + } + orig_len = len; + + if (*loc++ == ':') { +// TODO: number() instead? It does error checking... + len = atoi(loc); + } else { + len = orig_len; + while (*loc && *loc != ':') + loc++; + if (*loc++ == ':') +// TODO: number() instead? It does error checking... + len = atoi(loc); + } + if (pos >= orig_len) { + pos = 0; + len = 0; + } + if (len > (orig_len - pos)) + len = orig_len - pos; + + for (str = startp; pos; str++, pos--) { + if (quotes && *str == CTLESC) + str++; + } + for (loc = startp; len; len--) { + if (quotes && *str == CTLESC) + *loc++ = *str++; + *loc++ = *str++; + } + *loc = '\0'; + amount = loc - expdest; + STADJUST(amount, expdest); + return loc; +#endif + case VSQUESTION: varunset(p, str, startp, varflags); /* NOTREACHED */ } + resetloc = expdest - (char *)stackblock(); - subtype -= VSTRIMRIGHT; -#if DEBUG - if (subtype < 0 || subtype > 3) - abort(); -#endif + /* We'll comeback here if we grow the stack while handling + * a VSREPLACE or VSREPLACEALL, since our pointers into the + * stack will need rebasing, and we'll need to remove our work + * areas each time + */ + USE_ASH_BASH_COMPAT(restart:) + + amount = expdest - ((char *)stackblock() + resetloc); + STADJUST(-amount, expdest); + startp = (char *)stackblock() + startloc; rmesc = startp; - rmescend = stackblock() + strloc; + rmescend = (char *)stackblock() + strloc; if (quotes) { rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); if (rmesc != startp) { rmescend = expdest; - startp = stackblock() + startloc; + startp = (char *)stackblock() + startloc; } } rmescend--; - str = stackblock() + strloc; + str = (char *)stackblock() + strloc; preglob(str, varflags & VSQUOTE, 0); + workloc = expdest - (char *)stackblock(); + +#if ENABLE_ASH_BASH_COMPAT + if (subtype == VSREPLACE || subtype == VSREPLACEALL) { + char *idx, *end, *restart_detect; + + if (!repl) { + repl = parse_sub_pattern(str, varflags & VSQUOTE); + if (!repl) + repl = &null; + } + + /* If there's no pattern to match, return the expansion unmolested */ + if (*str == '\0') + return 0; + + len = 0; + idx = startp; + end = str - 1; + while (idx < end) { + loc = scanright(idx, rmesc, rmescend, str, quotes, 1); + if (!loc) { + /* No match, advance */ + restart_detect = stackblock(); + STPUTC(*idx, expdest); + if (quotes && *idx == CTLESC) { + idx++; + len++; + STPUTC(*idx, expdest); + } + if (stackblock() != restart_detect) + goto restart; + idx++; + len++; + rmesc++; + continue; + } + + if (subtype == VSREPLACEALL) { + while (idx < loc) { + if (quotes && *idx == CTLESC) + idx++; + idx++; + rmesc++; + } + } else { + idx = loc; + } + + for (loc = repl; *loc; loc++) { + restart_detect = stackblock(); + STPUTC(*loc, expdest); + if (stackblock() != restart_detect) + goto restart; + len++; + } + + if (subtype == VSREPLACE) { + while (*idx) { + restart_detect = stackblock(); + STPUTC(*idx, expdest); + if (stackblock() != restart_detect) + goto restart; + len++; + idx++; + } + break; + } + } + + /* We've put the replaced text into a buffer at workloc, now + * move it to the right place and adjust the stack. + */ + startp = stackblock() + startloc; + STPUTC('\0', expdest); + memmove(startp, stackblock() + workloc, len); + startp[len++] = '\0'; + amount = expdest - ((char *)stackblock() + startloc + len - 1); + STADJUST(-amount, expdest); + return startp; + } +#endif /* ENABLE_ASH_BASH_COMPAT */ + subtype -= VSTRIMRIGHT; +#if DEBUG + if (subtype < 0 || subtype > 7) + abort(); +#endif /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */ zero = subtype >> 1; /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */ @@ -5716,7 +6242,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla * Add the value of a specialized variable to the stack string. */ static ssize_t -varvalue(char *name, int varflags, int flags) +varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) { int num; char *p; @@ -5807,12 +6333,38 @@ varvalue(char *name, int varflags, int flags) case '7': case '8': case '9': +// TODO: number() instead? It does error checking... num = atoi(name); if (num < 0 || num > shellparam.nparam) return -1; p = num ? shellparam.p[num - 1] : arg0; goto value; default: + /* NB: name has form "VAR=..." */ + + /* "A=a B=$A" case: var_str_list is a list of "A=a" strings + * which should be considered before we check variables. */ + if (var_str_list) { + unsigned name_len = (strchrnul(name, '=') - name) + 1; + p = NULL; + do { + char *str, *eq; + str = var_str_list->text; + eq = strchr(str, '='); + if (!eq) /* stop at first non-assignment */ + break; + eq++; + if (name_len == (unsigned)(eq - str) + && strncmp(str, name, name_len) == 0) { + p = eq; + /* goto value; - WRONG! */ + /* think "A=1 A=2 B=$A" */ + } + var_str_list = var_str_list->next; + } while (var_str_list); + if (p) + goto value; + } p = lookupvar(name); value: if (!p) @@ -5834,20 +6386,17 @@ varvalue(char *name, int varflags, int flags) * input string. */ static char * -evalvar(char *p, int flag) +evalvar(char *p, int flag, struct strlist *var_str_list) { - int subtype; - int varflags; + char varflags; + char subtype; + char quoted; + char easy; char *var; int patloc; - int c; int startloc; ssize_t varlen; - int easy; - int quotes; - int quoted; - quotes = flag & (EXP_FULL | EXP_CASE); varflags = *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; @@ -5857,7 +6406,7 @@ evalvar(char *p, int flag) p = strchr(p, '=') + 1; again: - varlen = varvalue(var, varflags, flag); + varlen = varvalue(var, varflags, flag, var_str_list); if (varflags & VSNUL) varlen--; @@ -5871,7 +6420,8 @@ evalvar(char *p, int flag) if (varlen < 0) { argstr( p, flag | EXP_TILDE | - (quoted ? EXP_QWORD : EXP_WORD) + (quoted ? EXP_QWORD : EXP_WORD), + var_str_list ); goto end; } @@ -5882,7 +6432,11 @@ evalvar(char *p, int flag) if (subtype == VSASSIGN || subtype == VSQUESTION) { if (varlen < 0) { - if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) { + if (subevalvar(p, var, /* strloc: */ 0, + subtype, startloc, varflags, + /* quotes: */ 0, + var_str_list) + ) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond @@ -5907,10 +6461,8 @@ evalvar(char *p, int flag) } if (subtype == VSNORMAL) { - if (!easy) - goto end; - record: - recordregion(startloc, expdest - (char *)stackblock(), quoted); + if (easy) + goto record; goto end; } @@ -5920,6 +6472,11 @@ evalvar(char *p, int flag) case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: +#if ENABLE_ASH_BASH_COMPAT + case VSSUBSTR: + case VSREPLACE: + case VSREPLACEALL: +#endif break; default: abort(); @@ -5933,8 +6490,11 @@ evalvar(char *p, int flag) */ STPUTC('\0', expdest); patloc = expdest - (char *)stackblock(); - if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags, quotes) == 0) { + if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, + startloc, varflags, + /* quotes: */ flag & (EXP_FULL | EXP_CASE), + var_str_list) + ) { int amount = expdest - ( (char *)stackblock() + patloc - 1 ); @@ -5942,14 +6502,15 @@ evalvar(char *p, int flag) } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); - goto record; + record: + recordregion(startloc, expdest - (char *)stackblock(), quoted); } end: if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { - c = *p++; + char c = *p++; if (c == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { @@ -6012,7 +6573,7 @@ ifsbreakup(char *string, struct arglist *arglist) continue; } *q = '\0'; - sp = stalloc(sizeof(*sp)); + sp = stzalloc(sizeof(*sp)); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; @@ -6025,10 +6586,11 @@ ifsbreakup(char *string, struct arglist *arglist) q = p; if (*p == CTLESC) p++; - if (strchr(ifs, *p) == NULL ) { + if (strchr(ifs, *p) == NULL) { p = q; break; - } else if (strchr(defifs, *p) == NULL) { + } + if (strchr(defifs, *p) == NULL) { if (ifsspc) { p++; ifsspc = 0; @@ -6052,7 +6614,7 @@ ifsbreakup(char *string, struct arglist *arglist) return; add: - sp = stalloc(sizeof(*sp)); + sp = stzalloc(sizeof(*sp)); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; @@ -6084,7 +6646,7 @@ addfname(const char *name) { struct strlist *sp; - sp = stalloc(sizeof(*sp)); + sp = stzalloc(sizeof(*sp)); sp->text = ststrdup(name); *exparg.lastp = sp; exparg.lastp = &sp->next; @@ -6184,8 +6746,8 @@ expmeta(char *enddir, char *name) p++; if (*p == '.') matchdot++; - while (! intpending && (dp = readdir(dirp)) != NULL) { - if (dp->d_name[0] == '.' && ! matchdot) + while (!intpending && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && !matchdot) continue; if (pmatch(start, dp->d_name)) { if (atend) { @@ -6200,7 +6762,7 @@ expmeta(char *enddir, char *name) } } closedir(dirp); - if (! atend) + if (!atend) endname[-1] = '/'; } @@ -6216,7 +6778,7 @@ msort(struct strlist *list, int len) return list; half = len >> 1; p = list; - for (n = half; --n >= 0; ) { + for (n = half; --n >= 0;) { q = p; p = p->next; } @@ -6269,7 +6831,7 @@ expsort(struct strlist *str) } static void -expandmeta(struct strlist *str, int flag) +expandmeta(struct strlist *str /*, int flag*/) { static const char metachars[] ALIGN1 = { '*', '?', '[', 0 @@ -6334,7 +6896,8 @@ expandarg(union node *arg, struct arglist *arglist, int flag) STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; - argstr(arg->narg.text, flag); + argstr(arg->narg.text, flag, + /* var_str_list: */ arglist ? arglist->list : NULL); p = _STPUTC('\0', expdest); expdest = p - 1; if (arglist == NULL) { @@ -6349,11 +6912,11 @@ expandarg(union node *arg, struct arglist *arglist, int flag) ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; - expandmeta(exparg.list, flag); + expandmeta(exparg.list /*, flag*/); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ rmescapes(p); - sp = stalloc(sizeof(*sp)); + sp = stzalloc(sizeof(*sp)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; @@ -6400,7 +6963,8 @@ casematch(union node *pattern, char *val) argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + argstr(pattern->narg.text, EXP_TILDE | EXP_CASE, + /* var_str_list: */ NULL); STACKSTRNUL(expdest); result = patmatch(stackblock(), val); popstackmark(&smark); @@ -6416,13 +6980,19 @@ struct builtincmd { /* unsigned flags; */ }; #define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) +/* "regular" builtins always take precedence over commands, + * regardless of PATH=....%builtin... position */ #define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2) -#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4) +#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4) struct cmdentry { - int cmdtype; + smallint cmdtype; /* CMDxxx */ union param { int index; + /* index >= 0 for commands without path (slashes) */ + /* (TODO: what exactly does the value mean? PATH position?) */ + /* index == -1 for commands with slashes */ + /* index == (-2 - applet_no) for NOFORK applets */ const struct builtincmd *cmd; struct funcnode *func; } u; @@ -6454,38 +7024,38 @@ static void find_command(char *, struct cmdentry *, int, const char *); * would make the command name "hash" a misnomer. */ -#define CMDTABLESIZE 31 /* should be prime */ -#define ARB 1 /* actual size determined at run time */ - struct tblentry { struct tblentry *next; /* next entry in hash chain */ union param param; /* definition of builtin function */ - short cmdtype; /* index identifying command */ + smallint cmdtype; /* CMDxxx */ char rehash; /* if set, cd done since entry created */ - char cmdname[ARB]; /* name of command */ + char cmdname[1]; /* name of command */ }; -static struct tblentry *cmdtable[CMDTABLESIZE]; -static int builtinloc = -1; /* index in path of %builtin, or -1 */ +static struct tblentry **cmdtable; +#define INIT_G_cmdtable() do { \ + cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \ +} while (0) + +static int builtinloc = -1; /* index in path of %builtin, or -1 */ + static void -tryexec(char *cmd, char **argv, char **envp) +tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) { int repeated = 0; #if ENABLE_FEATURE_SH_STANDALONE - if (strchr(cmd, '/') == NULL) { - const struct bb_applet *a; - - a = find_applet_by_name(cmd); - if (a) { - if (a->noexec) - run_appletstruct_and_exit(a, argv); - /* re-exec ourselves with the new arguments */ - execve(bb_busybox_exec_path, argv, envp); - /* If they called chroot or otherwise made the binary no longer - * executable, fall through */ + if (applet_no >= 0) { + if (APPLET_IS_NOEXEC(applet_no)) { + while (*envp) + putenv(*envp++); + run_applet_no_and_exit(applet_no, argv); } + /* re-exec ourselves with the new arguments */ + execve(bb_busybox_exec_path, argv, envp); + /* If they called chroot or otherwise made the binary no longer + * executable, fall through */ } #endif @@ -6497,22 +7067,25 @@ tryexec(char *cmd, char **argv, char **envp) #else execve(cmd, argv, envp); #endif - if (repeated++) { + if (repeated) { free(argv); - } else if (errno == ENOEXEC) { + return; + } + if (errno == ENOEXEC) { char **ap; char **new; for (ap = argv; *ap; ap++) - ; - ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); + continue; + ap = new = ckmalloc((ap - argv + 2) * sizeof(ap[0])); ap[1] = cmd; ap[0] = cmd = (char *)DEFAULT_SHELL; ap += 2; argv++; - while ((*ap++ = *argv++)) - ; + while ((*ap++ = *argv++) != NULL) + continue; argv = new; + repeated++; goto repeat; } } @@ -6521,8 +7094,7 @@ tryexec(char *cmd, char **argv, char **envp) * Exec a program. Never returns. If you change this routine, you may * have to change the find_command routine as well. */ -#define environment() listvars(VEXPORT, VUNSET, 0) -static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; +static void shellexec(char **, const char *, int) NORETURN; static void shellexec(char **argv, const char *path, int idx) { @@ -6530,21 +7102,24 @@ shellexec(char **argv, const char *path, int idx) int e; char **envp; int exerrno; +#if ENABLE_FEATURE_SH_STANDALONE + int applet_no = -1; +#endif - clearredir(1); - envp = environment(); - if (strchr(argv[0], '/') + clearredir(/*drop:*/ 1); + envp = listvars(VEXPORT, VUNSET, 0); + if (strchr(argv[0], '/') != NULL #if ENABLE_FEATURE_SH_STANDALONE - || find_applet_by_name(argv[0]) + || (applet_no = find_applet_by_name(argv[0])) >= 0 #endif ) { - tryexec(argv[0], argv, envp); + tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); e = errno; } else { e = ENOENT; while ((cmdname = padvance(&path, argv[0])) != NULL) { if (--idx < 0 && pathopt == NULL) { - tryexec(cmdname, argv, envp); + tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) e = errno; } @@ -6566,7 +7141,7 @@ shellexec(char **argv, const char *path, int idx) } exitstatus = exerrno; TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", - argv[0], e, suppressint )); + argv[0], e, suppressint)); ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); /* NOTREACHED */ } @@ -6648,9 +7223,11 @@ cmdlookup(const char *name, int add) pp = &cmdp->next; } if (add && cmdp == NULL) { - cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB - + strlen(name) + 1); - cmdp->next = NULL; + cmdp = *pp = ckzalloc(sizeof(struct tblentry) + + strlen(name) + /* + 1 - already done because + * tblentry::cmdname is char[1] */); + /*cmdp->next = NULL; - ckzalloc did it */ cmdp->cmdtype = CMDUNKNOWN; strcpy(cmdp->cmdname, name); } @@ -6694,7 +7271,7 @@ addcmdentry(char *name, struct cmdentry *entry) } static int -hashcmd(int argc, char **argv) +hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct tblentry **pp; struct tblentry *cmdp; @@ -6702,10 +7279,11 @@ hashcmd(int argc, char **argv) struct cmdentry entry; char *name; - while ((c = nextopt("r")) != '\0') { + if (nextopt("r") != '\0') { clearcmdentry(0); return 0; } + if (*argptr == NULL) { for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { for (cmdp = *pp; cmdp; cmdp = cmdp->next) { @@ -6715,13 +7293,16 @@ hashcmd(int argc, char **argv) } return 0; } + c = 0; while ((name = *argptr) != NULL) { cmdp = cmdlookup(name, 0); if (cmdp != NULL && (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + ) { delete_cmd_entry(); + } find_command(name, &entry, DO_ERR, pathval()); if (entry.cmdtype == CMDUNKNOWN) c = 1; @@ -6742,12 +7323,13 @@ hashcd(void) for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) { for (cmdp = *pp; cmdp; cmdp = cmdp->next) { - if (cmdp->cmdtype == CMDNORMAL || ( - cmdp->cmdtype == CMDBUILTIN && - !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) && - builtinloc > 0 - )) + if (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN + && !IS_BUILTIN_REGULAR(cmdp->param.cmd) + && builtinloc > 0) + ) { cmdp->rehash = 1; + } } } } @@ -6759,15 +7341,14 @@ hashcd(void) * Called with interrupts off. */ static void -changepath(const char *newval) +changepath(const char *new) { - const char *old, *new; - int idx; + const char *old; int firstchange; + int idx; int idx_bltin; old = pathval(); - new = newval; firstchange = 9999; /* assume no change */ idx = 0; idx_bltin = -1; @@ -6783,9 +7364,8 @@ changepath(const char *newval) break; if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) idx_bltin = idx; - if (*new == ':') { + if (*new == ':') idx++; - } new++, old++; } if (builtinloc < 0 && idx_bltin >= 0) @@ -6825,6 +7405,7 @@ changepath(const char *newval) #define TWHILE 26 #define TBEGIN 27 #define TEND 28 +typedef smallint token_id_t; /* first char is indicating which tokens mark the end of a list */ static const char *const tokname_array[] = { @@ -6943,7 +7524,7 @@ describe_command(char *command, int describe_command_verbose) case CMDNORMAL: { int j = entry.u.index; char *p; - if (j == -1) { + if (j < 0) { p = command; } else { do { @@ -6992,7 +7573,7 @@ describe_command(char *command, int describe_command_verbose) } static int -typecmd(int argc, char **argv) +typecmd(int argc UNUSED_PARAM, char **argv) { int i = 1; int err = 0; @@ -7003,7 +7584,7 @@ typecmd(int argc, char **argv) i++; verbose = 0; } - while (i < argc) { + while (argv[i]) { err |= describe_command(argv[i++], verbose); } return err; @@ -7011,7 +7592,7 @@ typecmd(int argc, char **argv) #if ENABLE_ASH_CMDCMD static int -commandcmd(int argc, char **argv) +commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int c; enum { @@ -7028,8 +7609,10 @@ commandcmd(int argc, char **argv) else if (c != 'p') abort(); #endif - if (verify) + /* Mimic bash: just "command -v" doesn't complain, it's a nop */ + if (verify && (*argptr != NULL)) { return describe_command(*argptr, verify - VERIFY_BRIEF); + } return 0; } @@ -7038,43 +7621,46 @@ commandcmd(int argc, char **argv) /* ============ eval.c */ -static int funcblocksize; /* size of structures in function */ -static int funcstringsize; /* size of strings in node */ -static void *funcblock; /* block to allocate function from */ -static char *funcstring; /* block to allocate strings from */ +static int funcblocksize; /* size of structures in function */ +static int funcstringsize; /* size of strings in node */ +static void *funcblock; /* block to allocate function from */ +static char *funcstring; /* block to allocate strings from */ /* flags in argument to evaltree */ -#define EV_EXIT 01 /* exit after evaluating tree */ -#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ #define EV_BACKCMD 04 /* command executing within back quotes */ -static const short nodesize[26] = { - SHELL_ALIGN(sizeof(struct ncmd)), - SHELL_ALIGN(sizeof(struct npipe)), - SHELL_ALIGN(sizeof(struct nredir)), - SHELL_ALIGN(sizeof(struct nredir)), - SHELL_ALIGN(sizeof(struct nredir)), - SHELL_ALIGN(sizeof(struct nbinary)), - SHELL_ALIGN(sizeof(struct nbinary)), - SHELL_ALIGN(sizeof(struct nbinary)), - SHELL_ALIGN(sizeof(struct nif)), - SHELL_ALIGN(sizeof(struct nbinary)), - SHELL_ALIGN(sizeof(struct nbinary)), - SHELL_ALIGN(sizeof(struct nfor)), - SHELL_ALIGN(sizeof(struct ncase)), - SHELL_ALIGN(sizeof(struct nclist)), - SHELL_ALIGN(sizeof(struct narg)), - SHELL_ALIGN(sizeof(struct narg)), - SHELL_ALIGN(sizeof(struct nfile)), - SHELL_ALIGN(sizeof(struct nfile)), - SHELL_ALIGN(sizeof(struct nfile)), - SHELL_ALIGN(sizeof(struct nfile)), - SHELL_ALIGN(sizeof(struct nfile)), - SHELL_ALIGN(sizeof(struct ndup)), - SHELL_ALIGN(sizeof(struct ndup)), - SHELL_ALIGN(sizeof(struct nhere)), - SHELL_ALIGN(sizeof(struct nhere)), - SHELL_ALIGN(sizeof(struct nnot)), +static const short nodesize[N_NUMBER] = { + [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), + [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), + [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)), + [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)), + [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)), + [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)), + [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)), + [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)), + [NIF ] = SHELL_ALIGN(sizeof(struct nif)), + [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)), + [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)), + [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)), + [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)), + [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)), + [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)), + [NARG ] = SHELL_ALIGN(sizeof(struct narg)), + [NTO ] = SHELL_ALIGN(sizeof(struct nfile)), +#if ENABLE_ASH_BASH_COMPAT + [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)), +#endif + [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)), + [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)), + [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)), + [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)), + [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)), + [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)), + [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)), + [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)), + [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)), }; static void calcsize(union node *n); @@ -7144,6 +7730,9 @@ calcsize(union node *n) calcsize(n->narg.next); break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NFROM: case NFROMTO: @@ -7215,7 +7804,7 @@ copynode(union node *n) break; case NPIPE: new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); - new->npipe.backgnd = n->npipe.backgnd; + new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; break; case NREDIR: case NBACKGND: @@ -7257,6 +7846,9 @@ copynode(union node *n) new->narg.next = copynode(n->narg.next); break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NFROM: case NFROMTO: @@ -7331,6 +7923,7 @@ static int evalskip; /* set if we are skipping commands */ #define SKIPEVAL (1 << 4) static int skipcount; /* number of levels to skip */ static int funcnest; /* depth of function calls */ +static int loopnest; /* current loop nesting level */ /* forward decl way out to parsing code - dotrap needs it */ static int evalstring(char *s, int mask); @@ -7346,27 +7939,27 @@ dotrap(void) char *q; int i; int savestatus; - int skip = 0; + int skip; savestatus = exitstatus; pendingsig = 0; xbarrier(); - for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) { + for (i = 1, q = gotsig; i < NSIG; i++, q++) { if (!*q) continue; *q = '\0'; - p = trap[i + 1]; + p = trap[i]; if (!p) continue; skip = evalstring(p, SKIPEVAL); exitstatus = savestatus; if (skip) - break; + return skip; } - return skip; + return 0; } /* forward declarations - evaluation is fairly recursive business... */ @@ -7387,16 +7980,33 @@ static void prehash(union node *); static void evaltree(union node *n, int flags) { + + struct jmploc *volatile savehandler = exception_handler; + struct jmploc jmploc; int checkexit = 0; void (*evalfn)(union node *, int); - unsigned isor; int status; + if (n == NULL) { TRACE(("evaltree(NULL) called\n")); - goto out; + goto out1; } TRACE(("pid %d, evaltree(%p: %d, %d) called\n", getpid(), n, n->type, flags)); + + exception_handler = &jmploc; + { + int err = setjmp(jmploc.loc); + if (err) { + /* if it was a signal, check for trap handlers */ + if (exception == EXSIG) + goto out; + /* continue on the way out */ + exception_handler = savehandler; + longjmp(exception_handler->loc, err); + } + } + switch (n->type) { default: #if DEBUG @@ -7415,7 +8025,7 @@ evaltree(union node *n, int flags) evaltree(n->nredir.n, flags & EV_TESTED); status = exitstatus; } - popredir(0); + popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); goto setstatus; case NCMD: evalfn = evalcommand; @@ -7442,19 +8052,20 @@ evaltree(union node *n, int flags) goto calleval; case NAND: case NOR: - case NSEMI: + case NSEMI: { + #if NAND + 1 != NOR #error NAND + 1 != NOR #endif #if NOR + 1 != NSEMI #error NOR + 1 != NSEMI #endif - isor = n->type - NAND; + unsigned is_or = n->type - NAND; evaltree( n->nbinary.ch1, - (flags | ((isor >> 1) - 1)) & EV_TESTED + (flags | ((is_or >> 1) - 1)) & EV_TESTED ); - if (!exitstatus == isor) + if (!exitstatus == is_or) break; if (!evalskip) { n = n->nbinary.ch2; @@ -7465,6 +8076,7 @@ evaltree(union node *n, int flags) break; } break; + } case NIF: evaltree(n->nif.test, EV_TESTED); if (evalskip) @@ -7485,8 +8097,11 @@ evaltree(union node *n, int flags) exitstatus = status; break; } + out: - if ((checkexit & exitstatus)) + exception_handler = savehandler; + out1: + if (checkexit & exitstatus) evalskip |= SKIPEVAL; else if (pendingsig && dotrap()) goto exexit; @@ -7502,8 +8117,6 @@ static #endif void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); -static int loopnest; /* current loop nesting level */ - static void evalloop(union node *n, int flags) { @@ -7549,6 +8162,7 @@ evalfor(union node *n, int flags) struct stackmark smark; setstackmark(&smark); + arglist.list = NULL; arglist.lastp = &arglist.list; for (argp = n->nfor.args; argp; argp = argp->narg.next) { expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD); @@ -7588,6 +8202,7 @@ evalcase(union node *n, int flags) struct stackmark smark; setstackmark(&smark); + arglist.list = NULL; arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); exitstatus = 0; @@ -7619,7 +8234,7 @@ evalsubshell(union node *n, int flags) if (!backgnd && flags & EV_EXIT && !trap[0]) goto nofork; INT_OFF; - jp = makejob(n, 1); + jp = makejob(/*n,*/ 1); if (forkshell(jp, n, backgnd) == 0) { INT_ON; flags |= EV_EXIT; @@ -7631,7 +8246,7 @@ evalsubshell(union node *n, int flags) /* never returns */ } status = 0; - if (! backgnd) + if (!backgnd) status = waitforjob(jp); exitstatus = status; INT_ON; @@ -7649,23 +8264,39 @@ expredir(union node *n) for (redir = n; redir; redir = redir->nfile.next) { struct arglist fn; - memset(&fn, 0, sizeof(fn)); + fn.list = NULL; fn.lastp = &fn.list; switch (redir->type) { case NFROMTO: case NFROM: case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); +#if ENABLE_ASH_BASH_COMPAT + store_expfname: +#endif redir->nfile.expfname = fn.list->text; break; case NFROMFD: - case NTOFD: + case NTOFD: /* >& */ if (redir->ndup.vname) { expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); if (fn.list == NULL) ash_msg_and_raise_error("redir error"); +#if ENABLE_ASH_BASH_COMPAT +//FIXME: we used expandarg with different args! + if (!isdigit_str9(fn.list->text)) { + /* >&file, not >&fd */ + if (redir->nfile.fd != 1) /* 123>&file - BAD */ + ash_msg_and_raise_error("redir error"); + redir->type = NTO2; + goto store_expfname; + } +#endif fixredir(redir, fn.list->text, 1); } break; @@ -7694,7 +8325,7 @@ evalpipe(union node *n, int flags) pipelen++; flags |= EV_EXIT; INT_OFF; - jp = makejob(n, pipelen); + jp = makejob(/*n,*/ pipelen); prevfd = -1; for (lp = n->npipe.cmdlist; lp; lp = lp->next) { prehash(lp->n); @@ -7705,7 +8336,7 @@ evalpipe(union node *n, int flags) ash_msg_and_raise_error("pipe call failed"); } } - if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { INT_ON; if (pip[1] >= 0) { close(pip[0]); @@ -7726,7 +8357,7 @@ evalpipe(union node *n, int flags) prevfd = pip[0]; close(pip[1]); } - if (n->npipe.backgnd == 0) { + if (n->npipe.pipe_backgnd == 0) { exitstatus = waitforjob(jp); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); } @@ -7739,7 +8370,7 @@ evalpipe(union node *n, int flags) static void setinteractive(int on) { - static int is_interactive; + static smallint is_interactive; if (++on == is_interactive) return; @@ -7765,15 +8396,6 @@ setinteractive(int on) #endif } -#if ENABLE_FEATURE_EDITING_VI -#define setvimode(on) do { \ - if (on) line_input_state->flags |= VI_MODE; \ - else line_input_state->flags &= ~VI_MODE; \ -} while (0) -#else -#define setvimode(on) viflag = 0 /* forcibly keep the option off */ -#endif - static void optschanged(void) { @@ -7782,7 +8404,14 @@ optschanged(void) #endif setinteractive(iflag); setjobctl(mflag); - setvimode(viflag); +#if ENABLE_FEATURE_EDITING_VI + if (viflag) + line_input_state->flags |= VI_MODE; + else + line_input_state->flags &= ~VI_MODE; +#else + viflag = 0; /* forcibly keep the option off */ +#endif } static struct localvar *localvars; @@ -7838,7 +8467,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) savehandler = exception_handler; exception_handler = &jmploc; localvars = NULL; - shellparam.malloc = 0; + shellparam.malloced = 0; func->count++; funcnest++; INT_ON; @@ -7849,7 +8478,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) shellparam.optoff = -1; #endif evaltree(&func->n, flags & EV_TESTED); -funcdone: + funcdone: INT_OFF; funcnest--; freefunc(func); @@ -7912,7 +8541,7 @@ mklocal(char *name) struct var *vp; INT_OFF; - lvp = ckmalloc(sizeof(struct localvar)); + lvp = ckzalloc(sizeof(struct localvar)); if (LONE_DASH(name)) { char *p; p = ckmalloc(sizeof(optlist)); @@ -7949,7 +8578,7 @@ mklocal(char *name) * The "local" command. */ static int -localcmd(int argc, char **argv) +localcmd(int argc UNUSED_PARAM, char **argv) { char *name; @@ -7961,21 +8590,21 @@ localcmd(int argc, char **argv) } static int -falsecmd(int argc, char **argv) +falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 1; } static int -truecmd(int argc, char **argv) +truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 0; } static int -execcmd(int argc, char **argv) +execcmd(int argc UNUSED_PARAM, char **argv) { - if (argc > 1) { + if (argv[1]) { iflag = 0; /* exit on error */ mflag = 0; optschanged(); @@ -7988,7 +8617,7 @@ execcmd(int argc, char **argv) * The return command. */ static int -returncmd(int argc, char **argv) +returncmd(int argc UNUSED_PARAM, char **argv) { /* * If called outside a function, do what ksh does; @@ -8002,19 +8631,13 @@ returncmd(int argc, char **argv) static int breakcmd(int, char **); static int dotcmd(int, char **); static int evalcmd(int, char **); -#if ENABLE_ASH_BUILTIN_ECHO -static int echocmd(int, char **); -#endif -#if ENABLE_ASH_BUILTIN_TEST -static int testcmd(int, char **); -#endif static int exitcmd(int, char **); static int exportcmd(int, char **); #if ENABLE_ASH_GETOPTS static int getoptscmd(int, char **); #endif #if !ENABLE_FEATURE_SH_EXTRA_QUIET -static int helpcmd(int argc, char **argv); +static int helpcmd(int, char **); #endif #if ENABLE_ASH_MATH_SUPPORT static int letcmd(int, char **); @@ -8037,13 +8660,31 @@ static int ulimitcmd(int, char **); #define BUILTIN_REG_ASSG "6" #define BUILTIN_SPEC_REG_ASSG "7" -/* make sure to keep these in proper order since it is searched via bsearch() */ +/* We do not handle [[ expr ]] bashism bash-compatibly, + * we make it a synonym of [ expr ]. + * Basically, word splitting and pathname expansion should NOT be performed + * Examples: + * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0" + * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0" + * Additional operators: + * || and && should work as -o and -a + * =~ regexp match + * Apart from the above, [[ expr ]] should work as [ expr ] + */ + +#define echocmd echo_main +#define printfcmd printf_main +#define testcmd test_main + +/* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG ".", dotcmd }, { BUILTIN_SPEC_REG ":", truecmd }, #if ENABLE_ASH_BUILTIN_TEST - { BUILTIN_REGULAR "[", testcmd }, - { BUILTIN_REGULAR "[[", testcmd }, + { BUILTIN_REGULAR "[", testcmd }, +#if ENABLE_ASH_BASH_COMPAT + { BUILTIN_REGULAR "[[", testcmd }, +#endif #endif #if ENABLE_ASH_ALIAS { BUILTIN_REG_ASSG "alias", aliascmd }, @@ -8084,6 +8725,9 @@ static const struct builtincmd builtintab[] = { { BUILTIN_NOSPEC "let", letcmd }, #endif { BUILTIN_ASSIGN "local", localcmd }, +#if ENABLE_ASH_BUILTIN_PRINTF + { BUILTIN_REGULAR "printf", printfcmd }, +#endif { BUILTIN_NOSPEC "pwd", pwdcmd }, { BUILTIN_REGULAR "read", readcmd }, { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd }, @@ -8092,7 +8736,7 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG "shift", shiftcmd }, { BUILTIN_SPEC_REG "source", dotcmd }, #if ENABLE_ASH_BUILTIN_TEST - { BUILTIN_REGULAR "test", testcmd }, + { BUILTIN_REGULAR "test", testcmd }, #endif { BUILTIN_SPEC_REG "times", timescmd }, { BUILTIN_SPEC_REG "trap", trapcmd }, @@ -8107,17 +8751,25 @@ static const struct builtincmd builtintab[] = { { BUILTIN_REGULAR "wait", waitcmd }, }; - -#define COMMANDCMD (builtintab + 5 + \ - 2 * ENABLE_ASH_BUILTIN_TEST + \ - ENABLE_ASH_ALIAS + \ - ENABLE_ASH_JOB_CONTROL) -#define EXECCMD (builtintab + 7 + \ - 2 * ENABLE_ASH_BUILTIN_TEST + \ - ENABLE_ASH_ALIAS + \ - ENABLE_ASH_JOB_CONTROL + \ - ENABLE_ASH_CMDCMD + \ - ENABLE_ASH_BUILTIN_ECHO) +/* Should match the above table! */ +#define COMMANDCMD (builtintab + \ + 2 + \ + 1 * ENABLE_ASH_BUILTIN_TEST + \ + 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ + 1 * ENABLE_ASH_ALIAS + \ + 1 * ENABLE_ASH_JOB_CONTROL + \ + 3) +#define EXECCMD (builtintab + \ + 2 + \ + 1 * ENABLE_ASH_BUILTIN_TEST + \ + 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ + 1 * ENABLE_ASH_ALIAS + \ + 1 * ENABLE_ASH_JOB_CONTROL + \ + 3 + \ + 1 * ENABLE_ASH_CMDCMD + \ + 1 + \ + ENABLE_ASH_BUILTIN_ECHO + \ + 1) /* * Search the table of builtin commands. @@ -8137,7 +8789,6 @@ find_builtin(const char *name) /* * Execute a simple command. */ -static int back_exitstatus; /* exit status of backquoted command */ static int isassignment(const char *p) { @@ -8147,7 +8798,7 @@ isassignment(const char *p) return *q == '='; } static int -bltincmd(int argc, char **argv) +bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { /* Preserve exitstatus of a previous possible redirection * as POSIX mandates */ @@ -8156,8 +8807,8 @@ bltincmd(int argc, char **argv) static void evalcommand(union node *cmd, int flags) { - static const struct builtincmd bltin = { - "\0\0", bltincmd + static const struct builtincmd null_bltin = { + "\0\0", bltincmd /* why three NULs? */ }; struct stackmark smark; union node *argp; @@ -8171,11 +8822,11 @@ evalcommand(union node *cmd, int flags) char *lastarg; const char *path; int spclbltin; - int cmd_is_exec; int status; char **nargv; struct builtincmd *bcmd; - int pseudovarflag = 0; + smallint cmd_is_exec; + smallint pseudovarflag = 0; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); @@ -8183,7 +8834,7 @@ evalcommand(union node *cmd, int flags) back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; - cmdentry.u.cmd = &bltin; + cmdentry.u.cmd = &null_bltin; varlist.lastp = &varlist.list; *varlist.lastp = NULL; arglist.lastp = &arglist.list; @@ -8259,7 +8910,7 @@ evalcommand(union node *cmd, int flags) } sp = arglist.list; } - full_write(preverrout_fd, "\n", 1); + safe_write(preverrout_fd, "\n", 1); } cmd_is_exec = 0; @@ -8275,8 +8926,8 @@ evalcommand(union node *cmd, int flags) for (;;) { find_command(argv[0], &cmdentry, cmd_flag, path); if (cmdentry.cmdtype == CMDUNKNOWN) { - status = 127; flush_stderr(); + status = 127; goto bail; } @@ -8286,7 +8937,7 @@ evalcommand(union node *cmd, int flags) if (spclbltin < 0) spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd); if (cmdentry.u.cmd == EXECCMD) - cmd_is_exec++; + cmd_is_exec = 1; #if ENABLE_ASH_CMDCMD if (cmdentry.u.cmd == COMMANDCMD) { path = oldpath; @@ -8314,10 +8965,25 @@ evalcommand(union node *cmd, int flags) /* Execute the command. */ switch (cmdentry.cmdtype) { default: + +#if ENABLE_FEATURE_SH_NOFORK +/* Hmmm... shouldn't it happen somewhere in forkshell() instead? + * Why "fork off a child process if necessary" doesn't apply to NOFORK? */ + { + /* find_command() encodes applet_no as (-2 - applet_no) */ + int applet_no = (- cmdentry.u.index - 2); + if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { + listsetvar(varlist.list, VEXPORT|VSTACK); + /* run _main() */ + exitstatus = run_nofork_applet(applet_no, argv); + break; + } + } +#endif /* Fork off a child process if necessary. */ if (!(flags & EV_EXIT) || trap[0]) { INT_OFF; - jp = makejob(cmd, 1); + jp = makejob(/*cmd,*/ 1); if (forkshell(jp, cmd, FORK_FG) != 0) { exitstatus = waitforjob(jp); INT_ON; @@ -8341,24 +9007,23 @@ evalcommand(union node *cmd, int flags) } listsetvar(list, i); } + /* Tight loop with builtins only: + * "while kill -0 $child; do true; done" + * will never exit even if $child died, unless we do this + * to reap the zombie and make kill detect that it's gone: */ + dowait(DOWAIT_NONBLOCK, NULL); + if (evalbltin(cmdentry.u.cmd, argc, argv)) { int exit_status; - int i, j; - - i = exception; + int i = exception; if (i == EXEXIT) goto raise; - exit_status = 2; - j = 0; if (i == EXINT) - j = SIGINT; + exit_status = 128 + SIGINT; if (i == EXSIG) - j = pendingsig; - if (j) - exit_status = j + 128; + exit_status = 128 + pendingsig; exitstatus = exit_status; - if (i == EXINT || spclbltin > 0) { raise: longjmp(exception_handler->loc, 1); @@ -8369,19 +9034,22 @@ evalcommand(union node *cmd, int flags) case CMDFUNCTION: listsetvar(varlist.list, 0); + /* See above for the rationale */ + dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) goto raise; break; } out: - popredir(cmd_is_exec); - if (lastarg) + popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); + if (lastarg) { /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ setvar("_", lastarg, 0); + } popstackmark(&smark); } @@ -8408,7 +9076,7 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) exitstatus |= ferror(stdout); clearerr(stdout); commandname = savecmdname; - exsig = 0; +// exsig = 0; exception_handler = savehandler; return i; @@ -8454,9 +9122,9 @@ prehash(union node *n) * in the standard shell so we don't make it one here. */ static int -breakcmd(int argc, char **argv) +breakcmd(int argc UNUSED_PARAM, char **argv) { - int n = argc > 1 ? number(argv[1]) : 1; + int n = argv[1] ? number(argv[1]) : 1; if (n <= 0) ash_msg_and_raise_error(illnum, argv[1]); @@ -8475,35 +9143,64 @@ breakcmd(int argc, char **argv) * This implements the input routines used by the parser. */ -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ - enum { INPUT_PUSH_FILE = 1, INPUT_NOFILE_OK = 2, }; -static int plinno = 1; /* input line number */ -/* number of characters left in input buffer */ -static int parsenleft; /* copy of parsefile->nleft */ -static int parselleft; /* copy of parsefile->lleft */ -/* next character in input buffer */ -static char *parsenextc; /* copy of parsefile->nextc */ - -static int checkkwd; +static smallint checkkwd; /* values of checkkwd variable */ #define CHKALIAS 0x1 #define CHKKWD 0x2 #define CHKNL 0x4 +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +#if !ENABLE_ASH_ALIAS +#define pushstring(s, ap) pushstring(s) +#endif +static void +pushstring(char *s, struct alias *ap) +{ + struct strpush *sp; + int len; + + len = strlen(s); + INT_OFF; + if (g_parsefile->strpush) { + sp = ckzalloc(sizeof(*sp)); + sp->prev = g_parsefile->strpush; + } else { + sp = &(g_parsefile->basestrpush); + } + g_parsefile->strpush = sp; + sp->prev_string = g_parsefile->next_to_pgetc; + sp->prev_left_in_line = g_parsefile->left_in_line; +#if ENABLE_ASH_ALIAS + sp->ap = ap; + if (ap) { + ap->flag |= ALIASINUSE; + sp->string = s; + } +#endif + g_parsefile->next_to_pgetc = s; + g_parsefile->left_in_line = len; + INT_ON; +} + static void popstring(void) { - struct strpush *sp = parsefile->strpush; + struct strpush *sp = g_parsefile->strpush; INT_OFF; #if ENABLE_ASH_ALIAS if (sp->ap) { - if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (g_parsefile->next_to_pgetc[-1] == ' ' + || g_parsefile->next_to_pgetc[-1] == '\t' + ) { checkkwd |= CHKALIAS; } if (sp->string != sp->ap->val) { @@ -8515,26 +9212,30 @@ popstring(void) } } #endif - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; -/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ - parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) + g_parsefile->next_to_pgetc = sp->prev_string; + g_parsefile->left_in_line = sp->prev_left_in_line; + g_parsefile->strpush = sp->prev; + if (sp != &(g_parsefile->basestrpush)) free(sp); INT_ON; } +//FIXME: BASH_COMPAT with "...&" does TWO pungetc(): +//it peeks whether it is &>, and then pushes back both chars. +//This function needs to save last *next_to_pgetc to buf[0] +//to make two pungetc() reliable. Currently, +// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... static int preadfd(void) { int nr; - char *buf = parsefile->buf; - parsenextc = buf; + char *buf = g_parsefile->buf; - retry: + g_parsefile->next_to_pgetc = buf; #if ENABLE_FEATURE_EDITING - if (!iflag || parsefile->fd) - nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + retry: + if (!iflag || g_parsefile->fd != STDIN_FILENO) + nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1); else { #if ENABLE_FEATURE_TAB_COMPLETION line_input_state->path_lookup = pathval(); @@ -8551,19 +9252,21 @@ preadfd(void) goto retry; } if (nr < 0 && errno == 0) { - /* Ctrl+D presend */ + /* Ctrl+D pressed */ nr = 0; } } #else - nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + nr = nonblock_safe_read(g_parsefile->fd, buf, BUFSIZ - 1); #endif +#if 0 +/* nonblock_safe_read() handles this problem */ if (nr < 0) { if (parsefile->fd == 0 && errno == EWOULDBLOCK) { int flags = fcntl(0, F_GETFL); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; + if (flags >= 0 && (flags & O_NONBLOCK)) { + flags &= ~O_NONBLOCK; if (fcntl(0, F_SETFL, flags) >= 0) { out2str("sh: turning off NDELAY mode\n"); goto retry; @@ -8571,6 +9274,7 @@ preadfd(void) } } } +#endif return nr; } @@ -8578,94 +9282,137 @@ preadfd(void) * Refill the input buffer and return the next input character: * * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading - * from a string so we can't refill the buffer, return EOF. + * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM) + * or we are reading from a string so we can't refill the buffer, + * return EOF. * 3) If the is more stuff in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ +//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) +#define pgetc_debug(...) ((void)0) static int preadbuffer(void) { char *q; int more; - char savec; - while (parsefile->strpush) { + while (g_parsefile->strpush) { #if ENABLE_ASH_ALIAS - if (parsenleft == -1 && parsefile->strpush->ap && - parsenextc[-1] != ' ' && parsenextc[-1] != '\t') { + if (g_parsefile->left_in_line == -1 + && g_parsefile->strpush->ap + && g_parsefile->next_to_pgetc[-1] != ' ' + && g_parsefile->next_to_pgetc[-1] != '\t' + ) { + pgetc_debug("preadbuffer PEOA"); return PEOA; } #endif popstring(); - if (--parsenleft >= 0) - return signed_char2int(*parsenextc++); - } - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + /* try "pgetc" now: */ + pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); + if (--g_parsefile->left_in_line >= 0) + return (unsigned char)(*g_parsefile->next_to_pgetc++); + } + /* on both branches above g_parsefile->left_in_line < 0. + * "pgetc" needs refilling. + */ + + /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read", + * pungetc() may increment it a few times. + * Assuming it won't increment it to less than -90. + */ + if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) { + pgetc_debug("preadbuffer PEOF1"); + /* even in failure keep left_in_line and next_to_pgetc + * in lock step, for correct multi-layer pungetc. + * left_in_line was decremented before preadbuffer(), + * must inc next_to_pgetc: */ + g_parsefile->next_to_pgetc++; return PEOF; - flush_stdout_stderr(); + } - more = parselleft; + more = g_parsefile->left_in_buffer; if (more <= 0) { + flush_stdout_stderr(); again: more = preadfd(); if (more <= 0) { - parselleft = parsenleft = EOF_NLEFT; + /* don't try reading again */ + g_parsefile->left_in_line = -99; + pgetc_debug("preadbuffer PEOF2"); + g_parsefile->next_to_pgetc++; return PEOF; } } - q = parsenextc; - - /* delete nul characters */ + /* Find out where's the end of line. + * Set g_parsefile->left_in_line + * and g_parsefile->left_in_buffer acordingly. + * NUL chars are deleted. + */ + q = g_parsefile->next_to_pgetc; for (;;) { - int c; + char c; more--; - c = *q; - if (!c) + c = *q; + if (c == '\0') { memmove(q, q + 1, more); - else { + } else { q++; if (c == '\n') { - parsenleft = q - parsenextc - 1; + g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; break; } } if (more <= 0) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) + g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; + if (g_parsefile->left_in_line < 0) goto again; break; } } - parselleft = more; - - savec = *q; - *q = '\0'; + g_parsefile->left_in_buffer = more; if (vflag) { - out2str(parsenextc); + char save = *q; + *q = '\0'; + out2str(g_parsefile->next_to_pgetc); + *q = save; } - *q = savec; - - return signed_char2int(*parsenextc++); + pgetc_debug("preadbuffer at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); + return (unsigned char)(*g_parsefile->next_to_pgetc++); } -#define pgetc_as_macro() (--parsenleft >= 0? signed_char2int(*parsenextc++) : preadbuffer()) +#define pgetc_as_macro() \ + (--g_parsefile->left_in_line >= 0 \ + ? (unsigned char)(*g_parsefile->next_to_pgetc++) \ + : preadbuffer() \ + ) + static int pgetc(void) { + pgetc_debug("pgetc_fast at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); return pgetc_as_macro(); } #if ENABLE_ASH_OPTIMIZE_FOR_SIZE -#define pgetc_macro() pgetc() +#define pgetc_fast() pgetc() #else -#define pgetc_macro() pgetc_as_macro() +#define pgetc_fast() pgetc_as_macro() #endif /* @@ -8676,18 +9423,17 @@ static int pgetc2(void) { int c; - do { - c = pgetc_macro(); + pgetc_debug("pgetc_fast at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); + c = pgetc_fast(); } while (c == PEOA); return c; } #else -static int -pgetc2(void) -{ - return pgetc_macro(); -} +#define pgetc2() pgetc() #endif /* @@ -8716,47 +9462,18 @@ pfgets(char *line, int len) } /* - * Undo the last call to pgetc. Only one character may be pushed back. - * PEOF may be pushed back. - */ -static void -pungetc(void) -{ - parsenleft++; - parsenextc--; -} - -/* - * Push a string back onto the input at this current parsefile level. - * We handle aliases this way. + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. */ static void -pushstring(char *s, void *ap) +pungetc(void) { - struct strpush *sp; - size_t len; - - len = strlen(s); - INT_OFF; -/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ - if (parsefile->strpush) { - sp = ckmalloc(sizeof(struct strpush)); - sp->prev = parsefile->strpush; - parsefile->strpush = sp; - } else - sp = parsefile->strpush = &(parsefile->basestrpush); - sp->prevstring = parsenextc; - sp->prevnleft = parsenleft; -#if ENABLE_ASH_ALIAS - sp->ap = (struct alias *)ap; - if (ap) { - ((struct alias *)ap)->flag |= ALIASINUSE; - sp->string = s; - } -#endif - parsenextc = s; - parsenleft = len; - INT_ON; + g_parsefile->left_in_line++; + g_parsefile->next_to_pgetc--; + pgetc_debug("pushed back to %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); } /* @@ -8768,22 +9485,18 @@ pushfile(void) { struct parsefile *pf; - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; - pf = ckmalloc(sizeof(*pf)); - pf->prev = parsefile; + pf = ckzalloc(sizeof(*pf)); + pf->prev = g_parsefile; pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; - parsefile = pf; + /*pf->strpush = NULL; - ckzalloc did it */ + /*pf->basestrpush.prev = NULL;*/ + g_parsefile = pf; } static void popfile(void) { - struct parsefile *pf = parsefile; + struct parsefile *pf = g_parsefile; INT_OFF; if (pf->fd >= 0) @@ -8791,12 +9504,8 @@ popfile(void) free(pf->buf); while (pf->strpush) popstring(); - parsefile = pf->prev; + g_parsefile = pf->prev; free(pf); - parsenleft = parsefile->nleft; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - plinno = parsefile->linno; INT_ON; } @@ -8806,7 +9515,7 @@ popfile(void) static void popallfiles(void) { - while (parsefile != &basepf) + while (g_parsefile != &basepf) popfile(); } @@ -8818,9 +9527,9 @@ static void closescript(void) { popallfiles(); - if (parsefile->fd > 0) { - close(parsefile->fd); - parsefile->fd = 0; + if (g_parsefile->fd > 0) { + close(g_parsefile->fd); + g_parsefile->fd = 0; } } @@ -8834,13 +9543,14 @@ setinputfd(int fd, int push) close_on_exec_on(fd); if (push) { pushfile(); - parsefile->buf = 0; + g_parsefile->buf = NULL; } - parsefile->fd = fd; - if (parsefile->buf == NULL) - parsefile->buf = ckmalloc(IBUFSIZ); - parselleft = parsenleft = 0; - plinno = 1; + g_parsefile->fd = fd; + if (g_parsefile->buf == NULL) + g_parsefile->buf = ckmalloc(IBUFSIZ); + g_parsefile->left_in_buffer = 0; + g_parsefile->left_in_line = 0; + g_parsefile->linno = 1; } /* @@ -8881,10 +9591,10 @@ setinputstring(char *string) { INT_OFF; pushfile(); - parsenextc = string; - parsenleft = strlen(string); - parsefile->buf = NULL; - plinno = 1; + g_parsefile->next_to_pgetc = string; + g_parsefile->left_in_line = strlen(string); + g_parsefile->buf = NULL; + g_parsefile->linno = 1; INT_ON; } @@ -8927,7 +9637,8 @@ chkmail(void) break; if (*p == '\0') continue; - for (q = p; *q; q++); + for (q = p; *q; q++) + continue; #if DEBUG if (q[-1] != '/') abort(); @@ -8950,7 +9661,7 @@ chkmail(void) } static void -changemail(const char *val) +changemail(const char *val UNUSED_PARAM) { mail_var_path_changed = 1; } @@ -8970,14 +9681,15 @@ setparam(char **argv) char **ap; int nparam; - for (nparam = 0; argv[nparam]; nparam++); + for (nparam = 0; argv[nparam]; nparam++) + continue; ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap)); while (*argv) { *ap++ = ckstrdup(*argv++); } *ap = NULL; freeparam(&shellparam); - shellparam.malloc = 1; + shellparam.malloced = 1; shellparam.nparam = nparam; shellparam.p = newparam; #if ENABLE_ASH_GETOPTS @@ -8989,9 +9701,26 @@ setparam(char **argv) /* * Process shell options. The global variable argptr contains a pointer * to the argument list; we advance it past the options. + * + * SUSv3 section 2.8.1 "Consequences of Shell Errors" says: + * For a non-interactive shell, an error condition encountered + * by a special built-in ... shall cause the shell to write a diagnostic message + * to standard error and exit as shown in the following table: + * Error Special Built-In + * ... + * Utility syntax error (option or operand error) Shall exit + * ... + * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142) + * we see that bash does not do that (set "finishes" with error code 1 instead, + * and shell continues), and people rely on this behavior! + * Testcase: + * set -o barfoo 2>/dev/null + * echo $? + * + * Oh well. Let's mimic that. */ -static void -minus_o(char *name, int val) +static int +plus_minus_o(char *name, int val) { int i; @@ -8999,15 +9728,20 @@ minus_o(char *name, int val) for (i = 0; i < NOPTS; i++) { if (strcmp(name, optnames(i)) == 0) { optlist[i] = val; - return; + return 0; } } - ash_msg_and_raise_error("illegal option -o %s", name); + ash_msg("illegal option %co %s", val ? '-' : '+', name); + return 1; + } + for (i = 0; i < NOPTS; i++) { + if (val) { + out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off"); + } else { + out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i)); + } } - out1str("Current option settings\n"); - for (i = 0; i < NOPTS; i++) - out1fmt("%-16s%s\n", optnames(i), - optlist[i] ? "on" : "off"); + return 0; } static void setoption(int flag, int val) @@ -9020,10 +9754,10 @@ setoption(int flag, int val) return; } } - ash_msg_and_raise_error("illegal option -%c", flag); + ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag); /* NOTREACHED */ } -static void +static int options(int cmdline) { char *p; @@ -9058,7 +9792,10 @@ options(int cmdline) if (c == 'c' && cmdline) { minusc = p; /* command is after shell args */ } else if (c == 'o') { - minus_o(*argptr, val); + if (plus_minus_o(*argptr, val)) { + /* it already printed err message */ + return 1; /* error */ + } if (*argptr) argptr++; } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ @@ -9073,30 +9810,32 @@ options(int cmdline) } } } + return 0; } /* * The shift builtin command. */ static int -shiftcmd(int argc, char **argv) +shiftcmd(int argc UNUSED_PARAM, char **argv) { int n; char **ap1, **ap2; n = 1; - if (argc > 1) + if (argv[1]) n = number(argv[1]); if (n > shellparam.nparam) - ash_msg_and_raise_error("can't shift that many"); + n = 0; /* bash compat, was = shellparam.nparam; */ INT_OFF; shellparam.nparam -= n; for (ap1 = shellparam.p; --n >= 0; ap1++) { - if (shellparam.malloc) + if (shellparam.malloced) free(*ap1); } ap2 = shellparam.p; - while ((*ap2++ = *ap1++) != NULL); + while ((*ap2++ = *ap1++) != NULL) + continue; #if ENABLE_ASH_GETOPTS shellparam.optind = 1; shellparam.optoff = -1; @@ -9140,37 +9879,57 @@ showvars(const char *sep_prefix, int on, int off) * The set command builtin. */ static int -setcmd(int argc, char **argv) +setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - if (argc == 1) + int retval; + + if (!argv[1]) return showvars(nullstr, 0, VUNSET); INT_OFF; - options(0); - optschanged(); - if (*argptr != NULL) { - setparam(argptr); + retval = 1; + if (!options(0)) { /* if no parse error... */ + retval = 0; + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } } INT_ON; - return 0; + return retval; } #if ENABLE_ASH_RANDOM_SUPPORT -/* Roughly copied from bash.. */ static void change_random(const char *value) { + /* Galois LFSR parameter */ + /* Taps at 32 31 29 1: */ + enum { MASK = 0x8000000b }; + /* Another example - taps at 32 31 30 10: */ + /* MASK = 0x00400007 */ + if (value == NULL) { /* "get", generate */ - char buf[16]; - - rseed = rseed * 1103515245 + 12345; - sprintf(buf, "%d", (unsigned int)((rseed & 32767))); + uint32_t t; + + /* LCG has period of 2^32 and alternating lowest bit */ + random_LCG = 1664525 * random_LCG + 1013904223; + /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ + t = (random_galois_LFSR << 1); + if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */ + t ^= MASK; + random_galois_LFSR = t; + /* Both are weak, combining them gives better randomness + * and ~2^64 period. & 0x7fff is probably bash compat + * for $RANDOM range. Combining with subtraction is + * just for fun. + and ^ would work equally well. */ + t = (t - random_LCG) & 0x7fff; /* set without recursion */ - setvar(vrandom.text, buf, VNOFUNC); + setvar(vrandom.text, utoa(t), VNOFUNC); vrandom.flags &= ~VNOFUNC; } else { /* set/reset */ - rseed = strtoul(value, (char **)NULL, 10); + random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10); } } #endif @@ -9190,7 +9949,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt return 1; optnext = optfirst + *param_optind - 1; - if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff) + if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff) p = NULL; else p = optnext[-1] + *optoff; @@ -9209,7 +9968,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt } c = *p++; - for (q = optstr; *q != c; ) { + for (q = optstr; *q != c;) { if (*q == '\0') { if (optstr[0] == ':') { s[0] = c; @@ -9299,22 +10058,30 @@ getoptscmd(int argc, char **argv) /* ============ Shell parser */ -/* - * NEOF is returned by parsecmd when it encounters an end of file. It - * must be distinct from NULL, so we use the address of a variable that - * happens to be handy. - */ +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + smallint striptabs; /* if set, strip leading tabs */ +}; + static smallint tokpushback; /* last token pushed back */ -#define NEOF ((union node *)&tokpushback) static smallint parsebackquote; /* nonzero if we are inside backquotes */ -static int lasttoken; /* last token read */ +static smallint quoteflag; /* set if (part of) last token was quoted */ +static token_id_t lasttoken; /* last token read (integer id Txxx) */ +static struct heredoc *heredoclist; /* list of here documents to read */ static char *wordtext; /* text of last word returned by readtoken */ static struct nodelist *backquotelist; static union node *redirnode; static struct heredoc *heredoc; -static smallint quoteflag; /* set if (part of) last token was quoted */ +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +#define NEOF ((union node *)&tokpushback) -static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN; +static void raise_error_syntax(const char *) NORETURN; static void raise_error_syntax(const char *msg) { @@ -9327,14 +10094,14 @@ raise_error_syntax(const char *msg) * is the token that is expected, or -1 if more than one type of token can * occur at this point. */ -static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN; +static void raise_error_unexpected_syntax(int) NORETURN; static void raise_error_unexpected_syntax(int token) { char msg[64]; int l; - l = sprintf(msg, "%s unexpected", tokname(lasttoken)); + l = sprintf(msg, "unexpected %s", tokname(lasttoken)); if (token >= 0) sprintf(msg + l, " (expecting %s)", tokname(token)); raise_error_syntax(msg); @@ -9343,15 +10110,6 @@ raise_error_unexpected_syntax(int token) #define EOFMARKLEN 79 -struct heredoc { - struct heredoc *next; /* next here document in list */ - union node *here; /* redirection node */ - char *eofmark; /* string indicating end of input */ - int striptabs; /* if set, strip leading tabs */ -}; - -static struct heredoc *heredoclist; /* list of here documents to read */ - /* parsing is heavily cross-recursive, need these forward decls */ static union node *andor(void); static union node *pipeline(void); @@ -9375,12 +10133,12 @@ list(int nlflag) tok = readtoken(); if (tok == TBACKGND) { if (n2->type == NPIPE) { - n2->npipe.backgnd = 1; + n2->npipe.pipe_backgnd = 1; } else { if (n2->type != NREDIR) { - n3 = stalloc(sizeof(struct nredir)); + n3 = stzalloc(sizeof(struct nredir)); n3->nredir.n = n2; - n3->nredir.redirect = NULL; + /*n3->nredir.redirect = NULL; - stzalloc did it */ n2 = n3; } n2->type = NBACKGND; @@ -9389,7 +10147,7 @@ list(int nlflag) if (n1 == NULL) { n1 = n2; } else { - n3 = stalloc(sizeof(struct nbinary)); + n3 = stzalloc(sizeof(struct nbinary)); n3->type = NSEMI; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; @@ -9446,7 +10204,7 @@ andor(void) } checkkwd = CHKNL | CHKKWD | CHKALIAS; n2 = pipeline(); - n3 = stalloc(sizeof(struct nbinary)); + n3 = stzalloc(sizeof(struct nbinary)); n3->type = t; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; @@ -9470,15 +10228,15 @@ pipeline(void) tokpushback = 1; n1 = parse_command(); if (readtoken() == TPIPE) { - pipenode = stalloc(sizeof(struct npipe)); + pipenode = stzalloc(sizeof(struct npipe)); pipenode->type = NPIPE; - pipenode->npipe.backgnd = 0; - lp = stalloc(sizeof(struct nodelist)); + /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */ + lp = stzalloc(sizeof(struct nodelist)); pipenode->npipe.cmdlist = lp; lp->n = n1; do { prev = lp; - lp = stalloc(sizeof(struct nodelist)); + lp = stzalloc(sizeof(struct nodelist)); checkkwd = CHKNL | CHKKWD | CHKALIAS; lp->n = parse_command(); prev->next = lp; @@ -9488,7 +10246,7 @@ pipeline(void) } tokpushback = 1; if (negate) { - n2 = stalloc(sizeof(struct nnot)); + n2 = stzalloc(sizeof(struct nnot)); n2->type = NNOT; n2->nnot.com = n1; return n2; @@ -9501,9 +10259,9 @@ makename(void) { union node *n; - n = stalloc(sizeof(struct narg)); + n = stzalloc(sizeof(struct narg)); n->type = NARG; - n->narg.next = NULL; + /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; n->narg.backquote = backquotelist; return n; @@ -9512,17 +10270,20 @@ makename(void) static void fixredir(union node *n, const char *text, int err) { + int fd; + TRACE(("Fix redir %s %d\n", text, err)); if (!err) n->ndup.vname = NULL; - if (isdigit(text[0]) && text[1] == '\0') - n->ndup.dupfd = text[0] - '0'; + fd = bb_strtou(text, NULL, 10); + if (!errno && fd >= 0) + n->ndup.dupfd = fd; else if (LONE_DASH(text)) n->ndup.dupfd = -1; else { if (err) - raise_error_syntax("Bad fd number"); + raise_error_syntax("bad fd number"); n->ndup.vname = makename(); } } @@ -9565,14 +10326,15 @@ parsefname(void) n->type = NXHERE; TRACE(("Here document %d\n", n->type)); if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) - raise_error_syntax("Illegal eof marker for << redirection"); + raise_error_syntax("illegal eof marker for << redirection"); rmescapes(wordtext); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) heredoclist = here; else { - for (p = heredoclist; p->next; p = p->next); + for (p = heredoclist; p->next; p = p->next) + continue; p->next = here; } } else if (n->type == NTOFD || n->type == NFROMFD) { @@ -9590,6 +10352,9 @@ simplecmd(void) union node *vars, **vpp; union node **rpp, *redir; int savecheckkwd; +#if ENABLE_ASH_BASH_COMPAT + smallint double_brackets_flag = 0; +#endif args = NULL; app = &args; @@ -9600,12 +10365,30 @@ simplecmd(void) savecheckkwd = CHKALIAS; for (;;) { + int t; checkkwd = savecheckkwd; - switch (readtoken()) { + t = readtoken(); + switch (t) { +#if ENABLE_ASH_BASH_COMPAT + case TAND: /* "&&" */ + case TOR: /* "||" */ + if (!double_brackets_flag) { + tokpushback = 1; + goto out; + } + wordtext = (char *) (t == TAND ? "-a" : "-o"); +#endif case TWORD: - n = stalloc(sizeof(struct narg)); + n = stzalloc(sizeof(struct narg)); n->type = NARG; + /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; +#if ENABLE_ASH_BASH_COMPAT + if (strcmp("[[", wordtext) == 0) + double_brackets_flag = 1; + else if (strcmp("]]", wordtext) == 0) + double_brackets_flag = 0; +#endif n->narg.backquote = backquotelist; if (savecheckkwd && isassignment(wordtext)) { *vpp = n; @@ -9635,7 +10418,7 @@ simplecmd(void) if (!goodname(name) || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) ) { - raise_error_syntax("Bad function name"); + raise_error_syntax("bad function name"); } n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; @@ -9652,7 +10435,7 @@ simplecmd(void) *app = NULL; *vpp = NULL; *rpp = NULL; - n = stalloc(sizeof(struct ncmd)); + n = stzalloc(sizeof(struct ncmd)); n->type = NCMD; n->ncmd.args = args; n->ncmd.assign = vars; @@ -9678,7 +10461,7 @@ parse_command(void) raise_error_unexpected_syntax(-1); /* NOTREACHED */ case TIF: - n1 = stalloc(sizeof(struct nif)); + n1 = stzalloc(sizeof(struct nif)); n1->type = NIF; n1->nif.test = list(0); if (readtoken() != TTHEN) @@ -9686,7 +10469,7 @@ parse_command(void) n1->nif.ifpart = list(0); n2 = n1; while (readtoken() == TELIF) { - n2->nif.elsepart = stalloc(sizeof(struct nif)); + n2->nif.elsepart = stzalloc(sizeof(struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; n2->nif.test = list(0); @@ -9705,7 +10488,7 @@ parse_command(void) case TWHILE: case TUNTIL: { int got; - n1 = stalloc(sizeof(struct nbinary)); + n1 = stzalloc(sizeof(struct nbinary)); n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL; n1->nbinary.ch1 = list(0); got = readtoken(); @@ -9719,17 +10502,18 @@ parse_command(void) break; } case TFOR: - if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) - raise_error_syntax("Bad for loop variable"); - n1 = stalloc(sizeof(struct nfor)); + if (readtoken() != TWORD || quoteflag || !goodname(wordtext)) + raise_error_syntax("bad for loop variable"); + n1 = stzalloc(sizeof(struct nfor)); n1->type = NFOR; n1->nfor.var = wordtext; checkkwd = CHKKWD | CHKALIAS; if (readtoken() == TIN) { app = ≈ while (readtoken() == TWORD) { - n2 = stalloc(sizeof(struct narg)); + n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; + /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = wordtext; n2->narg.backquote = backquotelist; *app = n2; @@ -9740,11 +10524,11 @@ parse_command(void) if (lasttoken != TNL && lasttoken != TSEMI) raise_error_unexpected_syntax(-1); } else { - n2 = stalloc(sizeof(struct narg)); + n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; + /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = (char *)dolatstr; - n2->narg.backquote = NULL; - n2->narg.next = NULL; + /*n2->narg.backquote = NULL;*/ n1->nfor.args = n2; /* * Newline or semicolon here is optional (but note @@ -9760,15 +10544,15 @@ parse_command(void) t = TDONE; break; case TCASE: - n1 = stalloc(sizeof(struct ncase)); + n1 = stzalloc(sizeof(struct ncase)); n1->type = NCASE; if (readtoken() != TWORD) raise_error_unexpected_syntax(TWORD); - n1->ncase.expr = n2 = stalloc(sizeof(struct narg)); + n1->ncase.expr = n2 = stzalloc(sizeof(struct narg)); n2->type = NARG; + /*n2->narg.next = NULL; - stzalloc did it */ n2->narg.text = wordtext; n2->narg.backquote = backquotelist; - n2->narg.next = NULL; do { checkkwd = CHKKWD | CHKALIAS; } while (readtoken() == TNL); @@ -9781,12 +10565,13 @@ parse_command(void) while (t != TESAC) { if (lasttoken == TLP) readtoken(); - *cpp = cp = stalloc(sizeof(struct nclist)); + *cpp = cp = stzalloc(sizeof(struct nclist)); cp->type = NCLIST; app = &cp->nclist.pattern; for (;;) { - *app = ap = stalloc(sizeof(struct narg)); + *app = ap = stzalloc(sizeof(struct narg)); ap->type = NARG; + /*ap->narg.next = NULL; - stzalloc did it */ ap->narg.text = wordtext; ap->narg.backquote = backquotelist; if (readtoken() != TPIPE) @@ -9794,7 +10579,7 @@ parse_command(void) app = &ap->narg.next; readtoken(); } - ap->narg.next = NULL; + //ap->narg.next = NULL; if (lasttoken != TRP) raise_error_unexpected_syntax(TRP); cp->nclist.body = list(2); @@ -9812,10 +10597,10 @@ parse_command(void) *cpp = NULL; goto redir; case TLP: - n1 = stalloc(sizeof(struct nredir)); + n1 = stzalloc(sizeof(struct nredir)); n1->type = NSUBSHELL; n1->nredir.n = list(0); - n1->nredir.redirect = NULL; + /*n1->nredir.redirect = NULL; - stzalloc did it */ t = TRP; break; case TBEGIN: @@ -9844,7 +10629,7 @@ parse_command(void) *rpp = NULL; if (redir) { if (n1->type != NSUBSHELL) { - n2 = stalloc(sizeof(struct nredir)); + n2 = stzalloc(sizeof(struct nredir)); n2->type = NREDIR; n2->nredir.n = n1; n1 = n2; @@ -9854,6 +10639,52 @@ parse_command(void) return n1; } +#if ENABLE_ASH_BASH_COMPAT +static int decode_dollar_squote(void) +{ + static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; + int c, cnt; + char *p; + char buf[4]; + + c = pgetc(); + p = strchr(C_escapes, c); + if (p) { + buf[0] = c; + p = buf; + cnt = 3; + if ((unsigned char)(c - '0') <= 7) { /* \ooo */ + do { + c = pgetc(); + *++p = c; + } while ((unsigned char)(c - '0') <= 7 && --cnt); + pungetc(); + } else if (c == 'x') { /* \xHH */ + do { + c = pgetc(); + *++p = c; + } while (isxdigit(c) && --cnt); + pungetc(); + if (cnt == 3) { /* \x but next char is "bad" */ + c = 'x'; + goto unrecognized; + } + } else { /* simple seq like \\ or \t */ + p++; + } + *p = '\0'; + p = buf; + c = bb_process_escape_sequence((void*)&p); + } else { /* unrecognized "\z": print both chars unless ' or " */ + if (c != '\'' && c != '"') { + unrecognized: + c |= 0x100; /* "please encode \, then me" */ + } + } + return c; +} +#endif + /* * If eofmark is NULL, read a word or a redirection symbol. If eofmark * is not NULL, read a here document. In the latter case, eofmark is the @@ -9865,14 +10696,12 @@ parse_command(void) * using goto's to implement the subroutine linkage. The following macros * will run code that appears at the end of readtoken1. */ - #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} - static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs) { @@ -9894,6 +10723,8 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) int parenlevel; /* levels of parens in arithmetic */ int dqvarnest; /* levels of variables expansion within double quotes */ + USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) + #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; @@ -9907,7 +10738,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) (void) &prevsyntax; (void) &syntax; #endif - startlinno = plinno; + startlinno = g_parsefile->linno; bqlist = NULL; quotef = 0; oldstyle = 0; @@ -9924,7 +10755,9 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) dqvarnest = 0; STARTSTACKSTR(out); - loop: { /* for each line, until end of word */ + loop: + /* For each line, until end of word */ + { CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ @@ -9933,7 +10766,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - plinno++; + g_parsefile->linno++; if (doprompt) setprompt(2); c = pgetc(); @@ -9944,6 +10777,15 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) case CCTL: if (eofmark == NULL || dblquote) USTPUTC(CTLESC, out); +#if ENABLE_ASH_BASH_COMPAT + if (c == '\\' && bash_dollar_squote) { + c = decode_dollar_squote(); + if (c & 0x100) { + USTPUTC('\\', out); + c = (unsigned char)c; + } + } +#endif USTPUTC(c, out); break; case CBACK: /* backslash */ @@ -9962,11 +10804,9 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) USTPUTC('\\', out); } #endif - if (dblquote && - c != '\\' && c != '`' && - c != '$' && ( - c != '"' || - eofmark != NULL) + if (dblquote && c != '\\' + && c != '`' && c != '$' + && (c != '"' || eofmark != NULL) ) { USTPUTC(CTLESC, out); USTPUTC('\\', out); @@ -9989,6 +10829,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) dblquote = 1; goto quotemark; case CENDQUOTE: + USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;) if (eofmark != NULL && arinest == 0 && varnest == 0 ) { @@ -10052,42 +10893,53 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) case CIGN: break; default: - if (varnest == 0) + if (varnest == 0) { +#if ENABLE_ASH_BASH_COMPAT + if (c == '&') { + if (pgetc() == '>') + c = 0x100 + '>'; /* flag &> */ + pungetc(); + } +#endif goto endword; /* exit outer loop */ + } #if ENABLE_ASH_ALIAS if (c != PEOA) #endif USTPUTC(c, out); } - c = pgetc_macro(); - } + c = pgetc_fast(); + } /* for (;;) */ } endword: #if ENABLE_ASH_MATH_SUPPORT if (syntax == ARISYNTAX) - raise_error_syntax("Missing '))'"); + raise_error_syntax("missing '))'"); #endif if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) - raise_error_syntax("Unterminated quoted string"); + raise_error_syntax("unterminated quoted string"); if (varnest != 0) { - startlinno = plinno; + startlinno = g_parsefile->linno; /* { */ - raise_error_syntax("Missing '}'"); + raise_error_syntax("missing '}'"); } USTPUTC('\0', out); len = out - (char *)stackblock(); out = stackblock(); if (eofmark == NULL) { - if ((c == '>' || c == '<') + if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>')) && quotef == 0 - && len <= 2 - && (*out == '\0' || isdigit(*out))) { - PARSEREDIR(); - return lasttoken = TREDIR; - } else { - pungetc(); + ) { + if (isdigit_str9(out)) { + PARSEREDIR(); /* passed as params: out, c */ + lasttoken = TREDIR; + return lasttoken; + } + /* else: non-number X seen, interpret it + * as "NNNX>file" = "NNNX >file" */ } + pungetc(); } quoteflag = quotef; backquotelist = bqlist; @@ -10119,10 +10971,11 @@ checkend: { char *p, *q; p = line; - for (q = eofmark + 1; *q && *p == *q; p++, q++); + for (q = eofmark + 1; *q && *p == *q; p++, q++) + continue; if (*p == '\n' && *q == '\0') { c = PEOF; - plinno++; + g_parsefile->linno++; needprompt = doprompt; } else { pushstring(line, NULL); @@ -10139,10 +10992,11 @@ checkend: { * first character of the redirection operator. */ parseredir: { - char fd = *out; + /* out is already checked to be a valid number or "" */ + int fd = (*out == '\0' ? -1 : atoi(out)); union node *np; - np = stalloc(sizeof(struct nfile)); + np = stzalloc(sizeof(struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); @@ -10152,27 +11006,36 @@ parseredir: { np->type = NCLOBBER; else if (c == '&') np->type = NTOFD; + /* it also can be NTO2 (>&file), but we can't figure it out yet */ else { np->type = NTO; pungetc(); } - } else { /* c == '<' */ - np->nfile.fd = 0; + } +#if ENABLE_ASH_BASH_COMPAT + else if (c == 0x100 + '>') { /* this flags &> redirection */ + np->nfile.fd = 1; + pgetc(); /* this is '>', no need to check */ + np->type = NTO2; + } +#endif + else { /* c == '<' */ + /*np->nfile.fd = 0; - stzalloc did it */ c = pgetc(); switch (c) { case '<': if (sizeof(struct nfile) != sizeof(struct nhere)) { - np = stalloc(sizeof(struct nhere)); - np->nfile.fd = 0; + np = stzalloc(sizeof(struct nhere)); + /*np->nfile.fd = 0; - stzalloc did it */ } np->type = NHERE; - heredoc = stalloc(sizeof(struct heredoc)); + heredoc = stzalloc(sizeof(struct heredoc)); heredoc->here = np; c = pgetc(); if (c == '-') { heredoc->striptabs = 1; } else { - heredoc->striptabs = 0; + /*heredoc->striptabs = 0; - stzalloc did it */ pungetc(); } break; @@ -10191,8 +11054,8 @@ parseredir: { break; } } - if (fd != '\0') - np->nfile.fd = fd - '0'; + if (fd >= 0) + np->nfile.fd = fd; redirnode = np; goto parseredir_return; } @@ -10205,8 +11068,8 @@ parseredir: { /* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise * (assuming ascii char codes, as the original implementation did) */ #define is_special(c) \ - ((((unsigned int)c) - 33 < 32) \ - && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1)) + (((unsigned)(c) - 33 < 32) \ + && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1)) parsesub: { int subtype; int typeloc; @@ -10215,18 +11078,22 @@ parsesub: { static const char types[] ALIGN1 = "}-+?="; c = pgetc(); - if ( - c <= PEOA_OR_PEOF || - (c != '(' && c != '{' && !is_name(c) && !is_special(c)) + if (c <= PEOA_OR_PEOF + || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { - USTPUTC('$', out); +#if ENABLE_ASH_BASH_COMPAT + if (c == '\'') + bash_dollar_squote = 1; + else +#endif + USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { #if ENABLE_ASH_MATH_SUPPORT PARSEARITH(); #else - raise_error_syntax("We unsupport $((arith))"); + raise_error_syntax("you disabled math support for $((arith)) syntax"); #endif } else { pungetc(); @@ -10261,16 +11128,25 @@ parsesub: { } else if (is_special(c)) { USTPUTC(c, out); c = pgetc(); - } else - badsub: raise_error_syntax("Bad substitution"); + } else { + badsub: + raise_error_syntax("bad substitution"); + } STPUTC('=', out); flags = 0; if (subtype == 0) { switch (c) { case ':': - flags = VSNUL; c = pgetc(); +#if ENABLE_ASH_BASH_COMPAT + if (c == ':' || c == '$' || isdigit(c)) { + pungetc(); + subtype = VSSUBSTR; + break; + } +#endif + flags = VSNUL; /*FALLTHROUGH*/ default: p = strchr(types, c); @@ -10279,18 +11155,26 @@ parsesub: { subtype = p - types + VSNORMAL; break; case '%': - case '#': - { - int cc = c; - subtype = c == '#' ? VSTRIMLEFT : - VSTRIMRIGHT; - c = pgetc(); - if (c == cc) - subtype++; - else - pungetc(); - break; - } + case '#': { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT; + c = pgetc(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } +#if ENABLE_ASH_BASH_COMPAT + case '/': + subtype = VSREPLACE; + c = pgetc(); + if (c == '/') + subtype++; /* VSREPLACEALL */ + else + pungetc(); + break; +#endif } } else { pungetc(); @@ -10367,7 +11251,7 @@ parsebackq: { case '\\': pc = pgetc(); if (pc == '\n') { - plinno++; + g_parsefile->linno++; if (doprompt) setprompt(2); /* @@ -10390,11 +11274,11 @@ parsebackq: { #if ENABLE_ASH_ALIAS case PEOA: #endif - startlinno = plinno; + startlinno = g_parsefile->linno; raise_error_syntax("EOF in backquote substitution"); case '\n': - plinno++; + g_parsefile->linno++; needprompt = doprompt; break; @@ -10414,8 +11298,8 @@ parsebackq: { nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; - *nlpp = stalloc(sizeof(**nlpp)); - (*nlpp)->next = NULL; + *nlpp = stzalloc(sizeof(**nlpp)); + /* (*nlpp)->next = NULL; - stzalloc did it */ parsebackquote = oldstyle; if (oldstyle) { @@ -10508,9 +11392,14 @@ parsearith: { #ifdef NEW_xxreadtoken /* singles must be first! */ static const char xxreadtoken_chars[7] ALIGN1 = { - '\n', '(', ')', '&', '|', ';', 0 + '\n', '(', ')', /* singles */ + '&', '|', ';', /* doubles */ + 0 }; +#define xxreadtoken_singles 3 +#define xxreadtoken_doubles 3 + static const char xxreadtoken_tokens[] ALIGN1 = { TNL, TLP, TRP, /* only single occurrence allowed */ TBACKGND, TPIPE, TSEMI, /* if single occurrence */ @@ -10518,11 +11407,6 @@ static const char xxreadtoken_tokens[] ALIGN1 = { TAND, TOR, TENDCASE /* if double occurrence */ }; -#define xxreadtoken_doubles \ - (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars)) -#define xxreadtoken_singles \ - (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1) - static int xxreadtoken(void) { @@ -10535,56 +11419,59 @@ xxreadtoken(void) if (needprompt) { setprompt(2); } - startlinno = plinno; + startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ - c = pgetc_macro(); + c = pgetc_fast(); + if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA)) + continue; - if ((c != ' ') && (c != '\t') -#if ENABLE_ASH_ALIAS - && (c != PEOA) -#endif - ) { - if (c == '#') { - while ((c = pgetc()) != '\n' && c != PEOF); + if (c == '#') { + while ((c = pgetc()) != '\n' && c != PEOF) + continue; + pungetc(); + } else if (c == '\\') { + if (pgetc() != '\n') { pungetc(); - } else if (c == '\\') { - if (pgetc() != '\n') { - pungetc(); - goto READTOKEN1; - } - startlinno = ++plinno; - if (doprompt) - setprompt(2); - } else { - const char *p - = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; + break; /* return readtoken1(...) */ + } + startlinno = ++g_parsefile->linno; + if (doprompt) + setprompt(2); + } else { + const char *p; - if (c != PEOF) { - if (c == '\n') { - plinno++; - needprompt = doprompt; - } + p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; + if (c != PEOF) { + if (c == '\n') { + g_parsefile->linno++; + needprompt = doprompt; + } - p = strchr(xxreadtoken_chars, c); - if (p == NULL) { - READTOKEN1: - return readtoken1(c, BASESYNTAX, (char *) NULL, 0); - } + p = strchr(xxreadtoken_chars, c); + if (p == NULL) + break; /* return readtoken1(...) */ - if (p - xxreadtoken_chars >= xxreadtoken_singles) { - if (pgetc() == *p) { /* double occurrence? */ - p += xxreadtoken_doubles + 1; - } else { - pungetc(); - } + if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) { + int cc = pgetc(); + if (cc == c) { /* double occurrence? */ + p += xxreadtoken_doubles + 1; + } else { + pungetc(); +#if ENABLE_ASH_BASH_COMPAT + if (c == '&' && cc == '>') /* &> */ + break; /* return readtoken1(...) */ +#endif } } - return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; } + lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; + return lasttoken; } - } /* for */ + } /* for (;;) */ + + return readtoken1(c, BASESYNTAX, (char *) NULL, 0); } -#else +#else /* old xxreadtoken */ #define RETURN(token) return lasttoken = token static int xxreadtoken(void) @@ -10598,9 +11485,9 @@ xxreadtoken(void) if (needprompt) { setprompt(2); } - startlinno = plinno; + startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ - c = pgetc_macro(); + c = pgetc_fast(); switch (c) { case ' ': case '\t': #if ENABLE_ASH_ALIAS @@ -10608,12 +11495,13 @@ xxreadtoken(void) #endif continue; case '#': - while ((c = pgetc()) != '\n' && c != PEOF); + while ((c = pgetc()) != '\n' && c != PEOF) + continue; pungetc(); continue; case '\\': if (pgetc() == '\n') { - startlinno = ++plinno; + startlinno = ++g_parsefile->linno; if (doprompt) setprompt(2); continue; @@ -10621,7 +11509,7 @@ xxreadtoken(void) pungetc(); goto breakloop; case '\n': - plinno++; + g_parsefile->linno++; needprompt = doprompt; RETURN(TNL); case PEOF: @@ -10653,7 +11541,7 @@ xxreadtoken(void) return readtoken1(c, BASESYNTAX, (char *)NULL, 0); #undef RETURN } -#endif /* NEW_xxreadtoken */ +#endif /* old xxreadtoken */ static int readtoken(void) @@ -10763,7 +11651,7 @@ parseheredoc(void) union node *n; here = heredoclist; - heredoclist = 0; + heredoclist = NULL; while (here) { if (needprompt) { @@ -10771,9 +11659,9 @@ parseheredoc(void) } readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, here->eofmark, here->striptabs); - n = stalloc(sizeof(struct narg)); + n = stzalloc(sizeof(struct narg)); n->narg.type = NARG; - n->narg.next = NULL; + /*n->narg.next = NULL; - stzalloc did it */ n->narg.text = wordtext; n->narg.backquote = backquotelist; here->here->nhere.doc = n; @@ -10838,20 +11726,19 @@ evalstring(char *s, int mask) * The eval command. */ static int -evalcmd(int argc, char **argv) +evalcmd(int argc UNUSED_PARAM, char **argv) { char *p; char *concat; - char **ap; - if (argc > 1) { + if (argv[1]) { p = argv[1]; - if (argc > 2) { + argv += 2; + if (argv[0]) { STARTSTACKSTR(concat); - ap = argv + 2; for (;;) { concat = stack_putstr(p, concat); - p = *ap++; + p = *argv++; if (p == NULL) break; STPUTC(' ', concat); @@ -10883,7 +11770,7 @@ cmdloop(int top) setstackmark(&smark); #if JOBS - if (jobctl) + if (doing_jobctl) showjobs(stderr, SHOW_CHANGED); #endif inter = 0; @@ -10962,16 +11849,15 @@ dotcmd(int argc, char **argv) for (sp = cmdenviron; sp; sp = sp->next) setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname; - - fullname = find_dot_file(argv[1]); - - if (argc > 2) { + if (argv[1]) { /* That's what SVR2 does */ + char *fullname = find_dot_file(argv[1]); + argv += 2; + argc -= 2; + if (argc) { /* argc > 0, argv[0] != NULL */ saveparam = shellparam; - shellparam.malloc = 0; - shellparam.nparam = argc - 2; - shellparam.p = argv + 2; + shellparam.malloced = 0; + shellparam.nparam = argc; + shellparam.p = argv; }; setinputfile(fullname, INPUT_PUSH_FILE); @@ -10979,7 +11865,7 @@ dotcmd(int argc, char **argv) cmdloop(0); popfile(); - if (argc > 2) { + if (argc) { freeparam(&shellparam); shellparam = saveparam; }; @@ -10989,32 +11875,16 @@ dotcmd(int argc, char **argv) } static int -exitcmd(int argc, char **argv) +exitcmd(int argc UNUSED_PARAM, char **argv) { if (stoppedjobs()) return 0; - if (argc > 1) + if (argv[1]) exitstatus = number(argv[1]); raise_exception(EXEXIT); /* NOTREACHED */ } -#if ENABLE_ASH_BUILTIN_ECHO -static int -echocmd(int argc, char **argv) -{ - return bb_echo(argc, argv); -} -#endif - -#if ENABLE_ASH_BUILTIN_TEST -static int -testcmd(int argc, char **argv) -{ - return test_main(argc, argv); -} -#endif - /* * Read a file containing shell functions. */ @@ -11113,10 +11983,13 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) } #if ENABLE_FEATURE_SH_STANDALONE - if (find_applet_by_name(name)) { - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; + { + int applet_no = find_applet_by_name(name); + if (applet_no >= 0) { + entry->cmdtype = CMDNORMAL; + entry->u.index = -2 - applet_no; + return; + } } #endif @@ -11142,11 +12015,10 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) if (bcmd) goto builtin_success; continue; - } else if (!(act & DO_NOFUNC) - && prefix(pathopt, "func")) { - /* handled below */ - } else { - /* ignore unimplemented options */ + } + if ((act & DO_NOFUNC) + || !prefix(pathopt, "func") + ) { /* ignore unimplemented options */ continue; } } @@ -11227,7 +12099,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) * The trap builtin. */ static int -trapcmd(int argc, char **argv) +trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *action; char **ap; @@ -11238,18 +12110,15 @@ trapcmd(int argc, char **argv) if (!*ap) { for (signo = 0; signo < NSIG; signo++) { if (trap[signo] != NULL) { - const char *sn; - - sn = get_signame(signo); out1fmt("trap -- %s %s\n", - single_quote(trap[signo]), sn); + single_quote(trap[signo]), + get_signame(signo)); } } return 0; } - if (!ap[1]) - action = NULL; - else + action = NULL; + if (ap[1]) action = *ap++; while (*ap) { signo = get_signum(*ap); @@ -11280,9 +12149,10 @@ trapcmd(int argc, char **argv) * Lists available builtins */ static int -helpcmd(int argc, char **argv) +helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - int col, i; + unsigned col; + unsigned i; out1fmt("\nBuilt-in commands:\n-------------------\n"); for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { @@ -11294,11 +12164,15 @@ helpcmd(int argc, char **argv) } } #if ENABLE_FEATURE_SH_STANDALONE - for (i = 0; i < NUM_APPLETS; i++) { - col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name); - if (col > 60) { - out1fmt("\n"); - col = 0; + { + const char *a = applet_names; + while (*a) { + col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a); + if (col > 60) { + out1fmt("\n"); + col = 0; + } + a += strlen(a) + 1; } } #endif @@ -11311,13 +12185,13 @@ helpcmd(int argc, char **argv) * The export and readonly commands. */ static int -exportcmd(int argc, char **argv) +exportcmd(int argc UNUSED_PARAM, char **argv) { struct var *vp; char *name; const char *p; char **aptr; - int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT; if (nextopt("p") != 'p') { aptr = argptr; @@ -11362,7 +12236,7 @@ unsetfunc(const char *name) * with the same name. */ static int -unsetcmd(int argc, char **argv) +unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char **ap; int i; @@ -11400,7 +12274,7 @@ static const unsigned char timescmd_str[] ALIGN1 = { }; static int -timescmd(int ac, char **av) +timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { long clk_tck, s, t; const unsigned char *p; @@ -11452,17 +12326,16 @@ dash_arith(const char *s) * Copyright (C) 2003 Vladimir Oleynik */ static int -letcmd(int argc, char **argv) +letcmd(int argc UNUSED_PARAM, char **argv) { - char **ap; - arith_t i = 0; + arith_t i; - ap = argv + 1; - if (!*ap) + argv++; + if (!*argv) ash_msg_and_raise_error("expression expected"); - for (ap = argv + 1; *ap; ap++) { - i = dash_arith(*ap); - } + do { + i = dash_arith(*argv); + } while (*++argv); return !i; } @@ -11481,14 +12354,24 @@ typedef enum __rlimit_resource rlim_t; #endif /* - * The read builtin. The -e option causes backslashes to escape the - * following character. - * + * The read builtin. Options: + * -r Do not interpret '\' specially + * -s Turn off echo (tty only) + * -n NCHARS Read NCHARS max + * -p PROMPT Display PROMPT on stderr (if input is from tty) + * -t SECONDS Timeout after SECONDS (tty or pipe only) + * -u FD Read from given FD instead of fd 0 * This uses unbuffered input, which may be avoidable in some cases. + * TODO: bash also has: + * -a ARRAY Read into array[0],[1],etc + * -d DELIM End on DELIM char, not newline + * -e Use line editing (tty only) */ static int -readcmd(int argc, char **argv) +readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { + static const char *const arg_REPLY[] = { "REPLY", NULL }; + char **ap; int backslash; char c; @@ -11499,31 +12382,23 @@ readcmd(int argc, char **argv) int startword; int status; int i; + int fd = 0; #if ENABLE_ASH_READ_NCHARS - int n_flag = 0; - int nchars = 0; + int nchars = 0; /* if != 0, -n is in effect */ int silent = 0; struct termios tty, old_tty; #endif #if ENABLE_ASH_READ_TIMEOUT - fd_set set; - struct timeval ts; - - ts.tv_sec = ts.tv_usec = 0; + unsigned end_ms = 0; + unsigned timeout = 0; #endif rflag = 0; prompt = NULL; -#if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT - while ((i = nextopt("p:rt:n:s")) != '\0') -#elif ENABLE_ASH_READ_NCHARS - while ((i = nextopt("p:rn:s")) != '\0') -#elif ENABLE_ASH_READ_TIMEOUT - while ((i = nextopt("p:rt:")) != '\0') -#else - while ((i = nextopt("p:r")) != '\0') -#endif - { + while ((i = nextopt("p:u:r" + USE_ASH_READ_TIMEOUT("t:") + USE_ASH_READ_NCHARS("n:s") + )) != '\0') { switch (i) { case 'p': prompt = optionarg; @@ -11533,7 +12408,7 @@ readcmd(int argc, char **argv) nchars = bb_strtou(optionarg, NULL, 10); if (nchars < 0 || errno) ash_msg_and_raise_error("invalid count"); - n_flag = nchars; /* just a flag "nchars is nonzero" */ + /* nchars == 0: off (bash 3.2 does this too) */ break; case 's': silent = 1; @@ -11541,6 +12416,11 @@ readcmd(int argc, char **argv) #endif #if ENABLE_ASH_READ_TIMEOUT case 't': + timeout = bb_strtou(optionarg, NULL, 10); + if (errno || timeout > UINT_MAX / 2048) + ash_msg_and_raise_error("invalid timeout"); + timeout *= 1000; +#if 0 /* even bash have no -t N.NNN support */ ts.tv_sec = bb_strtou(optionarg, &p, 10); ts.tv_usec = 0; /* EINVAL means number is ok, but not terminated by NUL */ @@ -11564,65 +12444,73 @@ readcmd(int argc, char **argv) if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ ash_msg_and_raise_error("invalid timeout"); } +#endif /* if 0 */ break; #endif case 'r': rflag = 1; break; + case 'u': + fd = bb_strtou(optionarg, NULL, 10); + if (fd < 0 || errno) + ash_msg_and_raise_error("invalid file descriptor"); + break; default: break; } } - if (prompt && isatty(0)) { + if (prompt && isatty(fd)) { out2str(prompt); } ap = argptr; if (*ap == NULL) - ash_msg_and_raise_error("arg count"); + ap = (char**)arg_REPLY; ifs = bltinlookup("IFS"); if (ifs == NULL) ifs = defifs; #if ENABLE_ASH_READ_NCHARS - if (n_flag || silent) { - if (tcgetattr(0, &tty) != 0) { - /* Not a tty */ - n_flag = 0; - silent = 0; - } else { - old_tty = tty; - if (n_flag) { - tty.c_lflag &= ~ICANON; - tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; - } - if (silent) { - tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); - } - tcsetattr(0, TCSANOW, &tty); + tcgetattr(fd, &tty); + old_tty = tty; + if (nchars || silent) { + if (nchars) { + tty.c_lflag &= ~ICANON; + tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; } - } -#endif -#if ENABLE_ASH_READ_TIMEOUT - if (ts.tv_sec || ts.tv_usec) { - FD_ZERO(&set); - FD_SET(0, &set); - - /* poll-based wait produces bigger code, using select */ - i = select(1, &set, NULL, NULL, &ts); - if (!i) { /* timed out! */ -#if ENABLE_ASH_READ_NCHARS - if (n_flag) - tcsetattr(0, TCSANOW, &old_tty); -#endif - return 1; + if (silent) { + tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); } + /* if tcgetattr failed, tcsetattr will fail too. + * Ignoring, it's harmless. */ + tcsetattr(fd, TCSANOW, &tty); } #endif + status = 0; startword = 1; backslash = 0; +#if ENABLE_ASH_READ_TIMEOUT + if (timeout) /* NB: ensuring end_ms is nonzero */ + end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1; +#endif STARTSTACKSTR(p); do { - if (read(0, &c, 1) != 1) { +#if ENABLE_ASH_READ_TIMEOUT + if (end_ms) { + struct pollfd pfd[1]; + pfd[0].fd = fd; + pfd[0].events = POLLIN; + timeout = end_ms - (unsigned)(monotonic_us() / 1000); + if ((int)timeout <= 0 /* already late? */ + || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ + ) { /* timed out! */ +#if ENABLE_ASH_READ_NCHARS + tcsetattr(fd, TCSANOW, &old_tty); +#endif + return 1; + } + } +#endif + if (nonblock_safe_read(fd, &c, 1) != 1) { status = 1; break; } @@ -11657,14 +12545,13 @@ readcmd(int argc, char **argv) } /* end of do {} while: */ #if ENABLE_ASH_READ_NCHARS - while (!n_flag || --nchars); + while (--nchars); #else while (1); #endif #if ENABLE_ASH_READ_NCHARS - if (n_flag || silent) - tcsetattr(0, TCSANOW, &old_tty); + tcsetattr(fd, TCSANOW, &old_tty); #endif STACKSTRNUL(p); @@ -11678,7 +12565,7 @@ readcmd(int argc, char **argv) } static int -umaskcmd(int argc, char **argv) +umaskcmd(int argc UNUSED_PARAM, char **argv) { static const char permuser[3] ALIGN1 = "ugo"; static const char permmode[3] ALIGN1 = "rwx"; @@ -11853,7 +12740,7 @@ printlim(enum limtype how, const struct rlimit *limit, } static int -ulimitcmd(int argc, char **argv) +ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int c; rlim_t val = 0; @@ -11929,6 +12816,7 @@ ulimitcmd(int argc, char **argv) while ((c = *p++) >= '0' && c <= '9') { val = (val * 10) + (long)(c - '0'); + // val is actually 'unsigned long int' and can't get < 0 if (val < (rlim_t) 0) break; } @@ -12172,7 +13060,7 @@ is_right_associativity(operator prec) || prec == PREC(TOK_CONDITIONAL)); } -typedef struct ARITCH_VAR_NUM { +typedef struct { arith_t val; arith_t contidional_second_val; char contidional_second_val_initialized; @@ -12180,9 +13068,9 @@ typedef struct ARITCH_VAR_NUM { else is variable name */ } v_n_t; -typedef struct CHK_VAR_RECURSIVE_LOOPED { +typedef struct chk_var_recursive_looped_t { const char *var; - struct CHK_VAR_RECURSIVE_LOOPED *next; + struct chk_var_recursive_looped_t *next; } chk_var_recursive_looped_t; static chk_var_recursive_looped_t *prev_chk_var_recursive; @@ -12263,7 +13151,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) --NUMPTR; numptr_val = rez; if (op == TOK_CONDITIONAL) { - if (! numptr_m1->contidional_second_val_initialized) { + if (!numptr_m1->contidional_second_val_initialized) { /* protect $((expr1 ? expr2)) without ": expr" */ goto err; } @@ -12418,7 +13306,7 @@ static const char op_tokens[] ALIGN1 = { 0 }; /* ptr to ")" */ -#define endexpression &op_tokens[sizeof(op_tokens)-7] +#define endexpression (&op_tokens[sizeof(op_tokens)-7]) static arith_t arith(const char *expr, int *perrcode) @@ -12426,21 +13314,19 @@ arith(const char *expr, int *perrcode) char arithval; /* Current character under analysis */ operator lasttok, op; operator prec; - + operator *stack, *stackptr; const char *p = endexpression; int errcode; - - size_t datasizes = strlen(expr) + 2; + v_n_t *numstack, *numstackptr; + unsigned datasizes = strlen(expr) + 2; /* Stack of integers */ /* The proof that there can be no more than strlen(startbuf)/2+1 integers * in any given correct or incorrect expression is left as an exercise to * the reader. */ - v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)), - *numstackptr = numstack; + numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); /* Stack of operator tokens */ - operator *stack = alloca((datasizes) * sizeof(operator)), - *stackptr = stack; + stackptr = stack = alloca(datasizes * sizeof(stack[0])); *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ *perrcode = errcode = 0; @@ -12469,7 +13355,8 @@ arith(const char *expr, int *perrcode) if (numstackptr != numstack+1) { /* ... but if there isn't, it's bad */ err: - return (*perrcode = -1); + *perrcode = -1; + return *perrcode; } if (numstack->var) { /* expression is $((var)) only, lookup now */ @@ -12516,7 +13403,7 @@ arith(const char *expr, int *perrcode) } for (o = expr; *p && *o == *p; p++) o++; - if (! *p) { + if (!*p) { /* found */ expr = o - 1; break; @@ -12616,7 +13503,7 @@ arith(const char *expr, int *perrcode) /* * Called to exit the shell. */ -static void exitshell(void) ATTRIBUTE_NORETURN; +static void exitshell(void) NORETURN; static void exitshell(void) { @@ -12652,7 +13539,7 @@ static void init(void) { /* from input.c: */ - basepf.nextc = basepf.buf = basebuf; + basepf.next_to_pgetc = basepf.buf = basebuf; /* from trap.c: */ signal(SIGCHLD, SIG_DFL); @@ -12687,7 +13574,7 @@ init(void) * Process the shell command line arguments. */ static void -procargs(int argc, char **argv) +procargs(char **argv) { int i; const char *xminusc; @@ -12695,12 +13582,15 @@ procargs(int argc, char **argv) xargv = argv; arg0 = xargv[0]; - if (argc > 0) + /* if (xargv[0]) - mmm, this is always true! */ xargv++; for (i = 0; i < NOPTS; i++) optlist[i] = 2; argptr = xargv; - options(1); + if (options(1)) { + /* it already printed err message */ + raise_exception(EXERROR); + } xargv = argptr; xminusc = minusc; if (*xargv == NULL) { @@ -12735,7 +13625,7 @@ procargs(int argc, char **argv) shellparam.optind = 1; shellparam.optoff = -1; #endif - /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */ while (*xargv) { shellparam.nparam++; xargv++; @@ -12770,13 +13660,14 @@ reset(void) evalskip = 0; loopnest = 0; /* from input.c: */ - parselleft = parsenleft = 0; /* clear input buffer */ + g_parsefile->left_in_buffer = 0; + g_parsefile->left_in_line = 0; /* clear input buffer */ popallfiles(); /* from parser.c: */ tokpushback = 0; checkkwd = 0; /* from redir.c: */ - clearredir(0); + clearredir(/*drop:*/ 0); } #if PROFILE @@ -12792,13 +13683,22 @@ extern int etext(); * is used to figure out how far we had gotten. */ int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int ash_main(int argc, char **argv) +int ash_main(int argc UNUSED_PARAM, char **argv) { char *shinit; volatile int state; struct jmploc jmploc; struct stackmark smark; + /* Initialize global data */ + INIT_G_misc(); + INIT_G_memstack(); + INIT_G_var(); +#if ENABLE_ASH_ALIAS + INIT_G_alias(); +#endif + INIT_G_cmdtable(); + #if PROFILE monitor(4, etext, profile_buf, sizeof(profile_buf), 50); #endif @@ -12842,11 +13742,14 @@ int ash_main(int argc, char **argv) rootpid = getpid(); #if ENABLE_ASH_RANDOM_SUPPORT - rseed = rootpid + time(NULL); + /* Can use monotonic_ns() for better randomness but for now it is + * not used anywhere else in busybox... so avoid bloat */ + random_galois_LFSR = random_LCG = rootpid + monotonic_us(); #endif init(); setstackmark(&smark); - procargs(argc, argv); + procargs(argv); + #if ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE"); @@ -12890,7 +13793,7 @@ int ash_main(int argc, char **argv) if (sflag || minusc == NULL) { #if ENABLE_FEATURE_EDITING_SAVEHISTORY - if ( iflag ) { + if (iflag) { const char *hp = lookupvar("HISTFILE"); if (hp != NULL)