library patch: syscall, os environ
This commit is contained in:
@@ -215,6 +215,7 @@ The currently supported libraries include:
|
||||
* [c/clang](https://pkg.go.dev/github.com/goplus/llgo/c/clang)
|
||||
* [c/llama2](https://pkg.go.dev/github.com/goplus/llgo/c/llama2)
|
||||
* [c/lua](https://pkg.go.dev/github.com/goplus/llgo/c/lua)
|
||||
* [c/neco](https://pkg.go.dev/github.com/goplus/llgo/c/neco)
|
||||
* [c/raylib](https://pkg.go.dev/github.com/goplus/llgo/c/raylib)
|
||||
* [c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite)
|
||||
* [c/zlib](https://pkg.go.dev/github.com/goplus/llgo/c/zlib)
|
||||
|
||||
31
_demo/osproc/exec.go
Normal file
31
_demo/osproc/exec.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("os.Environ:", os.Environ())
|
||||
|
||||
ls := "ls"
|
||||
args := []string{ls, "-l"}
|
||||
if runtime.GOOS == "windows" {
|
||||
ls = "dir"
|
||||
args = []string{ls}
|
||||
}
|
||||
lspath, _ := exec.LookPath(ls)
|
||||
if lspath != "" {
|
||||
ls = lspath
|
||||
}
|
||||
proc, err := os.StartProcess(ls, args, &os.ProcAttr{
|
||||
Files: []*os.File{nil, os.Stdout, os.Stderr},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("os.StartProcess error:", err)
|
||||
return
|
||||
}
|
||||
proc.Wait()
|
||||
}
|
||||
161
internal/lib/os/env.go
Normal file
161
internal/lib/os/env.go
Normal file
@@ -0,0 +1,161 @@
|
||||
// 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.
|
||||
|
||||
// General environment variables.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
/* TODO(xsw):
|
||||
// Expand replaces ${var} or $var in the string based on the mapping function.
|
||||
// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv).
|
||||
func Expand(s string, mapping func(string) string) string {
|
||||
var buf []byte
|
||||
// ${} is all ASCII, so bytes are fine for this operation.
|
||||
i := 0
|
||||
for j := 0; j < len(s); j++ {
|
||||
if s[j] == '$' && j+1 < len(s) {
|
||||
if buf == nil {
|
||||
buf = make([]byte, 0, 2*len(s))
|
||||
}
|
||||
buf = append(buf, s[i:j]...)
|
||||
name, w := getShellName(s[j+1:])
|
||||
if name == "" && w > 0 {
|
||||
// Encountered invalid syntax; eat the
|
||||
// characters.
|
||||
} else if name == "" {
|
||||
// Valid syntax, but $ was not followed by a
|
||||
// name. Leave the dollar character untouched.
|
||||
buf = append(buf, s[j])
|
||||
} else {
|
||||
buf = append(buf, mapping(name)...)
|
||||
}
|
||||
j += w
|
||||
i = j + 1
|
||||
}
|
||||
}
|
||||
if buf == nil {
|
||||
return s
|
||||
}
|
||||
return string(buf) + s[i:]
|
||||
}
|
||||
|
||||
// ExpandEnv replaces ${var} or $var in the string according to the values
|
||||
// of the current environment variables. References to undefined
|
||||
// variables are replaced by the empty string.
|
||||
func ExpandEnv(s string) string {
|
||||
return Expand(s, Getenv)
|
||||
}
|
||||
|
||||
// isShellSpecialVar reports whether the character identifies a special
|
||||
// shell variable such as $*.
|
||||
func isShellSpecialVar(c uint8) bool {
|
||||
switch c {
|
||||
case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore.
|
||||
func isAlphaNum(c uint8) bool {
|
||||
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
|
||||
}
|
||||
|
||||
// getShellName returns the name that begins the string and the number of bytes
|
||||
// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
|
||||
// expansion and two more bytes are needed than the length of the name.
|
||||
func getShellName(s string) (string, int) {
|
||||
switch {
|
||||
case s[0] == '{':
|
||||
if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' {
|
||||
return s[1:2], 3
|
||||
}
|
||||
// Scan to closing brace
|
||||
for i := 1; i < len(s); i++ {
|
||||
if s[i] == '}' {
|
||||
if i == 1 {
|
||||
return "", 2 // Bad syntax; eat "${}"
|
||||
}
|
||||
return s[1:i], i + 1
|
||||
}
|
||||
}
|
||||
return "", 1 // Bad syntax; eat "${"
|
||||
case isShellSpecialVar(s[0]):
|
||||
return s[0:1], 1
|
||||
}
|
||||
// Scan alphanumerics.
|
||||
var i int
|
||||
for i = 0; i < len(s) && isAlphaNum(s[i]); i++ {
|
||||
}
|
||||
return s[:i], i
|
||||
}
|
||||
*/
|
||||
|
||||
// Getenv retrieves the value of the environment variable named by the key.
|
||||
// It returns the value, which will be empty if the variable is not present.
|
||||
// To distinguish between an empty value and an unset value, use LookupEnv.
|
||||
func Getenv(key string) string {
|
||||
v, _ := syscall.Getenv(key)
|
||||
return v
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
func Getenv(key string) string {
|
||||
return c.GoString(os.Getenv(c.AllocaCStr(key)))
|
||||
}
|
||||
*/
|
||||
|
||||
// LookupEnv retrieves the value of the environment variable named
|
||||
// by the key. If the variable is present in the environment the
|
||||
// value (which may be empty) is returned and the boolean is true.
|
||||
// Otherwise the returned value will be empty and the boolean will
|
||||
// be false.
|
||||
func LookupEnv(key string) (string, bool) {
|
||||
return syscall.Getenv(key)
|
||||
}
|
||||
|
||||
// Setenv sets the value of the environment variable named by the key.
|
||||
// It returns an error, if any.
|
||||
func Setenv(key, value string) error {
|
||||
err := syscall.Setenv(key, value)
|
||||
if err != nil {
|
||||
return NewSyscallError("setenv", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
func Setenv(key, value string) error {
|
||||
ret := os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return &SyscallError{"setenv", syscall.Errno(ret)}
|
||||
}
|
||||
*/
|
||||
|
||||
// Unsetenv unsets a single environment variable.
|
||||
func Unsetenv(key string) error {
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
func Unsetenv(key string) error {
|
||||
ret := os.Unsetenv(c.AllocaCStr(key))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return syscall.Errno(ret)
|
||||
}
|
||||
*/
|
||||
|
||||
// Environ returns a copy of strings representing the environment,
|
||||
// in the form "key=value".
|
||||
func Environ() []string {
|
||||
return syscall.Environ()
|
||||
}
|
||||
@@ -148,10 +148,6 @@ func Clearenv()
|
||||
// TODO(xsw):
|
||||
// func DirFS(dir string) fs.FS
|
||||
|
||||
func Environ() []string {
|
||||
panic("todo: os.Environ")
|
||||
}
|
||||
|
||||
// func Executable() (string, error)
|
||||
|
||||
// TODO(xsw):
|
||||
@@ -162,10 +158,6 @@ func Getegid() int {
|
||||
return int(os.Getegid())
|
||||
}
|
||||
|
||||
func Getenv(key string) string {
|
||||
return c.GoString(os.Getenv(c.AllocaCStr(key)))
|
||||
}
|
||||
|
||||
func Geteuid() int {
|
||||
return int(os.Geteuid())
|
||||
}
|
||||
@@ -330,14 +322,6 @@ func Rename(oldpath, newpath string) error {
|
||||
// TODO(xsw):
|
||||
// func SameFile(fi1, fi2 FileInfo) bool
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
ret := os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return &SyscallError{"setenv", syscall.Errno(ret)}
|
||||
}
|
||||
|
||||
func Symlink(oldname, newname string) error {
|
||||
ret := os.Symlink(c.AllocaCStr(oldname), c.AllocaCStr(newname))
|
||||
if ret == 0 {
|
||||
@@ -357,14 +341,6 @@ func Truncate(name string, size int64) error {
|
||||
return toPathErr("truncate", name, ret)
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
ret := os.Unsetenv(c.AllocaCStr(key))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return syscall.Errno(ret)
|
||||
}
|
||||
|
||||
// UserCacheDir returns the default root directory to use for user-specific
|
||||
// cached data. Users should create their own application-specific subdirectory
|
||||
// within this one and use that.
|
||||
|
||||
173
internal/lib/syscall/env_unix.go
Normal file
173
internal/lib/syscall/env_unix.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
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 EINVAL
|
||||
}
|
||||
for i := 0; i < len(key); i++ {
|
||||
if key[i] == '=' || key[i] == 0 {
|
||||
return 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 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
|
||||
}
|
||||
96
internal/lib/syscall/env_windows.go
Normal file
96
internal/lib/syscall/env_windows.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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.
|
||||
|
||||
// Windows environment variables.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func Getenv(key string) (value string, found bool) {
|
||||
keyp, err := UTF16PtrFromString(key)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
n := uint32(100)
|
||||
for {
|
||||
b := make([]uint16, n)
|
||||
n, err = GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
|
||||
if n == 0 && err == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
if n <= uint32(len(b)) {
|
||||
return UTF16ToString(b[:n]), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
v, err := UTF16PtrFromString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyp, err := UTF16PtrFromString(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e := SetEnvironmentVariable(keyp, v)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
runtimeSetenv(key, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
keyp, err := UTF16PtrFromString(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e := SetEnvironmentVariable(keyp, nil)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
runtimeUnsetenv(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Clearenv() {
|
||||
for _, s := range Environ() {
|
||||
// Environment variables can begin with =
|
||||
// so start looking for the separator = at j=1.
|
||||
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
|
||||
for j := 1; j < len(s); j++ {
|
||||
if s[j] == '=' {
|
||||
Unsetenv(s[0:j])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Environ() []string {
|
||||
envp, e := GetEnvironmentStrings()
|
||||
if e != nil {
|
||||
return nil
|
||||
}
|
||||
defer FreeEnvironmentStrings(envp)
|
||||
|
||||
r := make([]string, 0, 50) // Empty with room to grow.
|
||||
const size = unsafe.Sizeof(*envp)
|
||||
for *envp != 0 { // environment block ends with empty string
|
||||
// find NUL terminator
|
||||
end := unsafe.Pointer(envp)
|
||||
for *(*uint16)(end) != 0 {
|
||||
end = unsafe.Add(end, size)
|
||||
}
|
||||
|
||||
entry := unsafe.Slice(envp, (uintptr(end)-uintptr(unsafe.Pointer(envp)))/size)
|
||||
r = append(r, UTF16ToString(entry))
|
||||
envp = (*uint16)(unsafe.Add(end, size))
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -73,14 +73,6 @@ func Getwd() (string, error) {
|
||||
return "", Errno(os.Errno)
|
||||
}
|
||||
|
||||
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 Getpid() (pid int) {
|
||||
return int(os.Getpid())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user