library patch: syscall, os environ

This commit is contained in:
xushiwei
2024-07-18 14:30:49 +08:00
parent f8bacfcc67
commit 3da3c8ecd8
7 changed files with 462 additions and 32 deletions

View File

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

View File

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

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

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

View File

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