svn commit: r204800 - in head: bin/sh tools/regression/bin/sh/builtins tools/regression/bin/sh/errors

Jilles Tjoelker jilles at FreeBSD.org
Sat Mar 6 16:57:55 UTC 2010


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


More information about the svn-src-all mailing list