ash,hush: show 'c' in $- if run in "sh -c CMD"
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 3 Jun 2019 10:21:04 +0000 (12:21 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 3 Jun 2019 10:21:04 +0000 (12:21 +0200)
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 <vda.linux@googlemail.com>
shell/ash.c
shell/hush.c

index c8857366c38cda2a4d591b96e648e67492ef1d79..e3bbac9a0c0f7d053229f42b3960ca7a55d74b17 100644 (file)
@@ -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) {
index 4b08232a4342231672d5db54be37849f4ef30037..f82747f74bedc8e3a8a80adb782694066dd090a0 100644 (file)
@@ -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 <script>" (which is never interactive (unless -i?))
@@ -10178,6 +10180,7 @@ int hush_main(int argc, char **argv)
 #endif
                goto final_return;
        }
+       /* "implicit" -s: bare interactive hush shows 's' in $- */
        G.opt_s = 1;
 
        /* Up to here, shell was non-interactive. Now it may become one.