Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Jul 2012 10:19:43 +0000 (UTC)
From:      Jilles Tjoelker <jilles@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r238468 - in head: bin/sh tools/regression/bin/sh/expansion
Message-ID:  <201207151019.q6FAJhis041090@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Sun Jul 15 10:19:43 2012
New Revision: 238468
URL: http://svn.freebsd.org/changeset/base/238468

Log:
  sh: Expand assignment-like words specially for export/readonly/local.
  
  Examples:
    export x=~
  now expands the tilde
    local y=$1
  is now safe, even if $1 contains IFS characters or metacharacters.
  
  For a word to "look like an assignment", it must start with a name followed
  by an equals sign, none of which may be quoted.
  
  The special treatment applies when the first word (potentially after
  "command") is "export", "readonly" or "local". There may be quoting
  characters but no expansions. If "local" is overridden with a function there
  is no special treatment ("export" and "readonly" cannot be overridden with a
  function).
  
  If things like
    local arr=(1 2 3)
  are ever allowed in the future, they cannot call a "local" function. This
  would either be a run-time error or it would call the builtin.
  
  This matches Austin Group bug #351, planned for the next issue of POSIX.1.
  
  PR:		bin/166771

Added:
  head/tools/regression/bin/sh/expansion/export2.0   (contents, props changed)
  head/tools/regression/bin/sh/expansion/export3.0   (contents, props changed)
  head/tools/regression/bin/sh/expansion/local1.0   (contents, props changed)
  head/tools/regression/bin/sh/expansion/local2.0   (contents, props changed)
  head/tools/regression/bin/sh/expansion/readonly1.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	Sun Jul 15 06:08:11 2012	(r238467)
+++ head/bin/sh/eval.c	Sun Jul 15 10:19:43 2012	(r238468)
@@ -672,6 +672,52 @@ out:
 		result->fd, result->buf, result->nleft, result->jp));
 }
 
