// Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2024 Datadog, Inc. package utils import ( "bytes" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" ) // This code is based on: https://github.com/mitchellh/go-homedir/blob/v1.1.0/homedir.go (MIT License) // ExpandPath expands a file path that starts with '~' to the user's home directory. // If the path does not start with '~', it is returned unchanged. // // Parameters: // // path - The file path to be expanded. // // Returns: // // The expanded file path, with '~' replaced by the user's home directory, if applicable. func ExpandPath(path string) string { if len(path) == 0 || path[0] != '~' { return path } // If the second character is not '/' or '\', return the path unchanged if len(path) > 1 && path[1] != '/' && path[1] != '\\' { return path } homeFolder := getHomeDir() if len(homeFolder) > 0 { return filepath.Join(homeFolder, path[1:]) } return path } // getHomeDir returns the home directory of the current user. // The method used to determine the home directory depends on the operating system. // // On Windows, it prefers the HOME environment variable, then USERPROFILE, and finally combines HOMEDRIVE and HOMEPATH. // On Unix-like systems, it prefers the HOME environment variable, and falls back to various shell commands // to determine the home directory if necessary. // // Returns: // // The home directory of the current user. func getHomeDir() string { if runtime.GOOS == "windows" { if home := os.Getenv("HOME"); home != "" { // First prefer the HOME environment variable return home } if userProfile := os.Getenv("USERPROFILE"); userProfile != "" { // Prefer the USERPROFILE environment variable return userProfile } homeDrive := os.Getenv("HOMEDRIVE") homePath := os.Getenv("HOMEPATH") return homeDrive + homePath } homeEnv := "HOME" if runtime.GOOS == "plan9" { // On plan9, environment variables are lowercase. homeEnv = "home" } if home := os.Getenv(homeEnv); home != "" { // Prefer the HOME environment variable return home } var stdout bytes.Buffer if runtime.GOOS == "darwin" { // On macOS, use dscl to read the NFSHomeDirectory 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 } } } else { // On other Unix-like systems, use getent to read the passwd entry for the current user cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) cmd.Stdout = &stdout if err := cmd.Run(); err == nil { if passwd := strings.TrimSpace(stdout.String()); passwd != "" { // The passwd entry is in the format: username:password:uid:gid:gecos:home:shell passwdParts := strings.SplitN(passwd, ":", 7) if len(passwdParts) > 5 { return passwdParts[5] } } } } // If all else fails, use the shell to determine the home directory stdout.Reset() cmd := exec.Command("sh", "-c", "cd && pwd") cmd.Stdout = &stdout if err := cmd.Run(); err == nil { return strings.TrimSpace(stdout.String()) } return "" }