mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #32601 from dnephin/builder-shell-words-buffer
Use a bytes.Buffer for shell_words string concat
This commit is contained in:
commit
41f4c3cf7e
1 changed files with 85 additions and 98 deletions
|
@ -7,10 +7,12 @@ package dockerfile
|
||||||
// be added by adding code to the "special ${} format processing" section
|
// be added by adding code to the "special ${} format processing" section
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"bytes"
|
||||||
"strings"
|
"strings"
|
||||||
"text/scanner"
|
"text/scanner"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type shellWord struct {
|
type shellWord struct {
|
||||||
|
@ -105,7 +107,7 @@ func (w *wordsStruct) getWords() []string {
|
||||||
// Process the word, starting at 'pos', and stop when we get to the
|
// Process the word, starting at 'pos', and stop when we get to the
|
||||||
// end of the word or the 'stopChar' character
|
// end of the word or the 'stopChar' character
|
||||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||||
var result string
|
var result bytes.Buffer
|
||||||
var words wordsStruct
|
var words wordsStruct
|
||||||
|
|
||||||
var charFuncMapping = map[rune]func() (string, error){
|
var charFuncMapping = map[rune]func() (string, error){
|
||||||
|
@ -127,7 +129,7 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", []string{}, err
|
return "", []string{}, err
|
||||||
}
|
}
|
||||||
result += tmp
|
result.WriteString(tmp)
|
||||||
|
|
||||||
if ch == rune('$') {
|
if ch == rune('$') {
|
||||||
words.addString(tmp)
|
words.addString(tmp)
|
||||||
|
@ -140,7 +142,6 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||||
|
|
||||||
if ch == sw.escapeToken {
|
if ch == sw.escapeToken {
|
||||||
// '\' (default escape token, but ` allowed) escapes, except end of line
|
// '\' (default escape token, but ` allowed) escapes, except end of line
|
||||||
|
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
|
|
||||||
if ch == scanner.EOF {
|
if ch == scanner.EOF {
|
||||||
|
@ -152,11 +153,11 @@ func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||||
words.addChar(ch)
|
words.addChar(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
result += string(ch)
|
result.WriteRune(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, words.getWords(), nil
|
return result.String(), words.getWords(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *shellWord) processSingleQuote() (string, error) {
|
func (sw *shellWord) processSingleQuote() (string, error) {
|
||||||
|
@ -169,25 +170,20 @@ func (sw *shellWord) processSingleQuote() (string, error) {
|
||||||
// all the characters (except single quotes, making it impossible to put
|
// all the characters (except single quotes, making it impossible to put
|
||||||
// single-quotes in a single-quoted string).
|
// single-quotes in a single-quoted string).
|
||||||
|
|
||||||
var result string
|
var result bytes.Buffer
|
||||||
|
|
||||||
sw.scanner.Next()
|
sw.scanner.Next()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ch := sw.scanner.Next()
|
ch := sw.scanner.Next()
|
||||||
|
switch ch {
|
||||||
if ch == scanner.EOF {
|
case scanner.EOF:
|
||||||
return "", fmt.Errorf("unexpected end of statement while looking for matching single-quote")
|
return "", errors.New("unexpected end of statement while looking for matching single-quote")
|
||||||
|
case '\'':
|
||||||
|
return result.String(), nil
|
||||||
}
|
}
|
||||||
|
result.WriteRune(ch)
|
||||||
if ch == '\'' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
result += string(ch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *shellWord) processDoubleQuote() (string, error) {
|
func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||||
|
@ -203,116 +199,107 @@ func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||||
// $ ` " \ <newline>.
|
// $ ` " \ <newline>.
|
||||||
// Otherwise it remains literal.
|
// Otherwise it remains literal.
|
||||||
|
|
||||||
var result string
|
var result bytes.Buffer
|
||||||
|
|
||||||
sw.scanner.Next()
|
sw.scanner.Next()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ch := sw.scanner.Peek()
|
switch sw.scanner.Peek() {
|
||||||
|
case scanner.EOF:
|
||||||
if ch == scanner.EOF {
|
return "", errors.New("unexpected end of statement while looking for matching double-quote")
|
||||||
return "", fmt.Errorf("unexpected end of statement while looking for matching double-quote")
|
case '"':
|
||||||
}
|
|
||||||
|
|
||||||
if ch == '"' {
|
|
||||||
sw.scanner.Next()
|
sw.scanner.Next()
|
||||||
break
|
return result.String(), nil
|
||||||
}
|
case '$':
|
||||||
|
value, err := sw.processDollar()
|
||||||
if ch == '$' {
|
|
||||||
tmp, err := sw.processDollar()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
result += tmp
|
result.WriteString(value)
|
||||||
} else {
|
default:
|
||||||
ch = sw.scanner.Next()
|
ch := sw.scanner.Next()
|
||||||
if ch == sw.escapeToken {
|
if ch == sw.escapeToken {
|
||||||
chNext := sw.scanner.Peek()
|
switch sw.scanner.Peek() {
|
||||||
|
case scanner.EOF:
|
||||||
if chNext == scanner.EOF {
|
|
||||||
// Ignore \ at end of word
|
// Ignore \ at end of word
|
||||||
continue
|
continue
|
||||||
}
|
case '"', '$', sw.escapeToken:
|
||||||
|
|
||||||
// Note: for now don't do anything special with ` chars.
|
|
||||||
// Not sure what to do with them anyway since we're not going
|
|
||||||
// to execute the text in there (not now anyway).
|
|
||||||
if chNext == '"' || chNext == '$' || chNext == sw.escapeToken {
|
|
||||||
// These chars can be escaped, all other \'s are left as-is
|
// These chars can be escaped, all other \'s are left as-is
|
||||||
|
// Note: for now don't do anything special with ` chars.
|
||||||
|
// Not sure what to do with them anyway since we're not going
|
||||||
|
// to execute the text in there (not now anyway).
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result += string(ch)
|
result.WriteRune(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *shellWord) processDollar() (string, error) {
|
func (sw *shellWord) processDollar() (string, error) {
|
||||||
sw.scanner.Next()
|
sw.scanner.Next()
|
||||||
ch := sw.scanner.Peek()
|
|
||||||
if ch == '{' {
|
|
||||||
sw.scanner.Next()
|
|
||||||
name := sw.processName()
|
|
||||||
ch = sw.scanner.Peek()
|
|
||||||
if ch == '}' {
|
|
||||||
// Normal ${xx} case
|
|
||||||
sw.scanner.Next()
|
|
||||||
return sw.getEnv(name), nil
|
|
||||||
}
|
|
||||||
if ch == ':' {
|
|
||||||
// Special ${xx:...} format processing
|
|
||||||
// Yes it allows for recursive $'s in the ... spot
|
|
||||||
|
|
||||||
sw.scanner.Next() // skip over :
|
|
||||||
modifier := sw.scanner.Next()
|
|
||||||
|
|
||||||
word, _, err := sw.processStopOn('}')
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the current value of the variable in question so we
|
|
||||||
// can use to to determine what to do based on the modifier
|
|
||||||
newValue := sw.getEnv(name)
|
|
||||||
|
|
||||||
switch modifier {
|
|
||||||
case '+':
|
|
||||||
if newValue != "" {
|
|
||||||
newValue = word
|
|
||||||
}
|
|
||||||
return newValue, nil
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
if newValue == "" {
|
|
||||||
newValue = word
|
|
||||||
}
|
|
||||||
return newValue, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("Unsupported modifier (%c) in substitution: %s", modifier, sw.word)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("Missing ':' in substitution: %s", sw.word)
|
|
||||||
}
|
|
||||||
// $xxx case
|
// $xxx case
|
||||||
name := sw.processName()
|
if sw.scanner.Peek() != '{' {
|
||||||
if name == "" {
|
name := sw.processName()
|
||||||
return "$", nil
|
if name == "" {
|
||||||
|
return "$", nil
|
||||||
|
}
|
||||||
|
return sw.getEnv(name), nil
|
||||||
}
|
}
|
||||||
return sw.getEnv(name), nil
|
|
||||||
|
sw.scanner.Next()
|
||||||
|
name := sw.processName()
|
||||||
|
ch := sw.scanner.Peek()
|
||||||
|
if ch == '}' {
|
||||||
|
// Normal ${xx} case
|
||||||
|
sw.scanner.Next()
|
||||||
|
return sw.getEnv(name), nil
|
||||||
|
}
|
||||||
|
if ch == ':' {
|
||||||
|
// Special ${xx:...} format processing
|
||||||
|
// Yes it allows for recursive $'s in the ... spot
|
||||||
|
|
||||||
|
sw.scanner.Next() // skip over :
|
||||||
|
modifier := sw.scanner.Next()
|
||||||
|
|
||||||
|
word, _, err := sw.processStopOn('}')
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the current value of the variable in question so we
|
||||||
|
// can use to to determine what to do based on the modifier
|
||||||
|
newValue := sw.getEnv(name)
|
||||||
|
|
||||||
|
switch modifier {
|
||||||
|
case '+':
|
||||||
|
if newValue != "" {
|
||||||
|
newValue = word
|
||||||
|
}
|
||||||
|
return newValue, nil
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
if newValue == "" {
|
||||||
|
newValue = word
|
||||||
|
}
|
||||||
|
return newValue, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", errors.Errorf("unsupported modifier (%c) in substitution: %s", modifier, sw.word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.Errorf("missing ':' in substitution: %s", sw.word)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *shellWord) processName() string {
|
func (sw *shellWord) processName() string {
|
||||||
// Read in a name (alphanumeric or _)
|
// Read in a name (alphanumeric or _)
|
||||||
// If it starts with a numeric then just return $#
|
// If it starts with a numeric then just return $#
|
||||||
var name string
|
var name bytes.Buffer
|
||||||
|
|
||||||
for sw.scanner.Peek() != scanner.EOF {
|
for sw.scanner.Peek() != scanner.EOF {
|
||||||
ch := sw.scanner.Peek()
|
ch := sw.scanner.Peek()
|
||||||
if len(name) == 0 && unicode.IsDigit(ch) {
|
if name.Len() == 0 && unicode.IsDigit(ch) {
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
return string(ch)
|
return string(ch)
|
||||||
}
|
}
|
||||||
|
@ -320,10 +307,10 @@ func (sw *shellWord) processName() string {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ch = sw.scanner.Next()
|
ch = sw.scanner.Next()
|
||||||
name += string(ch)
|
name.WriteRune(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return name
|
return name.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sw *shellWord) getEnv(name string) string {
|
func (sw *shellWord) getEnv(name string) string {
|
||||||
|
|
Loading…
Add table
Reference in a new issue