ash: eval: Add assignment built-in support again
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 19 Feb 2020 14:15:13 +0000 (15:15 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 20 Feb 2020 08:36:51 +0000 (09:36 +0100)
Upstream commit:

    Date: Sat, 19 May 2018 02:39:52 +0800
    eval: Add assignment built-in support again

    This patch adds assignment built-in support that used to exist
    in dash prior to 0.3.8-15.  This is because it will soon be part
    of POSIX, and the semantics are now much better defined.

    Recognition is done at execution time, so even "command -- export"
    or "var=export; command $var" should work.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c

index fea4b10a7ab4bceb2d65c184184fa9473713617c..1458962295ee66ddb12c6454645acebaf729757a 100644 (file)
@@ -8112,7 +8112,7 @@ struct cmdentry {
 #define DO_ABS          0x02    /* checks absolute paths */
 #define DO_NOFUNC       0x04    /* don't return shell functions, for command */
 #define DO_ALTPATH      0x08    /* using alternate path */
-#define DO_ALTBLTIN     0x20    /* %builtin in alt. path */
+#define DO_REGBLTIN     0x10    /* regular built-ins and functions only */
 
 static void find_command(char *, struct cmdentry *, int, const char *);
 
@@ -8718,24 +8718,43 @@ typecmd(int argc UNUSED_PARAM, char **argv)
 }
 
 #if ENABLE_ASH_CMDCMD
+static struct strlist *
+fill_arglist(struct arglist *arglist, union node **argpp)
+{
+       struct strlist **lastp = arglist->lastp;
+       union node *argp;
+
+       while ((argp = *argpp) != NULL) {
+               expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
+               *argpp = argp->narg.next;
+               if (*lastp)
+                       break;
+       }
+
+       return *lastp;
+}
+
 /* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
-static char **
-parse_command_args(char **argv, const char **path)
+static int
+parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
 {
+       struct strlist *sp = arglist->list;
        char *cp, c;
 
        for (;;) {
-               cp = *++argv;
-               if (!cp)
-                       return NULL;
+               sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
+               if (!sp)
+                       return 0;
+               cp = sp->text;
                if (*cp++ != '-')
                        break;
                c = *cp++;
                if (!c)
                        break;
                if (c == '-' && !*cp) {
-                       if (!*++argv)
-                               return NULL;
+                       if (!sp->next && !fill_arglist(arglist, argpp))
+                               return 0;
+                       sp = sp->next;
                        break;
                }
                do {
@@ -8745,12 +8764,14 @@ parse_command_args(char **argv, const char **path)
                                break;
                        default:
                                /* run 'typecmd' for other options */
-                               return NULL;
+                               return 0;
                        }
                        c = *cp++;
                } while (c);
        }
-       return argv;
+
+       arglist->list = sp;
+       return DO_NOFUNC;
 }
 
 static int FAST_FUNC
@@ -10124,7 +10145,7 @@ static int
 evalcommand(union node *cmd, int flags)
 {
        static const struct builtincmd null_bltin = {
-               "\0\0", bltincmd /* why three NULs? */
+               BUILTIN_REGULAR "", bltincmd
        };
        struct localvar_list *localvar_stop;
        struct parsefile *file_stop;
@@ -10134,12 +10155,14 @@ evalcommand(union node *cmd, int flags)
        struct arglist varlist;
        char **argv;
        int argc;
+       struct strlist *osp;
        const struct strlist *sp;
        struct cmdentry cmdentry;
        struct job *jp;
        char *lastarg;
        const char *path;
        int spclbltin;
+       int cmd_flag;
        int status;
        char **nargv;
        smallint cmd_is_exec;
@@ -10161,26 +10184,46 @@ evalcommand(union node *cmd, int flags)
        arglist.lastp = &arglist.list;
        *arglist.lastp = NULL;
 
+       cmd_flag = 0;
+       cmd_is_exec = 0;
+       spclbltin = -1;
+       path = NULL;
+
        argc = 0;
-       if (cmd->ncmd.args) {
-               struct builtincmd *bcmd;
-               smallint pseudovarflag;
+       argp = cmd->ncmd.args;
+       osp = fill_arglist(&arglist, &argp);
+       if (osp) {
+               int pseudovarflag = 0;
 
-               bcmd = find_builtin(cmd->ncmd.args->narg.text);
-               pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
+               for (;;) {
+                       find_command(arglist.list->text, &cmdentry,
+                                       cmd_flag | DO_REGBLTIN, pathval());
 
-               for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
-                       struct strlist **spp;
+                       /* implement bltin and command here */
+                       if (cmdentry.cmdtype != CMDBUILTIN)
+                               break;
 
-                       spp = arglist.lastp;
-                       if (pseudovarflag && isassignment(argp->narg.text))
-                               expandarg(argp, &arglist, EXP_VARTILDE);
-                       else
-                               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+                       pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
+                       if (spclbltin < 0) {
+                               spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
+                       }
+                       cmd_is_exec = cmdentry.u.cmd == EXECCMD;
+                       if (cmdentry.u.cmd != COMMANDCMD)
+                               break;
 
-                       for (sp = *spp; sp; sp = sp->next)
-                               argc++;
+                       cmd_flag = parse_command_args(&arglist, &argp, &path);
+                       if (!cmd_flag)
+                               break;
                }
+
+               for (; argp; argp = argp->narg.next)
+                       expandarg(argp, &arglist,
+                                       pseudovarflag &&
+                                       isassignment(argp->narg.text) ?
+                                       EXP_VARTILDE : EXP_FULL | EXP_TILDE);
+
+               for (sp = arglist.list; sp; sp = sp->next)
+                       argc++;
        }
 
        /* Reserve one extra spot at the front for shellexec. */
@@ -10210,23 +10253,13 @@ evalcommand(union node *cmd, int flags)
        }
        status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
 
-       path = vpath.var_text;
        for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
                struct strlist **spp;
-               char *p;
 
                spp = varlist.lastp;
                expandarg(argp, &varlist, EXP_VARTILDE);
 
                mklocal((*spp)->text);
-
-               /*
-                * Modify the command lookup path, if a PATH= assignment
-                * is present
-                */
-               p = (*spp)->text;
-               if (varcmp(p, path) == 0)
-                       path = p;
        }
 
        /* Print the command if xflag is set. */
