Merge pull request #124 from colincross/escape
Add proptools functions to escape strings
This commit is contained in:
commit
888b21f87b
4 changed files with 212 additions and 5 deletions
|
@ -69,12 +69,14 @@ bootstrap_go_package(
|
|||
pkgPath = "github.com/google/blueprint/proptools",
|
||||
srcs = [
|
||||
"proptools/clone.go",
|
||||
"proptools/escape.go",
|
||||
"proptools/extend.go",
|
||||
"proptools/proptools.go",
|
||||
"proptools/typeequal.go",
|
||||
],
|
||||
testSrcs = [
|
||||
"proptools/clone_test.go",
|
||||
"proptools/escape_test.go",
|
||||
"proptools/extend_test.go",
|
||||
"proptools/typeequal_test.go",
|
||||
],
|
||||
|
|
|
@ -73,7 +73,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:83:1
|
||||
# Defined: Blueprints:85:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
||||
|
@ -100,7 +100,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:102:1
|
||||
# Defined: Blueprints:104:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
|
@ -173,6 +173,7 @@ default $
|
|||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
: g.bootstrap.compile ${g.bootstrap.srcDir}/proptools/clone.go $
|
||||
${g.bootstrap.srcDir}/proptools/escape.go $
|
||||
${g.bootstrap.srcDir}/proptools/extend.go $
|
||||
${g.bootstrap.srcDir}/proptools/proptools.go $
|
||||
${g.bootstrap.srcDir}/proptools/typeequal.go | $
|
||||
|
@ -186,7 +187,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:135:1
|
||||
# Defined: Blueprints:137:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
||||
|
@ -209,7 +210,7 @@ default ${g.bootstrap.BinDir}/gotestmain
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:140:1
|
||||
# Defined: Blueprints:142:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
||||
|
@ -232,7 +233,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:114:1
|
||||
# Defined: Blueprints:116:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
|
||||
|
|
78
proptools/escape.go
Normal file
78
proptools/escape.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import "strings"
|
||||
|
||||
// NinjaEscape takes a slice of strings that may contain characters that are meaningful to ninja
|
||||
// ($), and escapes each string so they will be passed to bash. It is not necessary on input,
|
||||
// output, or dependency names, those are handled by ModuleContext.Build. It is generally required
|
||||
// on strings from properties in Blueprint files that are used as Args to ModuleContext.Build. A
|
||||
// new slice containing the escaped strings is returned.
|
||||
func NinjaEscape(slice []string) []string {
|
||||
slice = append([]string(nil), slice...)
|
||||
for i, s := range slice {
|
||||
slice[i] = ninjaEscaper.Replace(s)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
var ninjaEscaper = strings.NewReplacer(
|
||||
"$", "$$")
|
||||
|
||||
// ShellEscape takes a slice of strings that may contain characters that are meaningful to bash and
|
||||
// escapes if necessary by wrapping them in single quotes, and replacing internal single quotes with
|
||||
// '\'' (one single quote to end the quoting, a shell-escaped single quote to insert a real single
|
||||
// quote, and then a single quote to restarting quoting. A new slice containing the escaped strings
|
||||
// is returned.
|
||||
func ShellEscape(slice []string) []string {
|
||||
shellUnsafeChar := func(r rune) bool {
|
||||
switch {
|
||||
case 'A' <= r && r <= 'Z',
|
||||
'a' <= r && r <= 'z',
|
||||
'0' <= r && r <= '9',
|
||||
r == '_',
|
||||
r == '+',
|
||||
r == '-',
|
||||
r == '=',
|
||||
r == '.',
|
||||
r == ',',
|
||||
r == '/',
|
||||
r == ' ':
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
slice = append([]string(nil), slice...)
|
||||
|
||||
for i, s := range slice {
|
||||
if strings.IndexFunc(s, shellUnsafeChar) == -1 {
|
||||
// No escaping necessary
|
||||
continue
|
||||
}
|
||||
|
||||
slice[i] = `'` + singleQuoteReplacer.Replace(s) + `'`
|
||||
}
|
||||
return slice
|
||||
|
||||
}
|
||||
|
||||
func NinjaAndShellEscape(slice []string) []string {
|
||||
return ShellEscape(NinjaEscape(slice))
|
||||
}
|
||||
|
||||
var singleQuoteReplacer = strings.NewReplacer(`'`, `'\''`)
|
126
proptools/escape_test.go
Normal file
126
proptools/escape_test.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type escapeTestCase struct {
|
||||
name string
|
||||
in string
|
||||
out string
|
||||
}
|
||||
|
||||
var ninjaEscapeTestCase = []escapeTestCase{
|
||||
{
|
||||
name: "no escaping",
|
||||
in: `test`,
|
||||
out: `test`,
|
||||
},
|
||||
{
|
||||
name: "leading $",
|
||||
in: `$test`,
|
||||
out: `$$test`,
|
||||
},
|
||||
{
|
||||
name: "trailing $",
|
||||
in: `test$`,
|
||||
out: `test$$`,
|
||||
},
|
||||
{
|
||||
name: "leading and trailing $",
|
||||
in: `$test$`,
|
||||
out: `$$test$$`,
|
||||
},
|
||||
}
|
||||
|
||||
var shellEscapeTestCase = []escapeTestCase{
|
||||
{
|
||||
name: "no escaping",
|
||||
in: `test`,
|
||||
out: `test`,
|
||||
},
|
||||
{
|
||||
name: "leading $",
|
||||
in: `$test`,
|
||||
out: `'$test'`,
|
||||
},
|
||||
{
|
||||
name: "trailing $",
|
||||
in: `test$`,
|
||||
out: `'test$'`,
|
||||
},
|
||||
{
|
||||
name: "leading and trailing $",
|
||||
in: `$test$`,
|
||||
out: `'$test$'`,
|
||||
},
|
||||
{
|
||||
name: "single quote",
|
||||
in: `'`,
|
||||
out: `''\'''`,
|
||||
},
|
||||
{
|
||||
name: "multiple single quote",
|
||||
in: `''`,
|
||||
out: `''\'''\'''`,
|
||||
},
|
||||
{
|
||||
name: "double quote",
|
||||
in: `""`,
|
||||
out: `'""'`,
|
||||
},
|
||||
{
|
||||
name: "ORIGIN",
|
||||
in: `-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs`,
|
||||
out: `'-Wl,--rpath,${ORIGIN}/../bionic-loader-test-libs'`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestNinjaEscaping(t *testing.T) {
|
||||
for _, testCase := range ninjaEscapeTestCase {
|
||||
got := NinjaEscape([]string{testCase.in})[0]
|
||||
if got != testCase.out {
|
||||
t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestShellEscaping(t *testing.T) {
|
||||
for _, testCase := range shellEscapeTestCase {
|
||||
got := ShellEscape([]string{testCase.in})[0]
|
||||
if got != testCase.out {
|
||||
t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.out, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExternalShellEscaping(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
for _, testCase := range shellEscapeTestCase {
|
||||
cmd := "echo -n " + ShellEscape([]string{testCase.in})[0]
|
||||
got, err := exec.Command("/bin/sh", "-c", cmd).Output()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if string(got) != testCase.in {
|
||||
t.Errorf("%s: expected `%s` got `%s`", testCase.name, testCase.in, got)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue