Browse Source

Merge pull request #27644 from Microsoft/jjh/builderslash

Windows: Honour escape directive fully
Sebastiaan van Stijn 8 years ago
parent
commit
0797e3f61a

+ 2 - 2
builder/dockerfile/evaluator.go

@@ -169,13 +169,13 @@ func (b *Builder) dispatch(stepN int, stepTotal int, ast *parser.Node) error {
 			var words []string
 
 			if allowWordExpansion[cmd] {
-				words, err = ProcessWords(str, envs)
+				words, err = ProcessWords(str, envs, b.directive.EscapeToken)
 				if err != nil {
 					return err
 				}
 				strList = append(strList, words...)
 			} else {
-				str, err = ProcessWord(str, envs)
+				str, err = ProcessWord(str, envs, b.directive.EscapeToken)
 				if err != nil {
 					return err
 				}

+ 19 - 16
builder/dockerfile/shell_parser.go

@@ -14,19 +14,21 @@ import (
 )
 
 type shellWord struct {
-	word    string
-	scanner scanner.Scanner
-	envs    []string
-	pos     int
+	word        string
+	scanner     scanner.Scanner
+	envs        []string
+	pos         int
+	escapeToken rune
 }
 
 // ProcessWord will use the 'env' list of environment variables,
 // and replace any env var references in 'word'.
-func ProcessWord(word string, env []string) (string, error) {
+func ProcessWord(word string, env []string, escapeToken rune) (string, error) {
 	sw := &shellWord{
-		word: word,
-		envs: env,
-		pos:  0,
+		word:        word,
+		envs:        env,
+		pos:         0,
+		escapeToken: escapeToken,
 	}
 	sw.scanner.Init(strings.NewReader(word))
 	word, _, err := sw.process()
@@ -40,11 +42,12 @@ func ProcessWord(word string, env []string) (string, error) {
 // this splitting is done **after** the env var substitutions are done.
 // Note, each one is trimmed to remove leading and trailing spaces (unless
 // they are quoted", but ProcessWord retains spaces between words.
-func ProcessWords(word string, env []string) ([]string, error) {
+func ProcessWords(word string, env []string, escapeToken rune) ([]string, error) {
 	sw := &shellWord{
-		word: word,
-		envs: env,
-		pos:  0,
+		word:        word,
+		envs:        env,
+		pos:         0,
+		escapeToken: escapeToken,
 	}
 	sw.scanner.Init(strings.NewReader(word))
 	_, words, err := sw.process()
@@ -138,8 +141,8 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
 			// Not special, just add it to the result
 			ch = sw.scanner.Next()
 
-			if ch == '\\' {
-				// '\' escapes, except end of line
+			if ch == sw.escapeToken {
+				// '\' (default escape token, but ` allowed) escapes, except end of line
 
 				ch = sw.scanner.Next()
 
@@ -179,7 +182,7 @@ func (sw *shellWord) processSingleQuote() (string, error) {
 
 func (sw *shellWord) processDoubleQuote() (string, error) {
 	// All chars up to the next " are taken as-is, even ', except any $ chars
-	// But you can escape " with a \
+	// But you can escape " with a \ (or ` if escape token set accordingly)
 	var result string
 
 	sw.scanner.Next()
@@ -198,7 +201,7 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
 			result += tmp
 		} else {
 			ch = sw.scanner.Next()
-			if ch == '\\' {
+			if ch == sw.escapeToken {
 				chNext := sw.scanner.Peek()
 
 				if chNext == scanner.EOF {

+ 2 - 2
builder/dockerfile/shell_parser_test.go

@@ -40,7 +40,7 @@ func TestShellParser4EnvVars(t *testing.T) {
 		words[0] = strings.TrimSpace(words[0])
 		words[1] = strings.TrimSpace(words[1])
 
-		newWord, err := ProcessWord(words[0], envs)
+		newWord, err := ProcessWord(words[0], envs, '\\')
 
 		if err != nil {
 			newWord = "error"
@@ -83,7 +83,7 @@ func TestShellParser4Words(t *testing.T) {
 		test := strings.TrimSpace(words[0])
 		expected := strings.Split(strings.TrimLeft(words[1], " "), ",")
 
-		result, err := ProcessWords(test, envs)
+		result, err := ProcessWords(test, envs, '\\')
 
 		if err != nil {
 			result = []string{"error"}

+ 36 - 0
integration-cli/docker_cli_build_test.go

@@ -6884,6 +6884,42 @@ func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
 	}
 }
 
+// Verify that escape is being correctly applied to words when escape directive is not \.
+// Tests WORKDIR, ADD
+func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
+	testRequires(c, DaemonIsWindows)
+	name := "testbuildescapenotbackslashwordtesta"
+	_, out, err := buildImageWithOut(name,
+		`# escape= `+"`"+`
+		FROM `+minimalBaseImage()+`
+        WORKDIR c:\windows
+		RUN dir /w`,
+		true)
+	if err != nil {
+		c.Fatal(err)
+	}
+	if !strings.Contains(strings.ToLower(out), "[system32]") {
+		c.Fatalf("Line with '[windows]' not found in output %q", out)
+	}
+
+	name = "testbuildescapenotbackslashwordtestb"
+	_, out, err = buildImageWithOut(name,
+		`# escape= `+"`"+`
+		FROM `+minimalBaseImage()+`
+		SHELL ["powershell.exe"]
+        WORKDIR c:\foo
+		ADD Dockerfile c:\foo\
+		RUN dir Dockerfile`,
+		true)
+	if err != nil {
+		c.Fatal(err)
+	}
+	if !strings.Contains(strings.ToLower(out), "-a----") {
+		c.Fatalf("Line with '-a----' not found in output %q", out)
+	}
+
+}
+
 // #22868. Make sure shell-form CMD is marked as escaped in the config of the image
 func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
 	testRequires(c, DaemonIsWindows)