From f3634584d0fdeb4ae9e2fe0f5971d45b77e40296 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 3 Jun 2019 12:21:04 +0200 Subject: [PATCH] ash,hush: show 'c' in $- if run in "sh -c CMD" function old new delta options 552 599 +47 expand_one_var 2375 2385 +10 optletters_optnames 60 64 +4 hush_main 1108 1111 +3 ash_main 1150 1152 +2 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 5/0 up/down: 66/0) Total: 66 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 62 +++++++++++++++++++++++++++++++++++----------------- shell/hush.c | 13 ++++++----- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index c8857366c..e3bbac9a0 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -315,17 +315,18 @@ static const char *const optletters_optnames[] = { "e" "errexit", "f" "noglob", "I" "ignoreeof", -/* The below allows this invocation: +/* 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 this "set -i". bash also has no - * "set -o interactive". + * bash does not support such "set -i". + * In our code, this is denoted by empty long name: */ - "i" "interactive", + "i" "", "m" "monitor", "n" "noexec", -/* Ditto: bash has no "set -s" and "set -o stdin" */ - "s" "stdin", +/* Ditto: bash has no "set -s" */ + "s" "", + "c" "", "x" "xtrace", "v" "verbose", "C" "noclobber", @@ -359,7 +360,6 @@ static const char *const optletters_optnames[] = { #define optletters(n) optletters_optnames[n][0] #define optnames(n) (optletters_optnames[n] + 1) - enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; @@ -419,21 +419,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 */ @@ -11104,7 +11105,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; } @@ -11150,6 +11151,15 @@ options(int *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') { @@ -14170,8 +14180,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++) @@ -14359,10 +14374,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) { diff --git a/shell/hush.c b/shell/hush.c index 4b08232a4..f82747f74 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -903,6 +903,7 @@ struct globals { # define G_x_mode 0 #endif char opt_s; + char opt_c; #if ENABLE_HUSH_INTERACTIVE smallint promptmode; /* 0: PS1, 1: PS2 */ #endif @@ -1009,7 +1010,7 @@ struct globals { int debug_indent; #endif struct sigaction sa; - char optstring_buf[sizeof("eixs")]; + char optstring_buf[sizeof("eixcs")]; #if BASH_EPOCH_VARS char epoch_buf[sizeof("%lu.nnnnnn") + sizeof(long)*3]; #endif @@ -6414,9 +6415,10 @@ static NOINLINE int expand_one_var(o_string *output, int n, * commands read but are not executed, * so $- can not execute too, 'n' is never seen in $-. */ + if (G.opt_c) + *cp++ = 'c'; if (G.opt_s) *cp++ = 's'; -//TODO: show 'c' if executed via "hush -c 'CMDS'" (bash only, not ash) *cp = '\0'; break; } @@ -9859,7 +9861,6 @@ int hush_main(int argc, char **argv) { enum { OPT_login = (1 << 0), - OPT_s = (1 << 1), }; unsigned flags; unsigned builtin_argc; @@ -10029,6 +10030,7 @@ int hush_main(int argc, char **argv) } goto final_return; } + G.opt_c = 1; if (!G.global_argv[0]) { /* -c 'script' (no params): prevent empty $0 */ G.global_argv--; /* points to argv[i] of 'script' */ @@ -10044,7 +10046,7 @@ int hush_main(int argc, char **argv) /* G_interactive_fd++; */ break; case 's': - flags |= OPT_s; + G.opt_s = 1; break; case 'l': flags |= OPT_login; @@ -10154,7 +10156,7 @@ int hush_main(int argc, char **argv) } /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ - if (!(flags & OPT_s) && G.global_argv[1]) { + if (!G.opt_s && G.global_argv[1]) { HFILE *input; /* * "bash