Browse code

Merge pull request #32328 from duglin/fixEnvVars

Minor tweaks to quotes in env vars

Daniel Nephin authored on 2017/04/13 23:41:28
Showing 5 changed files
... ...
@@ -1,18 +1,21 @@
1 1
 A|hello                    |     hello
2 2
 A|he'll'o                  |     hello
3
-A|he'llo                   |     hello
3
+A|he'llo                   |     error
4 4
 A|he\'llo                  |     he'llo
5
-A|he\\'llo                 |     he\llo
5
+A|he\\'llo                 |     error
6 6
 A|abc\tdef                 |     abctdef
7 7
 A|"abc\tdef"               |     abc\tdef
8
+A|"abc\\tdef"              |     abc\tdef
8 9
 A|'abc\tdef'               |     abc\tdef
9 10
 A|hello\                   |     hello
10 11
 A|hello\\                  |     hello\
11
-A|"hello                   |     hello
12
-A|"hello\"                 |     hello"
12
+A|"hello                   |     error
13
+A|"hello\"                 |     error
13 14
 A|"hel'lo"                 |     hel'lo
14
-A|'hello                   |     hello
15
+A|'hello                   |     error
15 16
 A|'hello\'                 |     hello\
17
+A|'hello\there'            |     hello\there
18
+A|'hello\\there'           |     hello\\there
16 19
 A|"''"                     |     ''
17 20
 A|$.                       |     $.
18 21
 A|$1                       |
... ...
@@ -24,6 +27,8 @@ W|he$pwd.                  |     he/home.
24 24
 A|he$PWD                   |     he/home
25 25
 A|he\$PWD                  |     he$PWD
26 26
 A|he\\$PWD                 |     he\/home
27
+A|"he\$PWD"                |     he$PWD
28
+A|"he\\$PWD"               |     he\/home
27 29
 A|he\${}                   |     he${}
28 30
 A|he\${}xx                 |     he${}xx
29 31
 A|he${}                    |     he
... ...
@@ -60,18 +65,18 @@ A|he${XXX:-\$PWD:}xx       |     he$PWD:xx
60 60
 A|he${XXX:-\${PWD}z}xx     |     he${PWDz}xx
61 61
 A|안녕하세요                 |     안녕하세요
62 62
 A|안'녕'하세요               |     안녕하세요
63
-A|안'녕하세요                |     안녕하세요
63
+A|안'녕하세요                |     error
64 64
 A|안녕\'하세요               |     안녕'하세요
65
-A|안\\'녕하세요              |     안\녕하세요
65
+A|안\\'녕하세요              |     error
66 66
 A|안녕\t하세요               |     안녕t하세요
67 67
 A|"안녕\t하세요"             |     안녕\t하세요
68
-A|'안녕\t하세요              |     안녕\t하세요
68
+A|'안녕\t하세요              |     error
69 69
 A|안녕하세요\                |     안녕하세요
70 70
 A|안녕하세요\\               |     안녕하세요\
71
-A|"안녕하세요                |     안녕하세요
72
-A|"안녕하세요\"              |     안녕하세요"
71
+A|"안녕하세요                |     error
72
+A|"안녕하세요\"              |     error
73 73
 A|"안녕'하세요"              |     안녕'하세요
74
-A|'안녕하세요                |     안녕하세요
74
+A|'안녕하세요                |     error
75 75
 A|'안녕하세요\'              |     안녕하세요\
76 76
 A|안녕$1x                    |     안녕x
77 77
 A|안녕$.x                    |     안녕$.x
