aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJilles Tjoelker <jilles@FreeBSD.org>2010-03-06 16:57:53 +0000
committerJilles Tjoelker <jilles@FreeBSD.org>2010-03-06 16:57:53 +0000
commitc848bc18e8d3de0c74834f0980f57b141aa9b989 (patch)
treea292cee82566850ab924f6b484d9ac29b205226d
parent44d68185d1bb80945c24e1f87e67d223c56f92bc (diff)
downloadsrc-c848bc18e8d3de0c74834f0980f57b141aa9b989.tar.gz
src-c848bc18e8d3de0c74834f0980f57b141aa9b989.zip
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)
Notes
Notes: svn path=/head/; revision=204800
-rw-r--r--bin/sh/eval.c95
-rw-r--r--bin/sh/exec.c24
-rw-r--r--bin/sh/exec.h4
-rw-r--r--bin/sh/sh.110
-rw-r--r--tools/regression/bin/sh/builtins/command8.045
-rw-r--r--tools/regression/bin/sh/builtins/var-assign2.055
-rw-r--r--tools/regression/bin/sh/errors/redirection-error3.054
7 files changed, 244 insertions, 43 deletions
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index 30e05b8b6435..c0a7601a6165 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -597,6 +597,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
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, struct backcmd *backcmd)
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, struct backcmd *backcmd)
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, struct backcmd *backcmd)
&& (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;
}
diff --git a/bin/sh/exec.c b/bin/sh/exec.c
index a28ab0364a50..7c15f7960574 100644
--- a/bin/sh/exec.c
+++ b/bin/sh/exec.c
@@ -248,7 +248,7 @@ hashcmd(int argc __unused, char **argv __unused)
&& (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 verbose)
*/
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 cmdentry *entry, int printerr,
}
/* 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
diff --git a/bin/sh/exec.h b/bin/sh/exec.h
index 32bf13168342..45330a16ad6c 100644
--- a/bin/sh/exec.h
+++ b/bin/sh/exec.h
@@ -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 */
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index 8ec119545cae..4df5212e1900 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -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
diff --git a/tools/regression/bin/sh/builtins/command8.0 b/tools/regression/bin/sh/builtins/command8.0
new file mode 100644
index 000000000000..949ffedaee9f
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/command8.0
@@ -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
diff --git a/tools/regression/bin/sh/builtins/var-assign2.0 b/tools/regression/bin/sh/builtins/var-assign2.0
new file mode 100644
index 000000000000..8485df8629b6
--- /dev/null
+++ b/tools/regression/bin/sh/builtins/var-assign2.0
@@ -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
diff --git a/tools/regression/bin/sh/errors/redirection-error3.0 b/tools/regression/bin/sh/errors/redirection-error3.0
new file mode 100644
index 000000000000..93f526c7a125
--- /dev/null
+++ b/tools/regression/bin/sh/errors/redirection-error3.0
@@ -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