175 lines
3.2 KiB
Go
175 lines
3.2 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build unix || (js && wasm) || plan9 || wasip1
|
|
|
|
// Unix environment variables.
|
|
|
|
package syscall
|
|
|
|
import (
|
|
"runtime"
|
|
"sync"
|
|
|
|
"github.com/goplus/llgo/c"
|
|
"github.com/goplus/llgo/c/os"
|
|
"github.com/goplus/llgo/c/syscall"
|
|
)
|
|
|
|
var (
|
|
// envOnce guards initialization by copyenv, which populates env.
|
|
envOnce sync.Once
|
|
|
|
// envLock guards env and envs.
|
|
envLock sync.RWMutex
|
|
|
|
// env maps from an environment variable to its first occurrence in envs.
|
|
env map[string]int
|
|
|
|
// envs is provided by the runtime. elements are expected to
|
|
// be of the form "key=value". An empty string means deleted
|
|
// (or a duplicate to be ignored).
|
|
envs []string = runtimeEnvs()
|
|
)
|
|
|
|
func runtimeEnvs() []string {
|
|
ret := make([]string, 0, 8)
|
|
cenvs := os.Environ
|
|
i := 0
|
|
for {
|
|
ce := *c.Advance(cenvs, i)
|
|
if ce == nil {
|
|
return ret
|
|
}
|
|
ret = append(ret, c.GoString(ce))
|
|
i++
|
|
}
|
|
}
|
|
|
|
func copyenv() {
|
|
env = make(map[string]int)
|
|
for i, s := range envs {
|
|
for j := 0; j < len(s); j++ {
|
|
if s[j] == '=' {
|
|
key := s[:j]
|
|
if _, ok := env[key]; !ok {
|
|
env[key] = i // first mention of key
|
|
} else {
|
|
// Clear duplicate keys. This permits Unsetenv to
|
|
// safely delete only the first item without
|
|
// worrying about unshadowing a later one,
|
|
// which might be a security problem.
|
|
envs[i] = ""
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func Unsetenv(key string) error {
|
|
envOnce.Do(copyenv)
|
|
|
|
envLock.Lock()
|
|
defer envLock.Unlock()
|
|
|
|
if i, ok := env[key]; ok {
|
|
envs[i] = ""
|
|
delete(env, key)
|
|
}
|
|
os.Unsetenv(c.AllocaCStr(key))
|
|
return nil
|
|
}
|
|
|
|
func Getenv(key string) (value string, found bool) {
|
|
envOnce.Do(copyenv)
|
|
if len(key) == 0 {
|
|
return "", false
|
|
}
|
|
|
|
envLock.RLock()
|
|
defer envLock.RUnlock()
|
|
|
|
i, ok := env[key]
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
s := envs[i]
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == '=' {
|
|
return s[i+1:], true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
/* TODO(xsw):
|
|
func Getenv(key string) (value string, found bool) {
|
|
ret := os.Getenv(c.AllocaCStr(key))
|
|
if ret != nil {
|
|
return c.GoString(ret), true
|
|
}
|
|
return "", false
|
|
}
|
|
*/
|
|
|
|
func Setenv(key, value string) error {
|
|
envOnce.Do(copyenv)
|
|
if len(key) == 0 {
|
|
return Errno(syscall.EINVAL)
|
|
}
|
|
for i := 0; i < len(key); i++ {
|
|
if key[i] == '=' || key[i] == 0 {
|
|
return Errno(syscall.EINVAL)
|
|
}
|
|
}
|
|
// On Plan 9, null is used as a separator, eg in $path.
|
|
if runtime.GOOS != "plan9" {
|
|
for i := 0; i < len(value); i++ {
|
|
if value[i] == 0 {
|
|
return Errno(syscall.EINVAL)
|
|
}
|
|
}
|
|
}
|
|
|
|
envLock.Lock()
|
|
defer envLock.Unlock()
|
|
|
|
i, ok := env[key]
|
|
kv := key + "=" + value
|
|
if ok {
|
|
envs[i] = kv
|
|
} else {
|
|
i = len(envs)
|
|
envs = append(envs, kv)
|
|
}
|
|
env[key] = i
|
|
os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
|
|
return nil
|
|
}
|
|
|
|
func Clearenv() {
|
|
envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
|
|
|
|
envLock.Lock()
|
|
defer envLock.Unlock()
|
|
|
|
os.Clearenv()
|
|
env = make(map[string]int)
|
|
envs = []string{}
|
|
}
|
|
|
|
func Environ() []string {
|
|
envOnce.Do(copyenv)
|
|
envLock.RLock()
|
|
defer envLock.RUnlock()
|
|
a := make([]string, 0, len(envs))
|
|
for _, env := range envs {
|
|
if env != "" {
|
|
a = append(a, env)
|
|
}
|
|
}
|
|
return a
|
|
}
|