+static int
+mustexpandto(const char *argtext, const char *mask)
+{
+	for (;;) {
+		if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) {
+			argtext++;
+			continue;
+		}
+		if (*argtext == CTLESC)
+			argtext++;
+		else if (BASESYNTAX[(int)*argtext] == CCTL)
+			return (0);
+		if (*argtext != *mask)
+			return (0);
+		if (*argtext == '\0')
+			return (1);
+		argtext++;
+		mask++;
+	}
+}
+
+static int
+isdeclarationcmd(struct narg *arg)
+{
+	int have_command = 0;
+
+	if (arg == NULL)
+		return (0);
+	while (mustexpandto(arg->text, "command")) {
+		have_command = 1;
+		arg = &arg->next->narg;
+		if (arg == NULL)
+			return (0);
+		/*
+		 * To also allow "command -p" and "command --" as part of
+		 * a declaration command, add code here.
+		 * We do not do this, as ksh does not do it either and it
+		 * is not required by POSIX.
+		 */
+	}
+	return (mustexpandto(arg->text, "export") ||
+	    mustexpandto(arg->text, "readonly") ||
+	    (mustexpandto(arg->text, "local") &&
+		(have_command || !isfunc("local"))));
+}
+
 /*
  * Check if a builtin can safely be executed in the same process,
  * even though it should be in a subshell (command substitution).
@@ -743,11 +789,12 @@ evalcommand(union node *cmd, int flags, 
 	exitstatus = 0;
 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
 		if (varflag && isassignment(argp->narg.text)) {
-			expandarg(argp, &varlist, EXP_VARTILDE);
+			expandarg(argp, varflag == 1 ? &varlist : &arglist,
+			    EXP_VARTILDE);
 			continue;
-		}
+		} else if (varflag == 1)
+			varflag = isdeclarationcmd(&argp->narg) ? 2 : 0;
 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
-		varflag = 0;
 	}
 	*arglist.lastp = NULL;
 	*varlist.lastp = NULL;

Modified: head/bin/sh/exec.c
==============================================================================
--- head/bin/sh/exec.c	Sun Jul 15 06:08:11 2012	(r238467)
+++ head/bin/sh/exec.c	Sun Jul 15 10:19:43 2012	(r238468)
@@ -648,6 +648,19 @@ unsetfunc(const char *name)
 	return (0);
 }
 
+
+/*
+ * Check if a function by a certain name exists.
+ */
+int
+isfunc(const char *name)
+{
+	struct tblentry *cmdp;
+	cmdp = cmdlookup(name, 0);
+	return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION);
+}
+
+
 /*
  * Shared code for the following builtin commands:
  *    type, command -v, command -V

Modified: head/bin/sh/exec.h
==============================================================================
--- head/bin/sh/exec.h	Sun Jul 15 06:08:11 2012	(r238467)
+++ head/bin/sh/exec.h	Sun Jul 15 10:19:43 2012	(r238468)
@@ -72,5 +72,6 @@ void hashcd(void);
 void changepath(const char *);
 void defun(const char *, union node *);
 int unsetfunc(const char *);
+int isfunc(const char *);
 int typecmd_impl(int, char **, int, const char *);
 void clearcmdentry(void);

Modified: head/bin/sh/sh.1
==============================================================================
--- head/bin/sh/sh.1	Sun Jul 15 06:08:11 2012	(r238467)
+++ head/bin/sh/sh.1	Sun Jul 15 10:19:43 2012	(r238468)
@@ -32,7 +32,7 @@
 .\"	from: @(#)sh.1	8.6 (Berkeley) 5/4/95
 .\" $FreeBSD$
 .\"
-.Dd November 5, 2011
+.Dd July 15, 2012
 .Dt SH 1
 .Os
 .Sh NAME
@@ -1164,6 +1164,20 @@ Assignments are expanded differently fro
 tilde expansion is also performed after the equals sign and after any colon
 and usernames are also terminated by colons,
 and field splitting and pathname expansion are not performed.
+.Pp
+This special expansion applies not only to assignments that form a simple
+command by themselves or precede a command word,
+but also to words passed to the
+.Ic export ,
+.Ic local
+or
+.Ic readonly
+built-in commands that have this form.
+For this, the builtin's name must be literal
+(not the result of an expansion)
+and may optionally be preceded by one or more literal instances of
+.Ic command
+without options.
 .Ss Positional Parameters
 A positional parameter is a parameter denoted by a number greater than zero.
 The shell sets these initially to the values of its command line

Added: head/tools/regression/bin/sh/expansion/export2.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/export2.0	Sun Jul 15 10:19:43 2012	(r238468)
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+w='@ @'
+check() {
+	[ "$v" = "$w" ] || echo "Expected $w got $v"
+}
+
+export v=$w
+check
+
+HOME=/known/value
+check() {
+	[ "$v" = ~ ] || echo "Expected $HOME got $v"
+}
+
+export v=~
+check
+
+check() {
+	[ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+}
+
+export v=x:~
+check

Added: head/tools/regression/bin/sh/expansion/export3.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/export3.0	Sun Jul 15 10:19:43 2012	(r238468)
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+w='@ @'
+check() {
+	[ "$v" = "$w" ] || echo "Expected $w got $v"
+}
+
+command export v=$w
+check
+command command export v=$w
+check
+
+HOME=/known/value
+check() {
+	[ "$v" = ~ ] || echo "Expected $HOME got $v"
+}
+
+command export v=~
+check
+command command export v=~
+check
+
+check() {
+	[ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+}
+
+command export v=x:~
+check
+command command export v=x:~
+check

Added: head/tools/regression/bin/sh/expansion/local1.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/local1.0	Sun Jul 15 10:19:43 2012	(r238468)
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+run_test() {
+	w='@ @'
+	check() {
+		[ "$v" = "$w" ] || echo "Expected $w got $v"
+	}
+
+	local v=$w
+	check
+
+	HOME=/known/value
+	check() {
+		[ "$v" = ~ ] || echo "Expected $HOME got $v"
+	}
+
+	local v=~
+	check
+
+	check() {
+		[ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+	}
+
+	local v=x:~
+	check
+}
+
+run_test

Added: head/tools/regression/bin/sh/expansion/local2.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/local2.0	Sun Jul 15 10:19:43 2012	(r238468)
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+run_test() {
+	w='@ @'
+	check() {
+		[ "$v" = "$w" ] || echo "Expected $w got $v"
+	}
+
+	command local v=$w
+	check
+	command command local v=$w
+	check
+
+	HOME=/known/value
+	check() {
+		[ "$v" = ~ ] || echo "Expected $HOME got $v"
+	}
+
+	command local v=~
+	check
+	command command local v=~
+	check
+
+	check() {
+		[ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v"
+	}
+
+	command local v=x:~
+	check
+	command command local v=x:~
+	check
+}
+
+run_test

Added: head/tools/regression/bin/sh/expansion/readonly1.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/readonly1.0	Sun Jul 15 10:19:43 2012	(r238468)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+w='@ @'
+
+v=0 HOME=/known/value
+readonly v=~:~/:$w
+[ "$v" = "$HOME:$HOME/:$w" ] || echo "Expected $HOME/:$w got $v"



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201207151019.q6FAJhis041090>