... ...
@@ -162,15 +162,28 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
162 162
 func (sw *shellWord) processSingleQuote() (string, error) {
163 163
 	// All chars between single quotes are taken as-is
164 164
 	// Note, you can't escape '
165
+	//
166
+	// From the "sh" man page:
167
+	// Single Quotes
168
+	//   Enclosing characters in single quotes preserves the literal meaning of
169
+	//   all the characters (except single quotes, making it impossible to put
170
+	//   single-quotes in a single-quoted string).
171
+
165 172
 	var result string
166 173
 
167 174
 	sw.scanner.Next()
168 175
 
169 176
 	for {
170 177
 		ch := sw.scanner.Next()
171
-		if ch == '\'' || ch == scanner.EOF {
178
+
179
+		if ch == scanner.EOF {
180
+			return "", fmt.Errorf("unexpected end of statement while looking for matching single-quote")
181
+		}
182
+
183
+		if ch == '\'' {
172 184
 			break
173 185
 		}
186
+
174 187
 		result += string(ch)
175 188
 	}
176 189
 
... ...
@@ -180,16 +193,32 @@ func (sw *shellWord) processSingleQuote() (string, error) {
180 180
 func (sw *shellWord) processDoubleQuote() (string, error) {
181 181
 	// All chars up to the next " are taken as-is, even ', except any $ chars
182 182
 	// But you can escape " with a \ (or ` if escape token set accordingly)
183
+	//
184
+	// From the "sh" man page:
185
+	// Double Quotes
186
+	//  Enclosing characters within double quotes preserves the literal meaning
187
+	//  of all characters except dollarsign ($), backquote (`), and backslash
188
+	//  (\).  The backslash inside double quotes is historically weird, and
189
+	//  serves to quote only the following characters:
190
+	//    $ ` " \ <newline>.
191
+	//  Otherwise it remains literal.
192
+
183 193
 	var result string
184 194
 
185 195
 	sw.scanner.Next()
186 196
 
187
-	for sw.scanner.Peek() != scanner.EOF {
197
+	for {
188 198
 		ch := sw.scanner.Peek()
199
+
200
+		if ch == scanner.EOF {
201
+			return "", fmt.Errorf("unexpected end of statement while looking for matching double-quote")
202
+		}
203
+
189 204
 		if ch == '"' {
190 205
 			sw.scanner.Next()
191 206
 			break
192 207
 		}
208
+
193 209
 		if ch == '$' {
194 210
 			tmp, err := sw.processDollar()
195 211
 			if err != nil {
... ...
@@ -206,8 +235,11 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
206 206
 					continue
207 207
 				}
208 208
 
209
-				if chNext == '"' || chNext == '$' {
210
-					// \" and \$ can be escaped, all other \'s are left as-is
209
+				// Note: for now don't do anything special with ` chars.
210
+				// Not sure what to do with them anyway since we're not going
211
+				// to execute the text in there (not now anyway).
212
+				if chNext == '"' || chNext == '$' || chNext == sw.escapeToken {
213
+					// These chars can be escaped, all other \'s are left as-is
211 214
 					ch = sw.scanner.Next()
212 215
 				}
213 216
 			}
... ...
@@ -71,8 +71,10 @@ func TestShellParser4Words(t *testing.T) {
71 71
 
72 72
 	envs := []string{}
73 73
 	scanner := bufio.NewScanner(file)
74
+	lineNum := 0
74 75
 	for scanner.Scan() {
75 76
 		line := scanner.Text()
77
+		lineNum = lineNum + 1
76 78
 
77 79
 		if strings.HasPrefix(line, "#") {
78 80
 			continue
... ...
@@ -86,7 +88,7 @@ func TestShellParser4Words(t *testing.T) {
86 86
 
87 87
 		words := strings.Split(line, "|")
88 88
 		if len(words) != 2 {
89
-			t.Fatalf("Error in '%s' - should be exactly one | in: %q", fn, line)
89
+			t.Fatalf("Error in '%s'(line %d) - should be exactly one | in: %q", fn, lineNum, line)
90 90
 		}
91 91
 		test := strings.TrimSpace(words[0])
92 92
 		expected := strings.Split(strings.TrimLeft(words[1], " "), ",")
... ...
@@ -98,11 +100,11 @@ func TestShellParser4Words(t *testing.T) {
98 98
 		}
99 99
 
100 100
 		if len(result) != len(expected) {
101
-			t.Fatalf("Error. %q was suppose to result in %q, but got %q instead", test, expected, result)
101
+			t.Fatalf("Error on line %d. %q was suppose to result in %q, but got %q instead", lineNum, test, expected, result)
102 102
 		}
103 103
 		for i, w := range expected {
104 104
 			if w != result[i] {
105
-				t.Fatalf("Error. %q was suppose to result in %q, but got %q instead", test, expected, result)
105
+				t.Fatalf("Error on line %d. %q was suppose to result in %q, but got %q instead", lineNum, test, expected, result)
106 106
 			}
107 107
 		}
108 108
 	}
... ...
@@ -21,5 +21,10 @@ hel"lo${trailing}" | helloab c
21 21
 hello" there  " | hello there  
22 22
 hello there     | hello,there
23 23
 hello\ there | hello there
24
-hello" there | hello there
24
+hello" there | error
25 25
 hello\" there | hello",there
26
+hello"\\there" | hello\there
27
+hello"\there" | hello\there
28
+hello'\\there' | hello\\there
29
+hello'\there' | hello\there
30
+hello'$there' | hello$there
... ...
@@ -184,6 +184,14 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) {
184 184
   RUN [ "$abc3" = '$foo' ] && (echo "$abc3" | grep -q foo)
185 185
   ENV abc4 "\$foo"
186 186
   RUN [ "$abc4" = '$foo' ] && (echo "$abc4" | grep -q foo)
187
+  ENV foo2="abc\def"
188
+  RUN [ "$foo2" = 'abc\def' ]
189
+  ENV foo3="abc\\def"
190
+  RUN [ "$foo3" = 'abc\def' ]
191
+  ENV foo4='abc\\def'
192
+  RUN [ "$foo4" = 'abc\\def' ]
193
+  ENV foo5='abc\def'
194
+  RUN [ "$foo5" = 'abc\def' ]
187 195
   `))
188 196
 
189 197
 	envResult := []string{}