Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 Oct 2010 13:42:19 +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: r214512 - in head: bin/sh tools/regression/bin/sh/expansion
Message-ID:  <201010291342.o9TDgJnZ027780@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Fri Oct 29 13:42:18 2010
New Revision: 214512
URL: http://svn.freebsd.org/changeset/base/214512

Log:
  sh: Do IFS splitting on word in ${v+word} and ${v-word}.
  
  The code is inspired by NetBSD sh somewhat, but different because we
  preserve the old Almquist/Bourne/Korn ability to have an unquoted part in a
  quoted ${v+word}. For example, "${v-"*"}" expands to $v as a single field if
  v is set, but generates filenames otherwise.
  
  Note that this is the only place where we split text literally from the
  script (the similar ${v=word} assigns to v and then expands $v). The parser
  must now add additional markers to allow the expansion code to know whether
  arbitrary characters in substitutions are quoted.
  
  Example:
    for i in ${$+a b c}; do echo $i; done
  
  Exp-run done by:	pav (with some other sh(1) changes)

Added:
  head/tools/regression/bin/sh/expansion/plus-minus6.0   (contents, props changed)
Modified:
  head/bin/sh/expand.c
  head/bin/sh/expand.h
  head/bin/sh/mksyntax.c
  head/bin/sh/parser.c
  head/bin/sh/parser.h

Modified: head/bin/sh/expand.c
==============================================================================
--- head/bin/sh/expand.c	Fri Oct 29 13:34:57 2010	(r214511)
+++ head/bin/sh/expand.c	Fri Oct 29 13:42:18 2010	(r214512)
@@ -216,7 +216,12 @@ argstr(char *p, int flag)
 	char c;
 	int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);	/* do CTLESC */
 	int firsteq = 1;
+	int split_lit;
+	int lit_quoted;
 
+	split_lit = flag & EXP_SPLIT_LIT;
+	lit_quoted = flag & EXP_LIT_QUOTED;
+	flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
 	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
 		p = exptilde(p, flag);
 	for (;;) {
@@ -225,17 +230,25 @@ argstr(char *p, int flag)
 		case CTLENDVAR:
 			goto breakloop;
 		case CTLQUOTEMARK:
+			lit_quoted = 1;
 			/* "$@" syntax adherence hack */
 			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
 				break;
 			if ((flag & EXP_FULL) != 0)
 				STPUTC(c, expdest);
 			break;
+		case CTLQUOTEEND:
+			lit_quoted = 0;
+			break;
 		case CTLESC:
 			if (quotes)
 				STPUTC(c, expdest);
 			c = *p++;
 			STPUTC(c, expdest);
+			if (split_lit && !lit_quoted)
+				recordregion(expdest - stackblock() -
+				    (quotes ? 2 : 1),
+				    expdest - stackblock(), 0);
 			break;
 		case CTLVAR:
 			p = evalvar(p, flag);
@@ -255,18 +268,21 @@ argstr(char *p, int flag)
 			 * assignments (after the first '=' and after ':'s).
 			 */
 			STPUTC(c, expdest);
-			if (flag & EXP_VARTILDE && *p == '~') {
-				if (c == '=') {
-					if (firsteq)
-						firsteq = 0;
-					else
-						break;
-				}
+			if (split_lit && !lit_quoted)
+				recordregion(expdest - stackblock() - 1,
+				    expdest - stackblock(), 0);
+			if (flag & EXP_VARTILDE && *p == '~' &&
+			    (c != '=' || firsteq)) {
+				if (c == '=')
+					firsteq = 0;
 				p = exptilde(p, flag);
 			}
 			break;
 		default:
 			STPUTC(c, expdest);
+			if (split_lit && !lit_quoted)
+				recordregion(expdest - stackblock() - 1,
+				    expdest - stackblock(), 0);
 		}
 	}
 breakloop:;
