patch os: UserHomeDir/UserConfigDir/UserCacheDir

This commit is contained in:
xushiwei
2024-06-25 14:29:37 +08:00
parent 4385ca0966
commit 4421734da1
4 changed files with 817 additions and 7 deletions

View File

@@ -18,7 +18,9 @@ package os
// llgo:skipall
import (
"errors"
"io/fs"
"runtime"
_ "unsafe"
"github.com/goplus/llgo/c"
@@ -29,10 +31,6 @@ const (
LLGoPackage = true
)
type (
FileMode = fs.FileMode
)
type timeout interface {
Timeout() bool
}
@@ -102,6 +100,24 @@ func Chdir(dir string) error {
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), toMode(mode))
if ret == 0 {
@@ -110,6 +126,28 @@ func Chmod(name string, mode FileMode) error {
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 {
@@ -212,6 +250,34 @@ func Mkdir(name string, perm FileMode) error {
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 MkdirAll(path string, perm FileMode) error
// func MkdirTemp(dir, pattern string) (string, error)
@@ -247,6 +313,17 @@ func Rename(oldpath, newpath string) error {
return &LinkError{"rename", oldpath, newpath, toSyscallErr(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
@@ -285,8 +362,136 @@ func Unsetenv(key string) error {
return toSyscallErr(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 UserCacheDir() (string, error)
// func UserConfigDir() (string, error)
// func UserHomeDir() (string, error)
// func WriteFile(name string, data []byte, perm FileMode) error