2017-08-31 05:27:25 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2018-01-01 04:31:45 +08:00
|
|
|
"github.com/elves/elvish/eval/types"
|
2018-01-03 09:13:51 +08:00
|
|
|
"github.com/elves/elvish/eval/vartypes"
|
2018-01-25 08:48:31 +08:00
|
|
|
"github.com/elves/elvish/util"
|
2017-08-31 05:27:25 +08:00
|
|
|
"github.com/xiaq/persistent/vector"
|
|
|
|
)
|
|
|
|
|
2017-12-05 07:53:14 +08:00
|
|
|
var (
|
|
|
|
pathListSeparator = string(os.PathListSeparator)
|
|
|
|
forbiddenInPath = pathListSeparator + "\x00"
|
|
|
|
)
|
|
|
|
|
2017-08-31 05:27:25 +08:00
|
|
|
// Errors
|
|
|
|
var (
|
|
|
|
ErrCanOnlyAssignList = errors.New("can only assign compatible values")
|
|
|
|
ErrPathMustBeString = errors.New("path must be string")
|
|
|
|
ErrPathCannotContainColonZero = errors.New(`path cannot contain colon or \0`)
|
|
|
|
)
|
|
|
|
|
2017-12-05 07:53:14 +08:00
|
|
|
// EnvList is a variable whose value is constructed from an environment variable
|
|
|
|
// by splitting at pathListSeparator. Changes to it are also propagated to the
|
|
|
|
// corresponding environment variable. Its elements cannot contain
|
|
|
|
// pathListSeparator or \0; attempting to put any in its elements will result in
|
|
|
|
// an error.
|
2017-08-31 05:27:25 +08:00
|
|
|
type EnvList struct {
|
|
|
|
sync.RWMutex
|
|
|
|
envName string
|
|
|
|
cacheFor string
|
2018-01-01 04:31:45 +08:00
|
|
|
cacheValue types.Value
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2018-01-03 09:13:51 +08:00
|
|
|
_ vartypes.Variable = (*EnvList)(nil)
|
2017-08-31 05:27:25 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Get returns a Value for an EnvPathList.
|
2018-01-01 04:31:45 +08:00
|
|
|
func (envli *EnvList) Get() types.Value {
|
2017-08-31 05:27:25 +08:00
|
|
|
envli.Lock()
|
|
|
|
defer envli.Unlock()
|
|
|
|
|
|
|
|
value := os.Getenv(envli.envName)
|
|
|
|
if value == envli.cacheFor {
|
|
|
|
return envli.cacheValue
|
|
|
|
}
|
|
|
|
envli.cacheFor = value
|
|
|
|
v := vector.Empty
|
2017-12-05 07:53:14 +08:00
|
|
|
for _, path := range strings.Split(value, pathListSeparator) {
|
2018-01-02 09:34:09 +08:00
|
|
|
v = v.Cons(types.String(path))
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
2018-01-01 05:01:21 +08:00
|
|
|
envli.cacheValue = types.NewList(v)
|
2017-08-31 05:27:25 +08:00
|
|
|
return envli.cacheValue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set sets an EnvPathList. The underlying environment variable is set.
|
2018-01-03 10:04:14 +08:00
|
|
|
func (envli *EnvList) Set(v types.Value) error {
|
2018-01-25 08:48:31 +08:00
|
|
|
var (
|
|
|
|
paths []string
|
|
|
|
errElement error
|
|
|
|
)
|
|
|
|
errIterate := types.Iterate(v, func(v types.Value) bool {
|
2018-01-02 09:34:09 +08:00
|
|
|
s, ok := v.(types.String)
|
2017-08-31 05:27:25 +08:00
|
|
|
if !ok {
|
2018-01-25 08:48:31 +08:00
|
|
|
errElement = ErrPathMustBeString
|
2018-01-03 10:04:14 +08:00
|
|
|
return false
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
|
|
|
path := string(s)
|
2017-12-05 07:53:14 +08:00
|
|
|
if strings.ContainsAny(path, forbiddenInPath) {
|
2018-01-25 08:48:31 +08:00
|
|
|
errElement = ErrPathCannotContainColonZero
|
2018-01-03 10:04:14 +08:00
|
|
|
return false
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
|
|
|
paths = append(paths, string(s))
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
2018-01-25 08:48:31 +08:00
|
|
|
if errElement != nil || errIterate != nil {
|
|
|
|
return util.Errors(errElement, errIterate)
|
2018-01-03 10:04:14 +08:00
|
|
|
}
|
|
|
|
|
2017-08-31 05:27:25 +08:00
|
|
|
envli.Lock()
|
|
|
|
defer envli.Unlock()
|
2017-12-05 07:53:14 +08:00
|
|
|
os.Setenv(envli.envName, strings.Join(paths, pathListSeparator))
|
2018-01-03 10:04:14 +08:00
|
|
|
return nil
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|