/* SysUtils - a library to assist with various system-related functions Copyright (C) 2020 Brent Saner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package paths import ( "errors" `fmt` "os" "os/user" "path/filepath" `runtime` // "strconv" "strings" // "syscall" ) /* ExpandHome will take a tilde(~)-prefixed path and resolve it to the actual path in-place. Note that it only works for current user; the syntax ~someotheruser/foo/bar is currently unsupported. */ func ExpandHome(path *string) (err error) { var usr *user.User // Props to this guy. // https://stackoverflow.com/a/43578461/733214 if len(*path) == 0 { err = errors.New("empty path") return } else if (*path)[0] != '~' { return } // E(ffective)UID (e.g. chown'd user for SUID) /* uid := strconv.Itoa(syscall.Geteuid()) usr, err := user.LookupId(euid) */ // (Real)UID (invoking user) if usr, err = user.Current(); err != nil { return err } *path = filepath.Join(usr.HomeDir, (*path)[1:]) return } // GetPathEnv returns a slice of the PATH variable's items. func GetPathEnv() (paths []string, err error) { var pathVar string = "PATH" var sep string = ":" paths = make([]string, 0) if runtime.GOOS == "windows" { pathVar = "Path" sep = ";" } for _, p := range strings.Split(os.Getenv(pathVar), sep) { if err = RealPath(&p); err != nil { return } paths = append(paths, p) } return } // MakeDirIfNotExist will create a directory at a given path if it doesn't exist. func MakeDirIfNotExist(path string) (err error) { var stat os.FileInfo var exists bool var locPath string = path if exists, stat, err = RealPathExistsStat(&locPath); err != nil { if !exists { // This, at least as of golang 1.15, uses the user's umask. // It does not actually create a dir with 0777. // It's up to the caller to do an os.Chmod() on the path after, if desired. if err = os.MkdirAll(locPath, 0777); err != nil { return } err = nil return } else { return } } // So it exists, but it probably isn't a dir. if !stat.Mode().IsDir() { err = errors.New(fmt.Sprintf("path %v exists but is not a directory", locPath)) return } // This should probably never happen. Probably. err = errors.New("undefined") return } // RealPath will transform a given path into the very best guess for an absolute path in-place. func RealPath(path *string) (err error) { if err = ExpandHome(path); err != nil { return } if *path, err = filepath.Abs(*path); err != nil { return } return } /* RealPathExists is like RealPath, but will also return a boolean as to whether the path actually exists or not. It's hacky, but the "exists" bool along with err is a sort of proto-state-machine. If err != nil and bool is true, the error occurred during path absolution. If err != nil and bool is false, the path does not exist. */ func RealPathExists(path *string) (exists bool, err error) { if err = RealPath(path); err != nil { exists = true return } if _, err = os.Stat(*path); err != nil { return } exists = true return } // RealPathExistsStat is like RealPathExists except it will also return the os.FileInfo for the path (assuming it exists). func RealPathExistsStat(path *string) (exists bool, stat os.FileInfo, err error) { // See the comments for RealPathExists for details on this. if err = RealPath(path); err != nil { exists = true return } if stat, err = os.Stat(*path); err != nil { return } exists = true return }