From owner-svn-src-all@FreeBSD.ORG Sun Apr 13 21:49:46 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id C26492F7; Sun, 13 Apr 2014 21:49:46 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id A21C7190C; Sun, 13 Apr 2014 21:49:46 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s3DLnkgj057208; Sun, 13 Apr 2014 21:49:46 GMT (envelope-from jilles@svn.freebsd.org) Received: (from jilles@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s3DLniqu057196; Sun, 13 Apr 2014 21:49:44 GMT (envelope-from jilles@svn.freebsd.org) Message-Id: <201404132149.s3DLniqu057196@svn.freebsd.org> From: Jilles Tjoelker Date: Sun, 13 Apr 2014 21:49:44 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r264423 - in stable/9: bin/sh tools/regression/bin/sh/expansion X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 13 Apr 2014 21:49:46 -0000 Author: jilles Date: Sun Apr 13 21:49:44 2014 New Revision: 264423 URL: http://svnweb.freebsd.org/changeset/base/264423 Log: MFC r238468: 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. As for the MFC, it is easy to depend on this feature inadvertently, and adding this fixes a regression from stable/8 that may be apparent in things like local x=${y+a @}. PR: bin/166771 Relnotes: yes Added: stable/9/tools/regression/bin/sh/expansion/export2.0 - copied unchanged from r238468, head/tools/regression/bin/sh/expansion/export2.0 stable/9/tools/regression/bin/sh/expansion/export3.0 - copied unchanged from r238468, head/tools/regression/bin/sh/expansion/export3.0 stable/9/tools/regression/bin/sh/expansion/local1.0 - copied unchanged from r238468, head/tools/regression/bin/sh/expansion/local1.0 stable/9/tools/regression/bin/sh/expansion/local2.0 - copied unchanged from r238468, head/tools/regression/bin/sh/expansion/local2.0 stable/9/tools/regression/bin/sh/expansion/readonly1.0 - copied unchanged from r238468, head/tools/regression/bin/sh/expansion/readonly1.0 Modified: stable/9/bin/sh/eval.c stable/9/bin/sh/exec.c stable/9/bin/sh/exec.h stable/9/bin/sh/sh.1 Directory Properties: stable/9/bin/sh/ (props changed) stable/9/tools/regression/bin/sh/ (props changed) Modified: stable/9/bin/sh/eval.c ============================================================================== --- stable/9/bin/sh/eval.c Sun Apr 13 21:23:15 2014 (r264422) +++ stable/9/bin/sh/eval.c Sun Apr 13 21:49:44 2014 (r264423) @@ -657,6 +657,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). @@ -728,11 +774,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: stable/9/bin/sh/exec.c ============================================================================== --- stable/9/bin/sh/exec.c Sun Apr 13 21:23:15 2014 (r264422) +++ stable/9/bin/sh/exec.c Sun Apr 13 21:49:44 2014 (r264423) @@ -644,6 +644,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: stable/9/bin/sh/exec.h ============================================================================== --- stable/9/bin/sh/exec.h Sun Apr 13 21:23:15 2014 (r264422) +++ stable/9/bin/sh/exec.h Sun Apr 13 21:49:44 2014 (r264423) @@ -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: stable/9/bin/sh/sh.1 ============================================================================== --- stable/9/bin/sh/sh.1 Sun Apr 13 21:23:15 2014 (r264422) +++ stable/9/bin/sh/sh.1 Sun Apr 13 21:49:44 2014 (r264423) @@ -1171,6 +1171,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 Copied: stable/9/tools/regression/bin/sh/expansion/export2.0 (from r238468, head/tools/regression/bin/sh/expansion/export2.0) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/tools/regression/bin/sh/expansion/export2.0 Sun Apr 13 21:49:44 2014 (r264423, copy of r238468, head/tools/regression/bin/sh/expansion/export2.0) @@ -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 Copied: stable/9/tools/regression/bin/sh/expansion/export3.0 (from r238468, head/tools/regression/bin/sh/expansion/export3.0) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/tools/regression/bin/sh/expansion/export3.0 Sun Apr 13 21:49:44 2014 (r264423, copy of r238468, head/tools/regression/bin/sh/expansion/export3.0) @@ -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 Copied: stable/9/tools/regression/bin/sh/expansion/local1.0 (from r238468, head/tools/regression/bin/sh/expansion/local1.0) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/tools/regression/bin/sh/expansion/local1.0 Sun Apr 13 21:49:44 2014 (r264423, copy of r238468, head/tools/regression/bin/sh/expansion/local1.0) @@ -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 Copied: stable/9/tools/regression/bin/sh/expansion/local2.0 (from r238468, head/tools/regression/bin/sh/expansion/local2.0) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/tools/regression/bin/sh/expansion/local2.0 Sun Apr 13 21:49:44 2014 (r264423, copy of r238468, head/tools/regression/bin/sh/expansion/local2.0) @@ -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 Copied: stable/9/tools/regression/bin/sh/expansion/readonly1.0 (from r238468, head/tools/regression/bin/sh/expansion/readonly1.0) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/9/tools/regression/bin/sh/expansion/readonly1.0 Sun Apr 13 21:49:44 2014 (r264423, copy of r238468, head/tools/regression/bin/sh/expansion/readonly1.0) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +w='@ @' + +v=0 HOME=/known/value +readonly v=~:~/:$w +[ "$v" = "$HOME:$HOME/:$w" ] || echo "Expected $HOME/:$w got $v"