X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fash.c;h=138f19abdaeb7681c1aa8449f56208d4d9bab486;hb=e880b1fea8442b623d45a8f62227fc35cbcac7ec;hp=5586015439cf5d58030731d43c838702fba74c2a;hpb=ee1fd1246e7d25990531dbbb2d267605b8914d5d;p=oweals%2Fbusybox.git diff --git a/shell/ash.c b/shell/ash.c index 558601543..138f19abd 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -16,7 +16,7 @@ * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ //config:config ASH -//config: bool "ash (77 kb)" +//config: bool "ash (78 kb)" //config: default y //config: depends on !NOMMU //config: help @@ -177,11 +177,15 @@ #define JOBS ENABLE_ASH_JOB_CONTROL -#include #include #include #include /* for setting $HOSTNAME */ #include "busybox.h" /* for applet_names */ +#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS +# include "embedded_scripts.h" +#else +# define NUM_SCRIPTS 0 +#endif /* So far, all bash compat is controlled by one config option */ /* Separate defines document which part of code implements what */ @@ -198,15 +202,30 @@ #define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT #define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT -/* [[ EXPR ]] */ +/* BASH_TEST2: [[ EXPR ]] + * Status of [[ support: + * We replace && and || with -a and -o + * TODO: + * singleword+noglob expansion: + * v='a b'; [[ $v = 'a b' ]]; echo 0:$? + * [[ /bin/n* ]]; echo 0:$? + * -a/-o are not AND/OR ops! (they are just strings) + * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) + * = is glob match operator, not equality operator: STR = GLOB + * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") + * == same as = + * add =~ regex match operator: STR =~ REGEX + */ #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT #define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT #define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT +#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT #define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT #define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT #define BASH_READ_D ENABLE_ASH_BASH_COMPAT #define IF_BASH_READ_D IF_ASH_BASH_COMPAT +#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 /* Bionic at least up to version 24 has no glob() */ @@ -296,10 +315,18 @@ static const char *const optletters_optnames[] = { "e" "errexit", "f" "noglob", "I" "ignoreeof", - "i" "interactive", +/* The below allowed this invocation: + * ash -c 'set -i; echo $-; sleep 5; echo $-' + * to be ^C-ed and get to interactive ash prompt. + * bash does not support such "set -i". + * In our code, this is denoted by empty long name: + */ + "i" "", "m" "monitor", "n" "noexec", - "s" "stdin", +/* Ditto: bash has no "set -s" */ + "s" "", + "c" "", "x" "xtrace", "v" "verbose", "C" "noclobber", @@ -315,6 +342,20 @@ static const char *const optletters_optnames[] = { ,"\0" "debug" #endif }; +//bash 4.4.23 also has these opts (with these defaults): +//braceexpand on +//emacs on +//errtrace off +//functrace off +//hashall on +//histexpand off +//history on +//interactive-comments on +//keyword off +//onecmd off +//physical off +//posix off +//privileged off #define optletters(n) optletters_optnames[n][0] #define optnames(n) (optletters_optnames[n] + 1) @@ -343,6 +384,7 @@ struct globals_misc { uint8_t exitstatus; /* exit status of last command */ uint8_t back_exitstatus;/* exit status of backquoted command */ smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ + int savestatus; /* exit status of last command outside traps */ int rootpid; /* pid of main shell */ /* shell level: 0 for the main shell, 1 for its children, and so on */ int shlvl; @@ -378,21 +420,22 @@ struct globals_misc { #define mflag optlist[4] #define nflag optlist[5] #define sflag optlist[6] -#define xflag optlist[7] -#define vflag optlist[8] -#define Cflag optlist[9] -#define aflag optlist[10] -#define bflag optlist[11] -#define uflag optlist[12] -#define viflag optlist[13] +#define cflag optlist[7] +#define xflag optlist[8] +#define vflag optlist[9] +#define Cflag optlist[10] +#define aflag optlist[11] +#define bflag optlist[12] +#define uflag optlist[13] +#define viflag optlist[14] #if BASH_PIPEFAIL -# define pipefail optlist[14] +# define pipefail optlist[15] #else # define pipefail 0 #endif #if DEBUG -# define nolog optlist[14 + BASH_PIPEFAIL] -# define debug optlist[15 + BASH_PIPEFAIL] +# define nolog optlist[15 + BASH_PIPEFAIL] +# define debug optlist[16 + BASH_PIPEFAIL] #endif /* trap handler commands */ @@ -424,6 +467,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; #define exitstatus (G_misc.exitstatus ) #define back_exitstatus (G_misc.back_exitstatus ) #define job_warning (G_misc.job_warning) +#define savestatus (G_misc.savestatus ) #define rootpid (G_misc.rootpid ) #define shlvl (G_misc.shlvl ) #define errlinno (G_misc.errlinno ) @@ -447,8 +491,9 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc; #define random_gen (G_misc.random_gen ) #define backgndpid (G_misc.backgndpid ) #define INIT_G_misc() do { \ - (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ + (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \ barrier(); \ + savestatus = -1; \ curdir = nullstr; \ physdir = nullstr; \ trap_ptr = trap; \ @@ -668,7 +713,7 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...) ret = vsnprintf(outbuf, length, fmt, ap); va_end(ap); INT_ON; - return ret; + return ret > (int)length ? length : ret; } static void @@ -1500,7 +1545,7 @@ extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack; #define g_stacknleft (G_memstack.g_stacknleft) #define stackbase (G_memstack.stackbase ) #define INIT_G_memstack() do { \ - (*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \ + (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \ barrier(); \ g_stackp = &stackbase; \ g_stacknxt = stackbase.space; \ @@ -2035,6 +2080,10 @@ static void changepath(const char *) FAST_FUNC; #if ENABLE_ASH_RANDOM_SUPPORT static void change_random(const char *) FAST_FUNC; #endif +#if BASH_EPOCH_VARS +static void change_seconds(const char *) FAST_FUNC; +static void change_realtime(const char *) FAST_FUNC; +#endif static const struct { int flags; @@ -2061,6 +2110,10 @@ static const struct { #if ENABLE_ASH_RANDOM_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, #endif +#if BASH_EPOCH_VARS + { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds }, + { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime }, +#endif #if ENABLE_LOCALE_SUPPORT { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all }, { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype }, @@ -2092,30 +2145,30 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var; #define linenovar (G_var.linenovar ) #define vifs varinit[0] #if ENABLE_ASH_MAIL -# define vmail (&vifs)[1] -# define vmpath (&vmail)[1] -# define vpath (&vmpath)[1] -#else -# define vpath (&vifs)[1] -#endif -#define vps1 (&vpath)[1] -#define vps2 (&vps1)[1] -#define vps4 (&vps2)[1] +# define vmail varinit[1] +# define vmpath varinit[2] +#endif +#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2) +#define vpath varinit[VAR_OFFSET1 + 1] +#define vps1 varinit[VAR_OFFSET1 + 2] +#define vps2 varinit[VAR_OFFSET1 + 3] +#define vps4 varinit[VAR_OFFSET1 + 4] #if ENABLE_ASH_GETOPTS -# define voptind (&vps4)[1] -# define vlineno (&voptind)[1] -# if ENABLE_ASH_RANDOM_SUPPORT -# define vrandom (&vlineno)[1] -# endif -#else -# define vlineno (&vps4)[1] -# if ENABLE_ASH_RANDOM_SUPPORT -# define vrandom (&vlineno)[1] -# endif +# define voptind varinit[VAR_OFFSET1 + 5] +#endif +#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS) +#define vlineno varinit[VAR_OFFSET2 + 5] +#if ENABLE_ASH_RANDOM_SUPPORT +# define vrandom varinit[VAR_OFFSET2 + 6] +#endif +#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT) +#if BASH_EPOCH_VARS +# define vepochs varinit[VAR_OFFSET3 + 6] +# define vepochr varinit[VAR_OFFSET3 + 7] #endif #define INIT_G_var() do { \ unsigned i; \ - (*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \ + (*(struct globals_var**)not_const_pp(&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; \ @@ -2250,7 +2303,7 @@ lookupvar(const char *name) v = *findvar(hashvar(name), name); if (v) { -#if ENABLE_ASH_RANDOM_SUPPORT +#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS /* * Dynamic variables are implemented roughly the same way they are * in bash. Namely, they're "special" so long as they aren't unset. @@ -2346,6 +2399,10 @@ setvareq(char *s, int flags) } flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); +#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS + if (flags & VUNSET) + flags &= ~VDYNAMIC; +#endif } else { /* variable s is not found */ if (flags & VNOSET) @@ -2393,13 +2450,12 @@ setvar(const char *name, const char *val, int flags) } INT_OFF; - nameeq = ckmalloc(namelen + vallen + 2); + nameeq = ckzalloc(namelen + vallen + 2); p = mempcpy(nameeq, name, namelen); if (val) { *p++ = '='; - p = mempcpy(p, val, vallen); + memcpy(p, val, vallen); } - *p = '\0'; vp = setvareq(nameeq, flags | VNOSAVE); INT_ON; @@ -3506,7 +3562,7 @@ struct procstat { struct job { struct procstat ps0; /* status of process */ - struct procstat *ps; /* status or processes when more than one */ + struct procstat *ps; /* status of processes when more than one */ #if JOBS int stopstatus; /* status of a stopped job */ #endif @@ -4147,12 +4203,11 @@ fg_bgcmd(int argc UNUSED_PARAM, char **argv) #endif static int -sprint_status48(char *s, int status, int sigonly) +sprint_status48(char *os, int status, int sigonly) { - int col; + char *s = os; int st; - col = 0; if (!WIFEXITED(status)) { #if JOBS if (WIFSTOPPED(status)) @@ -4170,17 +4225,17 @@ sprint_status48(char *s, int status, int sigonly) } st &= 0x7f; //TODO: use bbox's get_signame? strsignal adds ~600 bytes to text+rodata - col = fmtstr(s, 32, strsignal(st)); + //s = stpncpy(s, strsignal(st), 32); //not all libc have stpncpy() + s += fmtstr(s, 32, strsignal(st)); if (WCOREDUMP(status)) { - strcpy(s + col, " (core dumped)"); - col += sizeof(" (core dumped)")-1; + s = stpcpy(s, " (core dumped)"); } } else if (!sigonly) { st = WEXITSTATUS(status); - col = fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st); + s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st); } out: - return col; + return s - os; } static int @@ -4201,7 +4256,7 @@ wait_block_or_sig(int *status) /* Children exist, but none are ready. Sleep until interesting signal */ #if 1 sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &mask); + sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ while (!got_sigchld && !pending_sig) sigsuspend(&mask); sigprocmask(SIG_SETMASK, &mask, NULL); @@ -4219,6 +4274,9 @@ wait_block_or_sig(int *status) #define DOWAIT_NONBLOCK 0 #define DOWAIT_BLOCK 1 #define DOWAIT_BLOCK_OR_SIG 2 +#if BASH_WAIT_N +# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ +#endif static int dowait(int block, struct job *job) @@ -4226,7 +4284,11 @@ dowait(int block, struct job *job) int pid; int status; struct job *jp; - struct job *thisjob = NULL; + struct job *thisjob; +#if BASH_WAIT_N + bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); + block = (block & ~DOWAIT_JOBSTATUS); +#endif TRACE(("dowait(0x%x) called\n", block)); @@ -4263,10 +4325,10 @@ dowait(int block, struct job *job) } TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); + thisjob = NULL; if (pid <= 0) goto out; - thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { int jobstate; struct procstat *ps; @@ -4325,6 +4387,13 @@ dowait(int block, struct job *job) out: INT_ON; +#if BASH_WAIT_N + if (want_jobexitstatus) { + pid = -1; + if (thisjob && thisjob->state == JOBDONE) + pid = thisjob->ps[thisjob->nprocs - 1].ps_status; + } +#endif if (thisjob && thisjob == job) { char s[48 + 1]; int len; @@ -4507,15 +4576,24 @@ waitcmd(int argc UNUSED_PARAM, char **argv) struct job *job; int retval; struct job *jp; - +#if BASH_WAIT_N + int status; + char one = nextopt("n"); +#else nextopt(nullstr); +#endif retval = 0; argv = argptr; - if (!*argv) { - /* wait for all jobs */ + if (!argv[0]) { + /* wait for all jobs / one job if -n */ for (;;) { jp = curjob; +#if BASH_WAIT_N + if (one && !jp) + /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */ + retval = 127; +#endif while (1) { if (!jp) /* no running procs */ goto ret; @@ -4531,13 +4609,31 @@ waitcmd(int argc UNUSED_PARAM, char **argv) * with an exit status greater than 128, immediately after which * the trap is executed." */ +#if BASH_WAIT_N + status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL); +#else dowait(DOWAIT_BLOCK_OR_SIG, NULL); +#endif /* if child sends us a signal *and immediately exits*, * dowait() returns pid > 0. Check this case, * not "if (dowait() < 0)"! */ if (pending_sig) goto sigout; +#if BASH_WAIT_N + if (one) { + /* wait -n waits for one _job_, not one _process_. + * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date + * should wait for 2 seconds. Not 1 or 3. + */ + if (status != -1 && !WIFSTOPPED(status)) { + retval = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + retval = WTERMSIG(status) + 128; + goto ret; + } + } +#endif } } @@ -5890,7 +5986,7 @@ static int substr_atoi(const char *s) #define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ #define EXP_VARTILDE2 0x20 /* expand tildes after colons only */ #define EXP_WORD 0x40 /* expand word in parameter expansion */ -#define EXP_QUOTED 0x80 /* expand word in double quotes */ +#define EXP_QUOTED 0x100 /* expand word in double quotes */ /* * rmescape() flags */ @@ -6224,9 +6320,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes) if (quotes & QUOTES_ESC) { int n = SIT(c, syntax); if (n == CCTL - || (((quotes & EXP_FULL) || syntax != BASESYNTAX) - && n == CBACK - ) + || (syntax != BASESYNTAX && n == CBACK) ) { USTPUTC(CTLESC, q); } @@ -6346,7 +6440,7 @@ exptilde(char *startp, char *p, int flags) goto lose; home = pw->pw_dir; } - if (!home || !*home) + if (!home) goto lose; *p = c; strtodest(home, SQSYNTAX, quotes); @@ -6842,8 +6936,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype, if (subtype == VSREPLACE || subtype == VSREPLACEALL) { /* Find '/' and replace with NUL */ repl = p; + /* The pattern can't be empty. + * IOW: if the first char after "${v//" is a slash, + * it does not terminate the pattern - it's the first char of the pattern: + * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/") + * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r") + */ + if (*repl == '/') + repl++; for (;;) { - /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ if (*repl == '\0') { repl = NULL; break; @@ -6852,6 +6953,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype, *repl = '\0'; break; } + /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */ if ((unsigned char)*repl == CTLESC && repl[1]) repl++; repl++; @@ -7155,14 +7257,13 @@ subevalvar(char *p, char *varname, int strloc, int subtype, * ash -c 'echo ${#1#}' name:'1=#' */ static NOINLINE ssize_t -varvalue(char *name, int varflags, int flags, int *quotedp) +varvalue(char *name, int varflags, int flags, int quoted) { const char *p; int num; int i; ssize_t len = 0; int sep; - int quoted = *quotedp; int subtype = varflags & VSTYPE; int discard = subtype == VSPLUS || subtype == VSLENGTH; int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; @@ -7210,13 +7311,27 @@ varvalue(char *name, int varflags, int flags, int *quotedp) case '*': { char **ap; char sepc; + char c; - if (quoted) - sep = 0; - sep |= ifsset() ? ifsval()[0] : ' '; + /* We will set c to 0 or ~0 depending on whether + * we're doing field splitting. We won't do field + * splitting if either we're quoted or sep is zero. + * + * Instead of testing (quoted || !sep) the following + * trick optimises away any branches by using the + * fact that EXP_QUOTED (which is the only bit that + * can be set in quoted) is the same as EXP_FULL << + * CHAR_BIT (which is the only bit that can be set + * in sep). + */ +#if EXP_QUOTED >> CHAR_BIT != EXP_FULL +#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT +#endif + c = !((quoted | ~sep) & EXP_QUOTED) - 1; + sep &= ~quoted; + sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' '; param: sepc = sep; - *quotedp = !sepc; ap = shellparam.p; if (!ap) return -1; @@ -7281,7 +7396,6 @@ evalvar(char *p, int flag) char varflags; char subtype; int quoted; - char easy; char *var; int patloc; int startloc; @@ -7295,12 +7409,11 @@ evalvar(char *p, int flag) quoted = flag & EXP_QUOTED; var = p; - easy = (!quoted || (*var == '@' && shellparam.nparam)); startloc = expdest - (char *)stackblock(); p = strchr(p, '=') + 1; //TODO: use var_end(p)? again: - varlen = varvalue(var, varflags, flag, "ed); + varlen = varvalue(var, varflags, flag, quoted); if (varflags & VSNUL) varlen--; @@ -7346,8 +7459,11 @@ evalvar(char *p, int flag) if (subtype == VSNORMAL) { record: - if (!easy) - goto end; + if (quoted) { + quoted = *var == '@' && shellparam.nparam; + if (!quoted) + goto end; + } recordregion(startloc, expdest - (char *)stackblock(), quoted); goto end; } @@ -7563,9 +7679,16 @@ expandmeta(struct strlist *str /*, int flag*/) /* * Do metacharacter (i.e. *, ?, [...]) expansion. */ +typedef struct exp_t { + char *dir; + unsigned dir_max; +} exp_t; static void -expmeta(char *expdir, char *enddir, char *name) +expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len) { +#define expdir exp->dir +#define expdir_max exp->dir_max + char *enddir = expdir + expdir_len; char *p; const char *cp; char *start; @@ -7598,7 +7721,7 @@ expmeta(char *expdir, char *enddir, char *name) } } } else { - if (*p == '\\') + if (*p == '\\' && p[1]) esc++; if (p[esc] == '/') { if (metaflag) @@ -7608,15 +7731,15 @@ expmeta(char *expdir, char *enddir, char *name) } } if (metaflag == 0) { /* we've reached the end of the file name */ - if (enddir != expdir) - metaflag++; + if (!expdir_len) + return; p = name; do { - if (*p == '\\') + if (*p == '\\' && p[1]) p++; *enddir++ = *p; } while (*p++); - if (metaflag == 0 || lstat(expdir, &statb) >= 0) + if (lstat(expdir, &statb) == 0) addfname(expdir); return; } @@ -7624,24 +7747,19 @@ expmeta(char *expdir, char *enddir, char *name) if (name < start) { p = name; do { - if (*p == '\\') + if (*p == '\\' && p[1]) p++; *enddir++ = *p++; } while (p < start); } - if (enddir == expdir) { + *enddir = '\0'; + cp = expdir; + expdir_len = enddir - cp; + if (!expdir_len) cp = "."; - } else if (enddir == expdir + 1 && *expdir == '/') { - cp = "/"; - } else { - cp = expdir; - enddir[-1] = '\0'; - } dirp = opendir(cp); if (dirp == NULL) return; - if (enddir != expdir) - enddir[-1] = '/'; if (*endname == 0) { atend = 1; } else { @@ -7649,6 +7767,7 @@ expmeta(char *expdir, char *enddir, char *name) *endname = '\0'; endname += esc + 1; } + name_len -= endname - name; matchdot = 0; p = start; if (*p == '\\') @@ -7663,16 +7782,30 @@ expmeta(char *expdir, char *enddir, char *name) strcpy(enddir, dp->d_name); addfname(expdir); } else { - for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';) - continue; - p[-1] = '/'; - expmeta(expdir, p, endname); + unsigned offset; + unsigned len; + + p = stpcpy(enddir, dp->d_name); + *p = '/'; + + offset = p - expdir + 1; + len = offset + name_len + NAME_MAX; + if (len > expdir_max) { + len += PATH_MAX; + expdir = ckrealloc(expdir, len); + expdir_max = len; + } + + expmeta(exp, endname, name_len, offset); + enddir = expdir + expdir_len; } } } closedir(dirp); if (!atend) endname[-esc - 1] = esc ? '\\' : '/'; +#undef expdir +#undef expdir_max } static struct strlist * @@ -7745,10 +7878,11 @@ expandmeta(struct strlist *str /*, int flag*/) /* TODO - EXP_REDIR */ while (str) { - char *expdir; + exp_t exp; struct strlist **savelastp; struct strlist *sp; char *p; + unsigned len; if (fflag) goto nometa; @@ -7758,13 +7892,12 @@ expandmeta(struct strlist *str /*, int flag*/) INT_OFF; p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); - { - int i = strlen(str->text); -//BUGGY estimation of how long expanded name can be - expdir = ckmalloc(i < 2048 ? 2048 : i+1); - } - expmeta(expdir, expdir, p); - free(expdir); + len = strlen(p); + exp.dir_max = len + PATH_MAX; + exp.dir = ckmalloc(exp.dir_max); + + expmeta(&exp, p, len, 0); + free(exp.dir); if (p != str->text) free(p); INT_ON; @@ -7975,6 +8108,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c #else execve(cmd, argv, envp); #endif + if (cmd != bb_busybox_exec_path && errno == ENOEXEC) { /* Run "cmd" as a shell script: * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html @@ -8047,15 +8181,15 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) /* Map to POSIX errors */ switch (e) { - case EACCES: + default: exerrno = 126; break; + case ELOOP: + case ENAMETOOLONG: case ENOENT: + case ENOTDIR: exerrno = 127; break; - default: - exerrno = 2; - break; } exitstatus = exerrno; TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", @@ -8483,7 +8617,8 @@ describe_command(char *command, const char *path, int describe_command_verbose) case CMDFUNCTION: if (describe_command_verbose) { - out1str(" is a shell function"); + /*out1str(" is a shell function");*/ + out1str(" is a function"); /* bash says this */ } else { out1str(command); } @@ -8922,12 +9057,17 @@ dotrap(void) { uint8_t *g; int sig; - uint8_t last_status; + int status, last_status; if (!pending_sig) return; - last_status = exitstatus; + status = savestatus; + last_status = status; + if (status < 0) { + status = exitstatus; + savestatus = status; + } pending_sig = 0; barrier(); @@ -8954,8 +9094,10 @@ dotrap(void) if (!p) continue; evalstring(p, 0); + exitstatus = status; } - exitstatus = last_status; + + savestatus = last_status; TRACE(("dotrap returns\n")); } @@ -8979,8 +9121,11 @@ evaltree(union node *n, int flags) { int checkexit = 0; int (*evalfn)(union node *, int); + struct stackmark smark; int status = 0; + setstackmark(&smark); + if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out; @@ -9094,6 +9239,7 @@ evaltree(union node *n, int flags) if (flags & EV_EXIT) raise_exception(EXEXIT); + popstackmark(&smark); TRACE(("leaving evaltree (no interrupts)\n")); return exitstatus; } @@ -9154,14 +9300,12 @@ evalfor(union node *n, int flags) struct arglist arglist; union node *argp; struct strlist *sp; - struct stackmark smark; int status = 0; errlinno = lineno = n->ncase.linno; if (funcline) lineno -= funcline - 1; - setstackmark(&smark); arglist.list = NULL; arglist.lastp = &arglist.list; for (argp = n->nfor.args; argp; argp = argp->narg.next) { @@ -9178,7 +9322,6 @@ evalfor(union node *n, int flags) break; } loopnest--; - popstackmark(&smark); return status; } @@ -9189,14 +9332,12 @@ evalcase(union node *n, int flags) union node *cp; union node *patp; struct arglist arglist; - struct stackmark smark; int status = 0; errlinno = lineno = n->ncase.linno; if (funcline) lineno -= funcline - 1; - setstackmark(&smark); arglist.list = NULL; arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); @@ -9215,8 +9356,6 @@ evalcase(union node *n, int flags) } } out: - popstackmark(&smark); - return status; } @@ -9393,6 +9532,11 @@ evalpipe(union node *n, int flags) return status; } +/* setinteractive needs this forward reference */ +#if EDITING_HAS_get_exe_name +static const char *get_builtin_name(int i) FAST_FUNC; +#endif + /* * Controls whether the shell is interactive or not. */ @@ -9407,8 +9551,8 @@ setinteractive(int on) setsignal(SIGINT); setsignal(SIGQUIT); setsignal(SIGTERM); -#if !ENABLE_FEATURE_SH_EXTRA_QUIET if (is_interactive > 1) { +#if !ENABLE_FEATURE_SH_EXTRA_QUIET /* Looks like they want an interactive shell */ static smallint did_banner; @@ -9422,8 +9566,16 @@ setinteractive(int on) ); did_banner = 1; } - } #endif +#if ENABLE_FEATURE_EDITING + if (!line_input_state) { + line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); +# if EDITING_HAS_get_exe_name + line_input_state->get_exe_name = get_builtin_name; +# endif + } +#endif + } } static void @@ -9435,10 +9587,12 @@ optschanged(void) setinteractive(iflag); setjobctl(mflag); #if ENABLE_FEATURE_EDITING_VI - if (viflag) - line_input_state->flags |= VI_MODE; - else - line_input_state->flags &= ~VI_MODE; + if (line_input_state) { + if (viflag) + line_input_state->flags |= VI_MODE; + else + line_input_state->flags &= ~VI_MODE; + } #else viflag = 0; /* forcibly keep the option off */ #endif @@ -9562,9 +9716,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) shellparam.optind = 1; shellparam.optoff = -1; #endif - pushlocalvars(); evaltree(func->n.ndefun.body, flags & EV_TESTED); - poplocalvars(0); funcdone: INT_OFF; funcline = savefuncline; @@ -9810,7 +9962,7 @@ static const struct builtincmd builtintab[] = { #if ENABLE_ASH_GETOPTS { BUILTIN_REGULAR "getopts" , getoptscmd }, #endif - { BUILTIN_NOSPEC "hash" , hashcmd }, + { BUILTIN_REGULAR "hash" , hashcmd }, #if ENABLE_ASH_HELP { BUILTIN_NOSPEC "help" , helpcmd }, #endif @@ -9828,7 +9980,7 @@ static const struct builtincmd builtintab[] = { #if ENABLE_ASH_PRINTF { BUILTIN_REGULAR "printf" , printfcmd }, #endif - { BUILTIN_NOSPEC "pwd" , pwdcmd }, + { BUILTIN_REGULAR "pwd" , pwdcmd }, { BUILTIN_REGULAR "read" , readcmd }, { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd }, { BUILTIN_SPEC_REG "return" , returncmd }, @@ -9843,8 +9995,8 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG "times" , timescmd }, { BUILTIN_SPEC_REG "trap" , trapcmd }, { BUILTIN_REGULAR "true" , truecmd }, - { BUILTIN_NOSPEC "type" , typecmd }, - { BUILTIN_NOSPEC "ulimit" , ulimitcmd }, + { BUILTIN_REGULAR "type" , typecmd }, + { BUILTIN_REGULAR "ulimit" , ulimitcmd }, { BUILTIN_REGULAR "umask" , umaskcmd }, #if ENABLE_ASH_ALIAS { BUILTIN_REGULAR "unalias" , unaliascmd }, @@ -9889,9 +10041,18 @@ find_builtin(const char *name) return bp; } +#if EDITING_HAS_get_exe_name +static const char * FAST_FUNC +get_builtin_name(int i) +{ + return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL; +} +#endif + /* * Execute a simple command. */ +static void unwindfiles(struct parsefile *stop); static int isassignment(const char *p) { @@ -9914,8 +10075,8 @@ evalcommand(union node *cmd, int flags) "\0\0", bltincmd /* why three NULs? */ }; struct localvar_list *localvar_stop; + struct parsefile *file_stop; struct redirtab *redir_stop; - struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; @@ -9937,8 +10098,8 @@ evalcommand(union node *cmd, int flags) /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); - setstackmark(&smark); localvar_stop = pushlocalvars(); + file_stop = g_parsefile; back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; @@ -10197,7 +10358,6 @@ evalcommand(union node *cmd, int flags) goto readstatus; case CMDFUNCTION: - poplocalvars(1); /* See above for the rationale */ dowait(DOWAIT_NONBLOCK, NULL); if (evalfun(cmdentry.u.func, argc, argv, flags)) @@ -10211,6 +10371,7 @@ evalcommand(union node *cmd, int flags) if (cmd->ncmd.redirect) popredir(/*drop:*/ cmd_is_exec); unwindredir(redir_stop); + unwindfiles(file_stop); unwindlocalvars(localvar_stop); if (lastarg) { /* dsl: I think this is intended to be used to support @@ -10219,7 +10380,6 @@ evalcommand(union node *cmd, int flags) */ setvar0("_", lastarg); } - popstackmark(&smark); return status; } @@ -10414,13 +10574,11 @@ preadfd(void) else { # if ENABLE_ASH_IDLE_TIMEOUT int timeout = -1; - if (iflag) { - const char *tmout_var = lookupvar("TMOUT"); - if (tmout_var) { - timeout = atoi(tmout_var) * 1000; - if (timeout <= 0) - timeout = -1; - } + const char *tmout_var = lookupvar("TMOUT"); + if (tmout_var) { + timeout = atoi(tmout_var) * 1000; + if (timeout <= 0) + timeout = -1; } line_input_state->timeout = timeout; # endif @@ -10680,6 +10838,12 @@ struct synstack { struct synstack *next; }; +static int +pgetc_top(struct synstack *stack) +{ + return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl(); +} + static void synstack_push(struct synstack **stack, struct synstack *next, int syntax) { @@ -10733,14 +10897,20 @@ popfile(void) INT_ON; } +static void +unwindfiles(struct parsefile *stop) +{ + while (g_parsefile != stop) + popfile(); +} + /* * Return to top level. */ static void popallfiles(void) { - while (g_parsefile != &basepf) - popfile(); + unwindfiles(&basepf); } /* @@ -10953,6 +11123,8 @@ plus_minus_o(char *name, int val) return 1; } for (i = 0; i < NOPTS; i++) { + if (optnames(i)[0] == '\0') + continue; if (val) { out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off"); } else { @@ -10967,7 +11139,7 @@ setoption(int flag, int val) int i; for (i = 0; i < NOPTS; i++) { - if (optletters(i) == flag) { + if (optletters(i) == flag && optnames(i)[0] != '\0') { optlist[i] = val; return; } @@ -10975,14 +11147,17 @@ setoption(int flag, int val) ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag); /* NOTREACHED */ } +/* If login_sh is not NULL, we are called to parse command line opts, + * not "set -opts" + */ static int -options(int cmdline, int *login_sh) +options(int *login_sh) { char *p; int val; int c; - if (cmdline) + if (login_sh) minusc = NULL; while ((p = *argptr) != NULL) { c = *p++; @@ -10993,7 +11168,7 @@ options(int cmdline, int *login_sh) if (c == '-') { val = 1; if (p[0] == '\0' || LONE_DASH(p)) { - if (!cmdline) { + if (!login_sh) { /* "-" means turn off -x and -v */ if (p[0] == '\0') xflag = vflag = 0; @@ -11006,26 +11181,40 @@ options(int cmdline, int *login_sh) } /* 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 */ - } else if (c == 'o') { + if (login_sh) { + /* bash 3.2 indeed handles -c CMD and +c CMD the same */ + if (c == 'c') { + minusc = p; /* command is after shell args */ + cflag = 1; + continue; + } + if (c == 's') { /* -s, +s */ + sflag = 1; + continue; + } + if (c == 'i') { /* -i, +i */ + iflag = 1; + continue; + } + if (c == 'l') { + *login_sh = 1; /* -l or +l == --login */ + continue; + } + /* bash does not accept +-login, we also won't */ + if (val && (c == '-')) { /* long options */ + if (strcmp(p, "login") == 0) { + *login_sh = 1; + } + break; + } + } + if (c == 'o') { if (plus_minus_o(*argptr, val)) { /* it already printed err message */ return 1; /* error */ } if (*argptr) argptr++; - } else if (cmdline && (c == 'l')) { /* -l or +l == --login */ - if (login_sh) - *login_sh = 1; - /* bash does not accept +-login, we also won't */ - } else if (cmdline && val && (c == '-')) { /* long options */ - if (strcmp(p, "login") == 0) { - if (login_sh) - *login_sh = 1; - } - break; } else { setoption(c, val); } @@ -11116,7 +11305,7 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) return showvars(nullstr, 0, VUNSET); INT_OFF; - retval = options(/*cmdline:*/ 0, NULL); + retval = options(/*login_sh:*/ NULL); if (retval == 0) { /* if no parse error... */ optschanged(); if (*argptr != NULL) { @@ -11147,6 +11336,32 @@ change_random(const char *value) } #endif +#if BASH_EPOCH_VARS +static void FAST_FUNC +change_epoch(struct var *vepoch, const char *fmt) +{ + struct timeval tv; + char buffer[sizeof("%lu.nnnnnn") + sizeof(long)*3]; + + gettimeofday(&tv, NULL); + sprintf(buffer, fmt, (unsigned long)tv.tv_sec, (unsigned)tv.tv_usec); + setvar(vepoch->var_text, buffer, VNOFUNC); + vepoch->flags &= ~VNOFUNC; +} + +static void FAST_FUNC +change_seconds(const char *value UNUSED_PARAM) +{ + change_epoch(&vepochs, "%lu"); +} + +static void FAST_FUNC +change_realtime(const char *value UNUSED_PARAM) +{ + change_epoch(&vepochr, "%lu.%06u"); +} +#endif + #if ENABLE_ASH_GETOPTS static int getopts(char *optstr, char *optvar, char **optfirst) @@ -11612,10 +11827,12 @@ simplecmd(void) case TLP: function_flag = 0; break; +# if BASH_TEST2 case TWORD: if (strcmp("[[", wordtext) == 0) goto do_func; /* fall through */ +# endif default: raise_error_unexpected_syntax(-1); } @@ -11663,7 +11880,8 @@ simplecmd(void) *vpp = NULL; *rpp = NULL; n = stzalloc(sizeof(struct ncmd)); - n->type = NCMD; + if (NCMD != 0) + n->type = NCMD; n->ncmd.linno = savelinno; n->ncmd.args = args; n->ncmd.assign = vars; @@ -11984,11 +12202,14 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ switch (SIT(c, synstack->syntax)) { case CNL: /* '\n' */ - if (synstack->syntax == BASESYNTAX) + if (synstack->syntax == BASESYNTAX + && !synstack->varnest + ) { goto endword; /* exit outer loop */ + } USTPUTC(c, out); nlprompt(); - c = pgetc(); + c = pgetc_top(synstack); goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); @@ -12020,8 +12241,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) USTPUTC(CTLESC, out); USTPUTC('\\', out); pungetc(); - } else if (c == '\n') { - nlprompt(); } else { if (pssyntax && c == '$') { USTPUTC(CTLESC, out); @@ -12141,7 +12360,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) IF_ASH_ALIAS(if (c != PEOA)) USTPUTC(c, out); } - c = pgetc(); + c = pgetc_top(synstack); } /* for (;;) */ endword: @@ -12204,7 +12423,13 @@ checkend: { for (p = eofmark; STPUTC(c, out), *p; p++) { if (c != *p) goto more_heredoc; - + /* FIXME: fails for backslash-newlined terminator: + * cat <[}] */ int cc = c; @@ -12402,7 +12627,8 @@ parsesub: { } USTPUTC(cc, out); - } + } else + goto badsub; if (c != '}' && subtype == VSLENGTH) { /* ${#VAR didn't end with } */ @@ -12878,9 +13104,12 @@ parseheredoc(void) heredoclist = NULL; while (here) { + tokpushback = 0; setprompt_if(needprompt, 2); - readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX, - here->eofmark, here->striptabs); + if (here->here->type == NHERE) + readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs); + else + readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs); n = stzalloc(sizeof(struct narg)); n->narg.type = NARG; /*n->narg.next = NULL; - stzalloc did it */ @@ -12897,41 +13126,54 @@ expandstr(const char *ps, int syntax_type) { union node n; int saveprompt; + struct parsefile *file_stop = g_parsefile; + volatile int saveint; + struct jmploc *volatile savehandler = exception_handler; + struct jmploc jmploc; + const char *volatile result; + int err; /* XXX Fix (char *) cast. */ setinputstring((char *)ps); saveprompt = doprompt; doprompt = 0; + result = ps; + + SAVE_INT(saveint); + err = setjmp(jmploc.loc); + if (err) + goto out; /* readtoken1() might die horribly. * Try a prompt with syntactically wrong command: * PS1='$(date "+%H:%M:%S) > ' */ - { - volatile int saveint; - struct jmploc *volatile savehandler = exception_handler; - struct jmploc jmploc; - SAVE_INT(saveint); - if (setjmp(jmploc.loc) == 0) { - exception_handler = &jmploc; - readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); - } - exception_handler = savehandler; - RESTORE_INT(saveint); - } - - doprompt = saveprompt; - - popfile(); + exception_handler = &jmploc; + readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0); n.narg.type = NARG; n.narg.next = NULL; n.narg.text = wordtext; n.narg.backquote = backquotelist; + /* expandarg() might fail too: + * PS1='$((123+))' + */ expandarg(&n, NULL, EXP_QUOTED); - return stackblock(); + result = stackblock(); + +out: + exception_handler = savehandler; + if (err && exception_type != EXERROR) + longjmp(exception_handler->loc, 1); + RESTORE_INT(saveint); + + doprompt = saveprompt; + /* Try: PS1='`xxx(`' */ + unwindfiles(file_stop); + + return result; } static inline int @@ -13183,8 +13425,10 @@ exitcmd(int argc UNUSED_PARAM, char **argv) { if (stoppedjobs()) return 0; + if (argv[1]) - exitstatus = number(argv[1]); + savestatus = number(argv[1]); + raise_exception(EXEXIT); /* NOTREACHED */ } @@ -13530,7 +13774,8 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) static int FAST_FUNC historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - show_history(line_input_state); + if (line_input_state) + show_history(line_input_state); return EXIT_SUCCESS; } #endif @@ -13707,38 +13952,35 @@ letcmd(int argc UNUSED_PARAM, char **argv) static int FAST_FUNC readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - char *opt_n = NULL; - char *opt_p = NULL; - char *opt_t = NULL; - char *opt_u = NULL; - char *opt_d = NULL; /* optimized out if !BASH */ - int read_flags = 0; + struct builtin_read_params params; const char *r; int i; + memset(¶ms, 0, sizeof(params)); + while ((i = nextopt("p:u:rt:n:sd:")) != '\0') { switch (i) { case 'p': - opt_p = optionarg; + params.opt_p = optionarg; break; case 'n': - opt_n = optionarg; + params.opt_n = optionarg; break; case 's': - read_flags |= BUILTIN_READ_SILENT; + params.read_flags |= BUILTIN_READ_SILENT; break; case 't': - opt_t = optionarg; + params.opt_t = optionarg; break; case 'r': - read_flags |= BUILTIN_READ_RAW; + params.read_flags |= BUILTIN_READ_RAW; break; case 'u': - opt_u = optionarg; + params.opt_u = optionarg; break; #if BASH_READ_D case 'd': - opt_d = optionarg; + params.opt_d = optionarg; break; #endif default: @@ -13746,21 +13988,16 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } } + params.argv = argptr; + params.setvar = setvar0; + params.ifs = bltinlookup("IFS"); /* can be NULL */ + /* "read -s" needs to save/restore termios, can't allow ^C * to jump out of it. */ again: INT_OFF; - r = shell_builtin_read(setvar0, - argptr, - bltinlookup("IFS"), /* can be NULL */ - read_flags, - opt_n, - opt_p, - opt_t, - opt_u, - opt_d - ); + r = shell_builtin_read(¶ms); INT_ON; if ((uintptr_t)r == 1 && errno == EINTR) { @@ -13851,18 +14088,15 @@ exitshell(void) { struct jmploc loc; char *p; - int status; #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT - save_history(line_input_state); + if (line_input_state) + save_history(line_input_state); #endif - status = exitstatus; - TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); - if (setjmp(loc.loc)) { - if (exception_type == EXEXIT) - status = exitstatus; + savestatus = exitstatus; + TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); + if (setjmp(loc.loc)) goto out; - } exception_handler = &loc; p = trap[0]; if (p) { @@ -13877,7 +14111,7 @@ exitshell(void) */ setjobctl(0); flush_stdout_stderr(); - _exit(status); + _exit(savestatus); /* NOTREACHED */ } @@ -13919,6 +14153,7 @@ init(void) } } + setvareq((char*)defifsvar, VTEXTFIXED); setvareq((char*)defoptindvar, VTEXTFIXED); setvar0("PPID", utoa(getppid())); @@ -13948,7 +14183,7 @@ init(void) //usage:#define ash_trivial_usage -//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" +//usage: "[-/+OPTIONS] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]" //usage:#define ash_full_usage "\n\n" //usage: "Unix shell interpreter" @@ -13965,13 +14200,17 @@ procargs(char **argv) xargv = argv; login_sh = xargv[0] && xargv[0][0] == '-'; +#if NUM_SCRIPTS > 0 + if (minusc) + goto setarg0; +#endif arg0 = xargv[0]; /* if (xargv[0]) - mmm, this is always true! */ xargv++; + argptr = xargv; for (i = 0; i < NOPTS; i++) optlist[i] = 2; - argptr = xargv; - if (options(/*cmdline:*/ 1, &login_sh)) { + if (options(&login_sh)) { /* it already printed err message */ raise_exception(EXERROR); } @@ -13982,8 +14221,13 @@ procargs(char **argv) ash_msg_and_raise_error(bb_msg_requires_arg, "-c"); sflag = 1; } - if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + if (iflag == 2 /* no explicit -i given */ + && sflag == 1 /* -s given (or implied) */ + && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ + && isatty(0) && isatty(1) /* we are on tty */ + ) { iflag = 1; + } if (mflag == 2) mflag = iflag; for (i = 0; i < NOPTS; i++) @@ -14043,6 +14287,10 @@ reset(void) /* from eval.c: */ evalskip = 0; loopnest = 0; + if (savestatus >= 0) { + exitstatus = savestatus; + savestatus = -1; + } /* from expand.c: */ ifsfree(); @@ -14072,7 +14320,12 @@ extern int etext(); * is used to figure out how far we had gotten. */ int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +#if NUM_SCRIPTS > 0 +int ash_main(int argc, char **argv) +#else int ash_main(int argc UNUSED_PARAM, char **argv) +#endif +/* note: 'argc' is used only if embedded scripts are enabled */ { volatile smallint state; struct jmploc jmploc; @@ -14092,9 +14345,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv) monitor(4, etext, profile_buf, sizeof(profile_buf), 50); #endif -#if ENABLE_FEATURE_EDITING - line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP); -#endif state = 0; if (setjmp(jmploc.loc)) { smallint e; @@ -14126,6 +14376,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv) init(); setstackmark(&smark); + +#if NUM_SCRIPTS > 0 + if (argc < 0) + /* Non-NULL minusc tells procargs that an embedded script is being run */ + minusc = get_script_content(-argc - 1); +#endif login_sh = procargs(argv); #if DEBUG TRACE(("Shell args: ")); @@ -14163,10 +14419,17 @@ int ash_main(int argc UNUSED_PARAM, char **argv) * Ensure we don't falsely claim that 0 (stdin) * is one of stacked source fds. * Testcase: ash -c 'exec 1>&0' must not complain. */ + // if (!sflag) g_parsefile->pf_fd = -1; // ^^ not necessary since now we special-case fd 0 // in save_fd_on_redirect() - evalstring(minusc, sflag ? 0 : EV_EXIT); + + // dash: evalstring(minusc, sflag ? 0 : EV_EXIT); + // The above makes + // ash -sc 'echo $-' + // continue reading input from stdin after running 'echo'. + // bash does not do this: it prints "hBcs" and exits. + evalstring(minusc, EV_EXIT); } if (sflag || minusc == NULL) {