diff --git a/internal/config/pan_config.go b/internal/config/pan_config.go index 0d0181e..e516fd3 100644 --- a/internal/config/pan_config.go +++ b/internal/config/pan_config.go @@ -19,6 +19,7 @@ import ( "github.com/tickstep/aliyunpan-api/aliyunpan" "github.com/tickstep/aliyunpan/cmder/cmdutil" "github.com/tickstep/aliyunpan/cmder/cmdutil/jsonhelper" + "github.com/tickstep/aliyunpan/library/homedir" "github.com/tickstep/library-go/logger" "github.com/tickstep/library-go/requester" "os" @@ -250,16 +251,45 @@ func (c *PanConfig) initDefaultConfig() { // GetConfigDir 获取配置路径 func GetConfigDir() string { - // 从环境变量读取 + // 按照以下顺序依次获取配置目录 + // 1.环境变量ALIYUNPAN_CONFIG_DIR => 2. /etc/aliyunpan/ => 3. ~/.aliyunpan/ => 4.当前程序目录 + + // 1. 从环境变量读取 configDir, ok := os.LookupEnv(EnvConfigDir) if ok { if filepath.IsAbs(configDir) { + logger.Verboseln("use config dir from ALIYUNPAN_CONFIG_DIR env: ", configDir) return configDir } // 如果不是绝对路径, 从程序目录寻找 - return cmdutil.ExecutablePathJoin(configDir) + configDir = cmdutil.ExecutablePathJoin(configDir) + logger.Verboseln("use config dir from ALIYUNPAN_CONFIG_DIR env: ", configDir) + } else { + // 2. /etc/aliyunpan/ + if runtime.GOOS == "linux" { + cd := "/etc/aliyunpan" + if IsFolderExist(cd) { + logger.Verboseln("use config dir: ", cd) + return cd + } + } + + // 3. ~/.aliyunpan/ + if runtime.GOOS == "linux" || runtime.GOOS == "windows" { + cd,er := homedir.Expand("~/.aliyunpan") + if er == nil { + if IsFolderExist(cd) { + logger.Verboseln("use config dir: ", cd) + return cd + } + } + } } - return cmdutil.ExecutablePathJoin(configDir) + + // 4.当前程序所在目录 + configDir = cmdutil.ExecutablePathJoin(configDir) + logger.Verboseln("use config dir: ", configDir) + return configDir } func (c *PanConfig) ActiveUser() *PanUser { diff --git a/internal/config/utils.go b/internal/config/utils.go index b34d6bb..08c3f77 100644 --- a/internal/config/utils.go +++ b/internal/config/utils.go @@ -21,6 +21,7 @@ import ( "github.com/tickstep/library-go/crypto" "github.com/tickstep/library-go/ids" "github.com/tickstep/library-go/logger" + "os" "strconv" "strings" ) @@ -105,3 +106,18 @@ func DecryptString(text string) string { } return string(r) } + +// isFolderExist 判断文件夹是否存在 +func IsFolderExist(pathStr string) bool { + fi, err := os.Stat(pathStr) + if err != nil { + if os.IsExist(err) { + return fi.IsDir() + } + if os.IsNotExist(err) { + return false + } + return false + } + return fi.IsDir() +} \ No newline at end of file diff --git a/library/homedir/homedir.go b/library/homedir/homedir.go new file mode 100644 index 0000000..e5e4236 --- /dev/null +++ b/library/homedir/homedir.go @@ -0,0 +1,167 @@ +package homedir + +import ( + "bytes" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" +) + +// DisableCache will disable caching of the home directory. Caching is enabled +// by default. +var DisableCache bool + +var homedirCache string +var cacheLock sync.RWMutex + +// Dir returns the home directory for the executing user. +// +// This uses an OS-specific method for discovering the home directory. +// An error is returned if a home directory cannot be detected. +func Dir() (string, error) { + if !DisableCache { + cacheLock.RLock() + cached := homedirCache + cacheLock.RUnlock() + if cached != "" { + return cached, nil + } + } + + cacheLock.Lock() + defer cacheLock.Unlock() + + var result string + var err error + if runtime.GOOS == "windows" { + result, err = dirWindows() + } else { + // Unix-like system, so just assume Unix + result, err = dirUnix() + } + + if err != nil { + return "", err + } + homedirCache = result + return result, nil +} + +// Expand expands the path to include the home directory if the path +// is prefixed with `~`. If it isn't prefixed with `~`, the path is +// returned as-is. +func Expand(path string) (string, error) { + if len(path) == 0 { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := Dir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + +// Reset clears the cache, forcing the next call to Dir to re-detect +// the home directory. This generally never has to be called, but can be +// useful in tests if you're modifying the home directory via the HOME +// env var or something. +func Reset() { + cacheLock.Lock() + defer cacheLock.Unlock() + homedirCache = "" +} + +func dirUnix() (string, error) { + homeEnv := "HOME" + if runtime.GOOS == "plan9" { + // On plan9, env vars are lowercase. + homeEnv = "home" + } + + // First prefer the HOME environmental variable + if home := os.Getenv(homeEnv); home != "" { + return home, nil + } + + var stdout bytes.Buffer + + // If that fails, try OS specific commands + if runtime.GOOS == "darwin" { + cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) + cmd.Stdout = &stdout + if err := cmd.Run(); err == nil { + result := strings.TrimSpace(stdout.String()) + if result != "" { + return result, nil + } + } + } else { + cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + // If the error is ErrNotFound, we ignore it. Otherwise, return it. + if err != exec.ErrNotFound { + return "", err + } + } else { + if passwd := strings.TrimSpace(stdout.String()); passwd != "" { + // username:password:uid:gid:gecos:home:shell + passwdParts := strings.SplitN(passwd, ":", 7) + if len(passwdParts) > 5 { + return passwdParts[5], nil + } + } + } + } + + // If all else fails, try the shell + stdout.Reset() + cmd := exec.Command("sh", "-c", "cd && pwd") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output when reading home directory") + } + + return result, nil +} + +func dirWindows() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + return home, nil + } + + // Prefer standard environment variable USERPROFILE + if home := os.Getenv("USERPROFILE"); home != "" { + return home, nil + } + + drive := os.Getenv("HOMEDRIVE") + path := os.Getenv("HOMEPATH") + home := drive + path + if drive == "" || path == "" { + return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") + } + + return home, nil +} \ No newline at end of file