diff --git a/vendor/github.com/hashicorp/go-discover/README.md b/vendor/github.com/hashicorp/go-discover/README.md index 15646f7b2..5375ed3df 100644 --- a/vendor/github.com/hashicorp/go-discover/README.md +++ b/vendor/github.com/hashicorp/go-discover/README.md @@ -5,16 +5,20 @@ ip addresses of nodes in cloud environments based on meta information like tags provided by the environment. -The configuration for the providers is provided as a list of `key=val -key=val ...` tuples where the values can be URL encoded. The provider is -determined through the `provider` key. Effectively, only spaces have to -be encoded with a `+` and on the command line you have to observe -quoting rules with your shell. +The configuration for the providers is provided as a list of `key=val key=val +...` tuples. If either the key or the value contains a space (` `), a backslash +(`\`) or double quotes (`"`) then it needs to be quoted with double quotes. +Within a quoted string you can use the backslash to escape double quotes or the +backslash itself, e.g. `key=val "some key"="some value"` + +Duplicate keys are reported as error and the provider is determined through the +`provider` key. ### Supported Providers The following cloud providers have implementations in the go-discover/provider -sub packages. Additional providers can be added through the [Register](https://godoc.org/github.com/hashicorp/go-discover#Register) +sub packages. Additional providers can be added through the +[Register](https://godoc.org/github.com/hashicorp/go-discover#Register) function. * Amazon AWS [Config options](http://godoc.org/github.com/hashicorp/go-discover/provider/aws) diff --git a/vendor/github.com/hashicorp/go-discover/config.go b/vendor/github.com/hashicorp/go-discover/config.go index e9e4ff05d..1853c9fa1 100644 --- a/vendor/github.com/hashicorp/go-discover/config.go +++ b/vendor/github.com/hashicorp/go-discover/config.go @@ -2,8 +2,8 @@ package discover import ( "fmt" - "net/url" "sort" + "strconv" "strings" ) @@ -11,28 +11,12 @@ import ( // functions to use. type Config map[string]string -// Parse parses a "key=val key=val ..." string into -// a config map. Values are URL escaped. +// Parse parses a "key=val key=val ..." string into a config map. Keys +// and values which contain spaces, backslashes or double-quotes must be +// quoted with double quotes. Use the backslash to escape special +// characters within quoted strings, e.g. "some key"="some \"value\"". func Parse(s string) (Config, error) { - s = strings.TrimSpace(s) - if s == "" { - return nil, nil - } - - c := Config{} - for _, v := range strings.Fields(s) { - p := strings.SplitN(v, "=", 2) - if len(p) != 2 { - return nil, fmt.Errorf("invalid format: %s", v) - } - key := p[0] - val, err := url.QueryUnescape(p[1]) - if err != nil { - return nil, fmt.Errorf("invalid format: %s", v) - } - c[key] = val - } - return c, nil + return parse(s) } // String formats a config map into the "key=val key=val ..." @@ -48,14 +32,195 @@ func (c Config) String() string { sort.Strings(keys) keys = append([]string{"provider"}, keys...) + quote := func(s string) string { + if strings.ContainsAny(s, ` "\`) { + return strconv.Quote(s) + } + return s + } + var vals []string for _, k := range keys { v := c[k] if v == "" { continue } - v = k + "=" + url.QueryEscape(v) - vals = append(vals, v) + vals = append(vals, quote(k)+"="+quote(v)) } return strings.Join(vals, " ") } + +func parse(in string) (Config, error) { + m := Config{} + s := []rune(strings.TrimSpace(in)) + state := stateKey + key := "" + for { + // exit condition + if len(s) == 0 { + break + } + + // get the next token + item, val, n := lex(s) + s = s[n:] + // fmt.Printf("parse: state: %q item: %q val: '%s' n: %d rest: '%s'\n", state, item, val, n, string(s)) + + switch state { + + case stateKey: + switch item { + case itemText: + key = val + if _, exists := m[key]; exists { + return nil, fmt.Errorf("%s: duplicate key", key) + } + state = stateEqual + default: + return nil, fmt.Errorf("%s: %s", key, val) + } + + case stateEqual: + switch item { + case itemEqual: + state = stateVal + default: + return nil, fmt.Errorf("%s: missing '='", key) + } + + case stateVal: + switch item { + case itemText: + m[key] = val + state = stateKey + case itemError: + return nil, fmt.Errorf("%s: %s", key, val) + default: + return nil, fmt.Errorf("%s: missing value", key) + } + } + } + + //fmt.Printf("parse: state: %q rest: '%s'\n", state, string(s)) + switch state { + case stateEqual: + return nil, fmt.Errorf("%s: missing '='", key) + case stateVal: + return nil, fmt.Errorf("%s: missing value", key) + } + if len(m) == 0 { + return nil, nil + } + return m, nil +} + +type itemType string + +const ( + itemText itemType = "TEXT" + itemEqual = "EQUAL" + itemError = "ERROR" +) + +func (t itemType) String() string { + return string(t) +} + +type state string + +const ( + + // lexer states + stateStart state = "start" + stateEqual = "equal" + stateText = "text" + stateQText = "qtext" + stateQTextEnd = "qtextend" + stateQTextEsc = "qtextesc" + + // parser states + stateKey = "key" + stateVal = "val" +) + +func lex(s []rune) (itemType, string, int) { + isEqual := func(r rune) bool { return r == '=' } + isEscape := func(r rune) bool { return r == '\\' } + isQuote := func(r rune) bool { return r == '"' } + isSpace := func(r rune) bool { return r == ' ' } + + unquote := func(r []rune) (string, error) { + v := strings.TrimSpace(string(r)) + return strconv.Unquote(v) + } + + var quote rune + state := stateStart + for i, r := range s { + // fmt.Println("lex:", "i:", i, "r:", string(r), "state:", string(state), "head:", string(s[:i]), "tail:", string(s[i:])) + switch state { + case stateStart: + switch { + case isSpace(r): + // state = stateStart + case isEqual(r): + state = stateEqual + case isQuote(r): + quote = r + state = stateQText + default: + state = stateText + } + + case stateEqual: + return itemEqual, "", i + + case stateText: + switch { + case isEqual(r) || isSpace(r): + v := strings.TrimSpace(string(s[:i])) + return itemText, v, i + default: + // state = stateText + } + + case stateQText: + switch { + case r == quote: + state = stateQTextEnd + case isEscape(r): + state = stateQTextEsc + default: + // state = stateQText + } + + case stateQTextEsc: + state = stateQText + + case stateQTextEnd: + v, err := unquote(s[:i]) + if err != nil { + return itemError, err.Error(), i + } + return itemText, v, i + } + } + + // fmt.Println("lex:", "state:", string(state)) + switch state { + case stateEqual: + return itemEqual, "", len(s) + case stateQText: + return itemError, "unbalanced quotes", len(s) + case stateQTextEsc: + return itemError, "unterminated escape sequence", len(s) + case stateQTextEnd: + v, err := unquote(s) + if err != nil { + return itemError, err.Error(), len(s) + } + return itemText, v, len(s) + default: + return itemText, strings.TrimSpace(string(s)), len(s) + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 21164a299..40878b5c2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -25,7 +25,7 @@ {"path":"github.com/hashicorp/errwrap","checksumSHA1":"cdOCt0Yb+hdErz8NAQqayxPmRsY=","revision":"7554cd9344cec97297fa6649b055a8c98c2a1e55","revisionTime":"2014-10-28T05:47:10Z"}, {"path":"github.com/hashicorp/go-checkpoint","checksumSHA1":"nd3S1qkFv7zZxA9be0bw4nT0pe0=","revision":"e4b2dc34c0f698ee04750bf2035d8b9384233e1b","revisionTime":"2015-10-22T18:15:14Z"}, {"path":"github.com/hashicorp/go-cleanhttp","checksumSHA1":"b8F628srIitj5p7Y130xc9k0QWs=","revision":"3573b8b52aa7b37b9358d966a898feb387f62437","revisionTime":"2017-02-11T01:34:15Z"}, - {"path":"github.com/hashicorp/go-discover","checksumSHA1":"uNoQWG5h2hzWHjaLi376ZXVaCr4=","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z"}, + {"path":"github.com/hashicorp/go-discover","checksumSHA1":"tWQmb13hmUBoWi0ZHnc4CLBqRc0=","revision":"745a463b035b25a4d0c12d056e208e3850fdd1be","revisionTime":"2017-10-04T16:46:45Z"}, {"path":"github.com/hashicorp/go-discover/provider/aws","checksumSHA1":"lyPRg8aZKgGiNkMILk/VKwOqMy4=","revision":"25e4565347de14cea0a0e0730374c9fcffa7bab0","revisionTime":"2017-09-25T01:06:15Z","tree":true}, {"path":"github.com/hashicorp/go-discover/provider/azure","checksumSHA1":"r97P32e+VmNMh2vwLkZa1zPEDQU=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true}, {"path":"github.com/hashicorp/go-discover/provider/gce","checksumSHA1":"KC/MepQsQF17904UShiM61jmaEs=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},