1
0
mirror of https://github.com/go-task/task.git synced 2025-03-17 21:08:01 +02:00

update dependencies

also make sure the module that enables /dev/null path of the sh
interpreter is enabled
This commit is contained in:
Andrey Nering 2017-10-15 17:41:15 -02:00
parent 2fc32414f5
commit 0513a21e25
36 changed files with 894 additions and 245 deletions

24
Gopkg.lock generated
View File

@ -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"

View File

@ -16,6 +16,7 @@ dl-deps:
update-deps:
desc: Updates dependencies
cmds:
- dep ensure
- dep ensure -update
- dep prune

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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

21
vendor/github.com/mattn/go-zglob/LICENSE generated vendored Normal file
View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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")

3
vendor/golang.org/x/net/README generated vendored
View File

@ -1,3 +0,0 @@
This repository holds supplementary Go networking libraries.
To submit changes to this repository, see http://golang.org/doc/contribute.html.

16
vendor/golang.org/x/net/README.md generated vendored Normal file
View File

@ -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.

2
vendor/golang.org/x/sync/README generated vendored
View File

@ -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.

18
vendor/golang.org/x/sync/README.md generated vendored Normal file
View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

100
vendor/mvdan.cc/sh/interp/module.go vendored Normal file
View File

@ -0,0 +1,100 @@
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// 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 }

View File

@ -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:

49
vendor/mvdan.cc/sh/interp/perm_unix.go vendored Normal file
View File

@ -0,0 +1,49 @@
// Copyright (c) 2017, Andrey Nering <andrey.nering@gmail.com>
// 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
}

View File

@ -0,0 +1,11 @@
// Copyright (c) 2017, Andrey Nering <andrey.nering@gmail.com>
// See LICENSE for licensing information
package interp
import "os"
// hasPermissionToDir is a no-op on Windows.
func hasPermissionToDir(info os.FileInfo) bool {
return true
}

View File

@ -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()
}

View File

@ -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:

View File

@ -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.

View File

@ -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
}

View File

@ -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)
}