diff --git a/c/c.go b/c/c.go index 0b39ed8b..7e8e34b0 100644 --- a/c/c.go +++ b/c/c.go @@ -77,6 +77,9 @@ func Memset(s Pointer, c Int, n uintptr) Pointer // ----------------------------------------------------------------------------- +//go:linkname GoString llgo.string +func GoString(cstr *Char, n ...int) string + //go:linkname GoStringData llgo.stringData func GoStringData(string) *Char diff --git a/c/os/os.go b/c/os/os.go new file mode 100644 index 00000000..1f1d8b46 --- /dev/null +++ b/c/os/os.go @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package os + +// #include +// #include +import "C" + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +const ( + LLGoPackage = "decl" +) + +const ( + PATH_MAX = C.PATH_MAX +) + +type ( + ModeT C.mode_t + UidT C.uid_t + GidT C.gid_t + OffT C.off_t + DevT C.dev_t + StatT C.struct_stat +) + +//go:linkname Errno errno +var Errno c.Int + +//go:linkname Umask C.umask +func Umask(cmask ModeT) ModeT + +//go:linkname Mkdir C.mkdir +func Mkdir(path *c.Char, mode ModeT) c.Int + +//go:linkname Rmdir C.rmdir +func Rmdir(path *c.Char) c.Int + +//go:linkname Link C.link +func Link(oldpath *c.Char, newpath *c.Char) c.Int + +//go:linkname Symlink C.symlink +func Symlink(target *c.Char, linkpath *c.Char) c.Int + +//go:linkname Readlink C.readlink +func Readlink(path *c.Char, buf c.Pointer, bufsize uintptr) int + +//go:linkname Unlink C.unlink +func Unlink(path *c.Char) c.Int + +//go:linkname Remove C.remove +func Remove(path *c.Char) c.Int + +//go:linkname Rename C.rename +func Rename(oldpath *c.Char, newpath *c.Char) c.Int + +//go:linkname Stat C.stat +func Stat(path *c.Char, buf *StatT) c.Int + +//go:linkname Lstat C.lstat +func Lstat(path *c.Char, buf *StatT) c.Int + +//go:linkname Truncate C.truncate +func Truncate(path *c.Char, length OffT) c.Int + +//go:linkname Chmod C.chmod +func Chmod(path *c.Char, mode ModeT) c.Int + +//go:linkname Chown C.chown +func Chown(path *c.Char, owner UidT, group GidT) c.Int + +//go:linkname Lchown C.lchown +func Lchown(path *c.Char, owner UidT, group GidT) c.Int + +// ----------------------------------------------------------------------------- + +//go:linkname Getcwd C.getcwd +func Getcwd(buffer c.Pointer, size uintptr) *c.Char + +//go:linkname Chdir C.chdir +func Chdir(path *c.Char) c.Int + +//go:linkname Chroot C.chroot +func Chroot(path *c.Char) c.Int + +// ----------------------------------------------------------------------------- + +//go:linkname Environ C.environ +func Environ() **c.Char + +//go:linkname Getenv C.getenv +func Getenv(name *c.Char) *c.Char + +//go:linkname Setenv C.setenv +func Setenv(name *c.Char, value *c.Char, overwrite c.Int) c.Int + +//go:linkname Putenv C.putenv +func Putenv(env *c.Char) c.Int + +//go:linkname Unsetenv C.unsetenv +func Unsetenv(name *c.Char) c.Int + +//go:linkname Clearenv C.clearenv +func Clearenv() + +// ----------------------------------------------------------------------------- + +//go:linkname Fchdir C.fchdir +func Fchdir(dirfd c.Int) c.Int + +//go:linkname Faccessat C.faccessat +func Faccessat(dirfd c.Int, path *c.Char, mode c.Int, flags c.Int) c.Int + +//go:linkname Fchmodat C.fchmodat +func Fchmodat(dirfd c.Int, path *c.Char, mode ModeT, flags c.Int) c.Int + +//go:linkname Fchownat C.fchownat +func Fchownat(dirfd c.Int, path *c.Char, owner UidT, group GidT, flags c.Int) c.Int + +//go:linkname Fstatat C.fstatat +func Fstatat(dirfd c.Int, path *c.Char, buf *StatT, flags c.Int) c.Int + +// ----------------------------------------------------------------------------- + +//go:linkname Open C.open +func Open(path *c.Char, flags c.Int, mode ModeT) c.Int + +//go:linkname Creat C.creat +func Creat(path *c.Char, mode ModeT) c.Int + +//go:linkname Dup C.dup +func Dup(fd c.Int) c.Int + +//go:linkname Dup2 C.dup2 +func Dup2(oldfd c.Int, newfd c.Int) c.Int + +/* TODO(xsw): +On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64, pipe() has the following prototype: +struct fd_pair { + long fd[2]; +}; +struct fd_pair pipe(void); +*/ +//go:linkname Pipe C.pipe +func Pipe(fds *[2]c.Int) c.Int + +//go:linkname Mkfifo C.mkfifo +func Mkfifo(path *c.Char, mode ModeT) c.Int + +//go:linkname Mknod C.mknod +func Mknod(path *c.Char, mode ModeT, dev DevT) c.Int + +//go:linkname Close C.close +func Close(fd c.Int) c.Int + +//go:linkname Read C.read +func Read(fd c.Int, buf c.Pointer, count uintptr) int + +//go:linkname Write C.write +func Write(fd c.Int, buf c.Pointer, count uintptr) int + +//go:linkname Lseek C.lseek +func Lseek(fd c.Int, offset OffT, whence c.Int) OffT + +//go:linkname Fsync C.fsync +func Fsync(fd c.Int) c.Int + +//go:linkname Ftruncate C.ftruncate +func Ftruncate(fd c.Int, length OffT) c.Int + +//go:linkname Fchmod C.fchmod +func Fchmod(fd c.Int, mode ModeT) c.Int + +//go:linkname Fchown C.fchown +func Fchown(fd c.Int, owner UidT, group GidT) c.Int + +//go:linkname Fstat C.fstat +func Fstat(fd c.Int, buf *StatT) c.Int + +//go:linkname Isatty C.isatty +func Isatty(fd c.Int) c.Int + +// ----------------------------------------------------------------------------- + +//go:linkname Exit C.exit +func Exit(c.Int) + +//go:linkname Getpid C.getpid +func Getpid() c.Int + +//go:linkname Getppid C.getppid +func Getppid() c.Int + +//go:linkname Getuid C.getuid +func Getuid() UidT + +//go:linkname Geteuid C.geteuid +func Geteuid() UidT + +//go:linkname Getgid C.getgid +func Getgid() GidT + +//go:linkname Getegid C.getegid +func Getegid() GidT + +// ----------------------------------------------------------------------------- diff --git a/c/pthread/sync/sync.go b/c/pthread/sync/sync.go index 065e4b44..16664719 100644 --- a/c/pthread/sync/sync.go +++ b/c/pthread/sync/sync.go @@ -47,10 +47,10 @@ func (o *Once) Do(f func()) c.Int { return 0 } type MutexType c.Int const ( - MutexNormal MutexType = C.PTHREAD_MUTEX_NORMAL - MutexErrorCheck MutexType = C.PTHREAD_MUTEX_ERRORCHECK - MutexRecursive MutexType = C.PTHREAD_MUTEX_RECURSIVE - MutexDefault MutexType = C.PTHREAD_MUTEX_DEFAULT + MUTEX_NORMAL MutexType = C.PTHREAD_MUTEX_NORMAL + MUTEX_ERRORCHECK MutexType = C.PTHREAD_MUTEX_ERRORCHECK + MUTEX_RECURSIVE MutexType = C.PTHREAD_MUTEX_RECURSIVE + MUTEX_DEFAULT MutexType = C.PTHREAD_MUTEX_DEFAULT ) // MutexAttr is a mutex attribute object. diff --git a/cl/_testlibgo/os/in.go b/cl/_testlibgo/os/in.go new file mode 100644 index 00000000..a8d77ed8 --- /dev/null +++ b/cl/_testlibgo/os/in.go @@ -0,0 +1,11 @@ +package main + +import "os" + +func main() { + wd, err := os.Getwd() + if err != nil { + panic(err) + } + println("cwd:", wd) +} diff --git a/cl/import.go b/cl/import.go index fa00b6b5..9244c90e 100644 --- a/cl/import.go +++ b/cl/import.go @@ -520,8 +520,8 @@ func ignoreName(name string) bool { return strings.HasPrefix(name, "internal/") || strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || strings.HasPrefix(name, "time.") || strings.HasPrefix(name, "syscall.") || - strings.HasPrefix(name, "os.") || strings.HasPrefix(name, "plugin.") || - strings.HasPrefix(name, "reflect.") || strings.HasPrefix(name, "runtime/") + strings.HasPrefix(name, "plugin.") || strings.HasPrefix(name, "reflect.") || + strings.HasPrefix(name, "runtime/") } // ----------------------------------------------------------------------------- diff --git a/internal/build/build.go b/internal/build/build.go index e7174e3e..cfcd715f 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -719,9 +719,12 @@ type none struct{} var hasAltPkg = map[string]none{ "errors": {}, + "io": {}, + "io/fs": {}, "math": {}, "sync": {}, "sync/atomic": {}, + "os": {}, "runtime": {}, } diff --git a/internal/lib/io/fs/fs.go b/internal/lib/io/fs/fs.go new file mode 100644 index 00000000..faaf0ade --- /dev/null +++ b/internal/lib/io/fs/fs.go @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fs + +// llgo:skipall +import ( + _ "unsafe" +) + +// ----------------------------------------------------------------------------- + +// A FileMode represents a file's mode and permission bits. +// The bits have the same definition on all systems, so that +// information about files can be moved from one system +// to another portably. Not all bits apply to all systems. +// The only required bit is [ModeDir] for directories. +type FileMode uint32 + +// ----------------------------------------------------------------------------- + +// PathError records an error and the operation and file path that caused it. +type PathError struct { + Op string + Path string + Err error +} + +func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() } + +func (e *PathError) Unwrap() error { return e.Err } + +// Timeout reports whether this error represents a timeout. +func (e *PathError) Timeout() bool { + t, ok := e.Err.(interface{ Timeout() bool }) + return ok && t.Timeout() +} + +// ----------------------------------------------------------------------------- diff --git a/internal/lib/io/io.go b/internal/lib/io/io.go new file mode 100644 index 00000000..9a30406e --- /dev/null +++ b/internal/lib/io/io.go @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io + +// llgo:skipall +import ( + _ "unsafe" +) diff --git a/internal/lib/os/os.go b/internal/lib/os/os.go new file mode 100644 index 00000000..d250a7e6 --- /dev/null +++ b/internal/lib/os/os.go @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package os + +// llgo:skipall +import ( + "io/fs" + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/os" +) + +const ( + LLGoPackage = true +) + +type ( + FileMode = fs.FileMode +) + +type timeout interface { + Timeout() bool +} + +type PathError = fs.PathError + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Err error +} + +func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() } + +func (e *SyscallError) Unwrap() error { return e.Err } + +// Timeout reports whether this error represents a timeout. +func (e *SyscallError) Timeout() bool { + t, ok := e.Err.(timeout) + return ok && t.Timeout() +} + +// NewSyscallError returns, as an error, a new SyscallError +// with the given system call name and error details. +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err error) error { + if err == nil { + return nil + } + return &SyscallError{syscall, err} +} + +// LinkError records an error during a link or symlink or rename +// system call and the paths that caused it. +type LinkError struct { + Op string + Old string + New string + Err error +} + +func (e *LinkError) Error() string { + return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error() +} + +func (e *LinkError) Unwrap() error { + return e.Err +} + +func toMode(mode FileMode) os.ModeT { + panic("todo") +} + +func toPathErr(op, path string, errno c.Int) error { + panic("todo") +} + +func toSyscallErr(errno c.Int) error { + panic("todo") +} + +func Chdir(dir string) error { + ret := os.Chdir(c.AllocaCStr(dir)) + if ret == 0 { + return nil + } + return toPathErr("chdir", dir, ret) +} + +func Chmod(name string, mode FileMode) error { + ret := os.Chmod(c.AllocaCStr(name), toMode(mode)) + if ret == 0 { + return nil + } + return toPathErr("chmod", name, ret) +} + +func Chown(name string, uid, gid int) error { + ret := os.Chown(c.AllocaCStr(name), os.UidT(uid), os.GidT(gid)) + if ret == 0 { + return nil + } + return toPathErr("chown", name, ret) +} + +// TODO(xsw): +// func Chtimes(name string, atime time.Time, mtime time.Time) error + +//go:linkname Clearenv C.clearenv +func Clearenv() + +// TODO(xsw): +// func DirFS(dir string) fs.FS +// func Environ() []string +// func Executable() (string, error) + +func Exit(code int) { + os.Exit(c.Int(code)) +} + +// TODO(xsw): +// func Expand(s string, mapping func(string) string) string +// func ExpandEnv(s string) string + +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()) +} + +func Getgid() int { + return int(os.Getgid()) +} + +// TODO(xsw): +// func Getgroups() ([]int, error) +// func Getpagesize() int + +func Getpid() int { + return int(os.Getpid()) +} + +func Getppid() int { + return int(os.Getppid()) +} + +func Getuid() int { + return int(os.Getuid()) +} + +func Getwd() (dir string, err error) { + wd := os.Getcwd(c.Alloca(os.PATH_MAX), os.PATH_MAX) + if wd != nil { + return c.GoString(wd), nil + } + return "", toSyscallErr(os.Errno) +} + +// TODO(xsw): +// func Hostname() (name string, err error) +// func IsExist(err error) bool +// func IsNotExist(err error) bool +// func IsPathSeparator(c uint8) bool +// func IsPermission(err error) bool +// func IsTimeout(err error) bool + +func Lchown(name string, uid, gid int) error { + ret := os.Lchown(c.AllocaCStr(name), os.UidT(uid), os.GidT(gid)) + if ret == 0 { + return nil + } + return toPathErr("lchown", name, ret) +} + +func Link(oldname, newname string) error { + ret := os.Link(c.AllocaCStr(oldname), c.AllocaCStr(newname)) + if ret == 0 { + return nil + } + return &LinkError{"link", oldname, newname, toSyscallErr(ret)} +} + +// TODO(xsw): +// func LookupEnv(key string) (string, bool) + +func Mkdir(name string, perm FileMode) error { + ret := os.Mkdir(c.AllocaCStr(name), toMode(perm)) + if ret == 0 { + return nil + } + return toPathErr("mkdir", name, ret) +} + +// TODO(xsw): +// func MkdirAll(path string, perm FileMode) error +// func MkdirTemp(dir, pattern string) (string, error) +// func NewSyscallError(syscall string, err error) error +// func Pipe() (r *File, w *File, err error) +// func ReadFile(name string) ([]byte, error) + +func Readlink(name string) (string, error) { + ptr := c.Alloca(os.PATH_MAX) + ret := os.Readlink(c.AllocaCStr(name), ptr, os.PATH_MAX) + if ret < os.PATH_MAX { + return c.GoString((*c.Char)(ptr), ret), nil + } + panic("todo: buffer too small") +} + +func Remove(name string) error { + ret := os.Remove(c.AllocaCStr(name)) + if ret == 0 { + return nil + } + return toPathErr("remove", name, ret) +} + +// TODO(xsw): +// func RemoveAll(path string) error + +func Rename(oldpath, newpath string) error { + ret := os.Rename(c.AllocaCStr(oldpath), c.AllocaCStr(newpath)) + if ret == 0 { + return nil + } + return &LinkError{"rename", oldpath, newpath, toSyscallErr(ret)} +} + +// 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{Syscall: "setenv", Err: toSyscallErr(ret)} +} + +func Symlink(oldname, newname string) error { + ret := os.Symlink(c.AllocaCStr(oldname), c.AllocaCStr(newname)) + if ret == 0 { + return nil + } + return &LinkError{"symlink", oldname, newname, toSyscallErr(ret)} +} + +// TODO(xsw): +// func TempDir() string + +func Truncate(name string, size int64) error { + ret := os.Truncate(c.AllocaCStr(name), os.OffT(size)) + if ret == 0 { + return nil + } + return toPathErr("truncate", name, ret) +} + +func Unsetenv(key string) error { + ret := os.Unsetenv(c.AllocaCStr(key)) + if ret == 0 { + return nil + } + return toSyscallErr(ret) +} + +// TODO(xsw): +// func UserCacheDir() (string, error) +// func UserConfigDir() (string, error) +// func UserHomeDir() (string, error) +// func WriteFile(name string, data []byte, perm FileMode) error