@@ -742,7 +758,8 @@ record:
 	case VSPLUS:
 	case VSMINUS:
 		if (!set) {
-			argstr(p, flag);
+			argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
+			    (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
 			break;
 		}
 		if (easy)
@@ -1495,13 +1512,13 @@ rmescapes(char *str)
 	char *p, *q;
 
 	p = str;
-	while (*p != CTLESC && *p != CTLQUOTEMARK) {
+	while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
 		if (*p++ == '\0')
 			return;
 	}
 	q = p;
 	while (*p) {
-		if (*p == CTLQUOTEMARK) {
+		if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
 			p++;
 			continue;
 		}

Modified: head/bin/sh/expand.h
==============================================================================
--- head/bin/sh/expand.h	Fri Oct 29 13:34:57 2010	(r214511)
+++ head/bin/sh/expand.h	Fri Oct 29 13:42:18 2010	(r214512)
@@ -52,6 +52,8 @@ struct arglist {
 #define	EXP_VARTILDE	0x4	/* expand tildes in an assignment */
 #define	EXP_REDIR	0x8	/* file glob for a redirection (1 match only) */
 #define EXP_CASE	0x10	/* keeps quotes around for CASE pattern */
+#define EXP_SPLIT_LIT	0x20	/* IFS split literal text ${v+-a b c} */
+#define EXP_LIT_QUOTED	0x40	/* for EXP_SPLIT_LIT, start off quoted */
 
 
 union node;

Modified: head/bin/sh/mksyntax.c
==============================================================================
--- head/bin/sh/mksyntax.c	Fri Oct 29 13:34:57 2010	(r214511)
+++ head/bin/sh/mksyntax.c	Fri Oct 29 13:42:18 2010	(r214512)
@@ -285,6 +285,7 @@ init(void)
 	syntax[base + CTLARI] = "CCTL";
 	syntax[base + CTLENDARI] = "CCTL";
 	syntax[base + CTLQUOTEMARK] = "CCTL";
+	syntax[base + CTLQUOTEEND] = "CCTL";
 }
 
 

Modified: head/bin/sh/parser.c
==============================================================================
--- head/bin/sh/parser.c	Fri Oct 29 13:34:57 2010	(r214511)
+++ head/bin/sh/parser.c	Fri Oct 29 13:42:18 2010	(r214512)
@@ -1161,7 +1161,7 @@ readtoken1(int firstc, char const *initi
 	loop: {	/* for each line, until end of word */
 		CHECKEND();	/* set c to PEOF if at end of here document */
 		for (;;) {	/* until end of line or end of word */
-			CHECKSTRSPACE(3, out);	/* permit 3 calls to USTPUTC */
+			CHECKSTRSPACE(4, out);	/* permit 4 calls to USTPUTC */
 
 			synentry = state[level].syntax[c];
 
@@ -1203,12 +1203,18 @@ readtoken1(int firstc, char const *initi
 						newvarnest == 0)) &&
 					    (c != '}' || state[level].category != TSTATE_VAR_OLD))
 						USTPUTC('\\', out);
+					if ((eofmark == NULL ||
+					    newvarnest > 0) &&
+					    state[level].syntax == BASESYNTAX)
+						USTPUTC(CTLQUOTEMARK, out);
 					if (SQSYNTAX[c] == CCTL)
 						USTPUTC(CTLESC, out);
-					else if (eofmark == NULL ||
-					    newvarnest > 0)
-						USTPUTC(CTLQUOTEMARK, out);
 					USTPUTC(c, out);
+					if ((eofmark == NULL ||
+					    newvarnest > 0) &&
+					    state[level].syntax == BASESYNTAX &&
+					    state[level].category == TSTATE_VAR_OLD)
+						USTPUTC(CTLQUOTEEND, out);
 					quotef++;
 				}
 				break;
@@ -1224,6 +1230,8 @@ readtoken1(int firstc, char const *initi
 				if (eofmark != NULL && newvarnest == 0)
 					USTPUTC(c, out);
 				else {
+					if (state[level].category == TSTATE_VAR_OLD)
+						USTPUTC(CTLQUOTEEND, out);
 					state[level].syntax = BASESYNTAX;
 					quotef++;
 				}

Modified: head/bin/sh/parser.h
==============================================================================
--- head/bin/sh/parser.h	Fri Oct 29 13:34:57 2010	(r214511)
+++ head/bin/sh/parser.h	Fri Oct 29 13:42:18 2010	(r214512)
@@ -43,6 +43,7 @@
 #define	CTLARI	'\206'
 #define	CTLENDARI '\207'
 #define	CTLQUOTEMARK '\210'
+#define	CTLQUOTEEND '\211' /* only for ${v+-...} */
 
 /* variable substitution byte (follows CTLVAR) */
 #define VSTYPE		0x0f	/* type of variable substitution */

Added: head/tools/regression/bin/sh/expansion/plus-minus6.0
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/tools/regression/bin/sh/expansion/plus-minus6.0	Fri Oct 29 13:42:18 2010	(r214512)
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+failures=0
+unset LC_ALL
+export LC_CTYPE=en_US.ISO8859-1
+nl='
+'
+i=1
+set -f
+while [ "$i" -le 255 ]; do
+	# A different byte still in the range 1..255.
+	i2=$((i^2+(i==2)))
+	# Add a character to work around command substitution's removal of
+	# final newlines, then remove it again.
+	c=$(printf \\"$(printf %o@ "$i")")
+	c=${c%@}
+	c2=$(printf \\"$(printf %o@ "$i2")")
+	c2=${c2%@}
+	case $c in
+		[\'$nl'$}();&|\"`']) c=M
+	esac
+	case $c2 in
+		[\'$nl'$}();&|\"`']) c2=N
+	esac
+	IFS=$c
+	command eval "set -- \${\$+$c2$c$c2$c$c2}"
+	if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] ||
+	    [ "$3" != "$c2" ]; then
+		echo "Bad results for separator $i (word $i2)" >&2
+		: $((failures += 1))
+	fi
+	i=$((i+1))
+done
+exit $((failures > 0))



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