1// Copyright 2016 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package proptools 16 17import "strings" 18 19// NinjaEscapeList takes a slice of strings that may contain characters that are meaningful to ninja 20// ($), and escapes each string so they will be passed to bash. It is not necessary on input, 21// output, or dependency names, those are handled by ModuleContext.Build. It is generally required 22// on strings from properties in Blueprint files that are used as Args to ModuleContext.Build. A 23// new slice containing the escaped strings is returned. 24func NinjaEscapeList(slice []string) []string { 25 slice = append([]string(nil), slice...) 26 for i, s := range slice { 27 slice[i] = NinjaEscape(s) 28 } 29 return slice 30} 31 32// NinjaEscapeList takes a string that may contain characters that are meaningful to ninja 33// ($), and escapes it so it will be passed to bash. It is not necessary on input, 34// output, or dependency names, those are handled by ModuleContext.Build. It is generally required 35// on strings from properties in Blueprint files that are used as Args to ModuleContext.Build. A 36// new slice containing the escaped strings is returned. 37func NinjaEscape(s string) string { 38 return ninjaEscaper.Replace(s) 39} 40 41var ninjaEscaper = strings.NewReplacer( 42 "$", "$$") 43 44// ShellEscapeList takes a slice of strings that may contain characters that are meaningful to bash and 45// escapes them if necessary by wrapping them in single quotes, and replacing internal single quotes with 46// '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single 47// quote, and then a single quote to restarting quoting. A new slice containing the escaped strings 48// is returned. 49func ShellEscapeList(slice []string) []string { 50 slice = append([]string(nil), slice...) 51 52 for i, s := range slice { 53 slice[i] = ShellEscape(s) 54 } 55 return slice 56} 57 58func ShellEscapeListIncludingSpaces(slice []string) []string { 59 slice = append([]string(nil), slice...) 60 61 for i, s := range slice { 62 slice[i] = ShellEscapeIncludingSpaces(s) 63 } 64 return slice 65} 66 67func shellUnsafeChar(r rune) bool { 68 switch { 69 case 'A' <= r && r <= 'Z', 70 'a' <= r && r <= 'z', 71 '0' <= r && r <= '9', 72 r == '_', 73 r == '+', 74 r == '-', 75 r == '=', 76 r == '.', 77 r == ',', 78 r == '/': 79 return false 80 default: 81 return true 82 } 83} 84 85// ShellEscape takes string that may contain characters that are meaningful to bash and 86// escapes it if necessary by wrapping it in single quotes, and replacing internal single quotes with 87// '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single 88// quote, and then a single quote to restarting quoting. 89func ShellEscape(s string) string { 90 shellUnsafeCharNotSpace := func(r rune) bool { 91 return r != ' ' && shellUnsafeChar(r) 92 } 93 94 if strings.IndexFunc(s, shellUnsafeCharNotSpace) == -1 { 95 // No escaping necessary 96 return s 97 } 98 99 return `'` + singleQuoteReplacer.Replace(s) + `'` 100} 101 102// ShellEscapeIncludingSpaces escapes the input `s` in a similar way to ShellEscape except that 103// this treats spaces as meaningful characters. 104func ShellEscapeIncludingSpaces(s string) string { 105 if strings.IndexFunc(s, shellUnsafeChar) == -1 { 106 // No escaping necessary 107 return s 108 } 109 110 return `'` + singleQuoteReplacer.Replace(s) + `'` 111} 112 113func NinjaAndShellEscapeList(slice []string) []string { 114 return ShellEscapeList(NinjaEscapeList(slice)) 115} 116 117func NinjaAndShellEscapeListIncludingSpaces(slice []string) []string { 118 return ShellEscapeListIncludingSpaces(NinjaEscapeList(slice)) 119} 120 121func NinjaAndShellEscape(s string) string { 122 return ShellEscape(NinjaEscape(s)) 123} 124 125var singleQuoteReplacer = strings.NewReplacer(`'`, `'\''`) 126