X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fash.c;h=580918cebac8a7ec1c65b8b93f38d64ac6cb0ef7;hb=68404f13d4bf4826e3609703dad5375763db28ab;hp=950cdaa02f6a0092deace5b87f925854addcada0;hpb=240a1cfbbe6cf42e8013ce06cb567b3f28c8727f;p=oweals%2Fbusybox.git diff --git a/shell/ash.c b/shell/ash.c index 950cdaa02..580918ceb 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -42,18 +42,19 @@ * 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" +#endif + +#include "busybox.h" /* for applet_names */ #include #include #include @@ -66,6 +67,13 @@ #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) @@ -90,19 +98,19 @@ static const char *const optletters_optnames[] = { "a" "allexport", "b" "notify", "u" "nounset", - "\0" "vi", + "\0" "vi" #if DEBUG - "\0" "nolog", - "\0" "debug", + ,"\0" "nolog" + ,"\0" "debug" #endif }; #define optletters(n) optletters_optnames[(n)][0] #define optnames(n) (&optletters_optnames[(n)][1]) -#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0])) +enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; -static char optlist[NOPTS]; +static char optlist[NOPTS] ALIGN1; #define eflag optlist[0] #define fflag optlist[1] @@ -126,36 +134,9 @@ static char optlist[NOPTS]; /* ============ Misc data */ -#ifdef __GLIBC__ -/* glibc sucks */ -static int *dash_errno; -#undef errno -#define errno (*dash_errno) -#endif - -static char nullstr[1]; /* zero length string */ -static const char homestr[] = "HOME"; -static const char snlfmt[] = "%s\n"; -static const char illnum[] = "Illegal number: %s"; - -static char *minusc; /* argument to -c option */ - -static int isloginsh; -/* 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]; -/* 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 */ +static const char homestr[] ALIGN1 = "HOME"; +static const char snlfmt[] ALIGN1 = "%s\n"; +static const char illnum[] ALIGN1 = "Illegal number: %s"; /* * We enclose jmp_buf in a structure so that we can declare pointers to @@ -169,37 +150,90 @@ 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, - */ + /* trap handler commands */ + char *trap[NSIG]; + smallint isloginsh; + char nullstr[1]; /* zero length string */ + /* + * 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]; +}; +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 trap (G_misc.trap ) +#define isloginsh (G_misc.isloginsh) +#define nullstr (G_misc.nullstr ) +#define sigmode (G_misc.sigmode ) +#define gotsig (G_misc.gotsig ) +#define INIT_G_misc() do { \ + (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ + barrier(); \ + curdir = nullstr; \ + physdir = nullstr; \ +} while (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. :-)) */ @@ -239,18 +273,17 @@ 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); } @@ -303,15 +336,6 @@ force_int_on(void) raise_interrupt(); \ } while (0) -#define EXSIGON \ - do { \ - exsig++; \ - xbarrier(); \ - if (pendingsig) \ - raise_exception(EXSIG); \ - } while (0) -/* EXSIG is turned off by evalbltin(). */ - /* * Ignore a signal. Only one usage site - in forkchild() */ @@ -333,10 +357,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; } @@ -453,7 +477,9 @@ out2str(const char *p) #define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */ #define VSLENGTH 0xa /* ${#var} */ -static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; +static const char dolatstr[] ALIGN1 = { + CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' +}; #define NCMD 0 #define NPIPE 1 @@ -730,7 +756,7 @@ opentrace(void) } } #ifdef O_APPEND - flags = fcntl(fileno(tracefile), F_GETFL, 0); + flags = fcntl(fileno(tracefile), F_GETFL); if (flags >= 0) fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); #endif @@ -846,23 +872,24 @@ shcmd(union node *cmd, FILE *fp) first = 1; for (np = cmd->ncmd.args; np; np = np->narg.next) { - if (! first) - putchar(' '); + if (!first) + putc(' ', fp); sharg(np, fp); first = 0; } for (np = cmd->ncmd.redirect; np; np = np->nfile.next) { - if (! first) - putchar(' '); + if (!first) + putc(' ', fp); + dftfd = 0; switch (np->nfile.type) { - case NTO: s = ">"; dftfd = 1; break; - case NCLOBBER: s = ">|"; dftfd = 1; break; - case NAPPEND: s = ">>"; dftfd = 1; break; - case NTOFD: s = ">&"; dftfd = 1; break; - case NFROM: s = "<"; dftfd = 0; break; - case NFROMFD: s = "<&"; dftfd = 0; break; - case NFROMTO: s = "<>"; dftfd = 0; break; - default: s = "*error*"; dftfd = 0; break; + case NTO: s = ">>"+1; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; break; + case NFROMFD: s = "<&"; break; + case NFROMTO: s = "<>"; break; + default: s = "*error*"; break; } if (np->nfile.fd != dftfd) fprintf(fp, "%d", np->nfile.fd); @@ -1093,7 +1120,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 { @@ -1108,16 +1135,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) @@ -1134,6 +1183,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. */ @@ -1161,7 +1216,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; @@ -1174,30 +1229,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)) { + if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) { write(2, "stunalloc\n", 10); abort(); } #endif - stacknleft += stacknxt - (char *)p; - stacknxt = p; + g_stacknleft += g_stacknxt - (char *)p; + g_stacknxt = p; } /* @@ -1213,9 +1274,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; } @@ -1230,13 +1291,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; } @@ -1255,13 +1316,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; @@ -1269,15 +1330,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; /* @@ -1286,20 +1347,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; + int 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; } } @@ -1307,8 +1368,8 @@ static void grabstackblock(size_t len) { len = SHELL_ALIGN(len); - stacknxt += len; - stacknleft -= len; + g_stacknxt += len; + g_stacknleft -= len; } /* @@ -1346,7 +1407,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 (;;) { @@ -1420,7 +1481,7 @@ prefix(const char *string, const char *pfx) { while (*pfx) { if (*pfx++ != *string++) - return 0; + return NULL; } return (char *) string; } @@ -1571,42 +1632,26 @@ static unsigned long rseed; /* ============ 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 - -static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin"; -#ifdef IFS_BROKEN -static const char defifsvar[] = "IFS= \t\n"; -#define defifs (defifsvar + 4) -#else -static const char defifs[] = " \t\n"; -#endif +/* + * 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; +}; 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. */ @@ -1615,7 +1660,7 @@ freeparam(volatile struct shparam *param) { char **ap; - if (param->malloc) { + if (param->malloced) { for (ap = param->p; *ap; ap++) free(*ap); free(param->p); @@ -1623,12 +1668,7 @@ freeparam(volatile struct shparam *param) } #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 { @@ -1646,7 +1686,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 */ +#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 + + +/* Need to be before varinit_data[] */ #if ENABLE_LOCALE_SUPPORT static void change_lc_all(const char *value) @@ -1670,53 +1734,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, defpathvar, 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 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 { \ + int 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 -#define defpath (defpathvar + 5) /* * The following macros access the values of the above variables. @@ -1725,38 +1824,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; -extern char **environ; -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). @@ -1846,7 +1939,7 @@ initvar(void) vps1.text = "PS1=# "; #endif vp = varinit; - end = vp + sizeof(varinit) / sizeof(varinit[0]); + end = vp + ARRAY_SIZE(varinit); do { vpp = hashvar(vp->text); vp->next = *vpp; @@ -1945,9 +2038,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))) @@ -2158,8 +2251,8 @@ padvance(const char **path, const char *name) /* ============ Prompt */ -static int doprompt; /* if set, prompt the user */ -static int needprompt; /* true if interactive and at start of line */ +static smallint doprompt; /* if set, prompt the user */ +static smallint needprompt; /* true if interactive and at start of line */ #if ENABLE_FEATURE_EDITING static line_input_t *line_input_state; @@ -2227,9 +2320,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) { @@ -2293,7 +2383,8 @@ updatepwd(const char *dir) break; } break; - } else if (p[1] == '\0') + } + if (p[1] == '\0') break; /* fall through */ default: @@ -2315,7 +2406,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; } @@ -2381,7 +2472,7 @@ docd(const char *dest, int flags) } static int -cdcmd(int argc, char **argv) +cdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { const char *dest; const char *path; @@ -2445,7 +2536,7 @@ cdcmd(int argc, char **argv) } static int -pwdcmd(int argc, char **argv) +pwdcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int flags; const char *dir = curdir; @@ -2463,7 +2554,7 @@ pwdcmd(int argc, char **argv) /* ============ ... */ -#define IBUFSIZ (BUFSIZ + 1) +#define IBUFSIZ COMMON_BUFSIZE #define basebuf bb_common_bufsiz1 /* buffer for top level input file */ /* Syntax classes */ @@ -2499,6 +2590,7 @@ pwdcmd(int argc, char **argv) #define DQSYNTAX 1 /* in double quotes */ #define SQSYNTAX 2 /* in single quotes */ #define ARISYNTAX 3 /* in arithmetic */ +#define PSSYNTAX 4 /* prompt */ #if ENABLE_ASH_OPTIMIZE_FOR_SIZE #define USE_SIT_FUNCTION @@ -2555,16 +2647,16 @@ static const char S_I_T[][3] = { static int SIT(int c, int syntax) { - static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; + static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; #if ENABLE_ASH_ALIAS - static const char syntax_index_table[] = { + static const char syntax_index_table[] ALIGN1 = { 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ 11, 3 /* "}~" */ }; #else - static const char syntax_index_table[] = { + static const char syntax_index_table[] ALIGN1 = { 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ @@ -2907,8 +2999,6 @@ static const char syntax_index_table[258] = { #define ALIASINUSE 1 #define ALIASDEAD 2 -#define ATABSIZE 39 - struct alias { struct alias *next; char *name; @@ -2916,7 +3006,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) { @@ -2987,11 +3082,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; @@ -3043,19 +3138,20 @@ printalias(const struct alias *ap) * TODO - sort output */ static int -aliascmd(int argc, char **argv) +aliascmd(int argc ATTRIBUTE_UNUSED, 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) { @@ -3077,7 +3173,7 @@ aliascmd(int argc, char **argv) } static int -unaliascmd(int argc, char **argv) +unaliascmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int i; @@ -3148,20 +3244,18 @@ struct job { }; static pid_t backgndpid; /* pid of last background process */ -static int job_warning; /* user was warned about stopped jobs */ -#if JOBS -static int jobctl; /* true if doing job control */ -#endif +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); static int forkshell(struct job *, union node *, int); static int waitforjob(struct job *); -#if ! JOBS -#define setjobctl(on) /* do nothing */ +#if !JOBS +enum { jobctl = 0 }; +#define setjobctl(on) do {} while (0) #else +static smallint jobctl; /* true if doing job control */ static void setjobctl(int); -static void showjobs(FILE *, int); #endif /* @@ -3176,12 +3270,11 @@ setsignal(int signo) struct sigaction act; t = trap[signo]; + action = S_IGN; if (t == NULL) action = S_DFL; else if (*t != '\0') action = S_CATCH; - else - action = S_IGN; if (rootshell && action == S_DFL) { switch (signo) { case SIGINT: @@ -3214,7 +3307,7 @@ setsignal(int signo) /* * current setting unknown */ - if (sigaction(signo, 0, &act) == -1) { + if (sigaction(signo, NULL, &act) == -1) { /* * Pretend it worked; maybe we should give a warning * here, but other shells don't. We don't alter @@ -3222,19 +3315,19 @@ setsignal(int signo) */ return; } + tsig = S_RESET; /* force to be set */ if (act.sa_handler == SIG_IGN) { + tsig = 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 */ + } } } if (tsig == S_HARD_IGN || tsig == action) return; + act.sa_handler = SIG_DFL; switch (action) { case S_CATCH: act.sa_handler = onsig; @@ -3242,13 +3335,11 @@ setsignal(int signo) 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); } /* mode flags for set_curjob */ @@ -3257,8 +3348,8 @@ 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 */ @@ -3467,15 +3558,18 @@ 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; - fcntl(fd, F_SETFD, FD_CLOEXEC); + /* fd is a tty at this point */ + close_on_exec_on(fd); do { /* while we are in the background */ pgrp = tcgetpgrp(fd); if (pgrp < 0) { @@ -3500,13 +3594,16 @@ setjobctl(int on) /* turning job control off */ fd = ttyfd; pgrp = initialpgrp; - xtcsetpgrp(fd, pgrp); + /* was xtcsetpgrp, but this can make exiting ash + * 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; @@ -3516,91 +3613,22 @@ setjobctl(int on) static int killcmd(int argc, char **argv) { - int signo = -1; - int list = 0; - int i; - pid_t pid; - struct job *jp; - - if (argc <= 1) { - usage: - ash_msg_and_raise_error( -"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" -"kill -l [exitstatus]" - ); - } - - if (**++argv == '-') { - signo = get_signum(*argv + 1); - if (signo < 0) { - int c; - - while ((c = nextopt("ls:")) != '\0') { - switch (c) { - default: -#if DEBUG - abort(); -#endif - case 'l': - list = 1; - break; - case 's': - signo = get_signum(optionarg); - if (signo < 0) { - ash_msg_and_raise_error( - "invalid signal number or name: %s", - optionarg - ); - } - break; - } - } - argv = argptr; - } else - argv++; - } - - if (!list && signo < 0) - signo = SIGTERM; - - if ((signo < 0 || !*argv) ^ list) { - goto usage; - } - - if (list) { - const char *name; - - if (!*argv) { - for (i = 1; i < NSIG; i++) { - name = get_signame(i); - if (isdigit(*name)) - out1fmt(snlfmt, name); + int i = 1; + if (argv[1] && strcmp(argv[1], "-l") != 0) { + do { + if (argv[i][0] == '%') { + struct job *jp = getjob(argv[i], 0); + unsigned pid = jp->ps[0].pid; + /* Enough space for ' -NNN' */ + argv[i] = alloca(sizeof(int)*3 + 3); + /* kill_main has matching code to expect + * leading space. Needed to not confuse + * negative pids with "kill -SIGNAL_NO" syntax */ + sprintf(argv[i], " -%u", pid); } - return 0; - } - name = get_signame(signo); - if (!isdigit(*name)) - ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr); - out1fmt(snlfmt, name); - return 0; + } while (argv[++i]); } - - i = 0; - do { - if (**argv == '%') { - jp = getjob(*argv, 0); - pid = -jp->ps[0].pid; - } else { - pid = **argv == '-' ? - -number(*argv + 1) : number(*argv); - } - if (kill(pid, signo) != 0) { - ash_msg("(%d) - %m", pid); - i = 1; - } - } while (*++argv); - - return i; + return kill_main(argc, argv); } static void @@ -3639,7 +3667,8 @@ restartjob(struct job *jp, int mode) if (WIFSTOPPED(ps->status)) { ps->status = -1; } - } while (ps++, --i); + ps++; + } while (--i); out: status = (mode == FORK_FG) ? waitforjob(jp) : 0; INT_ON; @@ -3647,7 +3676,7 @@ restartjob(struct job *jp, int mode) } static int -fg_bgcmd(int argc, char **argv) +fg_bgcmd(int argc ATTRIBUTE_UNUSED, char **argv) { struct job *jp; FILE *out; @@ -3739,24 +3768,21 @@ sprint_status(char *s, int status, int sigonly) * and the jobs command may give out of date information. */ static int -waitproc(int block, int *status) +waitproc(int wait_flags, int *status) { - int flags = 0; - #if JOBS if (jobctl) - flags |= WUNTRACED; + wait_flags |= WUNTRACED; #endif - if (block == 0) - flags |= WNOHANG; - return wait3(status, flags, (struct rusage *)NULL); + /* NB: _not_ safe_waitpid, we need to detect EINTR */ + return waitpid(-1, status, wait_flags); } /* * 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; @@ -3764,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)); - if (pid <= 0) + TRACE(("dowait(%d) called\n", wait_flags)); + pid = waitproc(wait_flags, &status); + TRACE(("wait returns pid=%d, status=%d\n", pid, status)); + if (pid <= 0) { + /* If we were doing blocking wait and (probably) got EINTR, + * check for pending sigs received while waiting. + * (NB: can be moved into callers if needed) */ + if (wait_flags == DOWAIT_BLOCK && pendingsig) + raise_exception(EXSIG); return pid; + } INT_OFF; thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { @@ -3804,7 +3836,6 @@ dowait(int block, struct job *job) #if JOBS if (!WIFSTOPPED(status)) #endif - jobless--; goto out; @@ -3834,7 +3865,7 @@ 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); } } @@ -3848,7 +3879,7 @@ showjob(FILE *out, struct job *jp, int mode) struct procstat *ps; struct procstat *psend; int col; - int indent; + int indent_col; char s[80]; ps = jp->ps; @@ -3860,7 +3891,7 @@ showjob(FILE *out, struct job *jp, int mode) } col = fmtstr(s, 16, "[%d] ", jobno(jp)); - indent = col; + indent_col = col; if (jp == curjob) s[col - 2] = '+'; @@ -3886,7 +3917,7 @@ showjob(FILE *out, struct job *jp, int mode) do { /* for each process */ - col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3; + col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3; start: fprintf(out, "%s%*c%s", s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd @@ -3909,11 +3940,32 @@ showjob(FILE *out, struct job *jp, int mode) } } +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + */ +static void +showjobs(FILE *out, int mode) +{ + struct job *jp; + + TRACE(("showjobs(%x) called\n", mode)); + + /* If not even one job changed, there is nothing to do */ + while (dowait(DOWAIT_NONBLOCK, NULL) > 0) + continue; + + for (jp = curjob; jp; jp = jp->prev_job) { + if (!(mode & SHOW_CHANGED) || jp->changed) { + showjob(out, jp, mode); + } + } +} + static int -jobscmd(int argc, char **argv) +jobscmd(int argc ATTRIBUTE_UNUSED, char **argv) { int mode, m; - FILE *out; mode = 0; while ((m = nextopt("lp"))) { @@ -3923,38 +3975,16 @@ jobscmd(int argc, char **argv) mode = SHOW_PGID; } - out = stdout; argv = argptr; if (*argv) { do - showjob(out, getjob(*argv,0), mode); + showjob(stdout, getjob(*argv,0), mode); while (*++argv); } else - showjobs(out, mode); + showjobs(stdout, mode); return 0; } - -/* - * Print a list of jobs. If "change" is nonzero, only print jobs whose - * statuses have changed since the last call to showjobs. - */ -static void -showjobs(FILE *out, int mode) -{ - struct job *jp; - - TRACE(("showjobs(%x) called\n", mode)); - - /* If not even one one job changed, there is nothing to do */ - while (dowait(DOWAIT_NORMAL, NULL) > 0) - continue; - - for (jp = curjob; jp; jp = jp->prev_job) { - if (!(mode & SHOW_CHANGED) || jp->changed) - showjob(out, jp, mode); - } -} #endif /* JOBS */ static int @@ -3986,13 +4016,16 @@ getstatus(struct job *job) } static int -waitcmd(int argc, char **argv) +waitcmd(int argc ATTRIBUTE_UNUSED, char **argv) { struct job *job; int retval; struct job *jp; - EXSIGON; +// exsig++; +// xbarrier(); + if (pendingsig) + raise_exception(EXSIG); nextopt(nullstr); retval = 0; @@ -4003,16 +4036,14 @@ 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); + dowait(DOWAIT_BLOCK, NULL); } } @@ -4021,27 +4052,25 @@ 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); + dowait(DOWAIT_BLOCK, NULL); job->waited = 1; retval = getstatus(job); repeat: ; } while (*++argv); - out: + ret: return retval; } @@ -4093,7 +4122,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; @@ -4116,6 +4145,8 @@ makejob(union node *node, int nprocs) } memset(jp, 0, sizeof(*jp)); #if JOBS + /* jp->jobctl is a bitfield. + * "jp->jobctl |= jobctl" likely to give awful code */ if (jobctl) jp->jobctl = 1; #endif @@ -4126,7 +4157,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; } @@ -4438,7 +4469,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; @@ -4448,10 +4479,13 @@ clear_traps(void) } } } -/* lives far away from here, needed for forkchild */ + +/* Lives far away from here, needed for forkchild */ 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; @@ -4498,12 +4532,14 @@ forkchild(struct job *jp, union node *n, int mode) jobless = 0; } +/* Called after fork(), in parent */ 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; } @@ -4549,7 +4585,7 @@ forkshell(struct job *jp, union node *n, int mode) ash_msg_and_raise_error("cannot fork"); } if (pid == 0) - forkchild(jp, n, mode); + forkchild(jp, /*n,*/ mode); else forkparent(jp, n, mode, pid); return pid; @@ -4596,8 +4632,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 @@ -4634,6 +4670,7 @@ 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 @@ -4858,23 +4895,21 @@ redirect(union node *redir, int flags) int i; int fd; int newfd; - int *p; - nullredirs++; + + g_nullredirs++; if (!redir) { return; } sv = NULL; INT_OFF; if (flags & REDIR_PUSH) { - struct redirtab *q; - q = ckmalloc(sizeof(struct redirtab)); - q->next = redirlist; - redirlist = q; - q->nullredirs = nullredirs - 1; + sv = ckmalloc(sizeof(*sv)); + sv->next = redirlist; + redirlist = sv; + sv->nullredirs = g_nullredirs - 1; for (i = 0; i < 10; i++) - q->renamed[i] = EMPTY; - nullredirs = 0; - sv = q; + sv->renamed[i] = EMPTY; + g_nullredirs = 0; } n = redir; do { @@ -4884,9 +4919,14 @@ redirect(union node *redir, int flags) continue; /* redirect from/to same file descriptor */ newfd = openredirect(n); - if (fd == newfd) + 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; - if (sv && *(p = &sv->renamed[fd]) == EMPTY) { + } + if (sv && sv->renamed[fd] == EMPTY) { i = fcntl(fd, F_DUPFD, 10); if (i == -1) { @@ -4898,7 +4938,7 @@ redirect(union node *redir, int flags) /* NOTREACHED */ } } else { - *p = i; + sv->renamed[fd] = i; close(fd); } } else { @@ -4907,7 +4947,7 @@ redirect(union node *redir, int flags) dupredirect(n, newfd); } while ((n = n->nfile.next)); INT_ON; - if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0) + if ((flags & REDIR_SAVEFD2) && sv && sv->renamed[2] >= 0) preverrout_fd = sv->renamed[2]; } @@ -4920,11 +4960,16 @@ popredir(int drop) 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) { + if (!drop) + close(i); + continue; + } if (rp->renamed[i] != EMPTY) { if (!drop) { close(i); @@ -4934,7 +4979,7 @@ popredir(int drop) } } redirlist = rp->next; - nullredirs = rp->nullredirs; + g_nullredirs = rp->nullredirs; free(rp); INT_ON; } @@ -4950,7 +4995,7 @@ static void clearredir(int drop) { for (;;) { - nullredirs = 0; + g_nullredirs = 0; if (!redirlist) break; popredir(drop); @@ -5067,8 +5112,9 @@ esclen(const char *start, const char *p) static char * _rmescapes(char *str, int flag) { + static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; + char *p, *q, *r; - static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; unsigned inquotes; int notescaped; int globbing; @@ -5187,8 +5233,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; } @@ -5326,7 +5372,7 @@ 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]); @@ -5381,15 +5427,14 @@ 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; p = buf; } - if (in.buf) - free(in.buf); + free(in.buf); if (in.fd >= 0) { close(in.fd); back_exitstatus = waitforjob(in.jp); @@ -5473,17 +5518,21 @@ 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[] = { + static const char spclchars[] ALIGN1 = { '=', ':', CTLQUOTEMARK, @@ -5583,7 +5632,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; @@ -5599,7 +5648,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; @@ -5620,7 +5669,7 @@ 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 ATTRIBUTE_UNUSED, char *str, int quotes, int zero) { char *loc; @@ -5703,7 +5752,8 @@ varunset(const char *end, const char *var, const char *umsg, int varflags) } 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) { char *startp; char *loc; @@ -5715,7 +5765,8 @@ subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varfla 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; @@ -5774,7 +5825,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; @@ -5871,6 +5922,31 @@ varvalue(char *name, int varflags, int flags) 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 == (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) @@ -5892,20 +5968,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; @@ -5915,7 +5988,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--; @@ -5929,7 +6002,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; } @@ -5940,7 +6014,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 @@ -5965,10 +6043,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; } @@ -5991,8 +6067,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 ); @@ -6000,14 +6079,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)) { @@ -6070,7 +6150,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; @@ -6086,7 +6166,8 @@ ifsbreakup(char *string, struct arglist *arglist) if (strchr(ifs, *p) == NULL ) { p = q; break; - } else if (strchr(defifs, *p) == NULL) { + } + if (strchr(defifs, *p) == NULL) { if (ifsspc) { p++; ifsspc = 0; @@ -6110,7 +6191,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; @@ -6142,7 +6223,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; @@ -6242,7 +6323,7 @@ expmeta(char *enddir, char *name) p++; if (*p == '.') matchdot++; - while (! intpending && (dp = readdir(dirp)) != NULL) { + while (!intpending && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && ! matchdot) continue; if (pmatch(start, dp->d_name)) { @@ -6327,9 +6408,9 @@ expsort(struct strlist *str) } static void -expandmeta(struct strlist *str, int flag) +expandmeta(struct strlist *str /*, int flag*/) { - static const char metachars[] = { + static const char metachars[] ALIGN1 = { '*', '?', '[', 0 }; /* TODO - EXP_REDIR */ @@ -6392,7 +6473,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) { @@ -6407,11 +6489,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; @@ -6458,7 +6540,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); @@ -6468,51 +6551,16 @@ casematch(union node *pattern, char *val) /* ============ find_command */ -#if ENABLE_FEATURE_SH_STANDALONE_SHELL -static int -is_safe_applet(char *name) -{ - /* It isn't a bug to have non-existent applet here... */ - /* ...just a waste of space... */ - static const char safe_applets[][8] = { - "[" - USE_AWK (, "awk" ) - USE_CAT (, "cat" ) - USE_CHMOD (, "chmod" ) - USE_CHOWN (, "chown" ) - USE_CP (, "cp" ) - USE_CUT (, "cut" ) - USE_DD (, "dd" ) - USE_ECHO (, "echo" ) - USE_FIND (, "find" ) - USE_HEXDUMP(, "hexdump") - USE_LN (, "ln" ) - USE_LS (, "ls" ) - USE_MKDIR (, "mkdir" ) - USE_RM (, "rm" ) - USE_SORT (, "sort" ) - USE_TEST (, "test" ) - USE_TOUCH (, "touch" ) - USE_XARGS (, "xargs" ) - }; - int n = sizeof(safe_applets) / sizeof(safe_applets[0]); - int i; - for (i = 0; i < n; i++) - if (strcmp(safe_applets[i], name) == 0) - return 1; - - return 0; -} -#endif - struct builtincmd { const char *name; int (*builtin)(int, char **); /* 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; @@ -6549,7 +6597,6 @@ 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 { @@ -6560,30 +6607,27 @@ struct tblentry { char cmdname[ARB]; /* 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) { int repeated = 0; -#if ENABLE_FEATURE_SH_STANDALONE_SHELL +#if ENABLE_FEATURE_SH_STANDALONE if (strchr(cmd, '/') == NULL) { - struct BB_applet *a; - char **c; - - a = find_applet_by_name(cmd); - if (a) { - if (is_safe_applet(cmd)) { - c = argv; - while (*c) - c++; - applet_name = cmd; - exit(a->main(c - argv, argv)); - } + int a = find_applet_by_name(cmd); + if (a >= 0) { + if (APPLET_IS_NOEXEC(a)) + run_applet_no_and_exit(a, argv); /* re-exec ourselves with the new arguments */ - execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp); + execve(bb_busybox_exec_path, argv, envp); /* If they called chroot or otherwise made the binary no longer * executable, fall through */ } @@ -6608,11 +6652,11 @@ tryexec(char *cmd, char **argv, char **envp) ; ap = new = ckmalloc((ap - argv + 2) * sizeof(char *)); ap[1] = cmd; - *ap = cmd = (char *)DEFAULT_SHELL; + ap[0] = cmd = (char *)DEFAULT_SHELL; ap += 2; argv++; while ((*ap++ = *argv++)) - ; + continue; argv = new; goto repeat; } @@ -6635,8 +6679,8 @@ shellexec(char **argv, const char *path, int idx) clearredir(1); envp = environment(); if (strchr(argv[0], '/') -#if ENABLE_FEATURE_SH_STANDALONE_SHELL - || find_applet_by_name(argv[0]) +#if ENABLE_FEATURE_SH_STANDALONE + || find_applet_by_name(argv[0]) >= 0 #endif ) { tryexec(argv[0], argv, envp); @@ -6749,9 +6793,9 @@ cmdlookup(const char *name, int add) pp = &cmdp->next; } if (add && cmdp == NULL) { - cmdp = *pp = ckmalloc(sizeof(struct tblentry) - ARB + cmdp = *pp = ckzalloc(sizeof(struct tblentry) - ARB + strlen(name) + 1); - cmdp->next = NULL; + /*cmdp->next = NULL; - ckzalloc did it */ cmdp->cmdtype = CMDUNKNOWN; strcpy(cmdp->cmdname, name); } @@ -6795,7 +6839,7 @@ addcmdentry(char *name, struct cmdentry *entry) } static int -hashcmd(int argc, char **argv) +hashcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { struct tblentry **pp; struct tblentry *cmdp; @@ -6803,10 +6847,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) { @@ -6816,13 +6861,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; @@ -6843,12 +6891,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; + } } } } @@ -6860,15 +6909,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; @@ -6884,9 +6932,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) @@ -6990,21 +7037,15 @@ static const char *const * findkwd(const char *s) { return bsearch(s, tokname_array + KWDOFFSET, - (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET, - sizeof(char *), pstrcmp); + ARRAY_SIZE(tokname_array) - KWDOFFSET, + sizeof(tokname_array[0]), pstrcmp); } /* * Locate and print what a word is... */ -#if ENABLE_ASH_CMDCMD static int describe_command(char *command, int describe_command_verbose) -#else -#define describe_command_verbose 1 -static int -describe_command(char *command) -#endif { struct cmdentry entry; struct tblentry *cmdp; @@ -7027,13 +7068,12 @@ describe_command(char *command) /* Then look at the aliases */ ap = lookupalias(command, 0); if (ap != NULL) { - if (describe_command_verbose) { - out1fmt(" is an alias for %s", ap->val); - } else { + if (!describe_command_verbose) { out1str("alias "); printalias(ap); return 0; } + out1fmt(" is an alias for %s", ap->val); goto out; } #endif @@ -7100,24 +7140,26 @@ describe_command(char *command) } static int -typecmd(int argc, char **argv) +typecmd(int argc ATTRIBUTE_UNUSED, char **argv) { - int i; + int i = 1; int err = 0; + int verbose = 1; - for (i = 1; i < argc; i++) { -#if ENABLE_ASH_CMDCMD - err |= describe_command(argv[i], 1); -#else - err |= describe_command(argv[i]); -#endif + /* type -p ... ? (we don't bother checking for 'p') */ + if (argv[1] && argv[1][0] == '-') { + i++; + verbose = 0; + } + while (argv[i]) { + err |= describe_command(argv[i++], verbose); } return err; } #if ENABLE_ASH_CMDCMD static int -commandcmd(int argc, char **argv) +commandcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int c; enum { @@ -7452,7 +7494,7 @@ dotrap(void) char *q; int i; int savestatus; - int skip = 0; + int skip; savestatus = exitstatus; pendingsig = 0; @@ -7469,10 +7511,10 @@ dotrap(void) skip = evalstring(p, SKIPEVAL); exitstatus = savestatus; if (skip) - break; + return skip; } - return skip; + return 0; } /* forward declarations - evaluation is fairly recursive business... */ @@ -7655,6 +7697,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); @@ -7694,6 +7737,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; @@ -7725,7 +7769,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; @@ -7755,7 +7799,7 @@ 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: @@ -7800,7 +7844,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); @@ -7856,16 +7900,16 @@ setinteractive(int on) #if !ENABLE_FEATURE_SH_EXTRA_QUIET if (is_interactive > 1) { /* Looks like they want an interactive shell */ - static smallint do_banner; + static smallint did_banner; - if (!do_banner) { + if (!did_banner) { out1fmt( "\n\n" - "%s Built-in shell (ash)\n" + "%s built-in shell (ash)\n" "Enter 'help' for a list of built-in commands." "\n\n", - BB_BANNER); - do_banner = 1; + bb_banner); + did_banner = 1; } } #endif @@ -7944,7 +7988,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; @@ -7955,7 +7999,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); @@ -7991,7 +8035,7 @@ parse_command_args(char **argv, const char **path) do { switch (c) { case 'p': - *path = defpath; + *path = bb_default_path; break; default: /* run 'typecmd' for other options */ @@ -8018,7 +8062,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)); @@ -8055,7 +8099,7 @@ mklocal(char *name) * The "local" command. */ static int -localcmd(int argc, char **argv) +localcmd(int argc ATTRIBUTE_UNUSED, char **argv) { char *name; @@ -8067,21 +8111,21 @@ localcmd(int argc, char **argv) } static int -falsecmd(int argc, char **argv) +falsecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { return 1; } static int -truecmd(int argc, char **argv) +truecmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { return 0; } static int -execcmd(int argc, char **argv) +execcmd(int argc ATTRIBUTE_UNUSED, char **argv) { - if (argc > 1) { + if (argv[1]) { iflag = 0; /* exit on error */ mflag = 0; optschanged(); @@ -8094,7 +8138,7 @@ execcmd(int argc, char **argv) * The return command. */ static int -returncmd(int argc, char **argv) +returncmd(int argc ATTRIBUTE_UNUSED, char **argv) { /* * If called outside a function, do what ksh does; @@ -8213,7 +8257,6 @@ static const struct builtincmd builtintab[] = { { BUILTIN_REGULAR "wait", waitcmd }, }; -#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0])) #define COMMANDCMD (builtintab + 5 + \ 2 * ENABLE_ASH_BUILTIN_TEST + \ @@ -8235,7 +8278,7 @@ find_builtin(const char *name) struct builtincmd *bp; bp = bsearch( - name, builtintab, NUMBUILTINS, sizeof(builtintab[0]), + name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), pstrcmp ); return bp; @@ -8254,7 +8297,7 @@ isassignment(const char *p) return *q == '='; } static int -bltincmd(int argc, char **argv) +bltincmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { /* Preserve exitstatus of a previous possible redirection * as POSIX mandates */ @@ -8263,8 +8306,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; @@ -8290,7 +8333,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; @@ -8353,12 +8396,12 @@ evalcommand(union node *cmd, int flags) const char *p = " %s"; p++; - dprintf(preverrout_fd, p, expandstr(ps4val())); + fdprintf(preverrout_fd, p, expandstr(ps4val())); sp = varlist.list; for (n = 0; n < 2; n++) { while (sp) { - dprintf(preverrout_fd, p, sp->text); + fdprintf(preverrout_fd, p, sp->text); sp = sp->next; if (*p == '%') { p--; @@ -8366,7 +8409,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; @@ -8424,7 +8467,7 @@ evalcommand(union node *cmd, int flags) /* 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; @@ -8450,22 +8493,15 @@ evalcommand(union node *cmd, int flags) } 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); @@ -8515,7 +8551,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; @@ -8561,9 +8597,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 ATTRIBUTE_UNUSED, 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]); @@ -8589,11 +8625,6 @@ enum { INPUT_NOFILE_OK = 2, }; -/* - * 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. - */ static int plinno = 1; /* input line number */ /* number of characters left in input buffer */ static int parsenleft; /* copy of parsefile->nleft */ @@ -8646,7 +8677,7 @@ preadfd(void) retry: #if ENABLE_FEATURE_EDITING if (!iflag || parsefile->fd) - nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1); else { #if ENABLE_FEATURE_TAB_COMPLETION line_input_state->path_lookup = pathval(); @@ -8663,19 +8694,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(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, 0); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; + int flags = fcntl(0, F_GETFL); + 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; @@ -8683,6 +8716,7 @@ preadfd(void) } } } +#endif return nr; } @@ -8852,7 +8886,7 @@ pushstring(char *s, void *ap) INT_OFF; /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ if (parsefile->strpush) { - sp = ckmalloc(sizeof(struct strpush)); + sp = ckzalloc(sizeof(struct strpush)); sp->prev = parsefile->strpush; parsefile->strpush = sp; } else @@ -8884,11 +8918,11 @@ pushfile(void) parsefile->lleft = parselleft; parsefile->nextc = parsenextc; parsefile->linno = plinno; - pf = ckmalloc(sizeof(*pf)); + pf = ckzalloc(sizeof(*pf)); pf->prev = parsefile; pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; + /*pf->strpush = NULL; - ckzalloc did it */ + /*pf->basestrpush.prev = NULL;*/ parsefile = pf; } @@ -8900,8 +8934,7 @@ popfile(void) INT_OFF; if (pf->fd >= 0) close(pf->fd); - if (pf->buf) - free(pf->buf); + free(pf->buf); while (pf->strpush) popstring(); parsefile = pf->prev; @@ -8944,7 +8977,7 @@ closescript(void) static void setinputfd(int fd, int push) { - fcntl(fd, F_SETFD, FD_CLOEXEC); + close_on_exec_on(fd); if (push) { pushfile(); parsefile->buf = 0; @@ -9014,7 +9047,7 @@ setinputstring(char *string) /* times of mailboxes */ static time_t mailtime[MAXMBOXES]; /* Set if MAIL or MAILPATH is changed. */ -static int mail_var_path_changed; +static smallint mail_var_path_changed; /* * Print appropriate message(s) if mail has arrived. @@ -9063,9 +9096,9 @@ chkmail(void) } static void -changemail(const char *val) +changemail(const char *val ATTRIBUTE_UNUSED) { - mail_var_path_changed++; + mail_var_path_changed = 1; } #endif /* ASH_MAIL */ @@ -9090,7 +9123,7 @@ setparam(char **argv) } *ap = NULL; freeparam(&shellparam); - shellparam.malloc = 1; + shellparam.malloced = 1; shellparam.nparam = nparam; shellparam.p = newparam; #if ENABLE_ASH_GETOPTS @@ -9102,8 +9135,25 @@ 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 +static int minus_o(char *name, int val) { int i; @@ -9112,15 +9162,17 @@ 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 -o %s", name); + return 1; } 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) @@ -9136,7 +9188,7 @@ setoption(int flag, int val) ash_msg_and_raise_error("illegal option -%c", flag); /* NOTREACHED */ } -static void +static int options(int cmdline) { char *p; @@ -9146,8 +9198,11 @@ options(int cmdline) if (cmdline) minusc = NULL; while ((p = *argptr) != NULL) { - argptr++; c = *p++; + if (c != '-' && c != '+') + break; + argptr++; + val = 0; /* val = 0 if c == '+' */ if (c == '-') { val = 1; if (p[0] == '\0' || LONE_DASH(p)) { @@ -9161,20 +9216,23 @@ options(int cmdline) } break; /* "-" or "--" terminates options */ } - } else if (c == '+') { - val = 0; - } else { - argptr--; - break; } + /* first char was + or - */ while ((c = *p++) != '\0') { + /* bash 3.2 indeed handles -c CMD and +c CMD the same */ if (c == 'c' && cmdline) { - minusc = p; /* command is after shell args*/ + minusc = p; /* command is after shell args */ } else if (c == 'o') { - minus_o(*argptr, val); + if (minus_o(*argptr, val)) { + /* it already printed err message */ + return 1; /* error */ + } if (*argptr) argptr++; - } else if (cmdline && (c == '-')) { // long options + } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ + isloginsh = 1; + /* bash does not accept +-login, we also won't */ + } else if (cmdline && val && (c == '-')) { /* long options */ if (strcmp(p, "login") == 0) isloginsh = 1; break; @@ -9183,26 +9241,27 @@ options(int cmdline) } } } + return 0; } /* * The shift builtin command. */ static int -shiftcmd(int argc, char **argv) +shiftcmd(int argc ATTRIBUTE_UNUSED, 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"); INT_OFF; shellparam.nparam -= n; for (ap1 = shellparam.p; --n >= 0; ap1++) { - if (shellparam.malloc) + if (shellparam.malloced) free(*ap1); } ap2 = shellparam.p; @@ -9250,18 +9309,23 @@ showvars(const char *sep_prefix, int on, int off) * The set command builtin. */ static int -setcmd(int argc, char **argv) +setcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { - 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 @@ -9409,15 +9473,20 @@ getoptscmd(int argc, char **argv) /* ============ Shell parser */ -static int tokpushback; /* last token pushed back */ +/* + * 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. + */ +static smallint tokpushback; /* last token pushed back */ #define NEOF ((union node *)&tokpushback) -static int parsebackquote; /* nonzero if we are inside backquotes */ +static smallint parsebackquote; /* nonzero if we are inside backquotes */ static int lasttoken; /* last token read */ static char *wordtext; /* text of last word returned by readtoken */ static struct nodelist *backquotelist; static union node *redirnode; static struct heredoc *heredoc; -static int quoteflag; /* set if (part of) last token was quoted */ +static smallint quoteflag; /* set if (part of) last token was quoted */ static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN; static void @@ -9450,7 +9519,7 @@ raise_error_unexpected_syntax(int token) struct heredoc { struct heredoc *next; /* next here document in list */ - union node *here; /* redirection node */ + union node *here; /* redirection node */ char *eofmark; /* string indicating end of input */ int striptabs; /* if set, strip leading tabs */ }; @@ -9483,9 +9552,9 @@ list(int nlflag) n2->npipe.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; @@ -9494,7 +9563,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; @@ -9511,7 +9580,7 @@ list(int nlflag) if (nlflag == 1) return n1; } else { - tokpushback++; + tokpushback = 1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; if (peektoken()) @@ -9526,7 +9595,7 @@ list(int nlflag) default: if (nlflag == 1) raise_error_unexpected_syntax(-1); - tokpushback++; + tokpushback = 1; return n1; } } @@ -9546,12 +9615,12 @@ andor(void) } else if (t == TOR) { t = NOR; } else { - tokpushback++; + tokpushback = 1; return n1; } 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; @@ -9572,18 +9641,18 @@ pipeline(void) negate = !negate; checkkwd = CHKKWD | CHKALIAS; } else - tokpushback++; + 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.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; @@ -9591,9 +9660,9 @@ pipeline(void) lp->next = NULL; n1 = pipenode; } - tokpushback++; + tokpushback = 1; if (negate) { - n2 = stalloc(sizeof(struct nnot)); + n2 = stzalloc(sizeof(struct nnot)); n2->type = NNOT; n2->nnot.com = n1; return n2; @@ -9606,9 +9675,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; @@ -9677,7 +9746,8 @@ parsefname(void) 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) { @@ -9708,8 +9778,9 @@ simplecmd(void) checkkwd = savecheckkwd; switch (readtoken()) { 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; n->narg.backquote = backquotelist; if (savecheckkwd && isassignment(wordtext)) { @@ -9749,7 +9820,7 @@ simplecmd(void) } /* fall through */ default: - tokpushback++; + tokpushback = 1; goto out; } } @@ -9757,7 +9828,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; @@ -9783,7 +9854,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) @@ -9791,7 +9862,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); @@ -9803,14 +9874,14 @@ parse_command(void) n2->nif.elsepart = list(0); else { n2->nif.elsepart = NULL; - tokpushback++; + tokpushback = 1; } t = TFI; break; 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(); @@ -9826,15 +9897,16 @@ parse_command(void) case TFOR: if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) raise_error_syntax("Bad for loop variable"); - n1 = stalloc(sizeof(struct nfor)); + 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; @@ -9845,18 +9917,18 @@ 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 * that the original Bourne shell only allowed NL). */ if (lasttoken != TNL && lasttoken != TSEMI) - tokpushback++; + tokpushback = 1; } checkkwd = CHKNL | CHKKWD | CHKALIAS; if (readtoken() != TDO) @@ -9865,15 +9937,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); @@ -9886,12 +9958,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) @@ -9899,7 +9972,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); @@ -9917,10 +9990,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: @@ -9929,7 +10002,7 @@ parse_command(void) break; case TWORD: case TREDIR: - tokpushback++; + tokpushback = 1; return simplecmd(); } @@ -9945,11 +10018,11 @@ parse_command(void) rpp = &n2->nfile.next; parsefname(); } - tokpushback++; + tokpushback = 1; *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; @@ -9971,8 +10044,6 @@ parse_command(void) * will run code that appears at the end of readtoken1. */ -static int parsebackquote; /* nonzero if we are inside backquotes */ - #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} @@ -9983,19 +10054,24 @@ static int parsebackquote; /* nonzero if we are inside backquotes */ static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs) { + /* NB: syntax parameter fits into smallint */ int c = firstc; char *out; int len; char line[EOFMARKLEN + 1]; - struct nodelist *bqlist = 0; - int quotef = 0; - int dblquote = 0; - int varnest = 0; /* levels of variables expansion */ - int arinest = 0; /* levels of arithmetic expansion */ - int parenlevel = 0; /* levels of parens in arithmetic */ - int dqvarnest = 0; /* levels of variables expansion within double quotes */ - int oldstyle = 0; - int prevsyntax = 0; /* syntax before arithmetic */ + struct nodelist *bqlist; + smallint quotef; + smallint dblquote; + smallint oldstyle; + smallint prevsyntax; /* syntax before arithmetic */ +#if ENABLE_ASH_EXPAND_PRMT + smallint pssyntax; /* we are expanding a prompt string */ +#endif + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int dqvarnest; /* levels of variables expansion within double quotes */ + #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; @@ -10009,13 +10085,17 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) (void) &prevsyntax; (void) &syntax; #endif - startlinno = plinno; - dblquote = 0; - if (syntax == DQSYNTAX) - dblquote = 1; - quotef = 0; bqlist = NULL; + quotef = 0; + oldstyle = 0; + prevsyntax = 0; +#if ENABLE_ASH_EXPAND_PRMT + pssyntax = (syntax == PSSYNTAX); + if (pssyntax) + syntax = DQSYNTAX; +#endif + dblquote = (syntax == DQSYNTAX); varnest = 0; arinest = 0; parenlevel = 0; @@ -10054,6 +10134,12 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) if (doprompt) setprompt(2); } else { +#if ENABLE_ASH_EXPAND_PRMT + if (c == '$' && pssyntax) { + USTPUTC(CTLESC, out); + USTPUTC('\\', out); + } +#endif if (dblquote && c != '\\' && c != '`' && c != '$' && ( @@ -10066,7 +10152,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) if (SIT(c, SQSYNTAX) == CCTL) USTPUTC(CTLESC, out); USTPUTC(c, out); - quotef++; + quotef = 1; } break; case CSQUOTE: @@ -10090,7 +10176,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) syntax = BASESYNTAX; dblquote = 0; } - quotef++; + quotef = 1; goto quotemark; } break; @@ -10122,10 +10208,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) if (--arinest == 0) { USTPUTC(CTLENDARI, out); syntax = prevsyntax; - if (syntax == DQSYNTAX) - dblquote = 1; - else - dblquote = 0; + dblquote = (syntax == DQSYNTAX); } else USTPUTC(')', out); } else { @@ -10237,7 +10320,7 @@ parseredir: { char fd = *out; union node *np; - np = stalloc(sizeof(struct nfile)); + np = stzalloc(sizeof(struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); @@ -10252,22 +10335,22 @@ parseredir: { pungetc(); } } else { /* c == '<' */ - np->nfile.fd = 0; + /*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; @@ -10307,7 +10390,7 @@ parsesub: { int typeloc; int flags; char *p; - static const char types[] = "}-+?="; + static const char types[] ALIGN1 = "}-+?="; c = pgetc(); if ( @@ -10411,21 +10494,20 @@ parsesub: { */ parsebackq: { struct nodelist **nlpp; - int savepbq; + smallint savepbq; union node *n; char *volatile str; struct jmploc jmploc; struct jmploc *volatile savehandler; size_t savelen; - int saveprompt = 0; + smallint saveprompt = 0; + #ifdef __GNUC__ (void) &saveprompt; #endif - savepbq = parsebackquote; if (setjmp(jmploc.loc)) { - if (str) - free(str); + free(str); parsebackquote = 0; exception_handler = savehandler; longjmp(exception_handler->loc, 1); @@ -10510,8 +10592,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) { @@ -10603,13 +10685,15 @@ parsearith: { #define NEW_xxreadtoken #ifdef NEW_xxreadtoken /* singles must be first! */ -static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 }; +static const char xxreadtoken_chars[7] ALIGN1 = { + '\n', '(', ')', '&', '|', ';', 0 +}; -static const char xxreadtoken_tokens[] = { +static const char xxreadtoken_tokens[] ALIGN1 = { TNL, TLP, TRP, /* only single occurrence allowed */ TBACKGND, TPIPE, TSEMI, /* if single occurrence */ TEOF, /* corresponds to trailing nul */ - TAND, TOR, TENDCASE, /* if double occurrence */ + TAND, TOR, TENDCASE /* if double occurrence */ }; #define xxreadtoken_doubles \ @@ -10754,7 +10838,7 @@ readtoken(void) { int t; #if DEBUG - int alreadyseen = tokpushback; + smallint alreadyseen = tokpushback; #endif #if ENABLE_ASH_ALIAS @@ -10820,7 +10904,7 @@ peektoken(void) int t; t = readtoken(); - tokpushback++; + tokpushback = 1; return tokname_array[t][0]; } @@ -10843,7 +10927,7 @@ parsecmd(int interact) return NEOF; if (t == TNL) return NULL; - tokpushback++; + tokpushback = 1; return list(1); } @@ -10857,7 +10941,7 @@ parseheredoc(void) union node *n; here = heredoclist; - heredoclist = 0; + heredoclist = NULL; while (here) { if (needprompt) { @@ -10865,9 +10949,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; @@ -10887,7 +10971,7 @@ expandstr(const char *ps) /* XXX Fix (char *) cast. */ setinputstring((char *)ps); - readtoken1(pgetc(), DQSYNTAX, nullstr, 0); + readtoken1(pgetc(), PSSYNTAX, nullstr, 0); popfile(); n.narg.type = NARG; @@ -10932,20 +11016,19 @@ evalstring(char *s, int mask) * The eval command. */ static int -evalcmd(int argc, char **argv) +evalcmd(int argc ATTRIBUTE_UNUSED, 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); @@ -10999,7 +11082,8 @@ cmdloop(int top) } numeof++; } else if (nflag == 0) { - job_warning = (job_warning == 2) ? 1 : 0; + /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ + job_warning >>= 1; numeof = 0; evaltree(n, 0); } @@ -11055,16 +11139,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); @@ -11072,7 +11155,7 @@ dotcmd(int argc, char **argv) cmdloop(0); popfile(); - if (argc > 2) { + if (argc) { freeparam(&shellparam); shellparam = saveparam; }; @@ -11082,11 +11165,11 @@ dotcmd(int argc, char **argv) } static int -exitcmd(int argc, char **argv) +exitcmd(int argc ATTRIBUTE_UNUSED, char **argv) { if (stoppedjobs()) return 0; - if (argc > 1) + if (argv[1]) exitstatus = number(argv[1]); raise_exception(EXEXIT); /* NOTREACHED */ @@ -11096,7 +11179,7 @@ exitcmd(int argc, char **argv) static int echocmd(int argc, char **argv) { - return bb_echo(argv); + return echo_main(argc, argv); } #endif @@ -11104,7 +11187,7 @@ echocmd(int argc, char **argv) static int testcmd(int argc, char **argv) { - return bb_test(argc, argv); + return test_main(argc, argv); } #endif @@ -11155,19 +11238,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) return; } -#if ENABLE_FEATURE_SH_STANDALONE_SHELL - if (find_applet_by_name(name)) { - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; - } - /* Already caught above - if (is_safe_applet(name)) { - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; - }*/ -#endif +/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ updatetbl = (path == pathval()); if (!updatetbl) { @@ -11217,6 +11288,14 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) } } +#if ENABLE_FEATURE_SH_STANDALONE + if (find_applet_by_name(name) >= 0) { + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } +#endif + /* We have to search path. */ prev = -1; /* where to start */ if (cmdp && cmdp->rehash) { /* doing a rehash */ @@ -11231,14 +11310,16 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) loop: while ((fullname = padvance(&path, name)) != NULL) { stunalloc(fullname); + /* NB: code below will still use fullname + * despite it being "unallocated" */ idx++; if (pathopt) { if (prefix(pathopt, "builtin")) { if (bcmd) goto builtin_success; continue; - } else if (!(act & DO_NOFUNC) && - prefix(pathopt, "func")) { + } else if (!(act & DO_NOFUNC) + && prefix(pathopt, "func")) { /* handled below */ } else { /* ignore unimplemented options */ @@ -11266,6 +11347,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) continue; if (pathopt) { /* this is a %func directory */ stalloc(strlen(fullname) + 1); + /* NB: stalloc will return space pointed by fullname + * (because we don't have any intervening allocations + * between stunalloc above and this stalloc) */ readcmdfile(fullname); cmdp = cmdlookup(name, 0); if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION) @@ -11319,7 +11403,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 ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { char *action; char **ap; @@ -11354,8 +11438,7 @@ trapcmd(int argc, char **argv) else action = ckstrdup(action); } - if (trap[signo]) - free(trap[signo]); + free(trap[signo]); trap[signo] = action; if (signo != 0) setsignal(signo); @@ -11373,12 +11456,12 @@ trapcmd(int argc, char **argv) * Lists available builtins */ static int -helpcmd(int argc, char **argv) +helpcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int col, i; out1fmt("\nBuilt-in commands:\n-------------------\n"); - for (col = 0, i = 0; i < NUMBUILTINS; i++) { + for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), builtintab[i].name + 1); if (col > 60) { @@ -11386,12 +11469,16 @@ helpcmd(int argc, char **argv) col = 0; } } -#if ENABLE_FEATURE_SH_STANDALONE_SHELL - for (i = 0; i < NUM_APPLETS; i++) { - col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name); - if (col > 60) { - out1fmt("\n"); - col = 0; +#if ENABLE_FEATURE_SH_STANDALONE + { + 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 @@ -11404,7 +11491,7 @@ helpcmd(int argc, char **argv) * The export and readonly commands. */ static int -exportcmd(int argc, char **argv) +exportcmd(int argc ATTRIBUTE_UNUSED, char **argv) { struct var *vp; char *name; @@ -11455,7 +11542,7 @@ unsetfunc(const char *name) * with the same name. */ static int -unsetcmd(int argc, char **argv) +unsetcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { char **ap; int i; @@ -11484,7 +11571,7 @@ unsetcmd(int argc, char **argv) #include -static const unsigned char timescmd_str[] = { +static const unsigned char timescmd_str[] ALIGN1 = { ' ', offsetof(struct tms, tms_utime), '\n', offsetof(struct tms, tms_stime), ' ', offsetof(struct tms, tms_cutime), @@ -11493,7 +11580,7 @@ static const unsigned char timescmd_str[] = { }; static int -timescmd(int ac, char **av) +timescmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { long clk_tck, s, t; const unsigned char *p; @@ -11545,17 +11632,16 @@ dash_arith(const char *s) * Copyright (C) 2003 Vladimir Oleynik */ static int -letcmd(int argc, char **argv) +letcmd(int argc ATTRIBUTE_UNUSED, 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; } @@ -11580,7 +11666,7 @@ typedef enum __rlimit_resource rlim_t; * This uses unbuffered input, which may be avoidable in some cases. */ static int -readcmd(int argc, char **argv) +readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { char **ap; int backslash; @@ -11593,7 +11679,7 @@ readcmd(int argc, char **argv) int status; int i; #if ENABLE_ASH_READ_NCHARS - int nch_flag = 0; + int n_flag = 0; int nchars = 0; int silent = 0; struct termios tty, old_tty; @@ -11623,10 +11709,10 @@ readcmd(int argc, char **argv) break; #if ENABLE_ASH_READ_NCHARS case 'n': - nchars = strtol(optionarg, &p, 10); - if (*p) + nchars = bb_strtou(optionarg, NULL, 10); + if (nchars < 0 || errno) ash_msg_and_raise_error("invalid count"); - nch_flag = (nchars > 0); + n_flag = nchars; /* just a flag "nchars is nonzero" */ break; case 's': silent = 1; @@ -11634,14 +11720,15 @@ readcmd(int argc, char **argv) #endif #if ENABLE_ASH_READ_TIMEOUT case 't': - ts.tv_sec = strtol(optionarg, &p, 10); + ts.tv_sec = bb_strtou(optionarg, &p, 10); ts.tv_usec = 0; - if (*p == '.') { + /* EINVAL means number is ok, but not terminated by NUL */ + if (*p == '.' && errno == EINVAL) { char *p2; if (*++p) { int scale; - ts.tv_usec = strtol(p, &p2, 10); - if (*p2) + ts.tv_usec = bb_strtou(p, &p2, 10); + if (errno) ash_msg_and_raise_error("invalid timeout"); scale = p2 - p; /* normalize to usec */ @@ -11650,11 +11737,12 @@ readcmd(int argc, char **argv) while (scale++ < 6) ts.tv_usec *= 10; } - } else if (*p) { + } else if (ts.tv_sec < 0 || errno) { ash_msg_and_raise_error("invalid timeout"); } - if ( ! ts.tv_sec && ! ts.tv_usec) + if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ ash_msg_and_raise_error("invalid timeout"); + } break; #endif case 'r': @@ -11674,29 +11762,34 @@ readcmd(int argc, char **argv) if (ifs == NULL) ifs = defifs; #if ENABLE_ASH_READ_NCHARS - if (nch_flag || silent) { - tcgetattr(0, &tty); - old_tty = tty; - if (nch_flag) { - tty.c_lflag &= ~ICANON; - tty.c_cc[VMIN] = nchars; - } - if (silent) { - tty.c_lflag &= ~(ECHO|ECHOK|ECHONL); - + 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); } - tcsetattr(0, TCSANOW, &tty); } #endif #if ENABLE_ASH_READ_TIMEOUT if (ts.tv_sec || ts.tv_usec) { - FD_ZERO (&set); - FD_SET (0, &set); + FD_ZERO(&set); + FD_SET(0, &set); - i = select(FD_SETSIZE, &set, NULL, NULL, &ts); - if (!i) { + /* 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 (nch_flag) + if (n_flag) tcsetattr(0, TCSANOW, &old_tty); #endif return 1; @@ -11707,13 +11800,8 @@ readcmd(int argc, char **argv) startword = 1; backslash = 0; STARTSTACKSTR(p); -#if ENABLE_ASH_READ_NCHARS - while (!nch_flag || nchars--) -#else - for (;;) -#endif - { - if (read(0, &c, 1) != 1) { + do { + if (nonblock_safe_read(0, &c, 1) != 1) { status = 1; break; } @@ -11746,8 +11834,15 @@ readcmd(int argc, char **argv) STPUTC(c, p); } } +/* end of do {} while: */ #if ENABLE_ASH_READ_NCHARS - if (nch_flag || silent) + while (!n_flag || --nchars); +#else + while (1); +#endif + +#if ENABLE_ASH_READ_NCHARS + if (n_flag || silent) tcsetattr(0, TCSANOW, &old_tty); #endif @@ -11762,11 +11857,11 @@ readcmd(int argc, char **argv) } static int -umaskcmd(int argc, char **argv) +umaskcmd(int argc ATTRIBUTE_UNUSED, char **argv) { - static const char permuser[3] = "ugo"; - static const char permmode[3] = "rwx"; - static const short int permmask[] = { + static const char permuser[3] ALIGN1 = "ugo"; + static const char permmode[3] ALIGN1 = "rwx"; + static const short permmask[] ALIGN2 = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH @@ -11840,48 +11935,81 @@ umaskcmd(int argc, char **argv) */ struct limits { - const char *name; - int cmd; - int factor; /* multiply by to get rlim_{cur,max} values */ + uint8_t cmd; /* RLIMIT_xxx fit into it */ + uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ char option; }; -static const struct limits limits[] = { +static const struct limits limits_tbl[] = { #ifdef RLIMIT_CPU - { "time(seconds)", RLIMIT_CPU, 1, 't' }, + { RLIMIT_CPU, 0, 't' }, #endif #ifdef RLIMIT_FSIZE - { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, + { RLIMIT_FSIZE, 9, 'f' }, #endif #ifdef RLIMIT_DATA - { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, + { RLIMIT_DATA, 10, 'd' }, #endif #ifdef RLIMIT_STACK - { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, + { RLIMIT_STACK, 10, 's' }, #endif -#ifdef RLIMIT_CORE - { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, +#ifdef RLIMIT_CORE + { RLIMIT_CORE, 9, 'c' }, #endif #ifdef RLIMIT_RSS - { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, + { RLIMIT_RSS, 10, 'm' }, #endif #ifdef RLIMIT_MEMLOCK - { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, + { RLIMIT_MEMLOCK, 10, 'l' }, #endif #ifdef RLIMIT_NPROC - { "process", RLIMIT_NPROC, 1, 'p' }, + { RLIMIT_NPROC, 0, 'p' }, #endif #ifdef RLIMIT_NOFILE - { "nofiles", RLIMIT_NOFILE, 1, 'n' }, + { RLIMIT_NOFILE, 0, 'n' }, #endif #ifdef RLIMIT_AS - { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' }, + { RLIMIT_AS, 10, 'v' }, #endif #ifdef RLIMIT_LOCKS - { "locks", RLIMIT_LOCKS, 1, 'w' }, + { RLIMIT_LOCKS, 0, 'w' }, #endif - { NULL, 0, 0, '\0' } }; +static const char limits_name[] = +#ifdef RLIMIT_CPU + "time(seconds)" "\0" +#endif +#ifdef RLIMIT_FSIZE + "file(blocks)" "\0" +#endif +#ifdef RLIMIT_DATA + "data(kb)" "\0" +#endif +#ifdef RLIMIT_STACK + "stack(kb)" "\0" +#endif +#ifdef RLIMIT_CORE + "coredump(blocks)" "\0" +#endif +#ifdef RLIMIT_RSS + "memory(kb)" "\0" +#endif +#ifdef RLIMIT_MEMLOCK + "locked memory(kb)" "\0" +#endif +#ifdef RLIMIT_NPROC + "process" "\0" +#endif +#ifdef RLIMIT_NOFILE + "nofiles" "\0" +#endif +#ifdef RLIMIT_AS + "vmemory(kb)" "\0" +#endif +#ifdef RLIMIT_LOCKS + "locks" "\0" +#endif +; enum limtype { SOFT = 0x1, HARD = 0x2 }; @@ -11898,13 +12026,13 @@ printlim(enum limtype how, const struct rlimit *limit, if (val == RLIM_INFINITY) out1fmt("unlimited\n"); else { - val /= l->factor; + val >>= l->factor_shift; out1fmt("%lld\n", (long long) val); } } static int -ulimitcmd(int argc, char **argv) +ulimitcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int c; rlim_t val = 0; @@ -11964,8 +12092,8 @@ ulimitcmd(int argc, char **argv) what = optc; } - for (l = limits; l->option != what; l++) - ; + for (l = limits_tbl; l->option != what; l++) + continue; set = *argptr ? 1 : 0; if (set) { @@ -11985,13 +12113,15 @@ ulimitcmd(int argc, char **argv) } if (c) ash_msg_and_raise_error("bad number"); - val *= l->factor; + val <<= l->factor_shift; } } if (all) { - for (l = limits; l->name; l++) { + const char *lname = limits_name; + for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) { getrlimit(l->cmd, &limit); - out1fmt("%-20s ", l->name); + out1fmt("%-20s ", lname); + lname += strlen(lname) + 1; printlim(how, &limit, l); } return 0; @@ -12423,7 +12553,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) } /* longest must be first */ -static const char op_tokens[] = { +static const char op_tokens[] ALIGN1 = { '<','<','=',0, TOK_LSHIFT_ASSIGN, '>','>','=',0, TOK_RSHIFT_ASSIGN, '<','<', 0, TOK_LSHIFT, @@ -12736,7 +12866,7 @@ init(void) * Process the shell command line arguments. */ static void -procargs(int argc, char **argv) +procargs(char **argv) { int i; const char *xminusc; @@ -12744,12 +12874,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) { @@ -12784,7 +12917,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++; @@ -12840,17 +12973,22 @@ extern int etext(); * exception occurs. When an exception occurs the variable "state" * is used to figure out how far we had gotten. */ -int ash_main(int argc, char **argv); -int ash_main(int argc, char **argv) +int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ash_main(int argc ATTRIBUTE_UNUSED, char **argv) { char *shinit; volatile int state; struct jmploc jmploc; struct stackmark smark; -#ifdef __GLIBC__ - dash_errno = __errno_location(); + /* 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); @@ -12899,7 +13037,8 @@ int ash_main(int argc, char **argv) #endif init(); setstackmark(&smark); - procargs(argc, argv); + procargs(argv); + #if ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE");