Author: jilles
Date: Sat Mar  6 16:57:53 2010
New Revision: 204800
URL: http://svn.freebsd.org/changeset/base/204800

Log:
  sh: Improve the command builtin:
  * avoid unnecessary fork
  * allow executing builtins via command
  * executing a special builtin via command removes its special properties
  
  Obtained from:        NetBSD (parts)

Added:
  head/tools/regression/bin/sh/builtins/command8.0   (contents, props changed)
  head/tools/regression/bin/sh/builtins/var-assign2.0   (contents, props 
changed)
  head/tools/regression/bin/sh/errors/redirection-error3.0   (contents, props 
changed)
Modified:
  head/bin/sh/eval.c
  head/bin/sh/exec.c
  head/bin/sh/exec.h
  head/bin/sh/sh.1

Modified: head/bin/sh/eval.c
==============================================================================
--- head/bin/sh/eval.c  Sat Mar  6 16:45:55 2010        (r204799)
+++ head/bin/sh/eval.c  Sat Mar  6 16:57:53 2010        (r204800)
@@ -597,6 +597,7 @@ evalcommand(union node *cmd, int flags, 
        char *lastarg;
        int realstatus;
        int do_clearcmdentry;
+       char *path = pathval();
 
        /* First expand the arguments. */
        TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
@@ -682,7 +683,7 @@ evalcommand(union node *cmd, int flags, 
                cmdentry.special = 1;
        } else {
                static const char PATH[] = "PATH=";
-               char *path = pathval();
+               int cmd_flags = 0, bltinonly = 0;
 
                /*
                 * Modify the command lookup path, if a PATH= assignment
@@ -713,24 +714,68 @@ evalcommand(union node *cmd, int flags, 
                                do_clearcmdentry = 1;
                        }
 
-               find_command(argv[0], &cmdentry, 0, path);
-               /* implement the bltin builtin here */
-               if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == 
BLTINCMD) {
-                       for (;;) {
-                               argv++;
-                               if (--argc == 0)
-                                       break;
-                               if ((cmdentry.u.index = find_builtin(*argv,
-                                   &cmdentry.special)) < 0) {
+               for (;;) {
+                       if (bltinonly) {
+                               cmdentry.u.index = find_builtin(*argv, 
&cmdentry.special);
+                               if (cmdentry.u.index < 0) {
                                        cmdentry.u.index = BLTINCMD;
                                        argv--;
                                        argc++;
                                        break;
                                }
-                               if (cmdentry.u.index != BLTINCMD)
+                       } else
+                               find_command(argv[0], &cmdentry, cmd_flags, 
path);
+                       /* implement the bltin and command builtins here */
+                       if (cmdentry.cmdtype != CMDBUILTIN)
+                               break;
+                       if (cmdentry.u.index == BLTINCMD) {
+                               if (argc == 1)
                                        break;
-                       }
+                               argv++;
+                               argc--;
+                               bltinonly = 1;
+                       } else if (cmdentry.u.index == COMMANDCMD) {
+                               if (argc == 1)
+                                       break;
+                               if (!strcmp(argv[1], "-p")) {
+                                       if (argc == 2)
+                                               break;
+                                       if (argv[2][0] == '-') {
+                                               if (strcmp(argv[2], "--"))
+                                                       break;
+                                               if (argc == 3)
+                                                       break;
+                                               argv += 3;
+                                               argc -= 3;
+                                       } else {
+                                               argv += 2;
+                                               argc -= 2;
+                                       }
+                                       path = _PATH_STDPATH;
+                                       clearcmdentry(0);
+                                       do_clearcmdentry = 1;
+                               } else if (!strcmp(argv[1], "--")) {
+                                       if (argc == 2)
+                                               break;
+                                       argv += 2;
+                                       argc -= 2;
+                               } else if (argv[1][0] == '-')
+                                       break;
+                               else {
+                                       argv++;
+                                       argc--;
+                               }
+                               cmd_flags |= DO_NOFUNC;
+                               bltinonly = 0;
+                       } else
+                               break;
                }
+               /*
+                * Special builtins lose their special properties when
+                * called via 'command'.
+                */
+               if (cmd_flags & DO_NOFUNC)
+                       cmdentry.special = 0;
        }
 
        /* Fork off a child process if necessary. */
@@ -741,9 +786,7 @@ evalcommand(union node *cmd, int flags, 
            && (cmdentry.cmdtype != CMDBUILTIN
                 || cmdentry.u.index == CDCMD
                 || cmdentry.u.index == DOTCMD
-                || cmdentry.u.index == EVALCMD))
-        || (cmdentry.cmdtype == CMDBUILTIN &&
-           cmdentry.u.index == COMMANDCMD)) {
+                || cmdentry.u.index == EVALCMD))) {
                jp = makejob(cmd, 1);
                mode = cmd->ncmd.backgnd;
                if (flags & EV_BACKCMD) {
@@ -889,7 +932,7 @@ cmddone:
                for (sp = varlist.list ; sp ; sp = sp->next)
                        setvareq(sp->text, VEXPORT|VSTACK);
                envp = environment();
-               shellexec(argv, envp, pathval(), cmdentry.u.index);
+               shellexec(argv, envp, path, cmdentry.u.index);
                /*NOTREACHED*/
        }
        goto out;
@@ -996,15 +1039,11 @@ int
 commandcmd(int argc, char **argv)
 {
        static char stdpath[] = _PATH_STDPATH;
-       struct jmploc loc, *old;
-       struct strlist *sp;
        char *path;
        int ch;
        int cmd = -1;
 
-       for (sp = cmdenviron; sp ; sp = sp->next)
-               setvareq(sp->text, VEXPORT|VSTACK);
-       path = pathval();
+       path = bltinlookup("PATH", 1);
 
        optind = optreset = 1;
        opterr = 0;
@@ -1032,22 +1071,14 @@ commandcmd(int argc, char **argv)
                        error("wrong number of arguments");
                return typecmd_impl(2, argv - 1, cmd, path);
        }
-       if (argc != 0) {
-               old = handler;
-               handler = &loc;
-               if (setjmp(handler->loc) == 0)
-                       shellexec(argv, environment(), path, 0);
-               handler = old;
-               if (exception == EXEXEC)
-                       exit(exerrno);
-               exraise(exception);
-       }
+       if (argc != 0)
+               error("commandcmd() called while it should not be");
 
        /*
         * Do nothing successfully if no command was specified;
         * ksh also does this.
         */
-       exit(0);
+       return 0;
 }
 
 

