2021-01-05 12:07:35 +08:00
|
|
|
package vars
|
2017-08-31 05:27:25 +08:00
|
|
|
|
2021-06-06 11:20:01 +08:00
|
|
|
// Note: This doesn't have an associated env_list_tests.go because most of its functionality is
|
|
|
|
// tested by TestSetEnv_PATH and related tests.
|
|
|
|
|
2017-08-31 05:27:25 +08:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2022-11-30 06:10:52 +08:00
|
|
|
"src.elv.sh/pkg/errutil"
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/eval/vals"
|
2017-08-31 05:27:25 +08:00
|
|
|
)
|
|
|
|
|
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 (
|
2021-06-07 05:46:27 +08:00
|
|
|
ErrPathMustBeString = errors.New("path must be string")
|
|
|
|
ErrPathContainsForbiddenChar = errors.New("path cannot contain NUL byte, colon on UNIX or semicolon on Windows")
|
2017-08-31 05:27:25 +08:00
|
|
|
)
|
|
|
|
|
2020-09-05 05:24:08 +08:00
|
|
|
// NewEnvListVar returns a variable whose value is a list synchronized with an
|
|
|
|
// environment variable with the elements joined by os.PathListSeparator.
|
|
|
|
//
|
|
|
|
// Elements in the value of the variable must be strings, and cannot contain
|
|
|
|
// os.PathListSeparator or \0; attempting to put any in its elements will result in
|
2017-12-05 07:53:14 +08:00
|
|
|
// an error.
|
2021-01-05 12:07:35 +08:00
|
|
|
func NewEnvListVar(name string) Var {
|
2020-09-05 05:24:08 +08:00
|
|
|
return &envListVar{envName: name}
|
|
|
|
}
|
|
|
|
|
|
|
|
type envListVar struct {
|
2017-08-31 05:27:25 +08:00
|
|
|
sync.RWMutex
|
|
|
|
envName string
|
|
|
|
cacheFor string
|
2022-03-20 23:50:25 +08:00
|
|
|
cacheValue any
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns a Value for an EnvPathList.
|
2022-03-20 23:50:25 +08:00
|
|
|
func (envli *envListVar) Get() any {
|
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
|
2021-12-17 08:21:20 +08:00
|
|
|
v := vals.EmptyList
|
2017-12-05 07:53:14 +08:00
|
|
|
for _, path := range strings.Split(value, pathListSeparator) {
|
2022-01-06 06:34:55 +08:00
|
|
|
v = v.Conj(path)
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
2018-01-28 01:26:22 +08:00
|
|
|
envli.cacheValue = v
|
2017-08-31 05:27:25 +08:00
|
|
|
return envli.cacheValue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set sets an EnvPathList. The underlying environment variable is set.
|
2022-03-20 23:50:25 +08:00
|
|
|
func (envli *envListVar) Set(v any) error {
|
2018-01-25 08:48:31 +08:00
|
|
|
var (
|
|
|
|
paths []string
|
|
|
|
errElement error
|
|
|
|
)
|
2022-03-20 23:50:25 +08:00
|
|
|
errIterate := vals.Iterate(v, func(v any) bool {
|
2018-01-25 09:40:15 +08:00
|
|
|
s, ok := v.(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
|
|
|
}
|
2018-01-25 09:40:15 +08:00
|
|
|
path := s
|
2017-12-05 07:53:14 +08:00
|
|
|
if strings.ContainsAny(path, forbiddenInPath) {
|
2021-06-07 05:46:27 +08:00
|
|
|
errElement = ErrPathContainsForbiddenChar
|
2018-01-03 10:04:14 +08:00
|
|
|
return false
|
2017-08-31 05:27:25 +08:00
|
|
|
}
|
2018-01-25 09:40:15 +08:00
|
|
|
paths = append(paths, s)
|
2017-08-31 05:27:25 +08:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
2018-01-25 08:48:31 +08:00
|
|
|
if errElement != nil || errIterate != nil {
|
2022-11-30 06:10:52 +08:00
|
|
|
return errutil.Multi(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
|
|
|
}
|