@@ -10265,46 +10298,16 @@ evalcommand(union node *cmd, int flags)
                safe_write(preverrout_fd, "\n", 1);
        }
 
-       cmd_is_exec = 0;
-       spclbltin = -1;
-
        /* Now locate the command. */
-       if (argc) {
-               int cmd_flag = DO_ERR;
-#if ENABLE_ASH_CMDCMD
-               const char *oldpath = path + 5;
-#endif
-               path += 5;
-               for (;;) {
-                       find_command(argv[0], &cmdentry, cmd_flag, path);
-                       if (cmdentry.cmdtype == CMDUNKNOWN) {
-                               flush_stdout_stderr();
-                               status = 127;
-                               goto bail;
-                       }
-
-                       /* implement bltin and command here */
-                       if (cmdentry.cmdtype != CMDBUILTIN)
-                               break;
-                       if (spclbltin < 0)
-                               spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
-                       if (cmdentry.u.cmd == EXECCMD)
-                               cmd_is_exec = 1;
-#if ENABLE_ASH_CMDCMD
-                       if (cmdentry.u.cmd == COMMANDCMD) {
-                               path = oldpath;
-                               nargv = parse_command_args(argv, &path);
-                               if (!nargv)
-                                       break;
-                               /* It's "command [-p] PROG ARGS" (that is, no -Vv).
-                                * nargv => "PROG". path is updated if -p.
-                                */
-                               argc -= nargv - argv;
-                               argv = nargv;
-                               cmd_flag |= DO_NOFUNC;
-                       } else
-#endif
-                               break;
+       if (cmdentry.cmdtype != CMDBUILTIN
+        || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
+       ) {
+               find_command(argv[0], &cmdentry, cmd_flag | DO_ERR,
+                               path ? path : pathval());
+               if (cmdentry.cmdtype == CMDUNKNOWN) {
+                       status = 127;
+                       flush_stdout_stderr();
+                       goto bail;
                }
        }
 
@@ -10383,6 +10386,7 @@ evalcommand(union node *cmd, int flags)
                        /* fall through to exec'ing external program */
                }
                listsetvar(varlist.list, VEXPORT|VSTACK);
+               path = path ? path : pathval();
                shellexec(argv[0], argv, path, cmdentry.u.index);
                /* NOTREACHED */
        } /* default */
@@ -13538,11 +13542,8 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
 /* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
 
        updatetbl = (path == pathval());
-       if (!updatetbl) {
+       if (!updatetbl)
                act |= DO_ALTPATH;
-               if (strstr(path, "%builtin") != NULL)
-                       act |= DO_ALTBLTIN;
-       }
 
        /* If name is in the table, check answer will be ok */
        cmdp = cmdlookup(name, 0);
@@ -13555,16 +13556,19 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                        abort();
 #endif
                case CMDNORMAL:
-                       bit = DO_ALTPATH;
+                       bit = DO_ALTPATH | DO_REGBLTIN;
                        break;
                case CMDFUNCTION:
                        bit = DO_NOFUNC;
                        break;
                case CMDBUILTIN:
-                       bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_ALTBLTIN;
+                       bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
                        break;
                }
                if (act & bit) {
+                       if (act & bit & DO_REGBLTIN)
+                               goto fail;
+
                        updatetbl = 0;
                        cmdp = NULL;
                } else if (cmdp->rehash == 0)
@@ -13577,14 +13581,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
        if (bcmd) {
                if (IS_BUILTIN_REGULAR(bcmd))
                        goto builtin_success;
-               if (act & DO_ALTPATH) {
-                       if (!(act & DO_ALTBLTIN))
-                               goto builtin_success;
-               } else if (builtinloc <= 0) {
+               if (act & DO_ALTPATH)
+                       goto builtin_success;
+               if (builtinloc <= 0)
                        goto builtin_success;
-               }
        }
 
+       if (act & DO_REGBLTIN)
+               goto fail;
+
 #if ENABLE_FEATURE_SH_STANDALONE
        {
                int applet_no = find_applet_by_name(name);
@@ -13688,6 +13693,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
 #endif
                ash_msg("%s: %s", name, errmsg(e, "not found"));
        }
+ fail:
        entry->cmdtype = CMDUNKNOWN;
        return;