Modified: head/bin/sh/exec.c
==============================================================================
--- head/bin/sh/exec.c  Sat Mar  6 16:45:55 2010        (r204799)
+++ head/bin/sh/exec.c  Sat Mar  6 16:57:53 2010        (r204800)
@@ -248,7 +248,7 @@ hashcmd(int argc __unused, char **argv _
                 && (cmdp->cmdtype == CMDNORMAL
                     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
                        delete_cmd_entry();
-               find_command(name, &entry, 1, pathval());
+               find_command(name, &entry, DO_ERR, pathval());
                if (verbose) {
                        if (entry.cmdtype != CMDUNKNOWN) {      /* if no error 
msg */
                                cmdp = cmdlookup(name, 0);
@@ -310,10 +310,10 @@ printentry(struct tblentry *cmdp, int ve
  */
 
 void
-find_command(const char *name, struct cmdentry *entry, int printerr,
+find_command(const char *name, struct cmdentry *entry, int act,
     const char *path)
 {
-       struct tblentry *cmdp;
+       struct tblentry *cmdp, loc_cmd;
        int idx;
        int prev;
        char *fullname;
@@ -330,13 +330,19 @@ find_command(const char *name, struct cm
        }
 
        /* If name is in the table, and not invalidated by cd, we're done */
-       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
-               goto success;
+       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
+               if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
+                       cmdp = NULL;
+               else
+                       goto success;
+       }
 
        /* If %builtin not in path, check for builtin next */
        if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) {
                INTOFF;
                cmdp = cmdlookup(name, 1);
+               if (cmdp->cmdtype == CMDFUNCTION)
+                       cmdp = &loc_cmd;
                cmdp->cmdtype = CMDBUILTIN;
                cmdp->param.index = i;
                cmdp->special = spec;
@@ -365,6 +371,8 @@ loop:
                                        goto loop;
                                INTOFF;
                                cmdp = cmdlookup(name, 1);
+                               if (cmdp->cmdtype == CMDFUNCTION)
+                                       cmdp = &loc_cmd;
                                cmdp->cmdtype = CMDBUILTIN;
                                cmdp->param.index = i;
                                cmdp->special = spec;
@@ -414,6 +422,8 @@ loop:
                TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
                INTOFF;
                cmdp = cmdlookup(name, 1);
+               if (cmdp->cmdtype == CMDFUNCTION)
+                       cmdp = &loc_cmd;
                cmdp->cmdtype = CMDNORMAL;
                cmdp->param.index = idx;
                INTON;
@@ -421,9 +431,9 @@ loop:
        }
 
        /* We failed.  If there was an entry for this command, delete it */
-       if (cmdp)
+       if (cmdp && cmdp->cmdtype != CMDFUNCTION)
                delete_cmd_entry();
-       if (printerr) {
+       if (act & DO_ERR) {
                if (e == ENOENT || e == ENOTDIR)
                        outfmt(out2, "%s: not found\n", name);
                else

Modified: head/bin/sh/exec.h
==============================================================================
--- head/bin/sh/exec.h  Sat Mar  6 16:45:55 2010        (r204799)
+++ head/bin/sh/exec.h  Sat Mar  6 16:57:53 2010        (r204800)
@@ -57,6 +57,10 @@ struct cmdentry {
 };
 
 
+/* action to find_command() */
+#define DO_ERR         0x01    /* prints errors */
+#define DO_NOFUNC      0x02    /* don't return shell functions, for command */
+
 extern const char *pathopt;    /* set by padvance */
 extern int exerrno;            /* last exec error */
 

Modified: head/bin/sh/sh.1
==============================================================================
--- head/bin/sh/sh.1    Sat Mar  6 16:45:55 2010        (r204799)
+++ head/bin/sh/sh.1    Sat Mar  6 16:57:53 2010        (r204800)
@@ -32,7 +32,7 @@
 .\"    from: @(#)sh.1  8.6 (Berkeley) 5/4/95
 .\" $FreeBSD$
 .\"
-.Dd December 31, 2009
+.Dd March 6, 2010
 .Dt SH 1
 .Os
 .Sh NAME
@@ -1571,10 +1571,12 @@ built-in command.
 .It Ic command Oo Fl p Oc Op Ar utility Op Ar argument ...
 .It Ic command Oo Fl v | V Oc Op Ar utility
 The first form of invocation executes the specified
+.Ar utility ,
+ignoring shell functions in the search.
+If
 .Ar utility
-as a simple command (see the
-.Sx Simple Commands
-section).
+is a special builtin,
+it is executed as if it were a regular builtin.
 .Pp
 If the
 .Fl p

Added: head/tools/regression/bin/sh/builtins/command8.0
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/tools/regression/bin/sh/builtins/command8.0    Sat Mar  6 16:57:53 
2010        (r204800)
@@ -0,0 +1,45 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+       :,\
+       continue,\
+       . /dev/null,\
+       eval,\
+       exec,\
+       export -p,\
+       readonly -p,\
+       set,\
+       shift 0,\
+       times,\
+       trap,\
+       unset foo"
+
+set -e
+
+# Check that special builtins can be executed via "command".
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+       sh -c "v=:; while \$v; do v=false; command ${cmd}; done" >/dev/null
+done
+
+while :; do
+       command break
+       echo Error on line $LINENO
+done
+
+set p q r
+command shift 2
+if [ $# -ne 1 ]; then
+       echo Error on line $LINENO
+fi
+
+(
+       command exec >/dev/null
+       echo Error on line $LINENO
+)
+
+set +e
+! command shift 2 2>/dev/null

Added: head/tools/regression/bin/sh/builtins/var-assign2.0
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/tools/regression/bin/sh/builtins/var-assign2.0 Sat Mar  6 16:57:53 
2010        (r204800)
@@ -0,0 +1,55 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+       :,\
+       continue,\
+       . /dev/null,\
+       eval,\
+       exec,\
+       export -p,\
+       readonly -p,\
+       set,\
+       shift 0,\
+       times,\
+       trap,\
+       unset foo"
+
+UTILS="alias,\
+       bg,\
+       bind,\
+       cd,\
+       command echo,\
+       echo,\
+       false,\
+       fc -l,\
+       fg,\
+       getopts a var,\
+       hash,\
+       jobs,\
+       printf a,\
+       pwd,\
+       read var < /dev/null,\
+       test,\
+       true,\
+       type ls,\
+       ulimit,\
+       umask,\
+       unalias -a,\
+       wait"
+
+set -e
+
+# With 'command', variable assignments affect the shell environment.
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+       sh -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done
+
+set -- ${UTILS}
+for cmd in "$@"
+do
+       sh -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
+done

Added: head/tools/regression/bin/sh/errors/redirection-error3.0
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/tools/regression/bin/sh/errors/redirection-error3.0    Sat Mar  6 
16:57:53 2010        (r204800)
@@ -0,0 +1,54 @@
+# $FreeBSD$
+IFS=,
+
+SPECIAL="break,\
+       :,\
+       continue,\
+       . /dev/null,\
+       eval,\
+       exec,\
+       export -p,\
+       readonly -p,\
+       set,\
+       shift,\
+       times,\
+       trap,\
+       unset foo"
+
+UTILS="alias,\
+       bg,\
+       bind,\
+       cd,\
+       command echo,\
+       echo,\
+       false,\
+       fc -l,\
+       fg,\
+       getopts a -a,\
+       hash,\
+       jobs,\
+       printf a,\
+       pwd,\
+       read var < /dev/null,\
+       test,\
+       true,\
+       type ls,\
+       ulimit,\
+       umask,\
+       unalias -a,\
+       wait"
+
+# When used with 'command', neither special built-in utilities nor other
+# utilities must abort on a redirection error.
+
+set -- ${SPECIAL}
+for cmd in "$@"
+do
+       sh -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
+done
+
+set -- ${UTILS}
+for cmd in "$@"
+do
+       sh -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
+done
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to