build: separate compiler and libs
This commit is contained in:
159
compiler/internal/lib/os/env.go
Normal file
159
compiler/internal/lib/os/env.go
Normal file
@@ -0,0 +1,159 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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()
|
||||
}
|
||||
144
compiler/internal/lib/os/error.go
Normal file
144
compiler/internal/lib/os/error.go
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Portable analogs of some common system call errors.
|
||||
//
|
||||
// Errors returned from this package may be tested against these errors
|
||||
// with errors.Is.
|
||||
var (
|
||||
// ErrInvalid indicates an invalid argument.
|
||||
// Methods on File will return this error when the receiver is nil.
|
||||
ErrInvalid = fs.ErrInvalid // "invalid argument"
|
||||
|
||||
ErrPermission = fs.ErrPermission // "permission denied"
|
||||
ErrExist = fs.ErrExist // "file already exists"
|
||||
ErrNotExist = fs.ErrNotExist // "file does not exist"
|
||||
ErrClosed = fs.ErrClosed // "file already closed"
|
||||
|
||||
// TODO(xsw):
|
||||
// ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
|
||||
// ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout"
|
||||
)
|
||||
|
||||
// func errNoDeadline() error { return poll.ErrNoDeadline }
|
||||
|
||||
// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded.
|
||||
// This error comes from the internal/poll package, which is also
|
||||
// used by package net. Doing it this way ensures that the net
|
||||
// package will return os.ErrDeadlineExceeded for an exceeded deadline,
|
||||
// as documented by net.Conn.SetDeadline, without requiring any extra
|
||||
// work in the net package and without requiring the internal/poll
|
||||
// package to import os (which it can't, because that would be circular).
|
||||
// func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded }
|
||||
|
||||
type timeout interface {
|
||||
Timeout() bool
|
||||
}
|
||||
|
||||
// PathError records an error and the operation and file path that caused it.
|
||||
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}
|
||||
}
|
||||
|
||||
// IsExist returns a boolean indicating whether the error is known to report
|
||||
// that a file or directory already exists. It is satisfied by ErrExist as
|
||||
// well as some syscall errors.
|
||||
//
|
||||
// This function predates errors.Is. It only supports errors returned by
|
||||
// the os package. New code should use errors.Is(err, fs.ErrExist).
|
||||
func IsExist(err error) bool {
|
||||
return underlyingErrorIs(err, ErrExist)
|
||||
}
|
||||
|
||||
// IsNotExist returns a boolean indicating whether the error is known to
|
||||
// report that a file or directory does not exist. It is satisfied by
|
||||
// ErrNotExist as well as some syscall errors.
|
||||
//
|
||||
// This function predates errors.Is. It only supports errors returned by
|
||||
// the os package. New code should use errors.Is(err, fs.ErrNotExist).
|
||||
func IsNotExist(err error) bool {
|
||||
return underlyingErrorIs(err, ErrNotExist)
|
||||
}
|
||||
|
||||
// IsPermission returns a boolean indicating whether the error is known to
|
||||
// report that permission is denied. It is satisfied by ErrPermission as well
|
||||
// as some syscall errors.
|
||||
//
|
||||
// This function predates errors.Is. It only supports errors returned by
|
||||
// the os package. New code should use errors.Is(err, fs.ErrPermission).
|
||||
func IsPermission(err error) bool {
|
||||
return underlyingErrorIs(err, ErrPermission)
|
||||
}
|
||||
|
||||
// IsTimeout returns a boolean indicating whether the error is known
|
||||
// to report that a timeout occurred.
|
||||
//
|
||||
// This function predates errors.Is, and the notion of whether an
|
||||
// error indicates a timeout can be ambiguous. For example, the Unix
|
||||
// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not.
|
||||
// New code should use errors.Is with a value appropriate to the call
|
||||
// returning the error, such as os.ErrDeadlineExceeded.
|
||||
func IsTimeout(err error) bool {
|
||||
terr, ok := underlyingError(err).(timeout)
|
||||
return ok && terr.Timeout()
|
||||
}
|
||||
|
||||
type syscallErrorType = syscall.Errno
|
||||
|
||||
func underlyingErrorIs(err, target error) bool {
|
||||
// Note that this function is not errors.Is:
|
||||
// underlyingError only unwraps the specific error-wrapping types
|
||||
// that it historically did, not all errors implementing Unwrap().
|
||||
err = underlyingError(err)
|
||||
if err == target {
|
||||
return true
|
||||
}
|
||||
// To preserve prior behavior, only examine syscall errors.
|
||||
e, ok := err.(syscallErrorType)
|
||||
return ok && e.Is(target)
|
||||
}
|
||||
|
||||
// underlyingError returns the underlying error for known os error types.
|
||||
func underlyingError(err error) error {
|
||||
switch err := err.(type) {
|
||||
case *PathError:
|
||||
return err.Err
|
||||
case *LinkError:
|
||||
return err.Err
|
||||
case *SyscallError:
|
||||
return err.Err
|
||||
}
|
||||
return err
|
||||
}
|
||||
172
compiler/internal/lib/os/exec.go
Normal file
172
compiler/internal/lib/os/exec.go
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrProcessDone indicates a Process has finished.
|
||||
var ErrProcessDone = errors.New("os: process already finished")
|
||||
|
||||
// Process stores the information about a process created by StartProcess.
|
||||
type Process struct {
|
||||
Pid int
|
||||
handle uintptr // handle is accessed atomically on Windows
|
||||
isdone atomic.Bool // process has been successfully waited on
|
||||
sigMu sync.RWMutex // avoid race between wait and signal
|
||||
}
|
||||
|
||||
func newProcess(pid int, handle uintptr) *Process {
|
||||
p := &Process{Pid: pid, handle: handle}
|
||||
runtime.SetFinalizer(p, (*Process).Release)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Process) setDone() {
|
||||
p.isdone.Store(true)
|
||||
}
|
||||
|
||||
func (p *Process) done() bool {
|
||||
return p.isdone.Load()
|
||||
}
|
||||
|
||||
// ProcAttr holds the attributes that will be applied to a new process
|
||||
// started by StartProcess.
|
||||
type ProcAttr struct {
|
||||
// If Dir is non-empty, the child changes into the directory before
|
||||
// creating the process.
|
||||
Dir string
|
||||
// If Env is non-nil, it gives the environment variables for the
|
||||
// new process in the form returned by Environ.
|
||||
// If it is nil, the result of Environ will be used.
|
||||
Env []string
|
||||
// Files specifies the open files inherited by the new process. The
|
||||
// first three entries correspond to standard input, standard output, and
|
||||
// standard error. An implementation may support additional entries,
|
||||
// depending on the underlying operating system. A nil entry corresponds
|
||||
// to that file being closed when the process starts.
|
||||
// On Unix systems, StartProcess will change these File values
|
||||
// to blocking mode, which means that SetDeadline will stop working
|
||||
// and calling Close will not interrupt a Read or Write.
|
||||
Files []*File
|
||||
|
||||
// Operating system-specific process creation attributes.
|
||||
// Note that setting this field means that your program
|
||||
// may not execute properly or even compile on some
|
||||
// operating systems.
|
||||
Sys *syscall.SysProcAttr
|
||||
}
|
||||
|
||||
// A Signal represents an operating system signal.
|
||||
// The usual underlying implementation is operating system-dependent:
|
||||
// on Unix it is syscall.Signal.
|
||||
type Signal interface {
|
||||
String() string
|
||||
Signal() // to distinguish from other Stringers
|
||||
}
|
||||
|
||||
// FindProcess looks for a running process by its pid.
|
||||
//
|
||||
// The Process it returns can be used to obtain information
|
||||
// about the underlying operating system process.
|
||||
//
|
||||
// On Unix systems, FindProcess always succeeds and returns a Process
|
||||
// for the given pid, regardless of whether the process exists. To test whether
|
||||
// the process actually exists, see whether p.Signal(syscall.Signal(0)) reports
|
||||
// an error.
|
||||
func FindProcess(pid int) (*Process, error) {
|
||||
return findProcess(pid)
|
||||
}
|
||||
|
||||
// StartProcess starts a new process with the program, arguments and attributes
|
||||
// specified by name, argv and attr. The argv slice will become os.Args in the
|
||||
// new process, so it normally starts with the program name.
|
||||
//
|
||||
// If the calling goroutine has locked the operating system thread
|
||||
// with runtime.LockOSThread and modified any inheritable OS-level
|
||||
// thread state (for example, Linux or Plan 9 name spaces), the new
|
||||
// process will inherit the caller's thread state.
|
||||
//
|
||||
// StartProcess is a low-level interface. The os/exec package provides
|
||||
// higher-level interfaces.
|
||||
//
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
|
||||
return startProcess(name, argv, attr)
|
||||
}
|
||||
|
||||
// Release releases any resources associated with the Process p,
|
||||
// rendering it unusable in the future.
|
||||
// Release only needs to be called if Wait is not.
|
||||
func (p *Process) Release() error {
|
||||
return p.release()
|
||||
}
|
||||
|
||||
// Kill causes the Process to exit immediately. Kill does not wait until
|
||||
// the Process has actually exited. This only kills the Process itself,
|
||||
// not any other processes it may have started.
|
||||
func (p *Process) Kill() error {
|
||||
return p.kill()
|
||||
}
|
||||
|
||||
// Wait waits for the Process to exit, and then returns a
|
||||
// ProcessState describing its status and an error, if any.
|
||||
// Wait releases any resources associated with the Process.
|
||||
// On most operating systems, the Process must be a child
|
||||
// of the current process or an error will be returned.
|
||||
func (p *Process) Wait() (*ProcessState, error) {
|
||||
return p.wait()
|
||||
}
|
||||
|
||||
// Signal sends a signal to the Process.
|
||||
// Sending Interrupt on Windows is not implemented.
|
||||
func (p *Process) Signal(sig Signal) error {
|
||||
return p.signal(sig)
|
||||
}
|
||||
|
||||
// UserTime returns the user CPU time of the exited process and its children.
|
||||
func (p *ProcessState) UserTime() time.Duration {
|
||||
return p.userTime()
|
||||
}
|
||||
|
||||
// SystemTime returns the system CPU time of the exited process and its children.
|
||||
func (p *ProcessState) SystemTime() time.Duration {
|
||||
return p.systemTime()
|
||||
}
|
||||
|
||||
// Exited reports whether the program has exited.
|
||||
// On Unix systems this reports true if the program exited due to calling exit,
|
||||
// but false if the program terminated due to a signal.
|
||||
func (p *ProcessState) Exited() bool {
|
||||
return p.exited()
|
||||
}
|
||||
|
||||
// Success reports whether the program exited successfully,
|
||||
// such as with exit status 0 on Unix.
|
||||
func (p *ProcessState) Success() bool {
|
||||
return p.success()
|
||||
}
|
||||
|
||||
// Sys returns system-dependent exit information about
|
||||
// the process. Convert it to the appropriate underlying
|
||||
// type, such as syscall.WaitStatus on Unix, to access its contents.
|
||||
func (p *ProcessState) Sys() any {
|
||||
return p.sys()
|
||||
}
|
||||
|
||||
// SysUsage returns system-dependent resource usage information about
|
||||
// the exited process. Convert it to the appropriate underlying
|
||||
// type, such as *syscall.Rusage on Unix, to access its contents.
|
||||
// (On Unix, *syscall.Rusage matches struct rusage as defined in the
|
||||
// getrusage(2) manual page.)
|
||||
func (p *ProcessState) SysUsage() any {
|
||||
return p.sysUsage()
|
||||
}
|
||||
1234
compiler/internal/lib/os/exec/exec.go
Normal file
1234
compiler/internal/lib/os/exec/exec.go
Normal file
File diff suppressed because it is too large
Load Diff
19
compiler/internal/lib/os/exec/exec_plan9.go
Normal file
19
compiler/internal/lib/os/exec/exec_plan9.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
package exec
|
||||
|
||||
import "io/fs"
|
||||
|
||||
// skipStdinCopyError optionally specifies a function which reports
|
||||
// whether the provided stdin copy error should be ignored.
|
||||
func skipStdinCopyError(err error) bool {
|
||||
// Ignore hungup errors copying to stdin if the program
|
||||
// completed successfully otherwise.
|
||||
// See Issue 35753.
|
||||
pe, ok := err.(*fs.PathError)
|
||||
return ok &&
|
||||
pe.Op == "write" && pe.Path == "|1" &&
|
||||
pe.Err.Error() == "i/o on hungup channel"
|
||||
}
|
||||
24
compiler/internal/lib/os/exec/exec_unix.go
Normal file
24
compiler/internal/lib/os/exec/exec_unix.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2015 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 !plan9 && !windows
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// skipStdinCopyError optionally specifies a function which reports
|
||||
// whether the provided stdin copy error should be ignored.
|
||||
func skipStdinCopyError(err error) bool {
|
||||
// Ignore EPIPE errors copying to stdin if the program
|
||||
// completed successfully otherwise.
|
||||
// See Issue 9173.
|
||||
pe, ok := err.(*fs.PathError)
|
||||
return ok &&
|
||||
pe.Op == "write" && pe.Path == "|1" &&
|
||||
pe.Err == syscall.EPIPE
|
||||
}
|
||||
23
compiler/internal/lib/os/exec/exec_windows.go
Normal file
23
compiler/internal/lib/os/exec/exec_windows.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// skipStdinCopyError optionally specifies a function which reports
|
||||
// whether the provided stdin copy error should be ignored.
|
||||
func skipStdinCopyError(err error) bool {
|
||||
// Ignore ERROR_BROKEN_PIPE and ERROR_NO_DATA errors copying
|
||||
// to stdin if the program completed successfully otherwise.
|
||||
// See Issue 20445.
|
||||
const _ERROR_NO_DATA = syscall.Errno(0xe8)
|
||||
pe, ok := err.(*fs.PathError)
|
||||
return ok &&
|
||||
pe.Op == "write" && pe.Path == "|1" &&
|
||||
(pe.Err == syscall.ERROR_BROKEN_PIPE || pe.Err == _ERROR_NO_DATA)
|
||||
}
|
||||
66
compiler/internal/lib/os/exec/lp_plan9.go
Normal file
66
compiler/internal/lib/os/exec/lp_plan9.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||
var ErrNotFound = errors.New("executable file not found in $path")
|
||||
|
||||
func findExecutable(file string) error {
|
||||
d, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
|
||||
return nil
|
||||
}
|
||||
return fs.ErrPermission
|
||||
}
|
||||
|
||||
// LookPath searches for an executable named file in the
|
||||
// directories named by the path environment variable.
|
||||
// If file begins with "/", "#", "./", or "../", it is tried
|
||||
// directly and the path is not consulted.
|
||||
// On success, the result is an absolute path.
|
||||
//
|
||||
// In older versions of Go, LookPath could return a path relative to the current directory.
|
||||
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
|
||||
// errors.Is(err, ErrDot). See the package documentation for more details.
|
||||
func LookPath(file string) (string, error) {
|
||||
// skip the path lookup for these prefixes
|
||||
skip := []string{"/", "#", "./", "../"}
|
||||
|
||||
for _, p := range skip {
|
||||
if strings.HasPrefix(file, p) {
|
||||
err := findExecutable(file)
|
||||
if err == nil {
|
||||
return file, nil
|
||||
}
|
||||
return "", &Error{file, err}
|
||||
}
|
||||
}
|
||||
|
||||
path := os.Getenv("path")
|
||||
for _, dir := range filepath.SplitList(path) {
|
||||
path := filepath.Join(dir, file)
|
||||
if err := findExecutable(path); err == nil {
|
||||
if !filepath.IsAbs(path) {
|
||||
if execerrdot.Value() != "0" {
|
||||
return path, &Error{file, ErrDot}
|
||||
}
|
||||
execerrdot.IncNonDefault()
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
return "", &Error{file, ErrNotFound}
|
||||
}
|
||||
84
compiler/internal/lib/os/exec/lp_unix.go
Normal file
84
compiler/internal/lib/os/exec/lp_unix.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||
var ErrNotFound = errors.New("executable file not found in $PATH")
|
||||
|
||||
func findExecutable(file string) error {
|
||||
d, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m := d.Mode()
|
||||
if m.IsDir() {
|
||||
return syscall.EISDIR
|
||||
}
|
||||
err = unixEaccess(file, unix_X_OK)
|
||||
// ENOSYS means Eaccess is not available or not implemented.
|
||||
// EPERM can be returned by Linux containers employing seccomp.
|
||||
// In both cases, fall back to checking the permission bits.
|
||||
if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) {
|
||||
return err
|
||||
}
|
||||
if m&0111 != 0 {
|
||||
return nil
|
||||
}
|
||||
return fs.ErrPermission
|
||||
}
|
||||
|
||||
// LookPath searches for an executable named file in the
|
||||
// directories named by the PATH environment variable.
|
||||
// If file contains a slash, it is tried directly and the PATH is not consulted.
|
||||
// Otherwise, on success, the result is an absolute path.
|
||||
//
|
||||
// In older versions of Go, LookPath could return a path relative to the current directory.
|
||||
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
|
||||
// errors.Is(err, ErrDot). See the package documentation for more details.
|
||||
func LookPath(file string) (string, error) {
|
||||
// NOTE(rsc): I wish we could use the Plan 9 behavior here
|
||||
// (only bypass the path if file begins with / or ./ or ../)
|
||||
// but that would not match all the Unix shells.
|
||||
|
||||
if strings.Contains(file, "/") {
|
||||
err := findExecutable(file)
|
||||
if err == nil {
|
||||
return file, nil
|
||||
}
|
||||
return "", &Error{file, err}
|
||||
}
|
||||
path := os.Getenv("PATH")
|
||||
for _, dir := range filepath.SplitList(path) {
|
||||
if dir == "" {
|
||||
// Unix shell semantics: path element "" means "."
|
||||
dir = "."
|
||||
}
|
||||
path := filepath.Join(dir, file)
|
||||
if err := findExecutable(path); err == nil {
|
||||
if !filepath.IsAbs(path) {
|
||||
/* TODO(xsw):
|
||||
if execerrdot.Value() != "0" {
|
||||
return path, &Error{file, ErrDot}
|
||||
}
|
||||
execerrdot.IncNonDefault()
|
||||
*/
|
||||
panic("todo: exec.LookPath: !filepath.IsAbs(path)")
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
return "", &Error{file, ErrNotFound}
|
||||
}
|
||||
23
compiler/internal/lib/os/exec/lp_wasm.go
Normal file
23
compiler/internal/lib/os/exec/lp_wasm.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2018 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 wasm
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||
var ErrNotFound = errors.New("executable file not found in $PATH")
|
||||
|
||||
// LookPath searches for an executable named file in the
|
||||
// directories named by the PATH environment variable.
|
||||
// If file contains a slash, it is tried directly and the PATH is not consulted.
|
||||
// The result may be an absolute path or a path relative to the current directory.
|
||||
func LookPath(file string) (string, error) {
|
||||
// Wasm can not execute processes, so act as if there are no executables at all.
|
||||
return "", &Error{file, ErrNotFound}
|
||||
}
|
||||
145
compiler/internal/lib/os/exec/lp_windows.go
Normal file
145
compiler/internal/lib/os/exec/lp_windows.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// 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.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ErrNotFound is the error resulting if a path search failed to find an executable file.
|
||||
var ErrNotFound = errors.New("executable file not found in %PATH%")
|
||||
|
||||
func chkStat(file string) error {
|
||||
d, err := os.Stat(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return fs.ErrPermission
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasExt(file string) bool {
|
||||
i := strings.LastIndex(file, ".")
|
||||
if i < 0 {
|
||||
return false
|
||||
}
|
||||
return strings.LastIndexAny(file, `:\/`) < i
|
||||
}
|
||||
|
||||
func findExecutable(file string, exts []string) (string, error) {
|
||||
if len(exts) == 0 {
|
||||
return file, chkStat(file)
|
||||
}
|
||||
if hasExt(file) {
|
||||
if chkStat(file) == nil {
|
||||
return file, nil
|
||||
}
|
||||
}
|
||||
for _, e := range exts {
|
||||
if f := file + e; chkStat(f) == nil {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return "", fs.ErrNotExist
|
||||
}
|
||||
|
||||
// LookPath searches for an executable named file in the
|
||||
// directories named by the PATH environment variable.
|
||||
// LookPath also uses PATHEXT environment variable to match
|
||||
// a suitable candidate.
|
||||
// If file contains a slash, it is tried directly and the PATH is not consulted.
|
||||
// Otherwise, on success, the result is an absolute path.
|
||||
//
|
||||
// In older versions of Go, LookPath could return a path relative to the current directory.
|
||||
// As of Go 1.19, LookPath will instead return that path along with an error satisfying
|
||||
// errors.Is(err, ErrDot). See the package documentation for more details.
|
||||
func LookPath(file string) (string, error) {
|
||||
var exts []string
|
||||
x := os.Getenv(`PATHEXT`)
|
||||
if x != "" {
|
||||
for _, e := range strings.Split(strings.ToLower(x), `;`) {
|
||||
if e == "" {
|
||||
continue
|
||||
}
|
||||
if e[0] != '.' {
|
||||
e = "." + e
|
||||
}
|
||||
exts = append(exts, e)
|
||||
}
|
||||
} else {
|
||||
exts = []string{".com", ".exe", ".bat", ".cmd"}
|
||||
}
|
||||
|
||||
if strings.ContainsAny(file, `:\/`) {
|
||||
f, err := findExecutable(file, exts)
|
||||
if err == nil {
|
||||
return f, nil
|
||||
}
|
||||
return "", &Error{file, err}
|
||||
}
|
||||
|
||||
// On Windows, creating the NoDefaultCurrentDirectoryInExePath
|
||||
// environment variable (with any value or no value!) signals that
|
||||
// path lookups should skip the current directory.
|
||||
// In theory we are supposed to call NeedCurrentDirectoryForExePathW
|
||||
// "as the registry location of this environment variable can change"
|
||||
// but that seems exceedingly unlikely: it would break all users who
|
||||
// have configured their environment this way!
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw
|
||||
// See also go.dev/issue/43947.
|
||||
var (
|
||||
dotf string
|
||||
dotErr error
|
||||
)
|
||||
if _, found := syscall.Getenv("NoDefaultCurrentDirectoryInExePath"); !found {
|
||||
if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
|
||||
if execerrdot.Value() == "0" {
|
||||
execerrdot.IncNonDefault()
|
||||
return f, nil
|
||||
}
|
||||
dotf, dotErr = f, &Error{file, ErrDot}
|
||||
}
|
||||
}
|
||||
|
||||
path := os.Getenv("path")
|
||||
for _, dir := range filepath.SplitList(path) {
|
||||
if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
|
||||
if dotErr != nil {
|
||||
// https://go.dev/issue/53536: if we resolved a relative path implicitly,
|
||||
// and it is the same executable that would be resolved from the explicit %PATH%,
|
||||
// prefer the explicit name for the executable (and, likely, no error) instead
|
||||
// of the equivalent implicit name with ErrDot.
|
||||
//
|
||||
// Otherwise, return the ErrDot for the implicit path as soon as we find
|
||||
// out that the explicit one doesn't match.
|
||||
dotfi, dotfiErr := os.Lstat(dotf)
|
||||
fi, fiErr := os.Lstat(f)
|
||||
if dotfiErr != nil || fiErr != nil || !os.SameFile(dotfi, fi) {
|
||||
return dotf, dotErr
|
||||
}
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(f) {
|
||||
if execerrdot.Value() != "0" {
|
||||
return f, &Error{file, ErrDot}
|
||||
}
|
||||
execerrdot.IncNonDefault()
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
if dotErr != nil {
|
||||
return dotf, dotErr
|
||||
}
|
||||
return "", &Error{file, ErrNotFound}
|
||||
}
|
||||
13
compiler/internal/lib/os/exec/unix_constants.go
Normal file
13
compiler/internal/lib/os/exec/unix_constants.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 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
|
||||
|
||||
package exec
|
||||
|
||||
const (
|
||||
unix_R_OK = 0x4
|
||||
unix_W_OK = 0x2
|
||||
unix_X_OK = 0x1
|
||||
)
|
||||
15
compiler/internal/lib/os/exec/unix_eaccess_linux.go
Normal file
15
compiler/internal/lib/os/exec/unix_eaccess_linux.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/goplus/llgo/c/syscall/unix"
|
||||
)
|
||||
|
||||
func unixEaccess(path string, mode uint32) error {
|
||||
return syscall.Faccessat(unix.AT_FDCWD, path, mode, unix.AT_EACCESS)
|
||||
}
|
||||
13
compiler/internal/lib/os/exec/unix_eaccess_other.go
Normal file
13
compiler/internal/lib/os/exec/unix_eaccess_other.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2022 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 && !linux
|
||||
|
||||
package exec
|
||||
|
||||
import "syscall"
|
||||
|
||||
func unixEaccess(path string, mode uint32) error {
|
||||
return syscall.ENOSYS
|
||||
}
|
||||
149
compiler/internal/lib/os/exec_plan9.go
Normal file
149
compiler/internal/lib/os/exec_plan9.go
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"internal/itoa"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The only signal values guaranteed to be present in the os package
|
||||
// on all systems are Interrupt (send the process an interrupt) and
|
||||
// Kill (force the process to exit). Interrupt is not implemented on
|
||||
// Windows; using it with os.Process.Signal will return an error.
|
||||
var (
|
||||
Interrupt Signal = syscall.Note("interrupt")
|
||||
Kill Signal = syscall.Note("kill")
|
||||
)
|
||||
|
||||
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
|
||||
sysattr := &syscall.ProcAttr{
|
||||
Dir: attr.Dir,
|
||||
Env: attr.Env,
|
||||
Sys: attr.Sys,
|
||||
}
|
||||
|
||||
sysattr.Files = make([]uintptr, 0, len(attr.Files))
|
||||
for _, f := range attr.Files {
|
||||
sysattr.Files = append(sysattr.Files, f.Fd())
|
||||
}
|
||||
|
||||
pid, h, e := syscall.StartProcess(name, argv, sysattr)
|
||||
if e != nil {
|
||||
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
|
||||
}
|
||||
|
||||
return newProcess(pid, h), nil
|
||||
}
|
||||
|
||||
func (p *Process) writeProcFile(file string, data string) error {
|
||||
f, e := OpenFile("/proc/"+itoa.Itoa(p.Pid)+"/"+file, O_WRONLY, 0)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer f.Close()
|
||||
_, e = f.Write([]byte(data))
|
||||
return e
|
||||
}
|
||||
|
||||
func (p *Process) signal(sig Signal) error {
|
||||
if p.done() {
|
||||
return ErrProcessDone
|
||||
}
|
||||
if e := p.writeProcFile("note", sig.String()); e != nil {
|
||||
return NewSyscallError("signal", e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Process) kill() error {
|
||||
return p.signal(Kill)
|
||||
}
|
||||
|
||||
func (p *Process) wait() (ps *ProcessState, err error) {
|
||||
var waitmsg syscall.Waitmsg
|
||||
|
||||
if p.Pid == -1 {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
err = syscall.WaitProcess(p.Pid, &waitmsg)
|
||||
if err != nil {
|
||||
return nil, NewSyscallError("wait", err)
|
||||
}
|
||||
|
||||
p.setDone()
|
||||
ps = &ProcessState{
|
||||
pid: waitmsg.Pid,
|
||||
status: &waitmsg,
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *Process) release() error {
|
||||
// NOOP for Plan 9.
|
||||
p.Pid = -1
|
||||
// no need for a finalizer anymore
|
||||
runtime.SetFinalizer(p, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func findProcess(pid int) (p *Process, err error) {
|
||||
// NOOP for Plan 9.
|
||||
return newProcess(pid, 0), nil
|
||||
}
|
||||
|
||||
// ProcessState stores information about a process, as reported by Wait.
|
||||
type ProcessState struct {
|
||||
pid int // The process's id.
|
||||
status *syscall.Waitmsg // System-dependent status info.
|
||||
}
|
||||
|
||||
// Pid returns the process id of the exited process.
|
||||
func (p *ProcessState) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *ProcessState) exited() bool {
|
||||
return p.status.Exited()
|
||||
}
|
||||
|
||||
func (p *ProcessState) success() bool {
|
||||
return p.status.ExitStatus() == 0
|
||||
}
|
||||
|
||||
func (p *ProcessState) sys() any {
|
||||
return p.status
|
||||
}
|
||||
|
||||
func (p *ProcessState) sysUsage() any {
|
||||
return p.status
|
||||
}
|
||||
|
||||
func (p *ProcessState) userTime() time.Duration {
|
||||
return time.Duration(p.status.Time[0]) * time.Millisecond
|
||||
}
|
||||
|
||||
func (p *ProcessState) systemTime() time.Duration {
|
||||
return time.Duration(p.status.Time[1]) * time.Millisecond
|
||||
}
|
||||
|
||||
func (p *ProcessState) String() string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return "exit status: " + p.status.Msg
|
||||
}
|
||||
|
||||
// ExitCode returns the exit code of the exited process, or -1
|
||||
// if the process hasn't exited or was terminated by a signal.
|
||||
func (p *ProcessState) ExitCode() int {
|
||||
// return -1 if the process hasn't started.
|
||||
if p == nil {
|
||||
return -1
|
||||
}
|
||||
return p.status.ExitStatus()
|
||||
}
|
||||
138
compiler/internal/lib/os/exec_posix.go
Normal file
138
compiler/internal/lib/os/exec_posix.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2009 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) || wasip1 || windows
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/goplus/llgo/compiler/internal/lib/internal/itoa"
|
||||
"github.com/goplus/llgo/compiler/internal/lib/internal/syscall/execenv"
|
||||
)
|
||||
|
||||
// The only signal values guaranteed to be present in the os package on all
|
||||
// systems are os.Interrupt (send the process an interrupt) and os.Kill (force
|
||||
// the process to exit). On Windows, sending os.Interrupt to a process with
|
||||
// os.Process.Signal is not implemented; it will return an error instead of
|
||||
// sending a signal.
|
||||
var (
|
||||
Interrupt Signal = syscall.SIGINT
|
||||
Kill Signal = syscall.SIGKILL
|
||||
)
|
||||
|
||||
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
|
||||
// If there is no SysProcAttr (ie. no Chroot or changed
|
||||
// UID/GID), double-check existence of the directory we want
|
||||
// to chdir into. We can make the error clearer this way.
|
||||
if attr != nil && attr.Sys == nil && attr.Dir != "" {
|
||||
if _, err := Stat(attr.Dir); err != nil {
|
||||
pe := err.(*PathError)
|
||||
pe.Op = "chdir"
|
||||
return nil, pe
|
||||
}
|
||||
}
|
||||
|
||||
sysattr := &syscall.ProcAttr{
|
||||
Dir: attr.Dir,
|
||||
Env: attr.Env,
|
||||
Sys: attr.Sys,
|
||||
}
|
||||
if sysattr.Env == nil {
|
||||
sysattr.Env, err = execenv.Default(sysattr.Sys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
sysattr.Files = make([]uintptr, 0, len(attr.Files))
|
||||
for _, f := range attr.Files {
|
||||
sysattr.Files = append(sysattr.Files, f.Fd())
|
||||
}
|
||||
|
||||
pid, h, e := syscall.StartProcess(name, argv, sysattr)
|
||||
|
||||
// TODO(xsw):
|
||||
// Make sure we don't run the finalizers of attr.Files.
|
||||
// runtime.KeepAlive(attr)
|
||||
|
||||
if e != nil {
|
||||
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
|
||||
}
|
||||
|
||||
return newProcess(pid, h), nil
|
||||
}
|
||||
|
||||
func (p *Process) kill() error {
|
||||
return p.Signal(Kill)
|
||||
}
|
||||
|
||||
// ProcessState stores information about a process, as reported by Wait.
|
||||
type ProcessState struct {
|
||||
pid int // The process's id.
|
||||
status syscall.WaitStatus // System-dependent status info.
|
||||
rusage *syscall.Rusage
|
||||
}
|
||||
|
||||
// Pid returns the process id of the exited process.
|
||||
func (p *ProcessState) Pid() int {
|
||||
return p.pid
|
||||
}
|
||||
|
||||
func (p *ProcessState) exited() bool {
|
||||
return p.status.Exited()
|
||||
}
|
||||
|
||||
func (p *ProcessState) success() bool {
|
||||
return p.status.ExitStatus() == 0
|
||||
}
|
||||
|
||||
func (p *ProcessState) sys() any {
|
||||
return p.status
|
||||
}
|
||||
|
||||
func (p *ProcessState) sysUsage() any {
|
||||
return p.rusage
|
||||
}
|
||||
|
||||
func (p *ProcessState) String() string {
|
||||
if p == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
status := p.Sys().(syscall.WaitStatus)
|
||||
res := ""
|
||||
switch {
|
||||
case status.Exited():
|
||||
code := status.ExitStatus()
|
||||
if runtime.GOOS == "windows" && uint(code) >= 1<<16 { // windows uses large hex numbers
|
||||
res = "exit status " + uitox(uint(code))
|
||||
} else { // unix systems use small decimal integers
|
||||
res = "exit status " + itoa.Itoa(code) // unix
|
||||
}
|
||||
case status.Signaled():
|
||||
res = "signal: " + status.Signal().String()
|
||||
case status.Stopped():
|
||||
res = "stop signal: " + status.StopSignal().String()
|
||||
if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
|
||||
res += " (trap " + itoa.Itoa(status.TrapCause()) + ")"
|
||||
}
|
||||
case status.Continued():
|
||||
res = "continued"
|
||||
}
|
||||
if status.CoreDump() {
|
||||
res += " (core dumped)"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ExitCode returns the exit code of the exited process, or -1
|
||||
// if the process hasn't exited or was terminated by a signal.
|
||||
func (p *ProcessState) ExitCode() int {
|
||||
// return -1 if the process hasn't started.
|
||||
if p == nil {
|
||||
return -1
|
||||
}
|
||||
return p.status.ExitStatus()
|
||||
}
|
||||
106
compiler/internal/lib/os/exec_unix.go
Normal file
106
compiler/internal/lib/os/exec_unix.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2009 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) || wasip1
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (p *Process) wait() (ps *ProcessState, err error) {
|
||||
if p.Pid == -1 {
|
||||
return nil, syscall.EINVAL
|
||||
}
|
||||
|
||||
// If we can block until Wait4 will succeed immediately, do so.
|
||||
ready, err := p.blockUntilWaitable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ready {
|
||||
// Mark the process done now, before the call to Wait4,
|
||||
// so that Process.signal will not send a signal.
|
||||
p.setDone()
|
||||
// Acquire a write lock on sigMu to wait for any
|
||||
// active call to the signal method to complete.
|
||||
p.sigMu.Lock()
|
||||
p.sigMu.Unlock()
|
||||
}
|
||||
|
||||
var (
|
||||
status syscall.WaitStatus
|
||||
rusage syscall.Rusage
|
||||
pid1 int
|
||||
e error
|
||||
)
|
||||
for {
|
||||
pid1, e = syscall.Wait4(p.Pid, &status, 0, &rusage)
|
||||
if e != syscall.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
if e != nil {
|
||||
return nil, NewSyscallError("wait", e)
|
||||
}
|
||||
if pid1 != 0 {
|
||||
p.setDone()
|
||||
}
|
||||
ps = &ProcessState{
|
||||
pid: pid1,
|
||||
status: status,
|
||||
rusage: &rusage,
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (p *Process) signal(sig Signal) error {
|
||||
if p.Pid == -1 {
|
||||
return errors.New("os: process already released")
|
||||
}
|
||||
if p.Pid == 0 {
|
||||
return errors.New("os: process not initialized")
|
||||
}
|
||||
p.sigMu.RLock()
|
||||
defer p.sigMu.RUnlock()
|
||||
if p.done() {
|
||||
return ErrProcessDone
|
||||
}
|
||||
s, ok := sig.(syscall.Signal)
|
||||
if !ok {
|
||||
return errors.New("os: unsupported signal type")
|
||||
}
|
||||
if e := syscall.Kill(p.Pid, s); e != nil {
|
||||
if e == syscall.ESRCH {
|
||||
return ErrProcessDone
|
||||
}
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Process) release() error {
|
||||
// NOOP for unix.
|
||||
p.Pid = -1
|
||||
// no need for a finalizer anymore
|
||||
runtime.SetFinalizer(p, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func findProcess(pid int) (p *Process, err error) {
|
||||
// NOOP for unix.
|
||||
return newProcess(pid, 0), nil
|
||||
}
|
||||
|
||||
func (p *ProcessState) userTime() time.Duration {
|
||||
return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
|
||||
}
|
||||
|
||||
func (p *ProcessState) systemTime() time.Duration {
|
||||
return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
|
||||
}
|
||||
183
compiler/internal/lib/os/exec_windows.go
Normal file
183
compiler/internal/lib/os/exec_windows.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (p *Process) wait() (ps *ProcessState, err error) {
|
||||
/* TODO(xsw):
|
||||
handle := atomic.LoadUintptr(&p.handle)
|
||||
s, e := syscall.WaitForSingleObject(syscall.Handle(handle), syscall.INFINITE)
|
||||
switch s {
|
||||
case syscall.WAIT_OBJECT_0:
|
||||
break
|
||||
case syscall.WAIT_FAILED:
|
||||
return nil, NewSyscallError("WaitForSingleObject", e)
|
||||
default:
|
||||
return nil, errors.New("os: unexpected result from WaitForSingleObject")
|
||||
}
|
||||
var ec uint32
|
||||
e = syscall.GetExitCodeProcess(syscall.Handle(handle), &ec)
|
||||
if e != nil {
|
||||
return nil, NewSyscallError("GetExitCodeProcess", e)
|
||||
}
|
||||
var u syscall.Rusage
|
||||
e = syscall.GetProcessTimes(syscall.Handle(handle), &u.CreationTime, &u.ExitTime, &u.KernelTime, &u.UserTime)
|
||||
if e != nil {
|
||||
return nil, NewSyscallError("GetProcessTimes", e)
|
||||
}
|
||||
p.setDone()
|
||||
// NOTE(brainman): It seems that sometimes process is not dead
|
||||
// when WaitForSingleObject returns. But we do not know any
|
||||
// other way to wait for it. Sleeping for a while seems to do
|
||||
// the trick sometimes.
|
||||
// See https://golang.org/issue/25965 for details.
|
||||
defer time.Sleep(5 * time.Millisecond)
|
||||
defer p.Release()
|
||||
return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil
|
||||
*/
|
||||
panic("todo: os.Process.wait")
|
||||
}
|
||||
|
||||
func (p *Process) signal(sig Signal) error {
|
||||
handle := atomic.LoadUintptr(&p.handle)
|
||||
if handle == uintptr(syscall.InvalidHandle) {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
if p.done() {
|
||||
return ErrProcessDone
|
||||
}
|
||||
if sig == Kill {
|
||||
var terminationHandle syscall.Handle
|
||||
e := syscall.DuplicateHandle(^syscall.Handle(0), syscall.Handle(handle), ^syscall.Handle(0), &terminationHandle, syscall.PROCESS_TERMINATE, false, 0)
|
||||
if e != nil {
|
||||
return NewSyscallError("DuplicateHandle", e)
|
||||
}
|
||||
runtime.KeepAlive(p)
|
||||
defer syscall.CloseHandle(terminationHandle)
|
||||
e = syscall.TerminateProcess(syscall.Handle(terminationHandle), 1)
|
||||
return NewSyscallError("TerminateProcess", e)
|
||||
}
|
||||
// TODO(rsc): Handle Interrupt too?
|
||||
return syscall.Errno(syscall.EWINDOWS)
|
||||
}
|
||||
|
||||
func (p *Process) release() error {
|
||||
handle := atomic.SwapUintptr(&p.handle, uintptr(syscall.InvalidHandle))
|
||||
if handle == uintptr(syscall.InvalidHandle) {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
e := syscall.CloseHandle(syscall.Handle(handle))
|
||||
if e != nil {
|
||||
return NewSyscallError("CloseHandle", e)
|
||||
}
|
||||
// no need for a finalizer anymore
|
||||
runtime.SetFinalizer(p, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func findProcess(pid int) (p *Process, err error) {
|
||||
const da = syscall.STANDARD_RIGHTS_READ |
|
||||
syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
|
||||
h, e := syscall.OpenProcess(da, false, uint32(pid))
|
||||
if e != nil {
|
||||
return nil, NewSyscallError("OpenProcess", e)
|
||||
}
|
||||
return newProcess(pid, uintptr(h)), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd := windows.UTF16PtrToString(syscall.GetCommandLine())
|
||||
if len(cmd) == 0 {
|
||||
arg0, _ := Executable()
|
||||
Args = []string{arg0}
|
||||
} else {
|
||||
Args = commandLineToArgv(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// appendBSBytes appends n '\\' bytes to b and returns the resulting slice.
|
||||
func appendBSBytes(b []byte, n int) []byte {
|
||||
for ; n > 0; n-- {
|
||||
b = append(b, '\\')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// readNextArg splits command line string cmd into next
|
||||
// argument and command line remainder.
|
||||
func readNextArg(cmd string) (arg []byte, rest string) {
|
||||
var b []byte
|
||||
var inquote bool
|
||||
var nslash int
|
||||
for ; len(cmd) > 0; cmd = cmd[1:] {
|
||||
c := cmd[0]
|
||||
switch c {
|
||||
case ' ', '\t':
|
||||
if !inquote {
|
||||
return appendBSBytes(b, nslash), cmd[1:]
|
||||
}
|
||||
case '"':
|
||||
b = appendBSBytes(b, nslash/2)
|
||||
if nslash%2 == 0 {
|
||||
// use "Prior to 2008" rule from
|
||||
// http://daviddeley.com/autohotkey/parameters/parameters.htm
|
||||
// section 5.2 to deal with double double quotes
|
||||
if inquote && len(cmd) > 1 && cmd[1] == '"' {
|
||||
b = append(b, c)
|
||||
cmd = cmd[1:]
|
||||
}
|
||||
inquote = !inquote
|
||||
} else {
|
||||
b = append(b, c)
|
||||
}
|
||||
nslash = 0
|
||||
continue
|
||||
case '\\':
|
||||
nslash++
|
||||
continue
|
||||
}
|
||||
b = appendBSBytes(b, nslash)
|
||||
nslash = 0
|
||||
b = append(b, c)
|
||||
}
|
||||
return appendBSBytes(b, nslash), ""
|
||||
}
|
||||
|
||||
// commandLineToArgv splits a command line into individual argument
|
||||
// strings, following the Windows conventions documented
|
||||
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
|
||||
func commandLineToArgv(cmd string) []string {
|
||||
var args []string
|
||||
for len(cmd) > 0 {
|
||||
if cmd[0] == ' ' || cmd[0] == '\t' {
|
||||
cmd = cmd[1:]
|
||||
continue
|
||||
}
|
||||
var arg []byte
|
||||
arg, cmd = readNextArg(cmd)
|
||||
args = append(args, string(arg))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func ftToDuration(ft *syscall.Filetime) time.Duration {
|
||||
n := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) // in 100-nanosecond intervals
|
||||
return time.Duration(n*100) * time.Nanosecond
|
||||
}
|
||||
|
||||
func (p *ProcessState) userTime() time.Duration {
|
||||
return ftToDuration(&p.rusage.UserTime)
|
||||
}
|
||||
|
||||
func (p *ProcessState) systemTime() time.Duration {
|
||||
return ftToDuration(&p.rusage.KernelTime)
|
||||
}
|
||||
530
compiler/internal/lib/os/file.go
Normal file
530
compiler/internal/lib/os/file.go
Normal file
@@ -0,0 +1,530 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Name returns the name of the file as presented to Open.
|
||||
func (f *File) Name() string { return f.name }
|
||||
|
||||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
||||
// standard output, and standard error file descriptors.
|
||||
//
|
||||
// Note that the Go runtime writes to standard error for panics and crashes;
|
||||
// closing Stderr may cause those messages to go elsewhere, perhaps
|
||||
// to a file opened later.
|
||||
var (
|
||||
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
|
||||
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
|
||||
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
|
||||
)
|
||||
|
||||
// Flags to OpenFile wrapping those of the underlying system. Not all
|
||||
// flags may be implemented on a given system.
|
||||
const (
|
||||
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
|
||||
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
|
||||
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
|
||||
O_RDWR int = syscall.O_RDWR // open the file read-write.
|
||||
// The remaining values may be or'ed in to control behavior.
|
||||
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
|
||||
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
|
||||
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
|
||||
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
|
||||
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
|
||||
)
|
||||
|
||||
// Seek whence values.
|
||||
//
|
||||
// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
|
||||
const (
|
||||
SEEK_SET int = 0 // seek relative to the origin of the file
|
||||
SEEK_CUR int = 1 // seek relative to the current offset
|
||||
SEEK_END int = 2 // seek relative to the end
|
||||
)
|
||||
|
||||
// Read reads up to len(b) bytes from the File and stores them in b.
|
||||
// It returns the number of bytes read and any error encountered.
|
||||
// At end of file, Read returns 0, io.EOF.
|
||||
func (f *File) Read(b []byte) (n int, err error) {
|
||||
if err := f.checkValid("read"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, e := f.read(b)
|
||||
return n, f.wrapErr("read", e)
|
||||
}
|
||||
|
||||
// ReadAt reads len(b) bytes from the File starting at byte offset off.
|
||||
// It returns the number of bytes read and the error, if any.
|
||||
// ReadAt always returns a non-nil error when n < len(b).
|
||||
// At end of file, that error is io.EOF.
|
||||
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
/*
|
||||
if err := f.checkValid("read"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
return 0, &PathError{Op: "readat", Path: f.name, Err: errors.New("negative offset")}
|
||||
}
|
||||
|
||||
for len(b) > 0 {
|
||||
m, e := f.pread(b, off)
|
||||
if e != nil {
|
||||
err = f.wrapErr("read", e)
|
||||
break
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
off += int64(m)
|
||||
}
|
||||
return
|
||||
*/
|
||||
panic("todo: os.File.ReadAt")
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
func (f *File) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
/*
|
||||
if err := f.checkValid("write"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, handled, e := f.readFrom(r)
|
||||
if !handled {
|
||||
return genericReadFrom(f, r) // without wrapping
|
||||
}
|
||||
return n, f.wrapErr("write", e)
|
||||
*/
|
||||
panic("todo: os.File.ReadFrom")
|
||||
}
|
||||
|
||||
func genericReadFrom(f *File, r io.Reader) (int64, error) {
|
||||
return io.Copy(fileWithoutReadFrom{f}, r)
|
||||
}
|
||||
|
||||
// fileWithoutReadFrom implements all the methods of *File other
|
||||
// than ReadFrom. This is used to permit ReadFrom to call io.Copy
|
||||
// without leading to a recursive call to ReadFrom.
|
||||
type fileWithoutReadFrom struct {
|
||||
*File
|
||||
}
|
||||
|
||||
// This ReadFrom method hides the *File ReadFrom method.
|
||||
func (fileWithoutReadFrom) ReadFrom(fileWithoutReadFrom) {
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Write writes len(b) bytes from b to the File.
|
||||
// It returns the number of bytes written and an error, if any.
|
||||
// Write returns a non-nil error when n != len(b).
|
||||
func (f *File) Write(b []byte) (n int, err error) {
|
||||
if err := f.checkValid("write"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, e := f.write(b)
|
||||
|
||||
// TODO(xsw):
|
||||
// epipecheck(f, e)
|
||||
|
||||
if e != nil {
|
||||
err = f.wrapErr("write", e)
|
||||
} else if n != len(b) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
var errWriteAtInAppendMode = errors.New("os: invalid use of WriteAt on file opened with O_APPEND")
|
||||
|
||||
// WriteAt writes len(b) bytes to the File starting at byte offset off.
|
||||
// It returns the number of bytes written and an error, if any.
|
||||
// WriteAt returns a non-nil error when n != len(b).
|
||||
//
|
||||
// If file was opened with the O_APPEND flag, WriteAt returns an error.
|
||||
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
|
||||
/*
|
||||
if err := f.checkValid("write"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if f.appendMode {
|
||||
return 0, errWriteAtInAppendMode
|
||||
}
|
||||
|
||||
if off < 0 {
|
||||
return 0, &PathError{Op: "writeat", Path: f.name, Err: errors.New("negative offset")}
|
||||
}
|
||||
|
||||
for len(b) > 0 {
|
||||
m, e := f.pwrite(b, off)
|
||||
if e != nil {
|
||||
err = f.wrapErr("write", e)
|
||||
break
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
off += int64(m)
|
||||
}
|
||||
return
|
||||
*/
|
||||
panic("todo: os.(*File).WriteAt")
|
||||
}
|
||||
|
||||
// Seek sets the offset for the next Read or Write on file to offset, interpreted
|
||||
// according to whence: 0 means relative to the origin of the file, 1 means
|
||||
// relative to the current offset, and 2 means relative to the end.
|
||||
// It returns the new offset and an error, if any.
|
||||
// The behavior of Seek on a file opened with O_APPEND is not specified.
|
||||
func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
|
||||
/*
|
||||
if err := f.checkValid("seek"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, e := f.seek(offset, whence)
|
||||
if e == nil && f.dirinfo != nil && r != 0 {
|
||||
e = syscall.EISDIR
|
||||
}
|
||||
if e != nil {
|
||||
return 0, f.wrapErr("seek", e)
|
||||
}
|
||||
return r, nil
|
||||
*/
|
||||
panic("todo: os.(*File).Seek")
|
||||
}
|
||||
|
||||
// WriteString is like Write, but writes the contents of string s rather than
|
||||
// a slice of bytes.
|
||||
func (f *File) WriteString(s string) (n int, err error) {
|
||||
/*
|
||||
b := unsafe.Slice(unsafe.StringData(s), len(s))
|
||||
return f.Write(b)
|
||||
*/
|
||||
panic("todo: os.(*File).WriteString")
|
||||
}
|
||||
|
||||
// setStickyBit adds ModeSticky to the permission bits of path, non atomic.
|
||||
func setStickyBit(name string) error {
|
||||
fi, err := Stat(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Chmod(name, fi.Mode()|ModeSticky)
|
||||
}
|
||||
|
||||
// Open opens the named file for reading. If successful, methods on
|
||||
// the returned file can be used for reading; the associated file
|
||||
// descriptor has mode O_RDONLY.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Open(name string) (*File, error) {
|
||||
return OpenFile(name, O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// Create creates or truncates the named file. If the file already exists,
|
||||
// it is truncated. If the file does not exist, it is created with mode 0666
|
||||
// (before umask). If successful, methods on the returned File can
|
||||
// be used for I/O; the associated file descriptor has mode O_RDWR.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Create(name string) (*File, error) {
|
||||
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
|
||||
}
|
||||
|
||||
// OpenFile is the generalized open call; most users will use Open
|
||||
// or Create instead. It opens the named file with specified flag
|
||||
// (O_RDONLY etc.). If the file does not exist, and the O_CREATE flag
|
||||
// is passed, it is created with mode perm (before umask). If successful,
|
||||
// methods on the returned File can be used for I/O.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
|
||||
f, err := openFileNolog(name, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.appendMode = flag&O_APPEND != 0
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
/*
|
||||
// lstat is overridden in tests.
|
||||
var lstat = Lstat
|
||||
|
||||
// Many functions in package syscall return a count of -1 instead of 0.
|
||||
// Using fixCount(call()) instead of call() corrects the count.
|
||||
func fixCount(n int, err error) (int, error) {
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO(xsw):
|
||||
// checkWrapErr is the test hook to enable checking unexpected wrapped errors of poll.ErrFileClosing.
|
||||
// It is set to true in the export_test.go for tests (including fuzz tests).
|
||||
// var checkWrapErr = false
|
||||
|
||||
// wrapErr wraps an error that occurred during an operation on an open file.
|
||||
// It passes io.EOF through unchanged, otherwise converts
|
||||
// poll.ErrFileClosing to ErrClosed and wraps the error in a PathError.
|
||||
func (f *File) wrapErr(op string, err error) error {
|
||||
if err == nil || err == io.EOF {
|
||||
return err
|
||||
}
|
||||
/* TODO(xsw):
|
||||
if err == poll.ErrFileClosing {
|
||||
err = ErrClosed
|
||||
} else if checkWrapErr && errors.Is(err, poll.ErrFileClosing) {
|
||||
panic("unexpected error wrapping poll.ErrFileClosing: " + err.Error())
|
||||
}
|
||||
*/
|
||||
return &PathError{Op: op, Path: f.name, Err: err}
|
||||
}
|
||||
|
||||
// TempDir returns the default directory to use for temporary files.
|
||||
//
|
||||
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
|
||||
// On Windows, it uses GetTempPath, returning the first non-empty
|
||||
// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
|
||||
// On Plan 9, it returns /tmp.
|
||||
//
|
||||
// The directory is neither guaranteed to exist nor have accessible
|
||||
// permissions.
|
||||
func TempDir() string {
|
||||
return tempDir()
|
||||
}
|
||||
|
||||
// Chmod changes the mode of the file to mode.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
|
||||
|
||||
// SetDeadline sets the read and write deadlines for a File.
|
||||
// It is equivalent to calling both SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// Only some kinds of files support setting a deadline. Calls to SetDeadline
|
||||
// for files that do not support deadlines will return ErrNoDeadline.
|
||||
// On most systems ordinary files do not support deadlines, but pipes do.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations fail with an
|
||||
// error instead of blocking. The deadline applies to all future and pending
|
||||
// I/O, not just the immediately following call to Read or Write.
|
||||
// After a deadline has been exceeded, the connection can be refreshed
|
||||
// by setting a deadline in the future.
|
||||
//
|
||||
// If the deadline is exceeded a call to Read or Write or to other I/O
|
||||
// methods will return an error that wraps ErrDeadlineExceeded.
|
||||
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
||||
// That error implements the Timeout method, and calling the Timeout
|
||||
// method will return true, but there are other possible errors for which
|
||||
// the Timeout will return true even if the deadline has not been exceeded.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
func (f *File) SetDeadline(t time.Time) error {
|
||||
return f.setDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls and any
|
||||
// currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
// Not all files support setting deadlines; see SetDeadline.
|
||||
func (f *File) SetReadDeadline(t time.Time) error {
|
||||
return f.setReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for any future Write calls and any
|
||||
// currently-blocked Write call.
|
||||
// Even if Write times out, it may return n > 0, indicating that
|
||||
// some of the data was successfully written.
|
||||
// A zero value for t means Write will not time out.
|
||||
// Not all files support setting deadlines; see SetDeadline.
|
||||
func (f *File) SetWriteDeadline(t time.Time) error {
|
||||
return f.setWriteDeadline(t)
|
||||
}
|
||||
|
||||
// SyscallConn returns a raw file.
|
||||
// This implements the syscall.Conn interface.
|
||||
func (f *File) SyscallConn() (syscall.RawConn, error) {
|
||||
/*
|
||||
if err := f.checkValid("SyscallConn"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newRawConn(f)
|
||||
*/
|
||||
panic("todo: os.(*File).SyscallConn")
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
|
||||
//
|
||||
// Note that DirFS("/prefix") only guarantees that the Open calls it makes to the
|
||||
// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the
|
||||
// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside
|
||||
// the /prefix tree, then using DirFS does not stop the access any more than using
|
||||
// os.Open does. Additionally, the root of the fs.FS returned for a relative path,
|
||||
// DirFS("prefix"), will be affected by later calls to Chdir. DirFS is therefore not
|
||||
// a general substitute for a chroot-style security mechanism when the directory tree
|
||||
// contains arbitrary content.
|
||||
//
|
||||
// The directory dir must not be "".
|
||||
//
|
||||
// The result implements [io/fs.StatFS], [io/fs.ReadFileFS] and
|
||||
// [io/fs.ReadDirFS].
|
||||
func DirFS(dir string) fs.FS {
|
||||
return dirFS(dir)
|
||||
}
|
||||
|
||||
// containsAny reports whether any bytes in chars are within s.
|
||||
func containsAny(s, chars string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
for j := 0; j < len(chars); j++ {
|
||||
if s[i] == chars[j] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type dirFS string
|
||||
|
||||
func (dir dirFS) Open(name string) (fs.File, error) {
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
f, err := Open(fullname)
|
||||
if err != nil {
|
||||
// DirFS takes a string appropriate for GOOS,
|
||||
// while the name argument here is always slash separated.
|
||||
// dir.join will have mixed the two; undo that for
|
||||
// error reporting.
|
||||
err.(*PathError).Path = name
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// The ReadFile method calls the [ReadFile] function for the file
|
||||
// with the given name in the directory. The function provides
|
||||
// robust handling for small files and special file systems.
|
||||
// Through this method, dirFS implements [io/fs.ReadFileFS].
|
||||
func (dir dirFS) ReadFile(name string) ([]byte, error) {
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "readfile", Path: name, Err: err}
|
||||
}
|
||||
return ReadFile(fullname)
|
||||
}
|
||||
|
||||
// ReadDir reads the named directory, returning all its directory entries sorted
|
||||
// by filename. Through this method, dirFS implements [io/fs.ReadDirFS].
|
||||
func (dir dirFS) ReadDir(name string) ([]DirEntry, error) {
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "readdir", Path: name, Err: err}
|
||||
}
|
||||
return ReadDir(fullname)
|
||||
}
|
||||
|
||||
func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
|
||||
fullname, err := dir.join(name)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
f, err := Stat(fullname)
|
||||
if err != nil {
|
||||
// See comment in dirFS.Open.
|
||||
err.(*PathError).Path = name
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// join returns the path for name in dir.
|
||||
func (dir dirFS) join(name string) (string, error) {
|
||||
if dir == "" {
|
||||
return "", errors.New("os: DirFS with empty root")
|
||||
}
|
||||
if !fs.ValidPath(name) {
|
||||
return "", ErrInvalid
|
||||
}
|
||||
name, err := safefilepath.FromFS(name)
|
||||
if err != nil {
|
||||
return "", ErrInvalid
|
||||
}
|
||||
if IsPathSeparator(dir[len(dir)-1]) {
|
||||
return string(dir) + name, nil
|
||||
}
|
||||
return string(dir) + string(PathSeparator) + name, nil
|
||||
}
|
||||
*/
|
||||
|
||||
// ReadFile reads the named file and returns the contents.
|
||||
// A successful call returns err == nil, not err == EOF.
|
||||
// Because ReadFile reads the whole file, it does not treat an EOF from Read
|
||||
// as an error to be reported.
|
||||
func ReadFile(name string) ([]byte, error) {
|
||||
f, err := Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var size int
|
||||
if info, err := f.Stat(); err == nil {
|
||||
size64 := info.Size()
|
||||
if int64(int(size64)) == size64 {
|
||||
size = int(size64)
|
||||
}
|
||||
}
|
||||
size++ // one byte for final read at EOF
|
||||
|
||||
// If a file claims a small size, read at least 512 bytes.
|
||||
// In particular, files in Linux's /proc claim size 0 but
|
||||
// then do not work right if read in small pieces,
|
||||
// so an initial read of 1 byte would not work correctly.
|
||||
if size < 512 {
|
||||
size = 512
|
||||
}
|
||||
|
||||
data := make([]byte, 0, size)
|
||||
for {
|
||||
if len(data) >= cap(data) {
|
||||
d := append(data[:cap(data)], 0)
|
||||
data = d[:len(data)]
|
||||
}
|
||||
n, err := f.Read(data[len(data):cap(data)])
|
||||
data = data[:len(data)+n]
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteFile writes data to the named file, creating it if necessary.
|
||||
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
|
||||
// otherwise WriteFile truncates it before writing, without changing permissions.
|
||||
// Since WriteFile requires multiple system calls to complete, a failure mid-operation
|
||||
// can leave the file in a partially written state.
|
||||
func WriteFile(name string, data []byte, perm FileMode) error {
|
||||
f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
if err1 := f.Close(); err1 != nil && err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
240
compiler/internal/lib/os/file_posix.go
Normal file
240
compiler/internal/lib/os/file_posix.go
Normal file
@@ -0,0 +1,240 @@
|
||||
// Copyright 2009 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) || wasip1 || windows
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Close closes the File, rendering it unusable for I/O.
|
||||
// On files that support SetDeadline, any pending I/O operations will
|
||||
// be canceled and return immediately with an ErrClosed error.
|
||||
// Close will return an error if it has already been called.
|
||||
func (f *File) Close() error {
|
||||
if f == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
return f.close()
|
||||
}
|
||||
|
||||
// pread reads len(b) bytes from the File starting at byte offset off.
|
||||
// It returns the number of bytes read and the error, if any.
|
||||
// EOF is signaled by a zero count with err set to nil.
|
||||
func (f *File) pread(b []byte, off int64) (n int, err error) {
|
||||
/*
|
||||
n, err = f.pfd.Pread(b, off)
|
||||
runtime.KeepAlive(f)
|
||||
return n, err
|
||||
*/
|
||||
panic("todo: os.(*File).pread")
|
||||
}
|
||||
|
||||
// pwrite writes len(b) bytes to the File starting at byte offset off.
|
||||
// It returns the number of bytes written and an error, if any.
|
||||
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
|
||||
/*
|
||||
n, err = f.pfd.Pwrite(b, off)
|
||||
runtime.KeepAlive(f)
|
||||
return n, err
|
||||
*/
|
||||
panic("todo: os.(*File).pwrite")
|
||||
}
|
||||
|
||||
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
|
||||
func syscallMode(i FileMode) (o uint32) {
|
||||
o |= uint32(i.Perm())
|
||||
if i&ModeSetuid != 0 {
|
||||
o |= syscall.S_ISUID
|
||||
}
|
||||
if i&ModeSetgid != 0 {
|
||||
o |= syscall.S_ISGID
|
||||
}
|
||||
if i&ModeSticky != 0 {
|
||||
o |= syscall.S_ISVTX
|
||||
}
|
||||
// No mapping for Go's ModeTemporary (plan9 only).
|
||||
return
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// See docs in file.go:Chmod.
|
||||
func chmod(name string, mode FileMode) error {
|
||||
longName := fixLongPath(name)
|
||||
e := ignoringEINTR(func() error {
|
||||
return syscall.Chmod(longName, syscallMode(mode))
|
||||
})
|
||||
if e != nil {
|
||||
return &PathError{Op: "chmod", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// See docs in file.go:(*File).Chmod.
|
||||
func (f *File) chmod(mode FileMode) error {
|
||||
/*
|
||||
if err := f.checkValid("chmod"); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := f.pfd.Fchmod(syscallMode(mode)); e != nil {
|
||||
return f.wrapErr("chmod", e)
|
||||
}
|
||||
return nil
|
||||
*/
|
||||
panic("todo: os.(*File).chmod")
|
||||
}
|
||||
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
//
|
||||
// On Windows, it always returns the syscall.EWINDOWS error, wrapped
|
||||
// in *PathError.
|
||||
func (f *File) Chown(uid, gid int) error {
|
||||
/*
|
||||
if err := f.checkValid("chown"); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := f.pfd.Fchown(uid, gid); e != nil {
|
||||
return f.wrapErr("chown", e)
|
||||
}
|
||||
return nil
|
||||
*/
|
||||
panic("todo: os.(*File).Chown")
|
||||
}
|
||||
|
||||
// Truncate changes the size of the file.
|
||||
// It does not change the I/O offset.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (f *File) Truncate(size int64) error {
|
||||
/*
|
||||
if err := f.checkValid("truncate"); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := f.pfd.Ftruncate(size); e != nil {
|
||||
return f.wrapErr("truncate", e)
|
||||
}
|
||||
return nil
|
||||
*/
|
||||
panic("todo: os.(*File).Truncate")
|
||||
}
|
||||
|
||||
// Sync commits the current contents of the file to stable storage.
|
||||
// Typically, this means flushing the file system's in-memory copy
|
||||
// of recently written data to disk.
|
||||
func (f *File) Sync() error {
|
||||
/*
|
||||
if err := f.checkValid("sync"); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := f.pfd.Fsync(); e != nil {
|
||||
return f.wrapErr("sync", e)
|
||||
}
|
||||
return nil
|
||||
*/
|
||||
panic("todo: os.(*File).Sync")
|
||||
}
|
||||
|
||||
/*
|
||||
// Chtimes changes the access and modification times of the named
|
||||
// file, similar to the Unix utime() or utimes() functions.
|
||||
// A zero time.Time value will leave the corresponding file time unchanged.
|
||||
//
|
||||
// The underlying filesystem may truncate or round the values to a
|
||||
// less precise time unit.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
var utimes [2]syscall.Timespec
|
||||
set := func(i int, t time.Time) {
|
||||
if t.IsZero() {
|
||||
utimes[i] = syscall.Timespec{Sec: _UTIME_OMIT, Nsec: _UTIME_OMIT}
|
||||
} else {
|
||||
utimes[i] = syscall.NsecToTimespec(t.UnixNano())
|
||||
}
|
||||
}
|
||||
set(0, atime)
|
||||
set(1, mtime)
|
||||
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
|
||||
return &PathError{Op: "chtimes", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// Chdir changes the current working directory to the file,
|
||||
// which must be a directory.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (f *File) Chdir() error {
|
||||
/*
|
||||
if err := f.checkValid("chdir"); err != nil {
|
||||
return err
|
||||
}
|
||||
if e := f.pfd.Fchdir(); e != nil {
|
||||
return f.wrapErr("chdir", e)
|
||||
}
|
||||
return nil
|
||||
*/
|
||||
panic("todo: os.(*File).Chdir")
|
||||
}
|
||||
|
||||
// setDeadline sets the read and write deadline.
|
||||
func (f *File) setDeadline(t time.Time) error {
|
||||
/*
|
||||
if err := f.checkValid("SetDeadline"); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.pfd.SetDeadline(t)
|
||||
*/
|
||||
panic("todo: os.(*File).setDeadline")
|
||||
}
|
||||
|
||||
// setReadDeadline sets the read deadline.
|
||||
func (f *File) setReadDeadline(t time.Time) error {
|
||||
/*
|
||||
if err := f.checkValid("SetReadDeadline"); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.pfd.SetReadDeadline(t)
|
||||
*/
|
||||
panic("todo: os.(*File).setReadDeadline")
|
||||
}
|
||||
|
||||
// setWriteDeadline sets the write deadline.
|
||||
func (f *File) setWriteDeadline(t time.Time) error {
|
||||
/*
|
||||
if err := f.checkValid("SetWriteDeadline"); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.pfd.SetWriteDeadline(t)
|
||||
*/
|
||||
panic("todo: os.(*File).setWriteDeadline")
|
||||
}
|
||||
|
||||
// checkValid checks whether f is valid for use.
|
||||
// If not, it returns an appropriate error, perhaps incorporating the operation name op.
|
||||
func (f *File) checkValid(op string) error {
|
||||
if f == nil {
|
||||
return ErrInvalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ignoringEINTR makes a function call and repeats it if it returns an
|
||||
// EINTR error. This appears to be required even though we install all
|
||||
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
|
||||
// Also #20400 and #36644 are issues in which a signal handler is
|
||||
// installed without setting SA_RESTART. None of these are the common case,
|
||||
// but there are enough of them that it seems that we can't avoid
|
||||
// an EINTR loop.
|
||||
func ignoringEINTR(fn func() error) error {
|
||||
for {
|
||||
err := fn()
|
||||
if err != syscall.EINTR {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
334
compiler/internal/lib/os/file_unix.go
Normal file
334
compiler/internal/lib/os/file_unix.go
Normal file
@@ -0,0 +1,334 @@
|
||||
// Copyright 2009 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) || wasip1
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/goplus/llgo/compiler/internal/lib/internal/syscall/unix"
|
||||
)
|
||||
|
||||
// Fd returns the integer Unix file descriptor referencing the open file.
|
||||
// If f is closed, the file descriptor becomes invalid.
|
||||
// If f is garbage collected, a finalizer may close the file descriptor,
|
||||
// making it invalid; see runtime.SetFinalizer for more information on when
|
||||
// a finalizer might be run. On Unix systems this will cause the SetDeadline
|
||||
// methods to stop working.
|
||||
// Because file descriptors can be reused, the returned file descriptor may
|
||||
// only be closed through the Close method of f, or by its finalizer during
|
||||
// garbage collection. Otherwise, during garbage collection the finalizer
|
||||
// may close an unrelated file descriptor with the same (reused) number.
|
||||
//
|
||||
// As an alternative, see the f.SyscallConn method.
|
||||
func (f *File) Fd() uintptr {
|
||||
if f == nil {
|
||||
return ^(uintptr(0))
|
||||
}
|
||||
|
||||
return f.fd
|
||||
}
|
||||
|
||||
func NewFile(fd uintptr, name string) *File {
|
||||
return &File{fd: fd, name: name}
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// NewFile returns a new File with the given file descriptor and
|
||||
// name. The returned value will be nil if fd is not a valid file
|
||||
// descriptor. On Unix systems, if the file descriptor is in
|
||||
// non-blocking mode, NewFile will attempt to return a pollable File
|
||||
// (one for which the SetDeadline methods work).
|
||||
//
|
||||
// After passing it to NewFile, fd may become invalid under the same
|
||||
// conditions described in the comments of the Fd method, and the same
|
||||
// constraints apply.
|
||||
func NewFile(fd uintptr, name string) *File {
|
||||
fdi := int(fd)
|
||||
if fdi < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
kind := kindNewFile
|
||||
appendMode := false
|
||||
if flags, err := unix.Fcntl(fdi, syscall.F_GETFL, 0); err == nil {
|
||||
if unix.HasNonblockFlag(flags) {
|
||||
kind = kindNonBlock
|
||||
}
|
||||
appendMode = flags&syscall.O_APPEND != 0
|
||||
}
|
||||
f := newFile(fdi, name, kind)
|
||||
f.appendMode = appendMode
|
||||
return f
|
||||
}
|
||||
|
||||
// net_newUnixFile is a hidden entry point called by net.conn.File.
|
||||
// This is used so that a nonblocking network connection will become
|
||||
// blocking if code calls the Fd method. We don't want that for direct
|
||||
// calls to NewFile: passing a nonblocking descriptor to NewFile should
|
||||
// remain nonblocking if you get it back using Fd. But for net.conn.File
|
||||
// the call to NewFile is hidden from the user. Historically in that case
|
||||
// the Fd method has returned a blocking descriptor, and we want to
|
||||
// retain that behavior because existing code expects it and depends on it.
|
||||
//
|
||||
//-go:linkname net_newUnixFile net.newUnixFile
|
||||
func net_newUnixFile(fd int, name string) *File {
|
||||
if fd < 0 {
|
||||
panic("invalid FD")
|
||||
}
|
||||
|
||||
f := newFile(fd, name, kindNonBlock)
|
||||
f.nonblock = true // tell Fd to return blocking descriptor
|
||||
return f
|
||||
}
|
||||
*/
|
||||
|
||||
// newFileKind describes the kind of file to newFile.
|
||||
type newFileKind int
|
||||
|
||||
const (
|
||||
// kindNewFile means that the descriptor was passed to us via NewFile.
|
||||
kindNewFile newFileKind = iota
|
||||
// kindOpenFile means that the descriptor was opened using
|
||||
// Open, Create, or OpenFile (without O_NONBLOCK).
|
||||
kindOpenFile
|
||||
// kindPipe means that the descriptor was opened using Pipe.
|
||||
kindPipe
|
||||
// kindNonBlock means that the descriptor is already in
|
||||
// non-blocking mode.
|
||||
kindNonBlock
|
||||
// kindNoPoll means that we should not put the descriptor into
|
||||
// non-blocking mode, because we know it is not a pipe or FIFO.
|
||||
// Used by openFdAt for directories.
|
||||
kindNoPoll
|
||||
)
|
||||
|
||||
// newFile is like NewFile, but if called from OpenFile or Pipe
|
||||
// (as passed in the kind parameter) it tries to add the file to
|
||||
// the runtime poller.
|
||||
func newFile(fd int, name string, kind newFileKind) *File {
|
||||
f := &File{
|
||||
fd: uintptr(fd),
|
||||
name: name,
|
||||
stdoutOrErr: fd == 1 || fd == 2,
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
pollable := kind == kindOpenFile || kind == kindPipe || kind == kindNonBlock
|
||||
|
||||
// If the caller passed a non-blocking filedes (kindNonBlock),
|
||||
// we assume they know what they are doing so we allow it to be
|
||||
// used with kqueue.
|
||||
if kind == kindOpenFile {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd":
|
||||
var st syscall.Stat_t
|
||||
err := ignoringEINTR(func() error {
|
||||
return syscall.Fstat(fd, &st)
|
||||
})
|
||||
typ := st.Mode & syscall.S_IFMT
|
||||
// Don't try to use kqueue with regular files on *BSDs.
|
||||
// On FreeBSD a regular file is always
|
||||
// reported as ready for writing.
|
||||
// On Dragonfly, NetBSD and OpenBSD the fd is signaled
|
||||
// only once as ready (both read and write).
|
||||
// Issue 19093.
|
||||
// Also don't add directories to the netpoller.
|
||||
if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) {
|
||||
pollable = false
|
||||
}
|
||||
|
||||
// In addition to the behavior described above for regular files,
|
||||
// on Darwin, kqueue does not work properly with fifos:
|
||||
// closing the last writer does not cause a kqueue event
|
||||
// for any readers. See issue #24164.
|
||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO {
|
||||
pollable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearNonBlock := false
|
||||
if pollable {
|
||||
if kind == kindNonBlock {
|
||||
// The descriptor is already in non-blocking mode.
|
||||
// We only set f.nonblock if we put the file into
|
||||
// non-blocking mode.
|
||||
} else if err := syscall.SetNonblock(fd, true); err == nil {
|
||||
f.nonblock = true
|
||||
clearNonBlock = true
|
||||
} else {
|
||||
pollable = false
|
||||
}
|
||||
}
|
||||
|
||||
// An error here indicates a failure to register
|
||||
// with the netpoll system. That can happen for
|
||||
// a file descriptor that is not supported by
|
||||
// epoll/kqueue; for example, disk files on
|
||||
// Linux systems. We assume that any real error
|
||||
// will show up in later I/O.
|
||||
// We do restore the blocking behavior if it was set by us.
|
||||
if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock {
|
||||
if err := syscall.SetNonblock(fd, false); err == nil {
|
||||
f.nonblock = false
|
||||
}
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(f.file, (*file).close)
|
||||
*/
|
||||
return f
|
||||
}
|
||||
|
||||
// TODO(xsw):
|
||||
// func sigpipe() // implemented in package runtime
|
||||
|
||||
// epipecheck raises SIGPIPE if we get an EPIPE error on standard
|
||||
// output or standard error. See the SIGPIPE docs in os/signal, and
|
||||
// issue 11845.
|
||||
func epipecheck(file *File, e error) {
|
||||
/* TODO(xsw):
|
||||
if e == syscall.EPIPE && file.stdoutOrErr {
|
||||
sigpipe()
|
||||
}
|
||||
*/
|
||||
panic("todo: os.epipecheck")
|
||||
}
|
||||
|
||||
// DevNull is the name of the operating system's “null device.”
|
||||
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
|
||||
const DevNull = "/dev/null"
|
||||
|
||||
// openFileNolog is the Unix implementation of OpenFile.
|
||||
// Changes here should be reflected in openFdAt, if relevant.
|
||||
func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
||||
setSticky := false
|
||||
if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
|
||||
if _, err := Stat(name); IsNotExist(err) {
|
||||
setSticky = true
|
||||
}
|
||||
}
|
||||
|
||||
var r int
|
||||
for {
|
||||
var e error
|
||||
r, e = syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// We have to check EINTR here, per issues 11180 and 39237.
|
||||
if e == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, &PathError{Op: "open", Path: name, Err: e}
|
||||
}
|
||||
|
||||
// open(2) itself won't handle the sticky bit on *BSD and Solaris
|
||||
if setSticky {
|
||||
setStickyBit(name)
|
||||
}
|
||||
|
||||
// There's a race here with fork/exec, which we are
|
||||
// content to live with. See ../syscall/exec_unix.go.
|
||||
if !supportsCloseOnExec {
|
||||
syscall.CloseOnExec(r)
|
||||
}
|
||||
|
||||
kind := kindOpenFile
|
||||
if unix.HasNonblockFlag(flag) {
|
||||
kind = kindNonBlock
|
||||
panic("todo: os.openFileNolog: unix.HasNonblockFlag")
|
||||
}
|
||||
|
||||
f := newFile(r, name, kind)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (file *File) close() error {
|
||||
return syscall.Close(int(file.fd))
|
||||
/* TODO(xsw):
|
||||
if file.dirinfo != nil {
|
||||
file.dirinfo.close()
|
||||
file.dirinfo = nil
|
||||
}
|
||||
var err error
|
||||
if e := file.pfd.Close(); e != nil {
|
||||
if e == poll.ErrFileClosing {
|
||||
e = ErrClosed
|
||||
}
|
||||
err = &PathError{Op: "close", Path: file.name, Err: e}
|
||||
}
|
||||
|
||||
// no need for a finalizer anymore
|
||||
runtime.SetFinalizer(file, nil)
|
||||
return err
|
||||
*/
|
||||
}
|
||||
|
||||
func tempDir() string {
|
||||
dir := Getenv("TMPDIR")
|
||||
if dir == "" {
|
||||
if runtime.GOOS == "android" {
|
||||
dir = "/data/local/tmp"
|
||||
} else {
|
||||
dir = "/tmp"
|
||||
}
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
type unixDirent struct {
|
||||
parent string
|
||||
name string
|
||||
typ FileMode
|
||||
info FileInfo
|
||||
}
|
||||
|
||||
func (d *unixDirent) Name() string { return d.name }
|
||||
func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
|
||||
func (d *unixDirent) Type() FileMode { return d.typ }
|
||||
|
||||
func (d *unixDirent) Info() (FileInfo, error) {
|
||||
/* TODO(xsw):
|
||||
if d.info != nil {
|
||||
return d.info, nil
|
||||
}
|
||||
return lstat(d.parent + "/" + d.name)
|
||||
*/
|
||||
panic("todo: os.unixDirent.Info")
|
||||
}
|
||||
|
||||
func (d *unixDirent) String() string {
|
||||
/* TODO(xsw):
|
||||
return fs.FormatDirEntry(d)
|
||||
*/
|
||||
panic("todo: os.unixDirent.String")
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
func newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
|
||||
ude := &unixDirent{
|
||||
parent: parent,
|
||||
name: name,
|
||||
typ: typ,
|
||||
}
|
||||
if typ != ^FileMode(0) && !testingForceReadDirLstat {
|
||||
return ude, nil
|
||||
}
|
||||
|
||||
info, err := lstat(parent + "/" + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ude.typ = info.Mode().Type()
|
||||
ude.info = info
|
||||
return ude, nil
|
||||
}
|
||||
*/
|
||||
464
compiler/internal/lib/os/os.go
Normal file
464
compiler/internal/lib/os/os.go
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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 (
|
||||
"errors"
|
||||
"runtime"
|
||||
"syscall"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/os"
|
||||
)
|
||||
|
||||
const (
|
||||
LLGoPackage = true
|
||||
)
|
||||
|
||||
// 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 toPathErr(op, path string, errno c.Int) error {
|
||||
return &PathError{Op: op, Path: path, Err: syscall.Errno(errno)}
|
||||
}
|
||||
|
||||
func Chdir(dir string) error {
|
||||
ret := os.Chdir(c.AllocaCStr(dir))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return toPathErr("chdir", dir, ret)
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Chdir changes the current working directory to the named directory.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Chdir(dir string) error {
|
||||
if e := syscall.Chdir(dir); e != nil {
|
||||
testlog.Open(dir) // observe likely non-existent directory
|
||||
return &PathError{Op: "chdir", Path: dir, Err: e}
|
||||
}
|
||||
if log := testlog.Logger(); log != nil {
|
||||
wd, err := Getwd()
|
||||
if err == nil {
|
||||
log.Chdir(wd)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func Chmod(name string, mode FileMode) error {
|
||||
ret := os.Chmod(c.AllocaCStr(name), os.ModeT(syscallMode(mode)))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return toPathErr("chmod", name, ret)
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Chmod changes the mode of the named file to mode.
|
||||
// If the file is a symbolic link, it changes the mode of the link's target.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
//
|
||||
// A different subset of the mode bits are used, depending on the
|
||||
// operating system.
|
||||
//
|
||||
// On Unix, the mode's permission bits, ModeSetuid, ModeSetgid, and
|
||||
// ModeSticky are used.
|
||||
//
|
||||
// On Windows, only the 0200 bit (owner writable) of mode is used; it
|
||||
// controls whether the file's read-only attribute is set or cleared.
|
||||
// The other bits are currently unused. For compatibility with Go 1.12
|
||||
// and earlier, use a non-zero mode. Use mode 0400 for a read-only
|
||||
// file and 0600 for a readable+writable file.
|
||||
//
|
||||
// On Plan 9, the mode's permission bits, ModeAppend, ModeExclusive,
|
||||
// and ModeTemporary are used.
|
||||
func Chmod(name string, mode FileMode) error { return chmod(name, mode) }
|
||||
*/
|
||||
|
||||
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):
|
||||
// Chown changes the numeric uid and gid of the named file.
|
||||
// If the file is a symbolic link, it changes the uid and gid of the link's target.
|
||||
// A uid or gid of -1 means to not change that value.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
//
|
||||
// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
|
||||
// EPLAN9 error, wrapped in *PathError.
|
||||
func Chown(name string, uid, gid int) error {
|
||||
e := ignoringEINTR(func() error {
|
||||
return syscall.Chown(name, uid, gid)
|
||||
})
|
||||
if e != nil {
|
||||
return &PathError{Op: "chown", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// 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 Executable() (string, error)
|
||||
|
||||
// TODO(xsw):
|
||||
// func Expand(s string, mapping func(string) string) string
|
||||
// func ExpandEnv(s string) string
|
||||
|
||||
func Getegid() int {
|
||||
return int(os.Getegid())
|
||||
}
|
||||
|
||||
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 "", syscall.Errno(os.Errno())
|
||||
}
|
||||
|
||||
// TODO(xsw):
|
||||
// func Hostname() (name string, err error)
|
||||
// func IsExist(err error) bool
|
||||
// func IsNotExist(err error) 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)
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Lchown changes the numeric uid and gid of the named file.
|
||||
// If the file is a symbolic link, it changes the uid and gid of the link itself.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
//
|
||||
// On Windows, it always returns the syscall.EWINDOWS error, wrapped
|
||||
// in *PathError.
|
||||
func Lchown(name string, uid, gid int) error {
|
||||
e := ignoringEINTR(func() error {
|
||||
return syscall.Lchown(name, uid, gid)
|
||||
})
|
||||
if e != nil {
|
||||
return &PathError{Op: "lchown", Path: name, Err: e}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
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, syscall.Errno(ret)}
|
||||
}
|
||||
|
||||
// TODO(xsw):
|
||||
// func LookupEnv(key string) (string, bool)
|
||||
|
||||
func Mkdir(name string, perm FileMode) error {
|
||||
ret := os.Mkdir(c.AllocaCStr(name), os.ModeT(syscallMode(perm)))
|
||||
if ret == 0 {
|
||||
return nil
|
||||
}
|
||||
return toPathErr("mkdir", name, ret)
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Mkdir creates a new directory with the specified name and permission
|
||||
// bits (before umask).
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Mkdir(name string, perm FileMode) error {
|
||||
longName := fixLongPath(name)
|
||||
e := ignoringEINTR(func() error {
|
||||
return syscall.Mkdir(longName, syscallMode(perm))
|
||||
})
|
||||
|
||||
if e != nil {
|
||||
return &PathError{Op: "mkdir", Path: name, Err: e}
|
||||
}
|
||||
|
||||
// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
|
||||
if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
|
||||
e = setStickyBit(name)
|
||||
|
||||
if e != nil {
|
||||
Remove(name)
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO(xsw):
|
||||
// func NewSyscallError(syscall string, err error) 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, syscall.Errno(ret)}
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Rename renames (moves) oldpath to newpath.
|
||||
// If newpath already exists and is not a directory, Rename replaces it.
|
||||
// OS-specific restrictions may apply when oldpath and newpath are in different directories.
|
||||
// Even within the same directory, on non-Unix platforms Rename is not an atomic operation.
|
||||
// If there is an error, it will be of type *LinkError.
|
||||
func Rename(oldpath, newpath string) error {
|
||||
return rename(oldpath, newpath)
|
||||
}
|
||||
*/
|
||||
|
||||
// TODO(xsw):
|
||||
// func SameFile(fi1, fi2 FileInfo) bool
|
||||
|
||||
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, syscall.Errno(ret)}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// On Unix systems, it returns $XDG_CACHE_HOME as specified by
|
||||
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
|
||||
// non-empty, else $HOME/.cache.
|
||||
// On Darwin, it returns $HOME/Library/Caches.
|
||||
// On Windows, it returns %LocalAppData%.
|
||||
// On Plan 9, it returns $home/lib/cache.
|
||||
//
|
||||
// If the location cannot be determined (for example, $HOME is not defined),
|
||||
// then it will return an error.
|
||||
func UserCacheDir() (string, error) {
|
||||
var dir string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
dir = Getenv("LocalAppData")
|
||||
if dir == "" {
|
||||
return "", errors.New("%LocalAppData% is not defined")
|
||||
}
|
||||
|
||||
case "darwin", "ios":
|
||||
dir = Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "", errors.New("$HOME is not defined")
|
||||
}
|
||||
dir += "/Library/Caches"
|
||||
|
||||
case "plan9":
|
||||
dir = Getenv("home")
|
||||
if dir == "" {
|
||||
return "", errors.New("$home is not defined")
|
||||
}
|
||||
dir += "/lib/cache"
|
||||
|
||||
default: // Unix
|
||||
dir = Getenv("XDG_CACHE_HOME")
|
||||
if dir == "" {
|
||||
dir = Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "", errors.New("neither $XDG_CACHE_HOME nor $HOME are defined")
|
||||
}
|
||||
dir += "/.cache"
|
||||
}
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// UserConfigDir returns the default root directory to use for user-specific
|
||||
// configuration data. Users should create their own application-specific
|
||||
// subdirectory within this one and use that.
|
||||
//
|
||||
// On Unix systems, it returns $XDG_CONFIG_HOME as specified by
|
||||
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
|
||||
// non-empty, else $HOME/.config.
|
||||
// On Darwin, it returns $HOME/Library/Application Support.
|
||||
// On Windows, it returns %AppData%.
|
||||
// On Plan 9, it returns $home/lib.
|
||||
//
|
||||
// If the location cannot be determined (for example, $HOME is not defined),
|
||||
// then it will return an error.
|
||||
func UserConfigDir() (string, error) {
|
||||
var dir string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
dir = Getenv("AppData")
|
||||
if dir == "" {
|
||||
return "", errors.New("%AppData% is not defined")
|
||||
}
|
||||
|
||||
case "darwin", "ios":
|
||||
dir = Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "", errors.New("$HOME is not defined")
|
||||
}
|
||||
dir += "/Library/Application Support"
|
||||
|
||||
case "plan9":
|
||||
dir = Getenv("home")
|
||||
if dir == "" {
|
||||
return "", errors.New("$home is not defined")
|
||||
}
|
||||
dir += "/lib"
|
||||
|
||||
default: // Unix
|
||||
dir = Getenv("XDG_CONFIG_HOME")
|
||||
if dir == "" {
|
||||
dir = Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "", errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined")
|
||||
}
|
||||
dir += "/.config"
|
||||
}
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// UserHomeDir returns the current user's home directory.
|
||||
//
|
||||
// On Unix, including macOS, it returns the $HOME environment variable.
|
||||
// On Windows, it returns %USERPROFILE%.
|
||||
// On Plan 9, it returns the $home environment variable.
|
||||
//
|
||||
// If the expected variable is not set in the environment, UserHomeDir
|
||||
// returns either a platform-specific default value or a non-nil error.
|
||||
func UserHomeDir() (string, error) {
|
||||
env, enverr := "HOME", "$HOME"
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
env, enverr = "USERPROFILE", "%userprofile%"
|
||||
case "plan9":
|
||||
env, enverr = "home", "$home"
|
||||
}
|
||||
if v := Getenv(env); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
// On some geese the home directory is not always defined.
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
return "/sdcard", nil
|
||||
case "ios":
|
||||
return "/", nil
|
||||
}
|
||||
return "", errors.New(enverr + " is not defined")
|
||||
}
|
||||
|
||||
// TODO(xsw):
|
||||
// func WriteFile(name string, data []byte, perm FileMode) error
|
||||
55
compiler/internal/lib/os/path.go
Normal file
55
compiler/internal/lib/os/path.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// MkdirAll creates a directory named path,
|
||||
// along with any necessary parents, and returns nil,
|
||||
// or else returns an error.
|
||||
// The permission bits perm (before umask) are used for all
|
||||
// directories that MkdirAll creates.
|
||||
// If path is already a directory, MkdirAll does nothing
|
||||
// and returns nil.
|
||||
func MkdirAll(path string, perm FileMode) error {
|
||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||
dir, err := Stat(path)
|
||||
if err == nil {
|
||||
if dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return &PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||
}
|
||||
|
||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||
i := len(path)
|
||||
for i > 0 && IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||
i--
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||
j--
|
||||
}
|
||||
|
||||
if j > 1 {
|
||||
// Create parent.
|
||||
err = MkdirAll(fixRootDirectory(path[:j-1]), perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Parent now exists; invoke Mkdir and use its result.
|
||||
err = Mkdir(path, perm)
|
||||
if err != nil {
|
||||
// Handle arguments like "foo/." by
|
||||
// double-checking that directory doesn't exist.
|
||||
dir, err1 := Lstat(path)
|
||||
if err1 == nil && dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
19
compiler/internal/lib/os/path_plan9.go
Normal file
19
compiler/internal/lib/os/path_plan9.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
package os
|
||||
|
||||
const (
|
||||
PathSeparator = '/' // OS-specific path separator
|
||||
PathListSeparator = '\000' // OS-specific path list separator
|
||||
)
|
||||
|
||||
// IsPathSeparator reports whether c is a directory separator character.
|
||||
func IsPathSeparator(c uint8) bool {
|
||||
return PathSeparator == c
|
||||
}
|
||||
|
||||
func fixRootDirectory(p string) string {
|
||||
return p
|
||||
}
|
||||
75
compiler/internal/lib/os/path_unix.go
Normal file
75
compiler/internal/lib/os/path_unix.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2011 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) || wasip1
|
||||
|
||||
package os
|
||||
|
||||
const (
|
||||
PathSeparator = '/' // OS-specific path separator
|
||||
PathListSeparator = ':' // OS-specific path list separator
|
||||
)
|
||||
|
||||
// IsPathSeparator reports whether c is a directory separator character.
|
||||
func IsPathSeparator(c uint8) bool {
|
||||
return PathSeparator == c
|
||||
}
|
||||
|
||||
// basename removes trailing slashes and the leading directory name from path name.
|
||||
func basename(name string) string {
|
||||
i := len(name) - 1
|
||||
// Remove trailing slashes
|
||||
for ; i > 0 && name[i] == '/'; i-- {
|
||||
name = name[:i]
|
||||
}
|
||||
// Remove leading directory name
|
||||
for i--; i >= 0; i-- {
|
||||
if name[i] == '/' {
|
||||
name = name[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// splitPath returns the base name and parent directory.
|
||||
func splitPath(path string) (string, string) {
|
||||
// if no better parent is found, the path is relative from "here"
|
||||
dirname := "."
|
||||
|
||||
// Remove all but one leading slash.
|
||||
for len(path) > 1 && path[0] == '/' && path[1] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
i := len(path) - 1
|
||||
|
||||
// Remove trailing slashes.
|
||||
for ; i > 0 && path[i] == '/'; i-- {
|
||||
path = path[:i]
|
||||
}
|
||||
|
||||
// if no slashes in path, base is path
|
||||
basename := path
|
||||
|
||||
// Remove leading directory path
|
||||
for i--; i >= 0; i-- {
|
||||
if path[i] == '/' {
|
||||
if i == 0 {
|
||||
dirname = path[:1]
|
||||
} else {
|
||||
dirname = path[:i]
|
||||
}
|
||||
basename = path[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return dirname, basename
|
||||
}
|
||||
|
||||
func fixRootDirectory(p string) string {
|
||||
return p
|
||||
}
|
||||
227
compiler/internal/lib/os/path_windows.go
Normal file
227
compiler/internal/lib/os/path_windows.go
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
package os
|
||||
|
||||
const (
|
||||
PathSeparator = '\\' // OS-specific path separator
|
||||
PathListSeparator = ';' // OS-specific path list separator
|
||||
)
|
||||
|
||||
// IsPathSeparator reports whether c is a directory separator character.
|
||||
func IsPathSeparator(c uint8) bool {
|
||||
// NOTE: Windows accepts / as path separator.
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
// basename removes trailing slashes and the leading
|
||||
// directory name and drive letter from path name.
|
||||
func basename(name string) string {
|
||||
// Remove drive letter
|
||||
if len(name) == 2 && name[1] == ':' {
|
||||
name = "."
|
||||
} else if len(name) > 2 && name[1] == ':' {
|
||||
name = name[2:]
|
||||
}
|
||||
i := len(name) - 1
|
||||
// Remove trailing slashes
|
||||
for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i-- {
|
||||
name = name[:i]
|
||||
}
|
||||
// Remove leading directory name
|
||||
for i--; i >= 0; i-- {
|
||||
if name[i] == '/' || name[i] == '\\' {
|
||||
name = name[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func isAbs(path string) (b bool) {
|
||||
v := volumeName(path)
|
||||
if v == "" {
|
||||
return false
|
||||
}
|
||||
path = path[len(v):]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return IsPathSeparator(path[0])
|
||||
}
|
||||
|
||||
func volumeName(path string) (v string) {
|
||||
if len(path) < 2 {
|
||||
return ""
|
||||
}
|
||||
// with drive letter
|
||||
c := path[0]
|
||||
if path[1] == ':' &&
|
||||
('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
|
||||
'A' <= c && c <= 'Z') {
|
||||
return path[:2]
|
||||
}
|
||||
// is it UNC
|
||||
if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
|
||||
!IsPathSeparator(path[2]) && path[2] != '.' {
|
||||
// first, leading `\\` and next shouldn't be `\`. its server name.
|
||||
for n := 3; n < l-1; n++ {
|
||||
// second, next '\' shouldn't be repeated.
|
||||
if IsPathSeparator(path[n]) {
|
||||
n++
|
||||
// third, following something characters. its share name.
|
||||
if !IsPathSeparator(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
for ; n < l; n++ {
|
||||
if IsPathSeparator(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return path[:n]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func fromSlash(path string) string {
|
||||
// Replace each '/' with '\\' if present
|
||||
var pathbuf []byte
|
||||
var lastSlash int
|
||||
for i, b := range path {
|
||||
if b == '/' {
|
||||
if pathbuf == nil {
|
||||
pathbuf = make([]byte, len(path))
|
||||
}
|
||||
copy(pathbuf[lastSlash:], path[lastSlash:i])
|
||||
pathbuf[i] = '\\'
|
||||
lastSlash = i + 1
|
||||
}
|
||||
}
|
||||
if pathbuf == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
copy(pathbuf[lastSlash:], path[lastSlash:])
|
||||
return string(pathbuf)
|
||||
}
|
||||
|
||||
func dirname(path string) string {
|
||||
vol := volumeName(path)
|
||||
i := len(path) - 1
|
||||
for i >= len(vol) && !IsPathSeparator(path[i]) {
|
||||
i--
|
||||
}
|
||||
dir := path[len(vol) : i+1]
|
||||
last := len(dir) - 1
|
||||
if last > 0 && IsPathSeparator(dir[last]) {
|
||||
dir = dir[:last]
|
||||
}
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
return vol + dir
|
||||
}
|
||||
|
||||
// This is set via go:linkname on runtime.canUseLongPaths, and is true when the OS
|
||||
// supports opting into proper long path handling without the need for fixups.
|
||||
var canUseLongPaths bool
|
||||
|
||||
// fixLongPath returns the extended-length (\\?\-prefixed) form of
|
||||
// path when needed, in order to avoid the default 260 character file
|
||||
// path limit imposed by Windows. If path is not easily converted to
|
||||
// the extended-length form (for example, if path is a relative path
|
||||
// or contains .. elements), or is short enough, fixLongPath returns
|
||||
// path unmodified.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||
func fixLongPath(path string) string {
|
||||
if canUseLongPaths {
|
||||
return path
|
||||
}
|
||||
// Do nothing (and don't allocate) if the path is "short".
|
||||
// Empirically (at least on the Windows Server 2013 builder),
|
||||
// the kernel is arbitrarily okay with < 248 bytes. That
|
||||
// matches what the docs above say:
|
||||
// "When using an API to create a directory, the specified
|
||||
// path cannot be so long that you cannot append an 8.3 file
|
||||
// name (that is, the directory name cannot exceed MAX_PATH
|
||||
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
|
||||
//
|
||||
// The MSDN docs appear to say that a normal path that is 248 bytes long
|
||||
// will work; empirically the path must be less then 248 bytes long.
|
||||
if len(path) < 248 {
|
||||
// Don't fix. (This is how Go 1.7 and earlier worked,
|
||||
// not automatically generating the \\?\ form)
|
||||
return path
|
||||
}
|
||||
|
||||
// The extended form begins with \\?\, as in
|
||||
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
|
||||
// The extended form disables evaluation of . and .. path
|
||||
// elements and disables the interpretation of / as equivalent
|
||||
// to \. The conversion here rewrites / to \ and elides
|
||||
// . elements as well as trailing or duplicate separators. For
|
||||
// simplicity it avoids the conversion entirely for relative
|
||||
// paths or paths containing .. elements. For now,
|
||||
// \\server\share paths are not converted to
|
||||
// \\?\UNC\server\share paths because the rules for doing so
|
||||
// are less well-specified.
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
// Don't canonicalize UNC paths.
|
||||
return path
|
||||
}
|
||||
if !isAbs(path) {
|
||||
// Relative path
|
||||
return path
|
||||
}
|
||||
|
||||
const prefix = `\\?`
|
||||
|
||||
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
|
||||
copy(pathbuf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case IsPathSeparator(path[r]):
|
||||
// empty block
|
||||
r++
|
||||
case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
|
||||
// /./
|
||||
r++
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
|
||||
// /../ is currently unhandled
|
||||
return path
|
||||
default:
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
for ; r < n && !IsPathSeparator(path[r]); r++ {
|
||||
pathbuf[w] = path[r]
|
||||
w++
|
||||
}
|
||||
}
|
||||
}
|
||||
// A drive's root directory needs a trailing \
|
||||
if w == len(`\\?\c:`) {
|
||||
pathbuf[w] = '\\'
|
||||
w++
|
||||
}
|
||||
return string(pathbuf[:w])
|
||||
}
|
||||
|
||||
// fixRootDirectory fixes a reference to a drive's root directory to
|
||||
// have the required trailing slash.
|
||||
func fixRootDirectory(p string) string {
|
||||
if len(p) == len(`\\?\c:`) {
|
||||
if IsPathSeparator(p[0]) && IsPathSeparator(p[1]) && p[2] == '?' && IsPathSeparator(p[3]) && p[5] == ':' {
|
||||
return p + `\`
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
22
compiler/internal/lib/os/pipe2_unix.go
Normal file
22
compiler/internal/lib/os/pipe2_unix.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2017 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 dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Pipe returns a connected pair of Files; reads from r return bytes written to w.
|
||||
// It returns the files and an error, if any.
|
||||
func Pipe() (r *File, w *File, err error) {
|
||||
var p [2]int
|
||||
|
||||
e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC)
|
||||
if e != nil {
|
||||
return nil, nil, NewSyscallError("pipe2", e)
|
||||
}
|
||||
|
||||
return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil
|
||||
}
|
||||
28
compiler/internal/lib/os/pipe_unix.go
Normal file
28
compiler/internal/lib/os/pipe_unix.go
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2009 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 aix || darwin
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Pipe returns a connected pair of Files; reads from r return bytes written to w.
|
||||
// It returns the files and an error, if any.
|
||||
func Pipe() (r *File, w *File, err error) {
|
||||
var p [2]int
|
||||
|
||||
// See ../syscall/exec.go for description of lock.
|
||||
syscall.ForkLock.RLock()
|
||||
e := syscall.Pipe(p[0:])
|
||||
if e != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, nil, NewSyscallError("pipe", e)
|
||||
}
|
||||
syscall.CloseOnExec(p[0])
|
||||
syscall.CloseOnExec(p[1])
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil
|
||||
}
|
||||
16
compiler/internal/lib/os/pipe_wasm.go
Normal file
16
compiler/internal/lib/os/pipe_wasm.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2023 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 wasm
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Pipe returns a connected pair of Files; reads from r return bytes written to w.
|
||||
// It returns the files and an error, if any.
|
||||
func Pipe() (r *File, w *File, err error) {
|
||||
// Neither GOOS=js nor GOOS=wasip1 have pipes.
|
||||
return nil, nil, NewSyscallError("pipe", syscall.ENOSYS)
|
||||
}
|
||||
65
compiler/internal/lib/os/proc.go
Normal file
65
compiler/internal/lib/os/proc.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// Process etc.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/os"
|
||||
)
|
||||
|
||||
// Args hold the command-line arguments, starting with the program name.
|
||||
var Args []string
|
||||
|
||||
func init() {
|
||||
if runtime.GOOS == "windows" {
|
||||
// TODO(xsw): check windows implementation
|
||||
// Initialized in exec_windows.go.
|
||||
return
|
||||
}
|
||||
Args = runtimeArgs(int(c.Argc), c.Argv)
|
||||
}
|
||||
|
||||
func runtimeArgs(argc int, argv **c.Char) []string {
|
||||
args := make([]string, argc)
|
||||
for i := 0; i < argc; i++ {
|
||||
arg := *c.Advance(argv, i)
|
||||
args[i] = unsafe.String((*byte)(unsafe.Pointer(arg)), c.Strlen(arg))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
|
||||
//
|
||||
// On Windows, it returns syscall.EWINDOWS. See the os/user package
|
||||
// for a possible alternative.
|
||||
func Getgroups() ([]int, error) {
|
||||
/* TODO(xsw):
|
||||
gids, e := syscall.Getgroups()
|
||||
return gids, NewSyscallError("getgroups", e)
|
||||
*/
|
||||
panic("todo: os.Getgroups")
|
||||
}
|
||||
|
||||
// Exit causes the current program to exit with the given status code.
|
||||
// Conventionally, code zero indicates success, non-zero an error.
|
||||
// The program terminates immediately; deferred functions are not run.
|
||||
//
|
||||
// For portability, the status code should be in the range [0, 125].
|
||||
func Exit(code int) {
|
||||
// Inform the runtime that os.Exit is being called. If -race is
|
||||
// enabled, this will give race detector a chance to fail the
|
||||
// program (racy programs do not have the right to finish
|
||||
// successfully). If coverage is enabled, then this call will
|
||||
// enable us to write out a coverage data file.
|
||||
// TODO(xsw):
|
||||
// runtime_beforeExit(code)
|
||||
|
||||
os.Exit(c.Int(code))
|
||||
}
|
||||
19
compiler/internal/lib/os/stat.go
Normal file
19
compiler/internal/lib/os/stat.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
package os
|
||||
|
||||
// Stat returns a FileInfo describing the named file.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Stat(name string) (FileInfo, error) {
|
||||
return statNolog(name)
|
||||
}
|
||||
|
||||
// Lstat returns a FileInfo describing the named file.
|
||||
// If the file is a symbolic link, the returned FileInfo
|
||||
// describes the symbolic link. Lstat makes no attempt to follow the link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Lstat(name string) (FileInfo, error) {
|
||||
return lstatNolog(name)
|
||||
}
|
||||
43
compiler/internal/lib/os/stat_darwin.go
Normal file
43
compiler/internal/lib/os/stat_darwin.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/c/syscall"
|
||||
)
|
||||
|
||||
func fillFileStatFromSys(fs *fileStat, name string) {
|
||||
fs.name = basename(name)
|
||||
fs.size = fs.sys.Size
|
||||
fs.modTime = time.Unix(fs.sys.Mtimespec.Unix())
|
||||
fs.mode = FileMode(fs.sys.Mode & 0777)
|
||||
switch fs.sys.Mode & syscall.S_IFMT {
|
||||
case syscall.S_IFBLK, syscall.S_IFWHT:
|
||||
fs.mode |= ModeDevice
|
||||
case syscall.S_IFCHR:
|
||||
fs.mode |= ModeDevice | ModeCharDevice
|
||||
case syscall.S_IFDIR:
|
||||
fs.mode |= ModeDir
|
||||
case syscall.S_IFIFO:
|
||||
fs.mode |= ModeNamedPipe
|
||||
case syscall.S_IFLNK:
|
||||
fs.mode |= ModeSymlink
|
||||
case syscall.S_IFREG:
|
||||
// nothing to do
|
||||
case syscall.S_IFSOCK:
|
||||
fs.mode |= ModeSocket
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISGID != 0 {
|
||||
fs.mode |= ModeSetgid
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISUID != 0 {
|
||||
fs.mode |= ModeSetuid
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISVTX != 0 {
|
||||
fs.mode |= ModeSticky
|
||||
}
|
||||
}
|
||||
42
compiler/internal/lib/os/stat_linux.go
Normal file
42
compiler/internal/lib/os/stat_linux.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func fillFileStatFromSys(fs *fileStat, name string) {
|
||||
fs.name = basename(name)
|
||||
fs.size = fs.sys.Size
|
||||
fs.modTime = time.Unix(fs.sys.Mtim.Unix())
|
||||
fs.mode = FileMode(fs.sys.Mode & 0777)
|
||||
switch fs.sys.Mode & syscall.S_IFMT {
|
||||
case syscall.S_IFBLK:
|
||||
fs.mode |= ModeDevice
|
||||
case syscall.S_IFCHR:
|
||||
fs.mode |= ModeDevice | ModeCharDevice
|
||||
case syscall.S_IFDIR:
|
||||
fs.mode |= ModeDir
|
||||
case syscall.S_IFIFO:
|
||||
fs.mode |= ModeNamedPipe
|
||||
case syscall.S_IFLNK:
|
||||
fs.mode |= ModeSymlink
|
||||
case syscall.S_IFREG:
|
||||
// nothing to do
|
||||
case syscall.S_IFSOCK:
|
||||
fs.mode |= ModeSocket
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISGID != 0 {
|
||||
fs.mode |= ModeSetgid
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISUID != 0 {
|
||||
fs.mode |= ModeSetuid
|
||||
}
|
||||
if fs.sys.Mode&syscall.S_ISVTX != 0 {
|
||||
fs.mode |= ModeSticky
|
||||
}
|
||||
}
|
||||
54
compiler/internal/lib/os/stat_unix.go
Normal file
54
compiler/internal/lib/os/stat_unix.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2016 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) || wasip1
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/os"
|
||||
"github.com/goplus/llgo/compiler/internal/lib/syscall"
|
||||
)
|
||||
|
||||
// Stat returns the FileInfo structure describing file.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (f *File) Stat() (FileInfo, error) {
|
||||
if f == nil {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
var fs fileStat
|
||||
err := os.Fstat(c.Int(f.fd), &fs.sys)
|
||||
if err != 0 {
|
||||
return nil, &PathError{Op: "stat", Path: f.name, Err: syscall.Errno(err)}
|
||||
}
|
||||
fillFileStatFromSys(&fs, f.name)
|
||||
return &fs, nil
|
||||
}
|
||||
|
||||
// statNolog stats a file with no test logging.
|
||||
func statNolog(name string) (FileInfo, error) {
|
||||
var fs fileStat
|
||||
err := ignoringEINTR(func() error {
|
||||
return syscall.Stat(name, &fs.sys)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "stat", Path: name, Err: err}
|
||||
}
|
||||
fillFileStatFromSys(&fs, name)
|
||||
return &fs, nil
|
||||
}
|
||||
|
||||
// lstatNolog lstats a file with no test logging.
|
||||
func lstatNolog(name string) (FileInfo, error) {
|
||||
var fs fileStat
|
||||
err := ignoringEINTR(func() error {
|
||||
return syscall.Lstat(name, &fs.sys)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "lstat", Path: name, Err: err}
|
||||
}
|
||||
fillFileStatFromSys(&fs, name)
|
||||
return &fs, nil
|
||||
}
|
||||
11
compiler/internal/lib/os/sticky_bsd.go
Normal file
11
compiler/internal/lib/os/sticky_bsd.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2014 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 aix || darwin || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris || wasip1
|
||||
|
||||
package os
|
||||
|
||||
// According to sticky(8), neither open(2) nor mkdir(2) will create
|
||||
// a file with the sticky bit set.
|
||||
const supportsCreateWithStickyBit = false
|
||||
9
compiler/internal/lib/os/sticky_nonbsd.go
Normal file
9
compiler/internal/lib/os/sticky_nonbsd.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2014 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 !aix && !darwin && !dragonfly && !freebsd && !js && !netbsd && !openbsd && !solaris && !wasip1
|
||||
|
||||
package os
|
||||
|
||||
const supportsCreateWithStickyBit = true
|
||||
39
compiler/internal/lib/os/str.go
Normal file
39
compiler/internal/lib/os/str.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
// Simple conversions to avoid depending on strconv.
|
||||
|
||||
package os
|
||||
|
||||
// itox converts val (an int) to a hexadecimal string.
|
||||
func itox(val int) string {
|
||||
if val < 0 {
|
||||
return "-" + uitox(uint(-val))
|
||||
}
|
||||
return uitox(uint(val))
|
||||
}
|
||||
|
||||
const hex = "0123456789abcdef"
|
||||
|
||||
// uitox converts val (a uint) to a hexadecimal string.
|
||||
func uitox(val uint) string {
|
||||
if val == 0 { // avoid string allocation
|
||||
return "0x0"
|
||||
}
|
||||
var buf [20]byte // big enough for 64bit value base 16 + 0x
|
||||
i := len(buf) - 1
|
||||
for val >= 16 {
|
||||
q := val / 16
|
||||
buf[i] = hex[val%16]
|
||||
i--
|
||||
val = q
|
||||
}
|
||||
// val < 16
|
||||
buf[i] = hex[val%16]
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
return string(buf[i:])
|
||||
}
|
||||
11
compiler/internal/lib/os/sys_js.go
Normal file
11
compiler/internal/lib/os/sys_js.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
package os
|
||||
|
||||
// supportsCloseOnExec reports whether the platform supports the
|
||||
// O_CLOEXEC flag.
|
||||
const supportsCloseOnExec = false
|
||||
14
compiler/internal/lib/os/sys_unix.go
Normal file
14
compiler/internal/lib/os/sys_unix.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
package os
|
||||
|
||||
// supportsCloseOnExec reports whether the platform supports the
|
||||
// O_CLOEXEC flag.
|
||||
// On Darwin, the O_CLOEXEC flag was introduced in OS X 10.7 (Darwin 11.0.0).
|
||||
// See https://support.apple.com/kb/HT1633.
|
||||
// On FreeBSD, the O_CLOEXEC flag was introduced in version 8.3.
|
||||
const supportsCloseOnExec = true
|
||||
11
compiler/internal/lib/os/sys_wasip1.go
Normal file
11
compiler/internal/lib/os/sys_wasip1.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 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 wasip1
|
||||
|
||||
package os
|
||||
|
||||
// supportsCloseOnExec reports whether the platform supports the
|
||||
// O_CLOEXEC flag.
|
||||
const supportsCloseOnExec = false
|
||||
115
compiler/internal/lib/os/tempfile.go
Normal file
115
compiler/internal/lib/os/tempfile.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/compiler/internal/lib/internal/bytealg"
|
||||
"github.com/goplus/llgo/compiler/internal/lib/internal/itoa"
|
||||
)
|
||||
|
||||
func nextRandom() string {
|
||||
return itoa.Uitoa(uint(uint32(c.Rand())))
|
||||
}
|
||||
|
||||
// CreateTemp creates a new temporary file in the directory dir,
|
||||
// opens the file for reading and writing, and returns the resulting file.
|
||||
// The filename is generated by taking pattern and adding a random string to the end.
|
||||
// If pattern includes a "*", the random string replaces the last "*".
|
||||
// If dir is the empty string, CreateTemp uses the default directory for temporary files, as returned by TempDir.
|
||||
// Multiple programs or goroutines calling CreateTemp simultaneously will not choose the same file.
|
||||
// The caller can use the file's Name method to find the pathname of the file.
|
||||
// It is the caller's responsibility to remove the file when it is no longer needed.
|
||||
func CreateTemp(dir, pattern string) (*File, error) {
|
||||
if dir == "" {
|
||||
dir = TempDir()
|
||||
}
|
||||
|
||||
prefix, suffix, err := prefixAndSuffix(pattern)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "createtemp", Path: pattern, Err: err}
|
||||
}
|
||||
prefix = joinPath(dir, prefix)
|
||||
|
||||
try := 0
|
||||
for {
|
||||
name := prefix + nextRandom() + suffix
|
||||
f, err := OpenFile(name, O_RDWR|O_CREATE|O_EXCL, 0600)
|
||||
if IsExist(err) {
|
||||
if try++; try < 10000 {
|
||||
continue
|
||||
}
|
||||
return nil, &PathError{Op: "createtemp", Path: prefix + "*" + suffix, Err: ErrExist}
|
||||
}
|
||||
return f, err
|
||||
}
|
||||
}
|
||||
|
||||
var errPatternHasSeparator = errors.New("pattern contains path separator")
|
||||
|
||||
// prefixAndSuffix splits pattern by the last wildcard "*", if applicable,
|
||||
// returning prefix as the part before "*" and suffix as the part after "*".
|
||||
func prefixAndSuffix(pattern string) (prefix, suffix string, err error) {
|
||||
for i := 0; i < len(pattern); i++ {
|
||||
if IsPathSeparator(pattern[i]) {
|
||||
return "", "", errPatternHasSeparator
|
||||
}
|
||||
}
|
||||
if pos := bytealg.LastIndexByteString(pattern, '*'); pos != -1 {
|
||||
prefix, suffix = pattern[:pos], pattern[pos+1:]
|
||||
} else {
|
||||
prefix = pattern
|
||||
}
|
||||
return prefix, suffix, nil
|
||||
}
|
||||
|
||||
// MkdirTemp creates a new temporary directory in the directory dir
|
||||
// and returns the pathname of the new directory.
|
||||
// The new directory's name is generated by adding a random string to the end of pattern.
|
||||
// If pattern includes a "*", the random string replaces the last "*" instead.
|
||||
// If dir is the empty string, MkdirTemp uses the default directory for temporary files, as returned by TempDir.
|
||||
// Multiple programs or goroutines calling MkdirTemp simultaneously will not choose the same directory.
|
||||
// It is the caller's responsibility to remove the directory when it is no longer needed.
|
||||
func MkdirTemp(dir, pattern string) (string, error) {
|
||||
if dir == "" {
|
||||
dir = TempDir()
|
||||
}
|
||||
|
||||
prefix, suffix, err := prefixAndSuffix(pattern)
|
||||
if err != nil {
|
||||
return "", &PathError{Op: "mkdirtemp", Path: pattern, Err: err}
|
||||
}
|
||||
prefix = joinPath(dir, prefix)
|
||||
|
||||
try := 0
|
||||
for {
|
||||
name := prefix + nextRandom() + suffix
|
||||
err := Mkdir(name, 0700)
|
||||
if err == nil {
|
||||
return name, nil
|
||||
}
|
||||
if IsExist(err) {
|
||||
if try++; try < 10000 {
|
||||
continue
|
||||
}
|
||||
return "", &PathError{Op: "mkdirtemp", Path: dir + string(PathSeparator) + prefix + "*" + suffix, Err: ErrExist}
|
||||
}
|
||||
if IsNotExist(err) {
|
||||
if _, err := Stat(dir); IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
func joinPath(dir, name string) string {
|
||||
if len(dir) > 0 && IsPathSeparator(dir[len(dir)-1]) {
|
||||
return dir + name
|
||||
}
|
||||
return dir + string(PathSeparator) + name
|
||||
}
|
||||
126
compiler/internal/lib/os/types.go
Normal file
126
compiler/internal/lib/os/types.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/os"
|
||||
)
|
||||
|
||||
// Getpagesize returns the underlying system's memory page size.
|
||||
func Getpagesize() int { return syscall.Getpagesize() }
|
||||
|
||||
// File represents an open file descriptor.
|
||||
type File struct {
|
||||
fd uintptr
|
||||
name string
|
||||
appendMode bool
|
||||
nonblock bool
|
||||
stdoutOrErr bool
|
||||
}
|
||||
|
||||
// write writes len(b) bytes to the File.
|
||||
// It returns the number of bytes written and an error, if any.
|
||||
func (f *File) write(b []byte) (int, error) {
|
||||
ret := os.Write(c.Int(f.fd), unsafe.Pointer(unsafe.SliceData(b)), uintptr(len(b)))
|
||||
if ret >= 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
return 0, syscall.Errno(os.Errno())
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// write writes len(b) bytes to the File.
|
||||
// It returns the number of bytes written and an error, if any.
|
||||
func (f *File) write(b []byte) (n int, err error) {
|
||||
n, err = f.pfd.Write(b)
|
||||
runtime.KeepAlive(f)
|
||||
return n, err
|
||||
}
|
||||
*/
|
||||
|
||||
// read reads up to len(b) bytes from the File.
|
||||
// It returns the number of bytes read and an error, if any.
|
||||
func (f *File) read(b []byte) (int, error) {
|
||||
ret := os.Read(c.Int(f.fd), unsafe.Pointer(unsafe.SliceData(b)), uintptr(len(b)))
|
||||
if ret > 0 {
|
||||
return int(ret), nil
|
||||
}
|
||||
if ret == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, syscall.Errno(os.Errno())
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// read reads up to len(b) bytes from the File.
|
||||
// It returns the number of bytes read and an error, if any.
|
||||
func (f *File) read(b []byte) (n int, err error) {
|
||||
n, err = f.pfd.Read(b)
|
||||
runtime.KeepAlive(f)
|
||||
return n, err
|
||||
}
|
||||
*/
|
||||
|
||||
// A FileInfo describes a file and is returned by Stat and Lstat.
|
||||
type FileInfo = fs.FileInfo
|
||||
|
||||
// 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 = fs.FileMode
|
||||
|
||||
// The defined file mode bits are the most significant bits of the FileMode.
|
||||
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
|
||||
// The values of these bits should be considered part of the public API and
|
||||
// may be used in wire protocols or disk representations: they must not be
|
||||
// changed, although new bits might be added.
|
||||
const (
|
||||
// The single letters are the abbreviations
|
||||
// used by the String method's formatting.
|
||||
ModeDir = fs.ModeDir // d: is a directory
|
||||
ModeAppend = fs.ModeAppend // a: append-only
|
||||
ModeExclusive = fs.ModeExclusive // l: exclusive use
|
||||
ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only
|
||||
ModeSymlink = fs.ModeSymlink // L: symbolic link
|
||||
ModeDevice = fs.ModeDevice // D: device file
|
||||
ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
|
||||
ModeSocket = fs.ModeSocket // S: Unix domain socket
|
||||
ModeSetuid = fs.ModeSetuid // u: setuid
|
||||
ModeSetgid = fs.ModeSetgid // g: setgid
|
||||
ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
|
||||
ModeSticky = fs.ModeSticky // t: sticky
|
||||
ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file
|
||||
|
||||
// Mask for the type bits. For regular files, none will be set.
|
||||
ModeType = fs.ModeType
|
||||
|
||||
ModePerm = fs.ModePerm // Unix permission bits, 0o777
|
||||
)
|
||||
|
||||
func (fs *fileStat) Name() string { return fs.name }
|
||||
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
|
||||
|
||||
// SameFile reports whether fi1 and fi2 describe the same file.
|
||||
// For example, on Unix this means that the device and inode fields
|
||||
// of the two underlying structures are identical; on other systems
|
||||
// the decision may be based on the path names.
|
||||
// SameFile only applies to results returned by this package's Stat.
|
||||
// It returns false in other cases.
|
||||
func SameFile(fi1, fi2 FileInfo) bool {
|
||||
fs1, ok1 := fi1.(*fileStat)
|
||||
fs2, ok2 := fi2.(*fileStat)
|
||||
if !ok1 || !ok2 {
|
||||
return false
|
||||
}
|
||||
return sameFile(fs1, fs2)
|
||||
}
|
||||
30
compiler/internal/lib/os/types_plan9.go
Normal file
30
compiler/internal/lib/os/types_plan9.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
|
||||
type fileStat struct {
|
||||
name string
|
||||
size int64
|
||||
mode FileMode
|
||||
modTime time.Time
|
||||
sys any
|
||||
}
|
||||
|
||||
func (fs *fileStat) Size() int64 { return fs.size }
|
||||
func (fs *fileStat) Mode() FileMode { return fs.mode }
|
||||
func (fs *fileStat) ModTime() time.Time { return fs.modTime }
|
||||
func (fs *fileStat) Sys() any { return fs.sys }
|
||||
|
||||
func sameFile(fs1, fs2 *fileStat) bool {
|
||||
a := fs1.sys.(*syscall.Dir)
|
||||
b := fs2.sys.(*syscall.Dir)
|
||||
return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
|
||||
}
|
||||
31
compiler/internal/lib/os/types_unix.go
Normal file
31
compiler/internal/lib/os/types_unix.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2009 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 !windows && !plan9
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/compiler/internal/lib/syscall"
|
||||
)
|
||||
|
||||
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
|
||||
type fileStat struct {
|
||||
name string
|
||||
size int64
|
||||
mode FileMode
|
||||
modTime time.Time
|
||||
sys syscall.Stat_t
|
||||
}
|
||||
|
||||
func (fs *fileStat) Size() int64 { return fs.size }
|
||||
func (fs *fileStat) Mode() FileMode { return fs.mode }
|
||||
func (fs *fileStat) ModTime() time.Time { return fs.modTime }
|
||||
func (fs *fileStat) Sys() any { return &fs.sys }
|
||||
|
||||
func sameFile(fs1, fs2 *fileStat) bool {
|
||||
return fs1.sys.Dev == fs2.sys.Dev && fs1.sys.Ino == fs2.sys.Ino
|
||||
}
|
||||
241
compiler/internal/lib/os/types_windows.go
Normal file
241
compiler/internal/lib/os/types_windows.go
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A fileStat is the implementation of FileInfo returned by Stat and Lstat.
|
||||
type fileStat struct {
|
||||
name string
|
||||
|
||||
// from ByHandleFileInformation, Win32FileAttributeData and Win32finddata
|
||||
FileAttributes uint32
|
||||
CreationTime syscall.Filetime
|
||||
LastAccessTime syscall.Filetime
|
||||
LastWriteTime syscall.Filetime
|
||||
FileSizeHigh uint32
|
||||
FileSizeLow uint32
|
||||
|
||||
// from Win32finddata
|
||||
ReparseTag uint32
|
||||
|
||||
// what syscall.GetFileType returns
|
||||
filetype uint32
|
||||
|
||||
// used to implement SameFile
|
||||
sync.Mutex
|
||||
path string
|
||||
vol uint32
|
||||
idxhi uint32
|
||||
idxlo uint32
|
||||
appendNameToPath bool
|
||||
}
|
||||
|
||||
// newFileStatFromGetFileInformationByHandle calls GetFileInformationByHandle
|
||||
// to gather all required information about the file handle h.
|
||||
func newFileStatFromGetFileInformationByHandle(path string, h syscall.Handle) (fs *fileStat, err error) {
|
||||
var d syscall.ByHandleFileInformation
|
||||
err = syscall.GetFileInformationByHandle(h, &d)
|
||||
if err != nil {
|
||||
return nil, &PathError{Op: "GetFileInformationByHandle", Path: path, Err: err}
|
||||
}
|
||||
|
||||
var ti windows.FILE_ATTRIBUTE_TAG_INFO
|
||||
err = windows.GetFileInformationByHandleEx(h, windows.FileAttributeTagInfo, (*byte)(unsafe.Pointer(&ti)), uint32(unsafe.Sizeof(ti)))
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == windows.ERROR_INVALID_PARAMETER {
|
||||
// It appears calling GetFileInformationByHandleEx with
|
||||
// FILE_ATTRIBUTE_TAG_INFO fails on FAT file system with
|
||||
// ERROR_INVALID_PARAMETER. Clear ti.ReparseTag in that
|
||||
// instance to indicate no symlinks are possible.
|
||||
ti.ReparseTag = 0
|
||||
} else {
|
||||
return nil, &PathError{Op: "GetFileInformationByHandleEx", Path: path, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
return &fileStat{
|
||||
name: basename(path),
|
||||
FileAttributes: d.FileAttributes,
|
||||
CreationTime: d.CreationTime,
|
||||
LastAccessTime: d.LastAccessTime,
|
||||
LastWriteTime: d.LastWriteTime,
|
||||
FileSizeHigh: d.FileSizeHigh,
|
||||
FileSizeLow: d.FileSizeLow,
|
||||
vol: d.VolumeSerialNumber,
|
||||
idxhi: d.FileIndexHigh,
|
||||
idxlo: d.FileIndexLow,
|
||||
ReparseTag: ti.ReparseTag,
|
||||
// fileStat.path is used by os.SameFile to decide if it needs
|
||||
// to fetch vol, idxhi and idxlo. But these are already set,
|
||||
// so set fileStat.path to "" to prevent os.SameFile doing it again.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newFileStatFromWin32finddata copies all required information
|
||||
// from syscall.Win32finddata d into the newly created fileStat.
|
||||
func newFileStatFromWin32finddata(d *syscall.Win32finddata) *fileStat {
|
||||
fs := &fileStat{
|
||||
FileAttributes: d.FileAttributes,
|
||||
CreationTime: d.CreationTime,
|
||||
LastAccessTime: d.LastAccessTime,
|
||||
LastWriteTime: d.LastWriteTime,
|
||||
FileSizeHigh: d.FileSizeHigh,
|
||||
FileSizeLow: d.FileSizeLow,
|
||||
}
|
||||
if d.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
// Per https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw:
|
||||
// “If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT
|
||||
// attribute, this member specifies the reparse point tag. Otherwise, this
|
||||
// value is undefined and should not be used.”
|
||||
fs.ReparseTag = d.Reserved0
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
func (fs *fileStat) isSymlink() bool {
|
||||
// As of https://go.dev/cl/86556, we treat MOUNT_POINT reparse points as
|
||||
// symlinks because otherwise certain directory junction tests in the
|
||||
// path/filepath package would fail.
|
||||
//
|
||||
// However,
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions
|
||||
// seems to suggest that directory junctions should be treated like hard
|
||||
// links, not symlinks.
|
||||
//
|
||||
// TODO(bcmills): Get more input from Microsoft on what the behavior ought to
|
||||
// be for MOUNT_POINT reparse points.
|
||||
|
||||
return fs.ReparseTag == syscall.IO_REPARSE_TAG_SYMLINK ||
|
||||
fs.ReparseTag == windows.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
func (fs *fileStat) Size() int64 {
|
||||
return int64(fs.FileSizeHigh)<<32 + int64(fs.FileSizeLow)
|
||||
}
|
||||
|
||||
func (fs *fileStat) Mode() (m FileMode) {
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
m |= 0444
|
||||
} else {
|
||||
m |= 0666
|
||||
}
|
||||
if fs.isSymlink() {
|
||||
return m | ModeSymlink
|
||||
}
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
m |= ModeDir | 0111
|
||||
}
|
||||
switch fs.filetype {
|
||||
case syscall.FILE_TYPE_PIPE:
|
||||
m |= ModeNamedPipe
|
||||
case syscall.FILE_TYPE_CHAR:
|
||||
m |= ModeDevice | ModeCharDevice
|
||||
}
|
||||
if fs.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 && m&ModeType == 0 {
|
||||
m |= ModeIrregular
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (fs *fileStat) ModTime() time.Time {
|
||||
return time.Unix(0, fs.LastWriteTime.Nanoseconds())
|
||||
}
|
||||
|
||||
// Sys returns syscall.Win32FileAttributeData for file fs.
|
||||
func (fs *fileStat) Sys() any {
|
||||
return &syscall.Win32FileAttributeData{
|
||||
FileAttributes: fs.FileAttributes,
|
||||
CreationTime: fs.CreationTime,
|
||||
LastAccessTime: fs.LastAccessTime,
|
||||
LastWriteTime: fs.LastWriteTime,
|
||||
FileSizeHigh: fs.FileSizeHigh,
|
||||
FileSizeLow: fs.FileSizeLow,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *fileStat) loadFileId() error {
|
||||
fs.Lock()
|
||||
defer fs.Unlock()
|
||||
if fs.path == "" {
|
||||
// already done
|
||||
return nil
|
||||
}
|
||||
var path string
|
||||
if fs.appendNameToPath {
|
||||
path = fs.path + `\` + fs.name
|
||||
} else {
|
||||
path = fs.path
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Per https://learn.microsoft.com/en-us/windows/win32/fileio/reparse-points-and-file-operations,
|
||||
// “Applications that use the CreateFile function should specify the
|
||||
// FILE_FLAG_OPEN_REPARSE_POINT flag when opening the file if it is a reparse
|
||||
// point.”
|
||||
//
|
||||
// And per https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew,
|
||||
// “If the file is not a reparse point, then this flag is ignored.”
|
||||
//
|
||||
// So we set FILE_FLAG_OPEN_REPARSE_POINT unconditionally, since we want
|
||||
// information about the reparse point itself.
|
||||
//
|
||||
// If the file is a symlink, the symlink target should have already been
|
||||
// resolved when the fileStat was created, so we don't need to worry about
|
||||
// resolving symlink reparse points again here.
|
||||
attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT)
|
||||
|
||||
h, err := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.CloseHandle(h)
|
||||
var i syscall.ByHandleFileInformation
|
||||
err = syscall.GetFileInformationByHandle(h, &i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.path = ""
|
||||
fs.vol = i.VolumeSerialNumber
|
||||
fs.idxhi = i.FileIndexHigh
|
||||
fs.idxlo = i.FileIndexLow
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveInfoFromPath saves full path of the file to be used by os.SameFile later,
|
||||
// and set name from path.
|
||||
func (fs *fileStat) saveInfoFromPath(path string) error {
|
||||
fs.path = path
|
||||
if !isAbs(fs.path) {
|
||||
var err error
|
||||
fs.path, err = syscall.FullPath(fs.path)
|
||||
if err != nil {
|
||||
return &PathError{Op: "FullPath", Path: path, Err: err}
|
||||
}
|
||||
}
|
||||
fs.name = basename(path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func sameFile(fs1, fs2 *fileStat) bool {
|
||||
e := fs1.loadFileId()
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
e = fs2.loadFileId()
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return fs1.vol == fs2.vol && fs1.idxhi == fs2.idxhi && fs1.idxlo == fs2.idxlo
|
||||
}
|
||||
21
compiler/internal/lib/os/wait_unimp.go
Normal file
21
compiler/internal/lib/os/wait_unimp.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// aix, darwin, js/wasm, openbsd, solaris and wasip1/wasm don't implement
|
||||
// waitid/wait6.
|
||||
|
||||
//go:build aix || darwin || (js && wasm) || openbsd || solaris || wasip1
|
||||
|
||||
package os
|
||||
|
||||
// blockUntilWaitable attempts to block until a call to p.Wait will
|
||||
// succeed immediately, and reports whether it has done so.
|
||||
// It does not actually call p.Wait.
|
||||
// This version is used on systems that do not implement waitid,
|
||||
// or where we have not implemented it yet. Note that this is racy:
|
||||
// a call to Process.Signal can in an extremely unlikely case send a
|
||||
// signal to the wrong process, see issue #13987.
|
||||
func (p *Process) blockUntilWaitable() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
32
compiler/internal/lib/os/wait_wait6.go
Normal file
32
compiler/internal/lib/os/wait_wait6.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2016 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 dragonfly || freebsd || netbsd
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// blockUntilWaitable attempts to block until a call to p.Wait will
|
||||
// succeed immediately, and reports whether it has done so.
|
||||
// It does not actually call p.Wait.
|
||||
func (p *Process) blockUntilWaitable() (bool, error) {
|
||||
var errno syscall.Errno
|
||||
for {
|
||||
_, errno = wait6(_P_PID, p.Pid, syscall.WEXITED|syscall.WNOWAIT)
|
||||
if errno != syscall.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
// TODO(xsw):
|
||||
// runtime.KeepAlive(p)
|
||||
if errno == syscall.ENOSYS {
|
||||
return false, nil
|
||||
} else if errno != 0 {
|
||||
return false, NewSyscallError("wait6", errno)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
55
compiler/internal/lib/os/wait_waitid.go
Normal file
55
compiler/internal/lib/os/wait_waitid.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// We used to used this code for Darwin, but according to issue #19314
|
||||
// waitid returns if the process is stopped, even when using WEXITED.
|
||||
|
||||
//go:build linux
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
)
|
||||
|
||||
const _P_PID = 1
|
||||
|
||||
// int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
|
||||
//
|
||||
//go:linkname waitid C.waitid
|
||||
func waitid(idtype, id uintptr, infop *uint64, options c.Int) c.Int
|
||||
|
||||
// blockUntilWaitable attempts to block until a call to p.Wait will
|
||||
// succeed immediately, and reports whether it has done so.
|
||||
// It does not actually call p.Wait.
|
||||
func (p *Process) blockUntilWaitable() (bool, error) {
|
||||
// The waitid system call expects a pointer to a siginfo_t,
|
||||
// which is 128 bytes on all Linux systems.
|
||||
// On darwin/amd64, it requires 104 bytes.
|
||||
// We don't care about the values it returns.
|
||||
var siginfo [16]uint64
|
||||
psig := &siginfo[0]
|
||||
var e syscall.Errno
|
||||
for {
|
||||
e = syscall.Errno(waitid(_P_PID, uintptr(p.Pid), psig, syscall.WEXITED|syscall.WNOWAIT))
|
||||
if e != syscall.EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
// TODO(xsw):
|
||||
// runtime.KeepAlive(p)
|
||||
if e != 0 {
|
||||
// waitid has been available since Linux 2.6.9, but
|
||||
// reportedly is not available in Ubuntu on Windows.
|
||||
// See issue 16610.
|
||||
if e == syscall.ENOSYS {
|
||||
return false, nil
|
||||
}
|
||||
return false, NewSyscallError("waitid", e)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user