github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/builder/dockerfile/dispatchers_windows.go (about) 1 package dockerfile 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "regexp" 9 "strings" 10 11 "github.com/docker/docker/pkg/system" 12 ) 13 14 var pattern = regexp.MustCompile(`^[a-zA-Z]:\.$`) 15 16 // normaliseWorkdir normalises a user requested working directory in a 17 // platform semantically consistent way. 18 func normaliseWorkdir(current string, requested string) (string, error) { 19 if requested == "" { 20 return "", errors.New("cannot normalise nothing") 21 } 22 23 // `filepath.Clean` will replace "" with "." so skip in that case 24 if current != "" { 25 current = filepath.Clean(current) 26 } 27 if requested != "" { 28 requested = filepath.Clean(requested) 29 } 30 31 // If either current or requested in Windows is: 32 // C: 33 // C:. 34 // then an error will be thrown as the definition for the above 35 // refers to `current directory on drive C:` 36 // Since filepath.Clean() will automatically normalize the above 37 // to `C:.`, we only need to check the last format 38 if pattern.MatchString(current) { 39 return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", current) 40 } 41 if pattern.MatchString(requested) { 42 return "", fmt.Errorf("%s is not a directory. If you are specifying a drive letter, please add a trailing '\\'", requested) 43 } 44 45 // Target semantics is C:\somefolder, specifically in the format: 46 // UPPERCASEDriveLetter-Colon-Backslash-FolderName. We are already 47 // guaranteed that `current`, if set, is consistent. This allows us to 48 // cope correctly with any of the following in a Dockerfile: 49 // WORKDIR a --> C:\a 50 // WORKDIR c:\\foo --> C:\foo 51 // WORKDIR \\foo --> C:\foo 52 // WORKDIR /foo --> C:\foo 53 // WORKDIR c:\\foo \ WORKDIR bar --> C:\foo --> C:\foo\bar 54 // WORKDIR C:/foo \ WORKDIR bar --> C:\foo --> C:\foo\bar 55 // WORKDIR C:/foo \ WORKDIR \\bar --> C:\foo --> C:\bar 56 // WORKDIR /foo \ WORKDIR c:/bar --> C:\foo --> C:\bar 57 if len(current) == 0 || system.IsAbs(requested) { 58 if (requested[0] == os.PathSeparator) || 59 (len(requested) > 1 && string(requested[1]) != ":") || 60 (len(requested) == 1) { 61 requested = filepath.Join(`C:\`, requested) 62 } 63 } else { 64 requested = filepath.Join(current, requested) 65 } 66 // Upper-case drive letter 67 return (strings.ToUpper(string(requested[0])) + requested[1:]), nil 68 } 69 70 func errNotJSON(command, original string) error { 71 // For Windows users, give a hint if it looks like it might contain 72 // a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"], 73 // as JSON must be escaped. Unfortunate... 74 // 75 // Specifically looking for quote-driveletter-colon-backslash, there's no 76 // double backslash and a [] pair. No, this is not perfect, but it doesn't 77 // have to be. It's simply a hint to make life a little easier. 78 extra := "" 79 original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1))) 80 if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 && 81 !strings.Contains(original, `\\`) && 82 strings.Contains(original, "[") && 83 strings.Contains(original, "]") { 84 extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original) 85 } 86 return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra) 87 } 88 89 // equalEnvKeys compare two strings and returns true if they are equal. On 90 // Windows this comparison is case insensitive. 91 func equalEnvKeys(from, to string) bool { 92 return strings.ToUpper(from) == strings.ToUpper(to) 93 }