diff --git a/Gopkg.lock b/Gopkg.lock index 2f1a54dd..a9040478 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -4,14 +4,14 @@ [[projects]] name = "github.com/Masterminds/semver" packages = ["."] - revision = "517734cc7d6470c0d07130e40fd40bdeb9bcd3fd" - version = "v1.3.1" + revision = "15d8430ab86497c5c0da827b748823945e1cf1e1" + version = "v1.4.0" [[projects]] branch = "master" name = "github.com/Masterminds/sprig" packages = ["."] - revision = "175e437013029f9a1c35bdf04bc451b0d20d4331" + revision = "82f6f19d47b416d27ae039939b44afaa0575860e" [[projects]] name = "github.com/aokoli/goutils" @@ -29,19 +29,19 @@ branch = "master" name = "github.com/huandu/xstrings" packages = ["."] - revision = "3959339b333561bf62a38b424fd41517c2c90f40" + revision = "d6590c0c31d16526217fa60fbd2067f7afcd78c5" [[projects]] - branch = "master" name = "github.com/imdario/mergo" packages = ["."] - revision = "e3000cb3d28c72b837601cac94debd91032d19fe" + revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874" + version = "0.2.4" [[projects]] branch = "master" name = "github.com/mattn/go-zglob" packages = [".","fastwalk"] - revision = "95345c4e1c0ebc9d16a3284177f09360f4d20fab" + revision = "4ecb59231939b2e499b1f2fd8f075565977d2452" [[projects]] name = "github.com/pmezard/go-difflib" @@ -65,7 +65,7 @@ branch = "master" name = "github.com/spf13/pflag" packages = ["."] - revision = "7aff26db30c1be810f9de5038ec5ef96ac41fd7c" + revision = "a9789e855c7696159b7db0db7f440b449edf2b31" [[projects]] name = "github.com/stretchr/testify" @@ -77,19 +77,19 @@ branch = "master" name = "golang.org/x/crypto" packages = ["pbkdf2","scrypt"] - revision = "81e90905daefcd6fd217b62423c0908922eadb30" + revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3" [[projects]] branch = "master" name = "golang.org/x/net" packages = ["context"] - revision = "66aacef3dd8a676686c7ae3716979581e8b03c47" + revision = "a04bdaca5b32abe1c069418fb7088ae607de5bd0" [[projects]] branch = "master" name = "golang.org/x/sync" packages = ["errgroup"] - revision = "f52d1811a62927559de87708c8913c1650ce4f26" + revision = "8e0aa688b654ef28caa72506fa5ec8dba9fc7690" [[projects]] branch = "v2" @@ -101,7 +101,7 @@ branch = "master" name = "mvdan.cc/sh" packages = ["interp","syntax"] - revision = "d8d2c36c06455d4bb8e2116cc2b955271046329d" + revision = "24a28656b7c1d2532406e69527366ecf9f455a17" [solve-meta] analyzer-name = "dep" diff --git a/Taskfile.yml b/Taskfile.yml index f6727fdb..4f3b5605 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -16,6 +16,7 @@ dl-deps: update-deps: desc: Updates dependencies cmds: + - dep ensure - dep ensure -update - dep prune diff --git a/execext/exec.go b/execext/exec.go index 6d1304cc..9e6ebb46 100644 --- a/execext/exec.go +++ b/execext/exec.go @@ -41,9 +41,13 @@ func RunCommand(opts *RunCommandOptions) error { Context: opts.Context, Dir: opts.Dir, Env: opts.Env, - Stdin: opts.Stdin, - Stdout: opts.Stdout, - Stderr: opts.Stderr, + + Exec: interp.DefaultExec, + Open: interp.OpenDevImpls(interp.DefaultOpen), + + Stdin: opts.Stdin, + Stdout: opts.Stdout, + Stderr: opts.Stderr, } if err = r.Reset(); err != nil { return err diff --git a/vendor/github.com/Masterminds/semver/CHANGELOG.md b/vendor/github.com/Masterminds/semver/CHANGELOG.md index 520cc85e..ba84c546 100644 --- a/vendor/github.com/Masterminds/semver/CHANGELOG.md +++ b/vendor/github.com/Masterminds/semver/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.4.0 (2017-10-04) + +## Changed +- #61: Update NewVersion to parse ints with a 64bit int size (thanks @zknill) + # 1.3.1 (2017-07-10) ## Fixed diff --git a/vendor/github.com/Masterminds/semver/version.go b/vendor/github.com/Masterminds/semver/version.go index 7a1ed774..b29c593f 100644 --- a/vendor/github.com/Masterminds/semver/version.go +++ b/vendor/github.com/Masterminds/semver/version.go @@ -64,14 +64,14 @@ func NewVersion(v string) (*Version, error) { } var temp int64 - temp, err := strconv.ParseInt(m[1], 10, 32) + temp, err := strconv.ParseInt(m[1], 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } sv.major = temp if m[2] != "" { - temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 32) + temp, err = strconv.ParseInt(strings.TrimPrefix(m[2], "."), 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } @@ -81,7 +81,7 @@ func NewVersion(v string) (*Version, error) { } if m[3] != "" { - temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 32) + temp, err = strconv.ParseInt(strings.TrimPrefix(m[3], "."), 10, 64) if err != nil { return nil, fmt.Errorf("Error parsing version segment: %s", err) } diff --git a/vendor/github.com/Masterminds/sprig/crypto.go b/vendor/github.com/Masterminds/sprig/crypto.go index a935b6c1..d1b46500 100644 --- a/vendor/github.com/Masterminds/sprig/crypto.go +++ b/vendor/github.com/Masterminds/sprig/crypto.go @@ -10,12 +10,16 @@ import ( "crypto/rsa" "crypto/sha256" "crypto/x509" + "crypto/x509/pkix" "encoding/asn1" "encoding/binary" "encoding/hex" "encoding/pem" + "errors" "fmt" "math/big" + "net" + "time" uuid "github.com/satori/go.uuid" "golang.org/x/crypto/scrypt" @@ -146,3 +150,231 @@ func pemBlockForKey(priv interface{}) *pem.Block { return nil } } + +type certificate struct { + Cert string + Key string +} + +func generateCertificateAuthority( + cn string, + daysValid int, +) (certificate, error) { + ca := certificate{} + + template, err := getBaseCertTemplate(cn, nil, nil, daysValid) + if err != nil { + return ca, err + } + // Override KeyUsage and IsCA + template.KeyUsage = x509.KeyUsageKeyEncipherment | + x509.KeyUsageDigitalSignature | + x509.KeyUsageCertSign + template.IsCA = true + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return ca, fmt.Errorf("error generating rsa key: %s", err) + } + + ca.Cert, ca.Key, err = getCertAndKey(template, priv, template, priv) + if err != nil { + return ca, err + } + + return ca, nil +} + +func generateSelfSignedCertificate( + cn string, + ips []interface{}, + alternateDNS []interface{}, + daysValid int, +) (certificate, error) { + cert := certificate{} + + template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid) + if err != nil { + return cert, err + } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return cert, fmt.Errorf("error generating rsa key: %s", err) + } + + cert.Cert, cert.Key, err = getCertAndKey(template, priv, template, priv) + if err != nil { + return cert, err + } + + return cert, nil +} + +func generateSignedCertificate( + cn string, + ips []interface{}, + alternateDNS []interface{}, + daysValid int, + ca certificate, +) (certificate, error) { + cert := certificate{} + + decodedSignerCert, _ := pem.Decode([]byte(ca.Cert)) + if decodedSignerCert == nil { + return cert, errors.New("unable to decode certificate") + } + signerCert, err := x509.ParseCertificate(decodedSignerCert.Bytes) + if err != nil { + return cert, fmt.Errorf( + "error parsing certificate: decodedSignerCert.Bytes: %s", + err, + ) + } + decodedSignerKey, _ := pem.Decode([]byte(ca.Key)) + if decodedSignerKey == nil { + return cert, errors.New("unable to decode key") + } + signerKey, err := x509.ParsePKCS1PrivateKey(decodedSignerKey.Bytes) + if err != nil { + return cert, fmt.Errorf( + "error parsing prive key: decodedSignerKey.Bytes: %s", + err, + ) + } + + template, err := getBaseCertTemplate(cn, ips, alternateDNS, daysValid) + if err != nil { + return cert, err + } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return cert, fmt.Errorf("error generating rsa key: %s", err) + } + + cert.Cert, cert.Key, err = getCertAndKey( + template, + priv, + signerCert, + signerKey, + ) + if err != nil { + return cert, err + } + + return cert, nil +} + +func getCertAndKey( + template *x509.Certificate, + signeeKey *rsa.PrivateKey, + parent *x509.Certificate, + signingKey *rsa.PrivateKey, +) (string, string, error) { + derBytes, err := x509.CreateCertificate( + rand.Reader, + template, + parent, + &signeeKey.PublicKey, + signingKey, + ) + if err != nil { + return "", "", fmt.Errorf("error creating certificate: %s", err) + } + + certBuffer := bytes.Buffer{} + if err := pem.Encode( + &certBuffer, + &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}, + ); err != nil { + return "", "", fmt.Errorf("error pem-encoding certificate: %s", err) + } + + keyBuffer := bytes.Buffer{} + if err := pem.Encode( + &keyBuffer, + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(signeeKey), + }, + ); err != nil { + return "", "", fmt.Errorf("error pem-encoding key: %s", err) + } + + return string(certBuffer.Bytes()), string(keyBuffer.Bytes()), nil +} + +func getBaseCertTemplate( + cn string, + ips []interface{}, + alternateDNS []interface{}, + daysValid int, +) (*x509.Certificate, error) { + ipAddresses, err := getNetIPs(ips) + if err != nil { + return nil, err + } + dnsNames, err := getAlternateDNSStrs(alternateDNS) + if err != nil { + return nil, err + } + return &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: cn, + }, + IPAddresses: ipAddresses, + DNSNames: dnsNames, + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(daysValid)), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{ + x509.ExtKeyUsageServerAuth, + x509.ExtKeyUsageClientAuth, + }, + BasicConstraintsValid: true, + }, nil +} + +func getNetIPs(ips []interface{}) ([]net.IP, error) { + if ips == nil { + return []net.IP{}, nil + } + var ipStr string + var ok bool + var netIP net.IP + netIPs := make([]net.IP, len(ips)) + for i, ip := range ips { + ipStr, ok = ip.(string) + if !ok { + return nil, fmt.Errorf("error parsing ip: %v is not a string", ip) + } + netIP = net.ParseIP(ipStr) + if netIP == nil { + return nil, fmt.Errorf("error parsing ip: %s", ipStr) + } + netIPs[i] = netIP + } + return netIPs, nil +} + +func getAlternateDNSStrs(alternateDNS []interface{}) ([]string, error) { + if alternateDNS == nil { + return []string{}, nil + } + var dnsStr string + var ok bool + alternateDNSStrs := make([]string, len(alternateDNS)) + for i, dns := range alternateDNS { + dnsStr, ok = dns.(string) + if !ok { + return nil, fmt.Errorf( + "error processing alternate dns name: %v is not a string", + dns, + ) + } + alternateDNSStrs[i] = dnsStr + } + return alternateDNSStrs, nil +} diff --git a/vendor/github.com/Masterminds/sprig/date.go b/vendor/github.com/Masterminds/sprig/date.go index e72a66bd..90bde036 100644 --- a/vendor/github.com/Masterminds/sprig/date.go +++ b/vendor/github.com/Masterminds/sprig/date.go @@ -69,3 +69,8 @@ func dateAgo(date interface{}) string { duration := time.Since(t) / time.Second * time.Second return duration.String() } + +func toDate(fmt, str string) time.Time { + t, _ := time.ParseInLocation(fmt, str, time.Local) + return t +} diff --git a/vendor/github.com/Masterminds/sprig/doc.go b/vendor/github.com/Masterminds/sprig/doc.go index 7b7eefd4..91ba4dc8 100644 --- a/vendor/github.com/Masterminds/sprig/doc.go +++ b/vendor/github.com/Masterminds/sprig/doc.go @@ -89,7 +89,7 @@ Integer Slice Functions: Conversions: - atoi: Convert a string to an integer. 0 if the integer could not be parsed. - - in64: Convert a string or another numeric type to an int64. + - int64: Convert a string or another numeric type to an int64. - int: Convert a string or another numeric type to an int. - float64: Convert a string or another numeric type to a float64. diff --git a/vendor/github.com/Masterminds/sprig/functions.go b/vendor/github.com/Masterminds/sprig/functions.go index c15522c0..e30c0ba2 100644 --- a/vendor/github.com/Masterminds/sprig/functions.go +++ b/vendor/github.com/Masterminds/sprig/functions.go @@ -99,6 +99,7 @@ var genericMap = map[string]interface{}{ "dateInZone": dateInZone, "dateModify": dateModify, "ago": dateAgo, + "toDate": toDate, // Strings "abbrev": abbrev, @@ -251,8 +252,11 @@ var genericMap = map[string]interface{}{ "has": func(needle interface{}, haystack []interface{}) bool { return inList(haystack, needle) }, // Crypto: - "genPrivateKey": generatePrivateKey, - "derivePassword": derivePassword, + "genPrivateKey": generatePrivateKey, + "derivePassword": derivePassword, + "genCA": generateCertificateAuthority, + "genSelfSignedCert": generateSelfSignedCertificate, + "genSignedCert": generateSignedCertificate, // UUIDs: "uuidv4": uuidv4, diff --git a/vendor/github.com/huandu/xstrings/convert.go b/vendor/github.com/huandu/xstrings/convert.go index 78ca34d4..783e73b6 100644 --- a/vendor/github.com/huandu/xstrings/convert.go +++ b/vendor/github.com/huandu/xstrings/convert.go @@ -43,19 +43,26 @@ func ToCamelCase(str string) string { if len(str) == 0 { return buf.String() } - - buf.WriteRune(unicode.ToUpper(r0)) - r0, size = utf8.DecodeRuneInString(str) - str = str[size:] + + r0 = unicode.ToUpper(r0) for len(str) > 0 { r1 = r0 r0, size = utf8.DecodeRuneInString(str) str = str[size:] - if r1 == '_' && r0 != '_' { + if r1 == '_' && r0 == '_' { + buf.WriteRune(r1) + continue + } + + if r1 == '_' { r0 = unicode.ToUpper(r0) } else { + r0 = unicode.ToLower(r0) + } + + if r1 != '_' { buf.WriteRune(r1) } } diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/github.com/imdario/mergo/map.go index 8e8c4ba8..99002565 100644 --- a/vendor/github.com/imdario/mergo/map.go +++ b/vendor/github.com/imdario/mergo/map.go @@ -61,6 +61,13 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over dstMap[fieldName] = src.Field(i).Interface() } } + case reflect.Ptr: + if dst.IsNil() { + v := reflect.New(dst.Type().Elem()) + dst.Set(v) + } + dst = dst.Elem() + fallthrough case reflect.Struct: srcMap := src.Interface().(map[string]interface{}) for key := range srcMap { @@ -85,6 +92,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over srcKind = reflect.Ptr } } + if !srcElement.IsValid() { continue } @@ -92,14 +100,16 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { return } - } else { - if srcKind == reflect.Map { - if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil { - return - } - } else { - return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) + } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface { + if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + return } + } else if srcKind == reflect.Map { + if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + return + } + } else { + return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) } } } diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go index 513774f4..052b9fe7 100644 --- a/vendor/github.com/imdario/mergo/merge.go +++ b/vendor/github.com/imdario/mergo/merge.go @@ -12,6 +12,18 @@ import ( "reflect" ) +func hasExportedField(dst reflect.Value) (exported bool) { + for i, n := 0, dst.NumField(); i < n; i++ { + field := dst.Type().Field(i) + if field.Anonymous { + exported = exported || hasExportedField(dst.Field(i)) + } else { + exported = exported || len(field.PkgPath) == 0 + } + } + return +} + // Traverses recursively both values, assigning src's fields values to dst. // The map argument tracks comparisons that have already been seen, which allows // short circuiting on recursive types. @@ -34,12 +46,22 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov } switch dst.Kind() { case reflect.Struct: - for i, n := 0, dst.NumField(); i < n; i++ { - if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil { - return + if hasExportedField(dst) { + for i, n := 0, dst.NumField(); i < n; i++ { + if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil { + return + } + } + } else { + if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) } } case reflect.Map: + if len(src.MapKeys()) == 0 && !src.IsNil() && len(dst.MapKeys()) == 0 { + dst.Set(reflect.MakeMap(dst.Type())) + return + } for _, key := range src.MapKeys() { srcElement := src.MapIndex(key) if !srcElement.IsValid() { @@ -67,6 +89,10 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov } } } + if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map { + continue + } + if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) @@ -77,6 +103,24 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov case reflect.Ptr: fallthrough case reflect.Interface: + if src.Kind() != reflect.Interface { + if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if src.Kind() == reflect.Ptr { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil { + return + } + } else if dst.Elem().Type() == src.Type() { + if err = deepMerge(dst.Elem(), src, visited, depth+1, overwrite); err != nil { + return + } + } else { + return ErrDifferentArgumentsTypes + } + break + } if src.IsNil() { break } else if dst.IsNil() || overwrite { diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/github.com/imdario/mergo/mergo.go index f8a0991e..79ccdf5c 100644 --- a/vendor/github.com/imdario/mergo/mergo.go +++ b/vendor/github.com/imdario/mergo/mergo.go @@ -45,7 +45,7 @@ func isEmptyValue(v reflect.Value) bool { return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 - case reflect.Interface, reflect.Ptr: + case reflect.Interface, reflect.Ptr, reflect.Func: return v.IsNil() } return false diff --git a/vendor/github.com/mattn/go-zglob/LICENSE b/vendor/github.com/mattn/go-zglob/LICENSE new file mode 100644 index 00000000..740fa931 --- /dev/null +++ b/vendor/github.com/mattn/go-zglob/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/spf13/pflag/count.go b/vendor/github.com/spf13/pflag/count.go index 250a4381..aa126e44 100644 --- a/vendor/github.com/spf13/pflag/count.go +++ b/vendor/github.com/spf13/pflag/count.go @@ -11,13 +11,13 @@ func newCountValue(val int, p *int) *countValue { } func (i *countValue) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - // -1 means that no specific value was passed, so increment - if v == -1 { + // "+1" means that no specific value was passed, so increment + if s == "+1" { *i = countValue(*i + 1) - } else { - *i = countValue(v) + return nil } + v, err := strconv.ParseInt(s, 0, 0) + *i = countValue(v) return err } @@ -54,7 +54,7 @@ func (f *FlagSet) CountVar(p *int, name string, usage string) { // CountVarP is like CountVar only take a shorthand for the flag name. func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) - flag.NoOptDefVal = "-1" + flag.NoOptDefVal = "+1" } // CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go index 7b84e2cd..187e4c9a 100644 --- a/vendor/github.com/spf13/pflag/flag.go +++ b/vendor/github.com/spf13/pflag/flag.go @@ -202,12 +202,18 @@ func sortFlags(flags map[NormalizedName]*Flag) []*Flag { func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { f.normalizeNameFunc = n f.sortedFormal = f.sortedFormal[:0] - for k, v := range f.orderedFormal { - delete(f.formal, NormalizedName(v.Name)) - nname := f.normalizeFlagName(v.Name) - v.Name = string(nname) - f.formal[nname] = v - f.orderedFormal[k] = v + for fname, flag := range f.formal { + nname := f.normalizeFlagName(flag.Name) + if fname == nname { + continue + } + flag.Name = string(nname) + delete(f.formal, fname) + f.formal[nname] = flag + if _, set := f.actual[fname]; set { + delete(f.actual, fname) + f.actual[nname] = flag + } } } @@ -440,13 +446,15 @@ func (f *FlagSet) Set(name, value string) error { return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) } - if f.actual == nil { - f.actual = make(map[NormalizedName]*Flag) - } - f.actual[normalName] = flag - f.orderedActual = append(f.orderedActual, flag) + if !flag.Changed { + if f.actual == nil { + f.actual = make(map[NormalizedName]*Flag) + } + f.actual[normalName] = flag + f.orderedActual = append(f.orderedActual, flag) - flag.Changed = true + flag.Changed = true + } if flag.Deprecated != "" { fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) @@ -664,6 +672,10 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { if flag.NoOptDefVal != "true" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } default: line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } @@ -916,6 +928,9 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin } err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } @@ -966,6 +981,9 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse } err = fn(flag, value) + if err != nil { + f.failf(err.Error()) + } return } diff --git a/vendor/golang.org/x/crypto/scrypt/scrypt.go b/vendor/golang.org/x/crypto/scrypt/scrypt.go index 14375c50..ff28aaef 100644 --- a/vendor/golang.org/x/crypto/scrypt/scrypt.go +++ b/vendor/golang.org/x/crypto/scrypt/scrypt.go @@ -220,9 +220,10 @@ func smix(b []byte, r, N int, v, xy []uint32) { // // dk, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32) // -// The recommended parameters for interactive logins as of 2009 are N=16384, -// r=8, p=1. They should be increased as memory latency and CPU parallelism -// increases. Remember to get a good random salt. +// The recommended parameters for interactive logins as of 2017 are N=32768, r=8 +// and p=1. The parameters N, r, and p should be increased as memory latency and +// CPU parallelism increases; consider setting N to the highest power of 2 you +// can derive within 100 milliseconds. Remember to get a good random salt. func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { if N <= 1 || N&(N-1) != 0 { return nil, errors.New("scrypt: N must be > 1 and a power of 2") diff --git a/vendor/golang.org/x/net/README b/vendor/golang.org/x/net/README deleted file mode 100644 index 6b13d8e5..00000000 --- a/vendor/golang.org/x/net/README +++ /dev/null @@ -1,3 +0,0 @@ -This repository holds supplementary Go networking libraries. - -To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/vendor/golang.org/x/net/README.md b/vendor/golang.org/x/net/README.md new file mode 100644 index 00000000..00a9b6eb --- /dev/null +++ b/vendor/golang.org/x/net/README.md @@ -0,0 +1,16 @@ +# Go Networking + +This repository holds supplementary Go networking libraries. + +## Download/Install + +The easiest way to install is to run `go get -u golang.org/x/net`. You can +also manually git clone the repository to `$GOPATH/src/golang.org/x/net`. + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit +changes to this repository, see https://golang.org/doc/contribute.html. +The main issue tracker for the net repository is located at +https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the +subject line, so it is easy to find. diff --git a/vendor/golang.org/x/sync/README b/vendor/golang.org/x/sync/README deleted file mode 100644 index 59c9dcb4..00000000 --- a/vendor/golang.org/x/sync/README +++ /dev/null @@ -1,2 +0,0 @@ -This repository provides Go concurrency primitives in addition to the -ones provided by the language and "sync" and "sync/atomic" packages. diff --git a/vendor/golang.org/x/sync/README.md b/vendor/golang.org/x/sync/README.md new file mode 100644 index 00000000..1f8436cc --- /dev/null +++ b/vendor/golang.org/x/sync/README.md @@ -0,0 +1,18 @@ +# Go Sync + +This repository provides Go concurrency primitives in addition to the +ones provided by the language and "sync" and "sync/atomic" packages. + +## Download/Install + +The easiest way to install is to run `go get -u golang.org/x/sync`. You can +also manually git clone the repository to `$GOPATH/src/golang.org/x/sync`. + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://golang.org/doc/contribute.html. + +The main issue tracker for the sync repository is located at +https://github.com/golang/go/issues. Prefix your issue with "x/sync:" in the +subject line, so it is easy to find. diff --git a/vendor/mvdan.cc/sh/.travis.yml b/vendor/mvdan.cc/sh/.travis.yml index 9e3d467f..6df9e6cc 100644 --- a/vendor/mvdan.cc/sh/.travis.yml +++ b/vendor/mvdan.cc/sh/.travis.yml @@ -14,3 +14,6 @@ script: - go test -short -race ./... - shfmt -version - goveralls + # at least make sure the builds work + - GOOS=windows go install ./interp + - GOOS=darwin go install ./interp diff --git a/vendor/mvdan.cc/sh/README.md b/vendor/mvdan.cc/sh/README.md index e142be94..4d940d7d 100644 --- a/vendor/mvdan.cc/sh/README.md +++ b/vendor/mvdan.cc/sh/README.md @@ -7,10 +7,6 @@ A shell parser, formatter and interpreter. Supports [POSIX Shell], [Bash] and [mksh]. Requires Go 1.8 or later. -**Please note that the import paths have been moved from -`github.com/mvdan/sh/...` to `mvdan.cc/sh/...` for 2.0.** This will help -future-proof the project by making it depend less on GitHub. - ### shfmt go get -u mvdan.cc/sh/cmd/shfmt diff --git a/vendor/mvdan.cc/sh/interp/arith.go b/vendor/mvdan.cc/sh/interp/arith.go index d12531de..08c1b7ea 100644 --- a/vendor/mvdan.cc/sh/interp/arith.go +++ b/vendor/mvdan.cc/sh/interp/arith.go @@ -36,7 +36,7 @@ func (r *Runner) arithm(expr syntax.ArithmExpr) int { } else { val-- } - r.setVar(name, strconv.Itoa(val)) + r.setVar(name, nil, strconv.Itoa(val)) if x.Post { return old } @@ -108,7 +108,7 @@ func (r *Runner) assgnArit(b *syntax.BinaryArithm) int { case syntax.ShrAssgn: val >>= uint(arg) } - r.setVar(name, strconv.Itoa(val)) + r.setVar(name, nil, strconv.Itoa(val)) return val } diff --git a/vendor/mvdan.cc/sh/interp/builtin.go b/vendor/mvdan.cc/sh/interp/builtin.go index fc1c5e3e..5bb6ecdc 100644 --- a/vendor/mvdan.cc/sh/interp/builtin.go +++ b/vendor/mvdan.cc/sh/interp/builtin.go @@ -19,7 +19,7 @@ func isBuiltin(name string) bool { "echo", "printf", "break", "continue", "pwd", "cd", "wait", "builtin", "trap", "type", "source", ".", "command", "pushd", "popd", "umask", "alias", "unalias", "fg", "bg", - "getopts", "eval", "test", "[": + "getopts", "eval", "test", "[", "exec": return true } return false @@ -161,6 +161,9 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int { if err != nil || !info.IsDir() { return 1 } + if !hasPermissionToDir(info) { + return 1 + } r.Dir = dir case "wait": if len(args) > 0 { @@ -213,7 +216,7 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int { if len(args) < 1 { r.runErr(pos, "source: need filename") } - f, err := os.Open(r.relPath(args[0])) + f, err := r.open(r.relPath(args[0]), os.O_RDONLY, 0, false) if err != nil { r.errf("eval: %v\n", err) return 1 @@ -247,6 +250,18 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int { p.next() expr := p.classicTest("[", false) return oneIf(r.bashTest(expr) == "") + case "exec": + // TODO: Consider syscall.Exec, i.e. actually replacing + // the process. It's in theory what a shell should do, + // but in practice it would kill the entire Go process + // and it's not available on Windows. + if len(args) == 0 { + // TODO: different behavior, apparently + return 0 + } + r.exec(args[0], args[1:]) + r.lastExit() + return r.exit case "trap", "command", "pushd", "popd", "umask", "alias", "unalias", "fg", "bg", "getopts": r.runErr(pos, "unhandled builtin: %s", name) @@ -255,8 +270,8 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int { } func (r *Runner) relPath(path string) string { - if filepath.IsAbs(path) { - return path + if !filepath.IsAbs(path) { + path = filepath.Join(r.Dir, path) } - return filepath.Join(r.Dir, path) + return filepath.Clean(path) } diff --git a/vendor/mvdan.cc/sh/interp/dev.go b/vendor/mvdan.cc/sh/interp/dev.go deleted file mode 100644 index 8e30ce58..00000000 --- a/vendor/mvdan.cc/sh/interp/dev.go +++ /dev/null @@ -1,21 +0,0 @@ -package interp - -import ( - "io" -) - -var _ io.ReadWriteCloser = devNull{} - -type devNull struct{} - -func (devNull) Read(_ []byte) (int, error) { - return 0, io.EOF -} - -func (devNull) Write(p []byte) (int, error) { - return len(p), nil -} - -func (devNull) Close() error { - return nil -} diff --git a/vendor/mvdan.cc/sh/interp/interp.go b/vendor/mvdan.cc/sh/interp/interp.go index 84849069..b45c3323 100644 --- a/vendor/mvdan.cc/sh/interp/interp.go +++ b/vendor/mvdan.cc/sh/interp/interp.go @@ -10,14 +10,12 @@ import ( "io" "math" "os" - "os/exec" "os/user" "path" "path/filepath" "strconv" "strings" "sync" - "syscall" "time" "mvdan.cc/sh/syntax" @@ -48,6 +46,9 @@ type Runner struct { // of vars. Params []string + Exec ModuleExec + Open ModuleOpen + filename string // only if Node was a File // Separate maps, note that bash allows a name to be both a var @@ -78,6 +79,76 @@ type Runner struct { stopOnCmdErr bool // set -e } +// Reset will set the unexported fields back to zero, fill any exported +// fields with their default values if not set, and prepare the runner +// to interpret a program. +// +// This function should be called once before running any node. It can +// be skipped before any following runs to keep internal state, such as +// declared variables. +func (r *Runner) Reset() error { + // reset the internal state + *r = Runner{ + Env: r.Env, + Dir: r.Dir, + Params: r.Params, + Context: r.Context, + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, + Exec: r.Exec, + Open: r.Open, + } + if r.Context == nil { + r.Context = context.Background() + } + if r.Env == nil { + r.Env = os.Environ() + } + r.envMap = make(map[string]string, len(r.Env)) + for _, kv := range r.Env { + i := strings.IndexByte(kv, '=') + if i < 0 { + return fmt.Errorf("env not in the form key=value: %q", kv) + } + name, val := kv[:i], kv[i+1:] + r.envMap[name] = val + } + if _, ok := r.envMap["HOME"]; !ok { + u, _ := user.Current() + r.envMap["HOME"] = u.HomeDir + } + if r.Dir == "" { + dir, err := os.Getwd() + if err != nil { + return fmt.Errorf("could not get current dir: %v", err) + } + r.Dir = dir + } + if r.Exec == nil { + r.Exec = DefaultExec + } + if r.Open == nil { + r.Open = DefaultOpen + } + return nil +} + +func (r *Runner) ctx() Ctxt { + c := Ctxt{ + Context: r.Context, + Env: r.Env, + Dir: r.Dir, + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, + } + for name, val := range r.cmdVars { + c.Env = append(c.Env, name+"="+varStr(val)) + } + return c +} + // varValue can hold a string, an indexed array ([]string) or an // associative array (map[string]string) // TODO: implement associative arrays @@ -136,36 +207,56 @@ func (e RunError) Error() string { return fmt.Sprintf("%s:%s: %s", e.Filename, e.Pos.String(), e.Text) } -func (r *Runner) runErr(pos syntax.Pos, format string, a ...interface{}) { +func (r *Runner) setErr(err error) { if r.err == nil { - r.err = RunError{ - Filename: r.filename, - Pos: pos, - Text: fmt.Sprintf(format, a...), - } + r.err = err } } +func (r *Runner) runErr(pos syntax.Pos, format string, a ...interface{}) { + r.setErr(RunError{ + Filename: r.filename, + Pos: pos, + Text: fmt.Sprintf(format, a...), + }) +} + func (r *Runner) lastExit() { if r.err == nil { r.err = ExitCode(r.exit) } } -func (r *Runner) setVar(name string, val varValue) { +func (r *Runner) setVar(name string, index syntax.ArithmExpr, val varValue) { if r.vars == nil { r.vars = make(map[string]varValue, 4) } - r.vars[name] = val + if index == nil { + r.vars[name] = val + return + } + // from the syntax package, we know that val must be a string if + // index is non-nil; nested arrays are forbidden. + valStr := val.(string) + var list []string + switch x := r.vars[name].(type) { + case string: + list = []string{x} + case []string: + list = x + } + k := r.arithm(index) + for len(list) < k+1 { + list = append(list, "") + } + list[k] = valStr + r.vars[name] = list } func (r *Runner) lookupVar(name string) (varValue, bool) { switch name { case "PWD": return r.Dir, true - case "HOME": - u, _ := user.Current() - return u.HomeDir, true } if val, e := r.cmdVars[name]; e { return val, true @@ -221,42 +312,6 @@ opts: return args, nil } -func (r *Runner) Reset() error { - // reset the internal state - *r = Runner{ - Env: r.Env, - Dir: r.Dir, - Params: r.Params, - Context: r.Context, - Stdin: r.Stdin, - Stdout: r.Stdout, - Stderr: r.Stderr, - } - if r.Context == nil { - r.Context = context.Background() - } - if r.Env == nil { - r.Env = os.Environ() - } - r.envMap = make(map[string]string, len(r.Env)) - for _, kv := range r.Env { - i := strings.IndexByte(kv, '=') - if i < 0 { - return fmt.Errorf("env not in the form key=value: %q", kv) - } - name, val := kv[:i], kv[i+1:] - r.envMap[name] = val - } - if r.Dir == "" { - dir, err := os.Getwd() - if err != nil { - return fmt.Errorf("could not get current dir: %v", err) - } - r.Dir = dir - } - return nil -} - // Run starts the interpreter and returns any error. func (r *Runner) Run(node syntax.Node) error { r.filename = "" @@ -278,6 +333,11 @@ func (r *Runner) Run(node syntax.Node) error { return r.err } +func (r *Runner) Stmt(stmt *syntax.Stmt) error { + r.stmt(stmt) + return r.err +} + func (r *Runner) outf(format string, a ...interface{}) { fmt.Fprintf(r.Stdout, format, a...) } @@ -375,7 +435,7 @@ func escapedGlob(parts []fieldPart) (escaped string, glob bool) { return buf.String(), glob } -func (r *Runner) fields(words []*syntax.Word) []string { +func (r *Runner) Fields(words []*syntax.Word) []string { fields := make([]string, 0, len(words)) baseDir, _ := escapedGlob([]fieldPart{{val: r.Dir}}) for _, word := range words { @@ -465,9 +525,22 @@ func (r *Runner) assignValue(as *syntax.Assign) varValue { return s } if as.Array != nil { - strs := make([]string, len(as.Array.Elems)) + maxIndex := len(as.Array.Elems) - 1 + indexes := make([]int, len(as.Array.Elems)) for i, elem := range as.Array.Elems { - strs[i] = r.loneWord(elem.Value) + if elem.Index == nil { + indexes[i] = i + continue + } + k := r.arithm(elem.Index) + indexes[i] = k + if k > maxIndex { + maxIndex = k + } + } + strs := make([]string, maxIndex+1) + for i, elem := range as.Array.Elems { + strs[indexes[i]] = r.loneWord(elem.Value) } if !as.Append || prev == nil { return strs @@ -524,10 +597,11 @@ func (r *Runner) cmd(cm syntax.Command) { r2 := *r r2.stmts(x.StmtList) r.exit = r2.exit + r.setErr(r2.err) case *syntax.CallExpr: if len(x.Args) == 0 { for _, as := range x.Assigns { - r.setVar(as.Name.Value, r.assignValue(as)) + r.setVar(as.Name.Value, as.Index, r.assignValue(as)) } break } @@ -538,7 +612,7 @@ func (r *Runner) cmd(cm syntax.Command) { for _, as := range x.Assigns { r.cmdVars[as.Name.Value] = r.assignValue(as) } - fields := r.fields(x.Args) + fields := r.Fields(x.Args) r.call(x.Args[0].Pos(), fields[0], fields[1:]) r.cmdVars = oldVars case *syntax.BinaryCmd: @@ -564,12 +638,17 @@ func (r *Runner) cmd(cm syntax.Command) { r2.Stderr = r.Stderr } r.Stdin = pr + var wg sync.WaitGroup + wg.Add(1) go func() { r2.stmt(x.X) pw.Close() + wg.Done() }() r.stmt(x.Y) pr.Close() + wg.Wait() + r.setErr(r2.err) } case *syntax.IfClause: r.stmts(x.Cond) @@ -592,8 +671,8 @@ func (r *Runner) cmd(cm syntax.Command) { switch y := x.Loop.(type) { case *syntax.WordIter: name := y.Name.Value - for _, field := range r.fields(y.Items) { - r.setVar(name, field) + for _, field := range r.Fields(y.Items) { + r.setVar(name, nil, field) if r.loopStmtsBroken(x.Do) { break } @@ -645,18 +724,16 @@ func (r *Runner) cmd(cm syntax.Command) { r.runErr(cm.Pos(), "unhandled declare opts") } for _, as := range x.Assigns { - r.setVar(as.Name.Value, r.assignValue(as)) + r.setVar(as.Name.Value, as.Index, r.assignValue(as)) } case *syntax.TimeClause: start := time.Now() if x.Stmt != nil { r.stmt(x.Stmt) } - elapsed := time.Since(start) + real := time.Since(start) r.outf("\n") - min := int(elapsed.Minutes()) - sec := math.Remainder(elapsed.Seconds(), 60.0) - r.outf("real\t%dm%.3fs\n", min, sec) + r.outf("real\t%s\n", elapsedString(real)) // TODO: can we do these? r.outf("user\t0m0.000s\n") r.outf("sys\t0m0.000s\n") @@ -668,6 +745,12 @@ func (r *Runner) cmd(cm syntax.Command) { } } +func elapsedString(d time.Duration) string { + min := int(d.Minutes()) + sec := math.Remainder(d.Seconds(), 60.0) + return fmt.Sprintf("%dm%.3fs", min, sec) +} + func (r *Runner) stmts(sl syntax.StmtList) { for _, stmt := range sl.Stmts { r.stmt(stmt) @@ -716,17 +799,9 @@ func (r *Runner) redir(rd *syntax.Redirect) (io.Closer, error) { case syntax.RdrOut, syntax.RdrAll: mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC } - var f io.ReadWriteCloser - switch arg { - case "/dev/null": - f = devNull{} - default: - var err error - f, err = os.OpenFile(r.relPath(arg), mode, 0644) - if err != nil { - // TODO: print to stderr? - return nil, err - } + f, err := r.open(r.relPath(arg), mode, 0644, true) + if err != nil { + return nil, err } switch rd.Op { case syntax.RdrIn: @@ -844,6 +919,7 @@ func (r *Runner) wordFields(wps []syntax.WordPart, quoted bool) [][]fieldPart { } else { splitAdd(val) } + r.setErr(r2.err) case *syntax.ArithmExp: curField = append(curField, fieldPart{ val: strconv.Itoa(r.arithm(x.X)), @@ -872,31 +948,31 @@ func (r *Runner) call(pos syntax.Pos, name string, args []string) { r.exit = r.builtinCode(pos, name, args) return } - cmd := exec.CommandContext(r.Context, name, args...) - cmd.Env = r.Env - for name, val := range r.cmdVars { - cmd.Env = append(cmd.Env, name+"="+varStr(val)) - } - cmd.Dir = r.Dir - cmd.Stdin = r.Stdin - cmd.Stdout = r.Stdout - cmd.Stderr = r.Stderr - err := cmd.Run() + r.exec(name, args) +} + +func (r *Runner) exec(name string, args []string) { + err := r.Exec(r.ctx(), name, args) switch x := err.(type) { - case *exec.ExitError: - // started, but errored - default to 1 if OS - // doesn't have exit statuses - r.exit = 1 - if status, ok := x.Sys().(syscall.WaitStatus); ok { - r.exit = status.ExitStatus() - } - case *exec.Error: - // did not start - // TODO: can this be anything other than - // "command not found"? - r.exit = 127 - // TODO: print something? - default: + case nil: r.exit = 0 + case ExitCode: + r.exit = int(x) + default: + r.setErr(err) } } + +func (r *Runner) open(path string, flags int, mode os.FileMode, print bool) (io.ReadWriteCloser, error) { + f, err := r.Open(r.ctx(), path, flags, mode) + switch err.(type) { + case nil: + case *os.PathError: + if print { + r.errf("%v\n", err) + } + default: + r.setErr(err) + } + return f, err +} diff --git a/vendor/mvdan.cc/sh/interp/module.go b/vendor/mvdan.cc/sh/interp/module.go new file mode 100644 index 00000000..acaedd00 --- /dev/null +++ b/vendor/mvdan.cc/sh/interp/module.go @@ -0,0 +1,100 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package interp + +import ( + "context" + "io" + "os" + "os/exec" + "syscall" +) + +// Ctxt is the type passed to all the module functions. It contains some +// of the current state of the Runner, as well as some fields necessary +// to implement some of the modules. +type Ctxt struct { + Context context.Context + Env []string + Dir string + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + +// ModuleExec is the module responsible for executing a program. It is +// executed for all CallExpr nodes where the name is neither a declared +// function nor a builtin. +// +// Use a return error of type ExitCode to set the exit code. A nil error +// has the same effect as ExitCode(0). If the error is of any other +// type, the interpreter will come to a stop. +// +// TODO: replace name with path, to avoid the common "path := +// exec.LookPath(name)"? +type ModuleExec func(ctx Ctxt, name string, args []string) error + +func DefaultExec(ctx Ctxt, name string, args []string) error { + cmd := exec.CommandContext(ctx.Context, name, args...) + cmd.Env = ctx.Env + cmd.Dir = ctx.Dir + cmd.Stdin = ctx.Stdin + cmd.Stdout = ctx.Stdout + cmd.Stderr = ctx.Stderr + err := cmd.Run() + switch x := err.(type) { + case *exec.ExitError: + // started, but errored - default to 1 if OS + // doesn't have exit statuses + if status, ok := x.Sys().(syscall.WaitStatus); ok { + return ExitCode(status.ExitStatus()) + } + return ExitCode(1) + case *exec.Error: + // did not start + // TODO: can this be anything other than + // "command not found"? + return ExitCode(127) + // TODO: print something? + default: + return nil + } +} + +// ModuleOpen is the module responsible for opening a file. It is +// executed for all files that are opened directly by the shell, such as +// in redirects. Files opened by executed programs are not included. +// +// The path parameter is absolute and has been cleaned. +// +// Use a return error of type *os.PathError to have the error printed to +// stderr and the exit code set to 1. If the error is of any other type, +// the interpreter will come to a stop. +// +// TODO: What about stat calls? They are used heavily in the builtin +// test expressions, and also when doing a cd. Should they have a +// separate module? +type ModuleOpen func(ctx Ctxt, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) + +func DefaultOpen(ctx Ctxt, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { + return os.OpenFile(path, flag, perm) +} + +func OpenDevImpls(next ModuleOpen) ModuleOpen { + return func(ctx Ctxt, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { + switch path { + case "/dev/null": + return devNull{}, nil + } + return next(ctx, path, flag, perm) + } +} + +var _ io.ReadWriteCloser = devNull{} + +type devNull struct{} + +func (devNull) Read(p []byte) (int, error) { return 0, io.EOF } +func (devNull) Write(p []byte) (int, error) { return len(p), nil } +func (devNull) Close() error { return nil } diff --git a/vendor/mvdan.cc/sh/interp/param.go b/vendor/mvdan.cc/sh/interp/param.go index bbfc0ec4..33caf278 100644 --- a/vendor/mvdan.cc/sh/interp/param.go +++ b/vendor/mvdan.cc/sh/interp/param.go @@ -136,7 +136,7 @@ func (r *Runner) paramExp(pe *syntax.ParamExp) string { fallthrough case syntax.SubstColAssgn: if str == "" { - r.setVar(name, arg) + r.setVar(name, nil, arg) str = arg } case syntax.RemSmallPrefix: diff --git a/vendor/mvdan.cc/sh/interp/perm_unix.go b/vendor/mvdan.cc/sh/interp/perm_unix.go new file mode 100644 index 00000000..7bdc622d --- /dev/null +++ b/vendor/mvdan.cc/sh/interp/perm_unix.go @@ -0,0 +1,49 @@ +// Copyright (c) 2017, Andrey Nering +// See LICENSE for licensing information + +// +build !windows + +package interp + +import ( + "os" + "os/user" + "strconv" + "syscall" +) + +// hasPermissionToDir returns if the OS current user has execute permission +// to the given directory +func hasPermissionToDir(info os.FileInfo) bool { + user, err := user.Current() + if err != nil { + return true + } + uid, _ := strconv.Atoi(user.Uid) + // super-user + if uid == 0 { + return true + } + + st, _ := info.Sys().(*syscall.Stat_t) + if st == nil { + return true + } + perm := info.Mode().Perm() + // user (u) + if perm&0100 != 0 && st.Uid == uint32(uid) { + return true + } + + gid, _ := strconv.Atoi(user.Gid) + // other users in group (g) + if perm&0010 != 0 && st.Uid != uint32(uid) && st.Gid == uint32(gid) { + return true + } + // remaining users (o) + if perm&0001 != 0 && st.Uid != uint32(uid) && st.Gid != uint32(gid) { + return true + } + + return false +} diff --git a/vendor/mvdan.cc/sh/interp/perm_windows.go b/vendor/mvdan.cc/sh/interp/perm_windows.go new file mode 100644 index 00000000..931ba55c --- /dev/null +++ b/vendor/mvdan.cc/sh/interp/perm_windows.go @@ -0,0 +1,11 @@ +// Copyright (c) 2017, Andrey Nering +// See LICENSE for licensing information + +package interp + +import "os" + +// hasPermissionToDir is a no-op on Windows. +func hasPermissionToDir(info os.FileInfo) bool { + return true +} diff --git a/vendor/mvdan.cc/sh/interp/test.go b/vendor/mvdan.cc/sh/interp/test.go index 8e565fcd..493a5c93 100644 --- a/vendor/mvdan.cc/sh/interp/test.go +++ b/vendor/mvdan.cc/sh/interp/test.go @@ -132,13 +132,13 @@ func (r *Runner) unTest(op syntax.UnTestOperator, x string) bool { //case syntax.TsUsrOwn: //case syntax.TsModif: case syntax.TsRead: - f, err := os.OpenFile(r.relPath(x), os.O_RDONLY, 0) + f, err := r.open(r.relPath(x), os.O_RDONLY, 0, false) if err == nil { f.Close() } return err == nil case syntax.TsWrite: - f, err := os.OpenFile(r.relPath(x), os.O_WRONLY, 0) + f, err := r.open(r.relPath(x), os.O_WRONLY, 0, false) if err == nil { f.Close() } diff --git a/vendor/mvdan.cc/sh/syntax/lexer.go b/vendor/mvdan.cc/sh/syntax/lexer.go index 02f652f9..d0c955ba 100644 --- a/vendor/mvdan.cc/sh/syntax/lexer.go +++ b/vendor/mvdan.cc/sh/syntax/lexer.go @@ -706,12 +706,32 @@ func (p *Parser) endLit() (s string) { if p.r == utf8.RuneSelf { s = string(p.litBs) } else if len(p.litBs) > 0 { - s = string(p.litBs[:len(p.litBs)-1]) + s = string(p.litBs[:len(p.litBs)-int(p.w)]) } p.litBs = nil return } +func (p *Parser) advanceNameCont(r rune) { + // we know that r is a letter or underscore +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch { + case r == '\\': + if r = p.rune(); r == '\n' { + p.discardLit(2) + } + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case '0' <= r && r <= '9': + default: + break loop + } + } + p.tok, p.val = _LitWord, p.endLit() +} + func (p *Parser) advanceLitOther(r rune) { tok := _LitWord loop: @@ -754,40 +774,18 @@ loop: if p.quote&allArithmExpr != 0 { break loop } - if p.quote == paramName && p.peekByte('(') { - tok = _Lit + case ':', '=', '%', '^', ',', '?': + if p.quote&allArithmExpr != 0 || p.quote == paramExpName { break loop } - case '?': - if p.quote == paramName && p.peekByte('(') { - tok = _Lit - break loop - } - fallthrough - case ':', '=', '%', '^', ',': - if p.quote&allArithmExpr != 0 || p.quote&allParamReg != 0 { - break loop - } - case '@': - if p.quote == paramName && p.peekByte('(') { - tok = _Lit - break loop - } - fallthrough - case '#', '[': + case '#', '[', '@': if p.quote&allParamReg != 0 { break loop } if r == '[' && p.lang != LangPOSIX && p.quote&allArithmExpr != 0 { break loop } - case '+': - if p.quote == paramName && p.peekByte('(') { - tok = _Lit - break loop - } - fallthrough - case '-': + case '+', '-': switch p.quote { case paramExpExp, paramExpRepl, sglQuotes: default: diff --git a/vendor/mvdan.cc/sh/syntax/nodes.go b/vendor/mvdan.cc/sh/syntax/nodes.go index b7560482..07343462 100644 --- a/vendor/mvdan.cc/sh/syntax/nodes.go +++ b/vendor/mvdan.cc/sh/syntax/nodes.go @@ -183,6 +183,9 @@ func (*CoprocClause) commandNode() {} // expression. In the latter, it will be a word with a single DblQuoted // part. // +// If Index is non-nil, the value will be a word and not an array as +// nested arrays are not allowed. +// // If Naked is true, it's part of a DeclClause and doesn't contain a // value. In that context, if the name wasn't a literal, it will be in // Value instead of Name. diff --git a/vendor/mvdan.cc/sh/syntax/parser.go b/vendor/mvdan.cc/sh/syntax/parser.go index 26ce8c30..8649cf33 100644 --- a/vendor/mvdan.cc/sh/syntax/parser.go +++ b/vendor/mvdan.cc/sh/syntax/parser.go @@ -232,7 +232,6 @@ const ( arithmExprBrack testRegexp switchCase - paramName paramExpName paramExpInd paramExpOff @@ -247,9 +246,9 @@ const ( switchCase | arrayElems allArithmExpr = arithmExpr | arithmExprLet | arithmExprCmd | arithmExprBrack | allParamArith - allRbrack = arithmExprBrack | paramExpInd | paramName + allRbrack = arithmExprBrack | paramExpInd allParamArith = paramExpInd | paramExpOff | paramExpLen - allParamReg = paramName | paramExpName | allParamArith + allParamReg = paramExpName | allParamArith allParamExp = allParamReg | paramExpRepl | paramExpExp ) @@ -676,13 +675,23 @@ func (p *Parser) wordPart() WordPart { return cs case dollar: r := p.r - if r == utf8.RuneSelf || wordBreak(r) || r == '"' || r == '\'' || r == '`' || r == '[' { + switch { + case singleRuneParam(r): + p.tok, p.val = _LitWord, string(r) + p.rune() + case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', + '0' <= r && r <= '9', r == '_', r == '\\': + p.advanceNameCont(r) + default: l := p.lit(p.pos, "$") p.next() return l } p.ensureNoNested() - return p.shortParamExp() + pe := &ParamExp{Dollar: p.pos, Short: true} + p.pos = posAddCol(p.pos, 1) + pe.Param = p.getLit() + return pe case cmdIn, cmdOut: p.ensureNoNested() ps := &ProcSubst{Op: ProcOperator(p.tok), OpPos: p.pos} @@ -1012,21 +1021,13 @@ func (p *Parser) arithmExprBase(compact bool) ArithmExpr { return x } -func (p *Parser) shortParamExp() *ParamExp { - pe := &ParamExp{Dollar: p.pos, Short: true} - p.pos = posAddCol(p.pos, 1) - switch p.r { - case '@', '*', '#', '$', '?', '!', '0', '-': - p.tok, p.val = _LitWord, string(p.r) - p.rune() - default: - old := p.quote - p.quote = paramName - p.advanceLitOther(p.r) - p.quote = old +func singleRuneParam(r rune) bool { + switch r { + case '@', '*', '#', '$', '?', '!', '-', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true } - pe.Param = p.getLit() - return pe + return false } func (p *Parser) paramExp() *ParamExp { @@ -1186,12 +1187,12 @@ func stopToken(tok token) bool { // ValidName returns whether val is a valid name as per the POSIX spec. func ValidName(val string) bool { - for i, c := range val { + for i, r := range val { switch { - case 'a' <= c && c <= 'z': - case 'A' <= c && c <= 'Z': - case c == '_': - case i > 0 && '0' <= c && c <= '9': + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case i > 0 && '0' <= r && r <= '9': default: return false } @@ -1275,6 +1276,9 @@ func (p *Parser) getAssign(needEqual bool) *Assign { if p.lang == LangPOSIX { p.curErr("arrays are a bash feature") } + if as.Index != nil { + p.curErr("arrays cannot be nested") + } as.Array = &ArrayExpr{Lparen: p.pos} newQuote := p.quote if p.lang == LangBash { @@ -1308,6 +1312,9 @@ func (p *Parser) getAssign(needEqual bool) *Assign { p.next() } if ae.Value = p.getWord(); ae.Value == nil { + if p.tok == leftParen { + p.curErr("arrays cannot be nested") + } p.curErr("array element values must be words") break } diff --git a/vendor/mvdan.cc/sh/syntax/walk.go b/vendor/mvdan.cc/sh/syntax/walk.go index fe040bfb..3344a9a2 100644 --- a/vendor/mvdan.cc/sh/syntax/walk.go +++ b/vendor/mvdan.cc/sh/syntax/walk.go @@ -9,6 +9,9 @@ func walkStmts(sl StmtList, f func(Node) bool) { for _, s := range sl.Stmts { Walk(s, f) } + for _, c := range sl.Last { + Walk(&c, f) + } } func walkWords(words []*Word, f func(Node) bool) { @@ -29,7 +32,15 @@ func Walk(node Node, f func(Node) bool) { switch x := node.(type) { case *File: walkStmts(x.StmtList, f) + case *Comment: case *Stmt: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } if x.Cmd != nil { Walk(x.Cmd, f) } @@ -146,7 +157,17 @@ func Walk(node Node, f func(Node) bool) { for _, ci := range x.Items { Walk(ci, f) } + for _, c := range x.Last { + Walk(&c, f) + } case *CaseItem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } walkWords(x.Patterns, f) walkStmts(x.StmtList, f) case *TestClause: @@ -160,7 +181,17 @@ func Walk(node Node, f func(Node) bool) { for _, el := range x.Elems { Walk(el, f) } + for _, c := range x.Last { + Walk(&c, f) + } case *ArrayElem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } if x.Index != nil { Walk(x.Index, f) }