Home | History | Annotate | Download | only in proptools
      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 
     15 package proptools
     16 
     17 import "strings"
     18 
     19 // NinjaEscape 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.
     24 func NinjaEscape(slice []string) []string {
     25 	slice = append([]string(nil), slice...)
     26 	for i, s := range slice {
     27 		slice[i] = ninjaEscaper.Replace(s)
     28 	}
     29 	return slice
     30 }
     31 
     32 var ninjaEscaper = strings.NewReplacer(
     33 	"$", "$$")
     34 
     35 // ShellEscape takes a slice of strings that may contain characters that are meaningful to bash and
     36 // escapes if necessary by wrapping them in single quotes, and replacing internal single quotes with
     37 // '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single
     38 // quote, and then a single quote to restarting quoting.  A new slice containing the escaped strings
     39 // is returned.
     40 func ShellEscape(slice []string) []string {
     41 	shellUnsafeChar := func(r rune) bool {
     42 		switch {
     43 		case 'A' <= r && r <= 'Z',
     44 			'a' <= r && r <= 'z',
     45 			'0' <= r && r <= '9',
     46 			r == '_',
     47 			r == '+',
     48 			r == '-',
     49 			r == '=',
     50 			r == '.',
     51 			r == ',',
     52 			r == '/',
     53 			r == ' ':
     54 			return false
     55 		default:
     56 			return true
     57 		}
     58 	}
     59 
     60 	slice = append([]string(nil), slice...)
     61 
     62 	for i, s := range slice {
     63 		if strings.IndexFunc(s, shellUnsafeChar) == -1 {
     64 			// No escaping necessary
     65 			continue
     66 		}
     67 
     68 		slice[i] = `'` + singleQuoteReplacer.Replace(s) + `'`
     69 	}
     70 	return slice
     71 
     72 }
     73 
     74 func NinjaAndShellEscape(slice []string) []string {
     75 	return ShellEscape(NinjaEscape(slice))
     76 }
     77 
     78 var singleQuoteReplacer = strings.NewReplacer(`'`, `'\''`)
     79