2022-01-26 01:23:18 -08:00
|
|
|
package environment
|
2019-03-13 04:14:30 -07:00
|
|
|
|
|
|
|
import (
|
2021-12-19 07:42:39 -08:00
|
|
|
"bytes"
|
2020-11-19 19:12:20 -08:00
|
|
|
"context"
|
2021-01-05 11:12:52 -08:00
|
|
|
"errors"
|
2021-08-01 06:25:15 -07:00
|
|
|
"fmt"
|
2022-02-25 03:51:38 -08:00
|
|
|
"io"
|
2020-10-07 04:32:42 -07:00
|
|
|
"io/ioutil"
|
2021-08-01 06:25:15 -07:00
|
|
|
"log"
|
2020-11-19 19:12:20 -08:00
|
|
|
"net/http"
|
2022-02-25 03:51:38 -08:00
|
|
|
"net/url"
|
2022-01-26 01:23:18 -08:00
|
|
|
"oh-my-posh/regex"
|
2019-03-13 04:14:30 -07:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2020-10-01 11:57:02 -07:00
|
|
|
"path/filepath"
|
2019-03-13 04:14:30 -07:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
2020-12-31 00:39:02 -08:00
|
|
|
"time"
|
2019-03-13 04:14:30 -07:00
|
|
|
|
|
|
|
"github.com/distatus/battery"
|
2021-11-13 14:46:06 -08:00
|
|
|
process "github.com/shirou/gopsutil/v3/process"
|
2019-03-13 04:14:30 -07:00
|
|
|
)
|
|
|
|
|
2020-11-12 00:43:32 -08:00
|
|
|
const (
|
2022-01-26 01:23:18 -08:00
|
|
|
Unknown = "unknown"
|
|
|
|
WindowsPlatform = "windows"
|
|
|
|
DarwinPlatform = "darwin"
|
|
|
|
LinuxPlatform = "linux"
|
2020-11-12 00:43:32 -08:00
|
|
|
)
|
|
|
|
|
2022-03-12 13:04:08 -08:00
|
|
|
type Flags struct {
|
|
|
|
ErrorCode int
|
|
|
|
Config string
|
|
|
|
Shell string
|
|
|
|
PWD string
|
|
|
|
PSWD string
|
|
|
|
ExecutionTime float64
|
|
|
|
Eval bool
|
|
|
|
StackCount int
|
|
|
|
Migrate bool
|
|
|
|
TerminalWidth int
|
|
|
|
|
|
|
|
// PrintInit *bool
|
|
|
|
// PrintConfig *bool
|
|
|
|
// PrintShell *bool
|
|
|
|
// PrintTransient *bool
|
|
|
|
// PrintSecondary *bool
|
|
|
|
// PrintValid *bool
|
|
|
|
// PrintError *bool
|
|
|
|
// ConfigFormat *string
|
|
|
|
// Version *bool
|
|
|
|
// Millis *bool
|
|
|
|
// Init *bool
|
|
|
|
// ExportPNG *bool
|
|
|
|
// Author *string
|
|
|
|
// CursorPadding *int
|
|
|
|
// RPromptOffset *int
|
|
|
|
// RPrompt *bool
|
|
|
|
// BGColor *string
|
|
|
|
// Command *string
|
|
|
|
// CachePath *bool
|
|
|
|
// Write *bool
|
2022-01-26 01:23:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
type CommandError struct {
|
|
|
|
Err string
|
|
|
|
ExitCode int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *CommandError) Error() string {
|
|
|
|
return e.Err
|
|
|
|
}
|
|
|
|
|
|
|
|
type NoBatteryError struct{}
|
|
|
|
|
|
|
|
func (m *NoBatteryError) Error() string {
|
2021-04-12 10:15:36 -07:00
|
|
|
return "no battery"
|
|
|
|
}
|
|
|
|
|
2022-01-23 12:37:51 -08:00
|
|
|
type FileInfo struct {
|
|
|
|
ParentFolder string
|
|
|
|
Path string
|
|
|
|
IsDir bool
|
2021-01-05 11:12:52 -08:00
|
|
|
}
|
|
|
|
|
2022-01-23 12:37:51 -08:00
|
|
|
type Cache interface {
|
|
|
|
Init(home string)
|
|
|
|
Close()
|
|
|
|
Get(key string) (string, bool)
|
2021-11-23 02:13:32 -08:00
|
|
|
// ttl in minutes
|
2022-01-23 12:37:51 -08:00
|
|
|
Set(key, value string, ttl int)
|
2021-09-21 11:22:59 -07:00
|
|
|
}
|
|
|
|
|
2021-12-14 23:49:32 -08:00
|
|
|
type HTTPRequestModifier func(request *http.Request)
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
type WindowsRegistryValueType int
|
2021-12-04 13:11:25 -08:00
|
|
|
|
|
|
|
const (
|
2022-01-26 01:23:18 -08:00
|
|
|
RegQword WindowsRegistryValueType = iota
|
|
|
|
RegDword
|
|
|
|
RegString
|
2021-12-04 13:11:25 -08:00
|
|
|
)
|
|
|
|
|
2022-01-23 12:37:51 -08:00
|
|
|
type WindowsRegistryValue struct {
|
2022-01-26 01:23:18 -08:00
|
|
|
ValueType WindowsRegistryValueType
|
|
|
|
Qword uint64
|
|
|
|
Dword uint32
|
|
|
|
Str string
|
2021-12-04 13:11:25 -08:00
|
|
|
}
|
|
|
|
|
2021-12-26 08:17:44 -08:00
|
|
|
type WifiType string
|
|
|
|
|
2022-01-23 12:37:51 -08:00
|
|
|
type WifiInfo struct {
|
2021-12-26 08:17:44 -08:00
|
|
|
SSID string
|
|
|
|
Interface string
|
|
|
|
RadioType WifiType
|
|
|
|
PhysType WifiType
|
|
|
|
Authentication WifiType
|
|
|
|
Cipher WifiType
|
|
|
|
Channel int
|
|
|
|
ReceiveRate int
|
|
|
|
TransmitRate int
|
|
|
|
Signal int
|
|
|
|
Error string
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
type TemplateCache struct {
|
|
|
|
Root bool
|
|
|
|
PWD string
|
|
|
|
Folder string
|
|
|
|
Shell string
|
|
|
|
UserName string
|
|
|
|
HostName string
|
|
|
|
Code int
|
|
|
|
Env map[string]string
|
|
|
|
OS string
|
|
|
|
WSL bool
|
|
|
|
}
|
|
|
|
|
2022-01-01 11:09:52 -08:00
|
|
|
type Environment interface {
|
2022-01-23 12:37:51 -08:00
|
|
|
Getenv(key string) string
|
|
|
|
Pwd() string
|
|
|
|
Home() string
|
|
|
|
User() string
|
|
|
|
Root() bool
|
|
|
|
Host() (string, error)
|
|
|
|
GOOS() string
|
|
|
|
Shell() string
|
|
|
|
Platform() string
|
|
|
|
ErrorCode() int
|
2022-02-19 10:04:06 -08:00
|
|
|
PathSeparator() string
|
2022-01-23 12:37:51 -08:00
|
|
|
HasFiles(pattern string) bool
|
|
|
|
HasFilesInDir(dir, pattern string) bool
|
|
|
|
HasFolder(folder string) bool
|
|
|
|
HasParentFilePath(path string) (fileInfo *FileInfo, err error)
|
2022-02-13 23:41:33 -08:00
|
|
|
HasFileInParentDirs(pattern string, depth uint) bool
|
2022-01-23 12:37:51 -08:00
|
|
|
HasCommand(command string) bool
|
|
|
|
FileContent(file string) string
|
|
|
|
FolderList(path string) []string
|
|
|
|
RunCommand(command string, args ...string) (string, error)
|
|
|
|
RunShellCommand(shell, command string) string
|
|
|
|
ExecutionTime() float64
|
2022-03-12 13:04:08 -08:00
|
|
|
Flags() *Flags
|
2022-01-23 12:37:51 -08:00
|
|
|
BatteryInfo() ([]*battery.Battery, error)
|
2022-03-05 23:41:02 -08:00
|
|
|
QueryWindowTitles(processName, windowTitleRegex string) (string, error)
|
2022-01-23 12:37:51 -08:00
|
|
|
WindowsRegistryKeyValue(path string) (*WindowsRegistryValue, error)
|
2022-01-07 10:41:58 -08:00
|
|
|
HTTPRequest(url string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error)
|
2022-01-23 12:37:51 -08:00
|
|
|
IsWsl() bool
|
|
|
|
IsWsl2() bool
|
|
|
|
StackCount() int
|
|
|
|
TerminalWidth() (int, error)
|
|
|
|
CachePath() string
|
|
|
|
Cache() Cache
|
|
|
|
Close()
|
|
|
|
Logs() string
|
|
|
|
InWSLSharedDrive() bool
|
|
|
|
ConvertToLinuxPath(path string) string
|
|
|
|
ConvertToWindowsPath(path string) string
|
|
|
|
WifiNetwork() (*WifiInfo, error)
|
|
|
|
TemplateCache() *TemplateCache
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2021-02-11 04:19:47 -08:00
|
|
|
type commandCache struct {
|
2021-09-20 03:46:07 -07:00
|
|
|
commands *concurrentMap
|
2021-02-11 04:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *commandCache) set(command, path string) {
|
2021-09-20 03:46:07 -07:00
|
|
|
c.commands.set(command, path)
|
2021-02-11 04:19:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *commandCache) get(command string) (string, bool) {
|
2021-09-21 22:53:59 -07:00
|
|
|
cmd, found := c.commands.get(command)
|
|
|
|
if !found {
|
|
|
|
return "", false
|
|
|
|
}
|
2021-09-23 13:57:38 -07:00
|
|
|
command, ok := cmd.(string)
|
|
|
|
return command, ok
|
2021-02-11 04:19:47 -08:00
|
|
|
}
|
|
|
|
|
2021-11-10 10:34:26 -08:00
|
|
|
type logType string
|
|
|
|
|
|
|
|
const (
|
|
|
|
Error logType = "error"
|
|
|
|
Debug logType = "debug"
|
|
|
|
)
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
type ShellEnvironment struct {
|
2022-03-12 13:04:08 -08:00
|
|
|
CmdFlags *Flags
|
2022-01-18 00:48:47 -08:00
|
|
|
cwd string
|
|
|
|
cmdCache *commandCache
|
|
|
|
fileCache *fileCache
|
2022-01-23 12:37:51 -08:00
|
|
|
tmplCache *TemplateCache
|
2022-01-18 00:48:47 -08:00
|
|
|
logBuilder strings.Builder
|
|
|
|
debug bool
|
2021-08-01 06:25:15 -07:00
|
|
|
}
|
|
|
|
|
2022-03-12 13:04:08 -08:00
|
|
|
func (env *ShellEnvironment) Init(debug bool) {
|
|
|
|
if env.CmdFlags == nil {
|
|
|
|
env.CmdFlags = &Flags{}
|
|
|
|
}
|
|
|
|
if len(env.CmdFlags.Config) == 0 {
|
|
|
|
env.CmdFlags.Config = env.Getenv("POSH_THEME")
|
|
|
|
}
|
2022-01-18 12:25:18 -08:00
|
|
|
env.fileCache = &fileCache{}
|
2022-01-23 12:37:51 -08:00
|
|
|
env.fileCache.Init(env.CachePath())
|
2022-01-26 01:23:18 -08:00
|
|
|
env.ResolveConfigPath()
|
2021-11-16 22:16:43 -08:00
|
|
|
env.cmdCache = &commandCache{
|
|
|
|
commands: newConcurrentMap(),
|
2021-08-01 06:25:15 -07:00
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
if debug {
|
2021-11-16 22:16:43 -08:00
|
|
|
env.debug = true
|
|
|
|
log.SetOutput(&env.logBuilder)
|
2021-08-01 06:25:15 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-25 03:51:38 -08:00
|
|
|
func (env *ShellEnvironment) getConfigPath(location string) {
|
|
|
|
cfg, err := env.HTTPRequest(location, 5000)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
configPath := filepath.Join(env.CachePath(), "config.omp.json")
|
|
|
|
out, err := os.Create(configPath)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
_, err = io.Copy(out, bytes.NewReader(cfg))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
env.CmdFlags.Config = configPath
|
2022-02-25 03:51:38 -08:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) ResolveConfigPath() {
|
2022-03-12 13:04:08 -08:00
|
|
|
if env.CmdFlags == nil || len(env.CmdFlags.Config) == 0 {
|
2021-12-30 06:39:30 -08:00
|
|
|
return
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
location, err := url.ParseRequestURI(env.CmdFlags.Config)
|
2022-02-25 03:51:38 -08:00
|
|
|
if err == nil {
|
|
|
|
env.getConfigPath(location.String())
|
|
|
|
return
|
|
|
|
}
|
2022-01-13 23:39:08 -08:00
|
|
|
// Cygwin path always needs the full path as we're on Windows but not really.
|
|
|
|
// Doing filepath actions will convert it to a Windows path and break the init script.
|
2022-01-26 01:23:18 -08:00
|
|
|
if env.Platform() == WindowsPlatform && env.Shell() == "bash" {
|
2022-01-13 23:39:08 -08:00
|
|
|
return
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
configFile := env.CmdFlags.Config
|
2021-12-30 06:39:30 -08:00
|
|
|
if strings.HasPrefix(configFile, "~") {
|
|
|
|
configFile = strings.TrimPrefix(configFile, "~")
|
2022-01-23 12:37:51 -08:00
|
|
|
configFile = filepath.Join(env.Home(), configFile)
|
2021-12-30 06:39:30 -08:00
|
|
|
}
|
|
|
|
if !filepath.IsAbs(configFile) {
|
|
|
|
if absConfigFile, err := filepath.Abs(configFile); err == nil {
|
|
|
|
configFile = absConfigFile
|
|
|
|
}
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
env.CmdFlags.Config = filepath.Clean(configFile)
|
2021-12-30 06:39:30 -08:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) trace(start time.Time, function string, args ...string) {
|
2021-11-16 22:16:43 -08:00
|
|
|
if !env.debug {
|
2021-08-01 06:25:15 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
elapsed := time.Since(start)
|
2021-08-04 03:52:54 -07:00
|
|
|
trace := fmt.Sprintf("%s duration: %s, args: %s", function, elapsed, strings.Trim(fmt.Sprint(args), "[]"))
|
2021-08-01 06:25:15 -07:00
|
|
|
log.Println(trace)
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) log(lt logType, function, message string) {
|
2021-11-16 22:16:43 -08:00
|
|
|
if !env.debug {
|
2021-10-20 02:55:53 -07:00
|
|
|
return
|
|
|
|
}
|
2021-11-10 10:34:26 -08:00
|
|
|
trace := fmt.Sprintf("%s: %s\n%s", lt, function, message)
|
2021-10-20 02:55:53 -07:00
|
|
|
log.Println(trace)
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Getenv(key string) string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "Getenv", key)
|
2021-11-10 10:34:26 -08:00
|
|
|
val := os.Getenv(key)
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Debug, "Getenv", val)
|
2021-11-10 10:34:26 -08:00
|
|
|
return val
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Pwd() string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "Pwd")
|
2022-01-20 04:31:22 -08:00
|
|
|
defer func() {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Debug, "Pwd", env.cwd)
|
2022-01-20 04:31:22 -08:00
|
|
|
}()
|
2020-10-12 00:04:37 -07:00
|
|
|
if env.cwd != "" {
|
|
|
|
return env.cwd
|
|
|
|
}
|
2020-10-08 10:23:20 -07:00
|
|
|
correctPath := func(pwd string) string {
|
2021-01-07 10:29:34 -08:00
|
|
|
// on Windows, and being case sensitive and not consistent and all, this gives silly issues
|
2022-01-26 01:23:18 -08:00
|
|
|
driveLetter := regex.GetCompiledRegex(`^[a-z]:`)
|
2021-11-05 01:02:21 -07:00
|
|
|
return driveLetter.ReplaceAllStringFunc(pwd, strings.ToUpper)
|
2020-10-08 10:23:20 -07:00
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
if env.CmdFlags != nil && env.CmdFlags.PWD != "" {
|
|
|
|
env.cwd = correctPath(env.CmdFlags.PWD)
|
2020-10-12 00:04:37 -07:00
|
|
|
return env.cwd
|
2020-10-08 10:23:20 -07:00
|
|
|
}
|
2020-10-02 12:50:13 -07:00
|
|
|
dir, err := os.Getwd()
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "Pwd", err.Error())
|
2020-10-02 12:50:13 -07:00
|
|
|
return ""
|
|
|
|
}
|
2020-10-12 00:04:37 -07:00
|
|
|
env.cwd = correctPath(dir)
|
|
|
|
return env.cwd
|
2020-10-02 12:50:13 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) HasFiles(pattern string) bool {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "HasFiles", pattern)
|
|
|
|
cwd := env.Pwd()
|
2022-02-19 10:04:06 -08:00
|
|
|
pattern = cwd + env.PathSeparator() + pattern
|
2020-10-01 11:57:02 -07:00
|
|
|
matches, err := filepath.Glob(pattern)
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "HasFiles", err.Error())
|
2020-10-01 11:57:02 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
return len(matches) > 0
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) HasFilesInDir(dir, pattern string) bool {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "HasFilesInDir", pattern)
|
2022-02-19 10:04:06 -08:00
|
|
|
pattern = dir + env.PathSeparator() + pattern
|
2020-12-01 11:43:30 -08:00
|
|
|
matches, err := filepath.Glob(pattern)
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "HasFilesInDir", err.Error())
|
2020-12-01 11:43:30 -08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
return len(matches) > 0
|
|
|
|
}
|
|
|
|
|
2022-02-13 23:41:33 -08:00
|
|
|
func (env *ShellEnvironment) HasFileInParentDirs(pattern string, depth uint) bool {
|
|
|
|
defer env.trace(time.Now(), "HasFileInParent", pattern, fmt.Sprint(depth))
|
|
|
|
currentFolder := env.Pwd()
|
|
|
|
|
|
|
|
for c := 0; c < int(depth); c++ {
|
|
|
|
if env.HasFilesInDir(currentFolder, pattern) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if dir := filepath.Dir(currentFolder); dir != currentFolder {
|
|
|
|
currentFolder = dir
|
|
|
|
} else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) HasFolder(folder string) bool {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "HasFolder", folder)
|
2020-10-07 04:32:42 -07:00
|
|
|
_, err := os.Stat(folder)
|
|
|
|
return !os.IsNotExist(err)
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) FileContent(file string) string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "FileContent", file)
|
2020-10-07 04:32:42 -07:00
|
|
|
content, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "FileContent", err.Error())
|
2020-10-07 04:32:42 -07:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return string(content)
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) FolderList(path string) []string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "FolderList", path)
|
2021-09-04 11:32:55 -07:00
|
|
|
content, err := os.ReadDir(path)
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "FolderList", err.Error())
|
2021-09-04 11:32:55 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var folderNames []string
|
|
|
|
for _, s := range content {
|
|
|
|
if s.IsDir() {
|
|
|
|
folderNames = append(folderNames, s.Name())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return folderNames
|
|
|
|
}
|
|
|
|
|
2022-02-19 10:04:06 -08:00
|
|
|
func (env *ShellEnvironment) PathSeparator() string {
|
|
|
|
defer env.trace(time.Now(), "PathSeparator")
|
2019-03-13 04:14:30 -07:00
|
|
|
return string(os.PathSeparator)
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) User() string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "User")
|
2020-10-12 03:53:54 -07:00
|
|
|
user := os.Getenv("USER")
|
|
|
|
if user == "" {
|
|
|
|
user = os.Getenv("USERNAME")
|
|
|
|
}
|
|
|
|
return user
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Host() (string, error) {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "Host")
|
2019-03-13 04:14:30 -07:00
|
|
|
hostName, err := os.Hostname()
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "Host", err.Error())
|
2019-03-13 04:14:30 -07:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return cleanHostName(hostName), nil
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) GOOS() string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "GOOS")
|
2019-03-13 04:14:30 -07:00
|
|
|
return runtime.GOOS
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) RunCommand(command string, args ...string) (string, error) {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "RunCommand", append([]string{command}, args...)...)
|
2021-02-11 04:19:47 -08:00
|
|
|
if cmd, ok := env.cmdCache.get(command); ok {
|
|
|
|
command = cmd
|
2021-01-05 04:05:37 -08:00
|
|
|
}
|
2021-08-03 23:46:59 -07:00
|
|
|
cmd := exec.Command(command, args...)
|
2021-12-19 07:42:39 -08:00
|
|
|
var out bytes.Buffer
|
|
|
|
var err bytes.Buffer
|
|
|
|
cmd.Stdout = &out
|
|
|
|
cmd.Stderr = &err
|
|
|
|
cmdErr := cmd.Run()
|
|
|
|
if cmdErr != nil {
|
|
|
|
output := err.String()
|
|
|
|
errorStr := fmt.Sprintf("cmd.Start() failed with '%s'", output)
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "RunCommand", errorStr)
|
2021-12-19 07:42:39 -08:00
|
|
|
return output, cmdErr
|
2021-08-03 23:46:59 -07:00
|
|
|
}
|
2022-02-10 10:39:54 -08:00
|
|
|
// some silly commands return 0 and the output is in stderr instead of stdout
|
|
|
|
result := out.String()
|
|
|
|
if len(result) == 0 {
|
|
|
|
result = err.String()
|
|
|
|
}
|
|
|
|
output := strings.TrimSpace(result)
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Debug, "RunCommand", output)
|
2021-11-10 10:34:26 -08:00
|
|
|
return output, nil
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) RunShellCommand(shell, command string) string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "RunShellCommand", shell, command)
|
2022-02-19 05:35:57 -08:00
|
|
|
if out, err := env.RunCommand(shell, "-c", command); err == nil {
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
return ""
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) HasCommand(command string) bool {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "HasCommand", command)
|
2021-02-11 04:19:47 -08:00
|
|
|
if _, ok := env.cmdCache.get(command); ok {
|
2021-01-05 04:05:37 -08:00
|
|
|
return true
|
|
|
|
}
|
2020-12-27 23:33:58 -08:00
|
|
|
path, err := exec.LookPath(command)
|
2021-01-05 04:05:37 -08:00
|
|
|
if err == nil {
|
2021-02-11 04:19:47 -08:00
|
|
|
env.cmdCache.set(command, path)
|
2021-01-05 04:05:37 -08:00
|
|
|
return true
|
|
|
|
}
|
2022-02-08 11:20:30 -08:00
|
|
|
path, err = env.LookWinAppPath(command)
|
|
|
|
if err == nil {
|
2022-02-07 09:50:52 -08:00
|
|
|
env.cmdCache.set(command, path)
|
|
|
|
return true
|
|
|
|
}
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "HasCommand", err.Error())
|
2021-01-05 04:05:37 -08:00
|
|
|
return false
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) ErrorCode() int {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "ErrorCode")
|
2022-03-12 13:04:08 -08:00
|
|
|
return env.CmdFlags.ErrorCode
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) ExecutionTime() float64 {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "ExecutionTime")
|
2022-03-12 13:04:08 -08:00
|
|
|
if env.CmdFlags.ExecutionTime < 0 {
|
2020-12-12 03:54:25 -08:00
|
|
|
return 0
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
return env.CmdFlags.ExecutionTime
|
2020-12-06 13:03:40 -08:00
|
|
|
}
|
|
|
|
|
2022-03-12 13:04:08 -08:00
|
|
|
func (env *ShellEnvironment) Flags() *Flags {
|
|
|
|
defer env.trace(time.Now(), "Flags")
|
|
|
|
return env.CmdFlags
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) BatteryInfo() ([]*battery.Battery, error) {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "BatteryInfo")
|
2021-09-19 22:01:00 -07:00
|
|
|
batteries, err := battery.GetAll()
|
|
|
|
// actual error, return it
|
|
|
|
if err != nil && len(batteries) == 0 {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "BatteryInfo", err.Error())
|
2021-09-19 22:01:00 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// there are no batteries found
|
|
|
|
if len(batteries) == 0 {
|
2022-01-26 01:23:18 -08:00
|
|
|
return nil, &NoBatteryError{}
|
2021-09-19 22:01:00 -07:00
|
|
|
}
|
|
|
|
// some batteries fail to get retrieved, filter them out if present
|
|
|
|
validBatteries := []*battery.Battery{}
|
|
|
|
for _, batt := range batteries {
|
|
|
|
if batt != nil {
|
|
|
|
validBatteries = append(validBatteries, batt)
|
|
|
|
}
|
|
|
|
}
|
2021-12-11 03:13:27 -08:00
|
|
|
// clean minor errors
|
2021-09-19 22:01:00 -07:00
|
|
|
unableToRetrieveBatteryInfo := "A device which does not exist was specified."
|
2021-12-11 03:13:27 -08:00
|
|
|
unknownChargeRate := "Unknown value received"
|
|
|
|
var fatalErr battery.Errors
|
2021-12-11 04:51:15 -08:00
|
|
|
ignoreErr := func(err error) bool {
|
2021-12-11 03:13:27 -08:00
|
|
|
if e, ok := err.(battery.ErrPartial); ok {
|
2021-12-11 04:51:15 -08:00
|
|
|
// ignore unknown charge rate value error
|
2021-12-11 03:13:27 -08:00
|
|
|
if e.Current == nil &&
|
|
|
|
e.Design == nil &&
|
|
|
|
e.DesignVoltage == nil &&
|
|
|
|
e.Full == nil &&
|
|
|
|
e.State == nil &&
|
|
|
|
e.Voltage == nil &&
|
2021-12-11 04:51:15 -08:00
|
|
|
e.ChargeRate != nil &&
|
2021-12-11 03:13:27 -08:00
|
|
|
e.ChargeRate.Error() == unknownChargeRate {
|
2021-12-11 04:51:15 -08:00
|
|
|
return true
|
2021-12-11 03:13:27 -08:00
|
|
|
}
|
|
|
|
}
|
2021-12-11 04:51:15 -08:00
|
|
|
return false
|
2021-12-11 03:13:27 -08:00
|
|
|
}
|
|
|
|
if batErr, ok := err.(battery.Errors); ok {
|
|
|
|
for _, err := range batErr {
|
2021-12-11 04:51:15 -08:00
|
|
|
if !ignoreErr(err) {
|
2021-12-11 03:13:27 -08:00
|
|
|
fatalErr = append(fatalErr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-19 22:01:00 -07:00
|
|
|
// when battery info fails to get retrieved but there is at least one valid battery, return it without error
|
2021-12-11 03:13:27 -08:00
|
|
|
if len(validBatteries) > 0 && fatalErr != nil && strings.Contains(fatalErr.Error(), unableToRetrieveBatteryInfo) {
|
2021-09-19 22:01:00 -07:00
|
|
|
return validBatteries, nil
|
|
|
|
}
|
|
|
|
// another error occurred (possibly unmapped use-case), return it
|
2021-12-11 03:13:27 -08:00
|
|
|
if fatalErr != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "BatteryInfo", fatalErr.Error())
|
2021-12-11 03:13:27 -08:00
|
|
|
return nil, fatalErr
|
2021-09-19 22:01:00 -07:00
|
|
|
}
|
|
|
|
// everything is fine
|
|
|
|
return validBatteries, nil
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Shell() string {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "Shell")
|
2022-03-12 13:04:08 -08:00
|
|
|
if env.CmdFlags.Shell != "" {
|
|
|
|
return env.CmdFlags.Shell
|
2020-12-27 05:59:40 -08:00
|
|
|
}
|
2020-09-15 04:44:53 -07:00
|
|
|
pid := os.Getppid()
|
2020-10-23 07:36:40 -07:00
|
|
|
p, _ := process.NewProcess(int32(pid))
|
|
|
|
name, err := p.Name()
|
2020-09-24 10:11:56 -07:00
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "Shell", err.Error())
|
2022-01-26 01:23:18 -08:00
|
|
|
return Unknown
|
2020-09-24 10:11:56 -07:00
|
|
|
}
|
2020-11-02 09:43:10 -08:00
|
|
|
if name == "cmd.exe" {
|
|
|
|
p, _ = p.Parent()
|
|
|
|
name, err = p.Name()
|
|
|
|
}
|
|
|
|
if err != nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "Shell", err.Error())
|
2022-01-26 01:23:18 -08:00
|
|
|
return Unknown
|
2020-11-02 09:43:10 -08:00
|
|
|
}
|
2020-12-27 10:51:32 -08:00
|
|
|
// Cache the shell value to speed things up.
|
2022-03-12 13:04:08 -08:00
|
|
|
env.CmdFlags.Shell = strings.Trim(strings.Replace(name, ".exe", "", 1), " ")
|
|
|
|
return env.CmdFlags.Shell
|
2020-09-15 04:44:53 -07:00
|
|
|
}
|
|
|
|
|
2022-02-25 03:51:38 -08:00
|
|
|
func (env *ShellEnvironment) HTTPRequest(targetURL string, timeout int, requestModifiers ...HTTPRequestModifier) ([]byte, error) {
|
|
|
|
defer env.trace(time.Now(), "HTTPRequest", targetURL)
|
2021-08-17 23:21:55 -07:00
|
|
|
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
|
2020-12-31 00:39:02 -08:00
|
|
|
defer cncl()
|
2022-02-25 03:51:38 -08:00
|
|
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
2020-11-19 19:12:20 -08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-12-14 23:49:32 -08:00
|
|
|
for _, modifier := range requestModifiers {
|
|
|
|
modifier(request)
|
|
|
|
}
|
2020-11-19 19:12:20 -08:00
|
|
|
response, err := client.Do(request)
|
|
|
|
if err != nil {
|
2022-01-07 10:41:58 -08:00
|
|
|
env.log(Error, "HTTPRequest", err.Error())
|
2020-11-19 19:12:20 -08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(response.Body)
|
|
|
|
if err != nil {
|
2022-01-07 10:41:58 -08:00
|
|
|
env.log(Error, "HTTPRequest", err.Error())
|
2020-11-19 19:12:20 -08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return body, nil
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) HasParentFilePath(path string) (*FileInfo, error) {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "HasParentFilePath", path)
|
|
|
|
currentFolder := env.Pwd()
|
2021-01-05 11:12:52 -08:00
|
|
|
for {
|
|
|
|
searchPath := filepath.Join(currentFolder, path)
|
|
|
|
info, err := os.Stat(searchPath)
|
|
|
|
if err == nil {
|
2022-01-23 12:37:51 -08:00
|
|
|
return &FileInfo{
|
|
|
|
ParentFolder: currentFolder,
|
|
|
|
Path: searchPath,
|
|
|
|
IsDir: info.IsDir(),
|
2021-01-05 11:12:52 -08:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if dir := filepath.Dir(currentFolder); dir != currentFolder {
|
|
|
|
currentFolder = dir
|
|
|
|
continue
|
|
|
|
}
|
2022-01-23 12:37:51 -08:00
|
|
|
env.log(Error, "HasParentFilePath", err.Error())
|
2021-01-05 11:12:52 -08:00
|
|
|
return nil, errors.New("no match at root level")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) StackCount() int {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "StackCount")
|
2022-03-12 13:04:08 -08:00
|
|
|
if env.CmdFlags.StackCount < 0 {
|
2021-04-12 01:58:03 -07:00
|
|
|
return 0
|
|
|
|
}
|
2022-03-12 13:04:08 -08:00
|
|
|
return env.CmdFlags.StackCount
|
2021-04-12 01:58:03 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Cache() Cache {
|
2021-09-21 11:22:59 -07:00
|
|
|
return env.fileCache
|
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Close() {
|
2022-01-23 12:37:51 -08:00
|
|
|
env.fileCache.Close()
|
2021-11-16 10:59:42 -08:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) Logs() string {
|
2021-11-16 22:16:43 -08:00
|
|
|
return env.logBuilder.String()
|
2021-09-21 11:22:59 -07:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func (env *ShellEnvironment) TemplateCache() *TemplateCache {
|
2022-01-23 12:37:51 -08:00
|
|
|
defer env.trace(time.Now(), "TemplateCache")
|
2022-01-18 00:48:47 -08:00
|
|
|
if env.tmplCache != nil {
|
|
|
|
return env.tmplCache
|
|
|
|
}
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache := &TemplateCache{
|
2022-01-23 12:37:51 -08:00
|
|
|
Root: env.Root(),
|
|
|
|
Shell: env.Shell(),
|
|
|
|
Code: env.ErrorCode(),
|
|
|
|
WSL: env.IsWsl(),
|
2022-01-18 00:48:47 -08:00
|
|
|
}
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.Env = make(map[string]string)
|
2022-01-18 00:48:47 -08:00
|
|
|
const separator = "="
|
|
|
|
values := os.Environ()
|
|
|
|
for value := range values {
|
|
|
|
splitted := strings.Split(values[value], separator)
|
|
|
|
if len(splitted) != 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
key := splitted[0]
|
|
|
|
val := splitted[1:]
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.Env[key] = strings.Join(val, separator)
|
2022-01-18 00:48:47 -08:00
|
|
|
}
|
2022-01-23 12:37:51 -08:00
|
|
|
pwd := env.Pwd()
|
|
|
|
pwd = strings.Replace(pwd, env.Home(), "~", 1)
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.PWD = pwd
|
|
|
|
tmplCache.Folder = Base(env, pwd)
|
|
|
|
tmplCache.UserName = env.User()
|
2022-01-23 12:37:51 -08:00
|
|
|
if host, err := env.Host(); err == nil {
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.HostName = host
|
2022-01-18 00:48:47 -08:00
|
|
|
}
|
2022-01-23 12:37:51 -08:00
|
|
|
goos := env.GOOS()
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.OS = goos
|
2022-02-03 02:42:31 -08:00
|
|
|
if goos == LinuxPlatform {
|
2022-02-03 08:45:35 -08:00
|
|
|
tmplCache.OS = env.Platform()
|
2022-02-03 02:42:31 -08:00
|
|
|
}
|
2022-02-03 08:45:35 -08:00
|
|
|
env.tmplCache = tmplCache
|
|
|
|
return tmplCache
|
2022-01-18 00:48:47 -08:00
|
|
|
}
|
|
|
|
|
2022-01-26 01:23:18 -08:00
|
|
|
func DirMatchesOneOf(env Environment, dir string, regexes []string) bool {
|
|
|
|
normalizedCwd := strings.ReplaceAll(dir, "\\", "/")
|
|
|
|
normalizedHomeDir := strings.ReplaceAll(env.Home(), "\\", "/")
|
|
|
|
|
|
|
|
for _, element := range regexes {
|
|
|
|
normalizedElement := strings.ReplaceAll(element, "\\\\", "/")
|
|
|
|
if strings.HasPrefix(normalizedElement, "~") {
|
|
|
|
normalizedElement = strings.Replace(normalizedElement, "~", normalizedHomeDir, 1)
|
|
|
|
}
|
|
|
|
pattern := fmt.Sprintf("^%s$", normalizedElement)
|
|
|
|
goos := env.GOOS()
|
|
|
|
if goos == WindowsPlatform || goos == DarwinPlatform {
|
|
|
|
pattern = "(?i)" + pattern
|
|
|
|
}
|
|
|
|
matched := regex.MatchString(pattern, normalizedCwd)
|
|
|
|
if matched {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Base returns the last element of path.
|
|
|
|
// Trailing path separators are removed before extracting the last element.
|
|
|
|
// If the path consists entirely of separators, Base returns a single separator.
|
|
|
|
func Base(env Environment, path string) string {
|
|
|
|
if path == "/" {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
volumeName := filepath.VolumeName(path)
|
|
|
|
// Strip trailing slashes.
|
2022-02-19 10:04:06 -08:00
|
|
|
for len(path) > 0 && string(path[len(path)-1]) == env.PathSeparator() {
|
2022-01-26 01:23:18 -08:00
|
|
|
path = path[0 : len(path)-1]
|
|
|
|
}
|
|
|
|
if volumeName == path {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
// Throw away volume name
|
|
|
|
path = path[len(filepath.VolumeName(path)):]
|
|
|
|
// Find the last element
|
|
|
|
i := len(path) - 1
|
2022-02-19 10:04:06 -08:00
|
|
|
for i >= 0 && string(path[i]) != env.PathSeparator() {
|
2022-01-26 01:23:18 -08:00
|
|
|
i--
|
|
|
|
}
|
|
|
|
if i >= 0 {
|
|
|
|
path = path[i+1:]
|
|
|
|
}
|
|
|
|
// If empty now, it had only slashes.
|
|
|
|
if path == "" {
|
2022-02-19 10:04:06 -08:00
|
|
|
return env.PathSeparator()
|
2022-01-26 01:23:18 -08:00
|
|
|
}
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2019-03-13 04:14:30 -07:00
|
|
|
func cleanHostName(hostName string) string {
|
|
|
|
garbage := []string{
|
|
|
|
".lan",
|
|
|
|
".local",
|
2020-10-07 08:14:26 -07:00
|
|
|
".localdomain",
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
for _, g := range garbage {
|
2020-10-07 08:14:26 -07:00
|
|
|
if strings.HasSuffix(hostName, g) {
|
|
|
|
hostName = strings.Replace(hostName, g, "", 1)
|
|
|
|
}
|
2019-03-13 04:14:30 -07:00
|
|
|
}
|
|
|
|
return hostName
|
|
|
|
}
|
2021-10-02 22:27:18 -07:00
|
|
|
|
|
|
|
func returnOrBuildCachePath(path string) string {
|
|
|
|
// validate root path
|
|
|
|
if _, err := os.Stat(path); err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
// validate oh-my-posh folder, if non existent, create it
|
2022-02-28 03:55:16 -08:00
|
|
|
cachePath := filepath.Join(path, "oh-my-posh")
|
2021-10-02 22:27:18 -07:00
|
|
|
if _, err := os.Stat(cachePath); err == nil {
|
|
|
|
return cachePath
|
|
|
|
}
|
|
|
|
if err := os.Mkdir(cachePath, 0755); err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return cachePath
|
|
|
|
}
|