refactor (this build is broken)
This commit is contained in:
parent
d46f900c43
commit
aa142afa78
@ -17,7 +17,7 @@ buildGoApplication {
|
||||
pname = "goGoFetch";
|
||||
version = "1.0";
|
||||
pwd = ./.;
|
||||
src = ./.;
|
||||
src = ./src;
|
||||
modules = ./gomod2nix.toml;
|
||||
|
||||
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
go-lint = pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "go-lint";
|
||||
dontBuild = true;
|
||||
src = ./.;
|
||||
src = ./src;
|
||||
doCheck = true;
|
||||
nativeBuildInputs = with pkgs; [
|
||||
golangci-lint
|
||||
|
||||
BIN
logos/artix.png
(Stored with Git LFS)
Normal file
BIN
logos/artix.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/cachyos.png
(Stored with Git LFS)
Normal file
BIN
logos/cachyos.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/endeavouros.png
(Stored with Git LFS)
Normal file
BIN
logos/endeavouros.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/fedora.png
(Stored with Git LFS)
Normal file
BIN
logos/fedora.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/gentoo.png
(Stored with Git LFS)
Normal file
BIN
logos/gentoo.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/kali.png
(Stored with Git LFS)
Normal file
BIN
logos/kali.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/manjaro.png
(Stored with Git LFS)
Normal file
BIN
logos/manjaro.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/mint.png
(Stored with Git LFS)
Normal file
BIN
logos/mint.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/opensuse.png
(Stored with Git LFS)
Normal file
BIN
logos/opensuse.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/ubuntu.png
(Stored with Git LFS)
Normal file
BIN
logos/ubuntu.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
logos/void.png
(Stored with Git LFS)
Normal file
BIN
logos/void.png
(Stored with Git LFS)
Normal file
Binary file not shown.
617
main.go
617
main.go
@ -1,617 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
_ "image/png"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
const (
|
||||
Reset = "\033[0m"
|
||||
Blue = "\033[1;34m"
|
||||
)
|
||||
|
||||
var dist string
|
||||
|
||||
var ansiEscapeRe = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||
|
||||
type Config struct {
|
||||
Ascii bool `toml:"ascii"`
|
||||
Layout []string `toml:"layout"`
|
||||
CustomLogo string `toml:"custom_logo"`
|
||||
LogoPosition string `toml:"logo_position"`
|
||||
}
|
||||
|
||||
func appendMissingConfigKeys(pathConf string, md toml.MetaData) error {
|
||||
var missing []string
|
||||
|
||||
if !md.IsDefined("ascii") {
|
||||
missing = append(missing, "# Set this to \"true\" if you like ascii more than images\nascii = false")
|
||||
}
|
||||
|
||||
if !md.IsDefined("layout") {
|
||||
missing = append(missing, `# Stats layout: reorder or remove items to customize your fetch
|
||||
# Available: "dist", "host", "cpu", "kernel", "ram", "gpu", "de/wm", "pkgs", "shell", "terminal", "uptime"
|
||||
layout = [
|
||||
"host",
|
||||
"dist",
|
||||
"cpu",
|
||||
"kernel",
|
||||
"ram",
|
||||
"gpu",
|
||||
"de/wm",
|
||||
"pkgs",
|
||||
"shell",
|
||||
"terminal",
|
||||
"uptime",
|
||||
]`)
|
||||
}
|
||||
|
||||
if !md.IsDefined("custom_logo") {
|
||||
missing = append(missing, "# Absolute path to a custom image or .txt file. Leave empty to use auto-detection.\ncustom_logo = \"\"")
|
||||
}
|
||||
|
||||
if !md.IsDefined("logo_position") {
|
||||
missing = append(missing, "# Position of the logo block relative to the stats block: \"left\" or \"right\".\nlogo_position = \"left\"")
|
||||
}
|
||||
|
||||
if len(missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(pathConf, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
|
||||
block := "\n# Added automatically after update\n\n" + strings.Join(missing, "\n\n") + "\n"
|
||||
_, err = f.WriteString(block)
|
||||
return err
|
||||
}
|
||||
|
||||
func getConfigPath() (string, error) {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
directoryName := "gogofetch"
|
||||
fullPath := filepath.Join(configDir, directoryName, "config.toml")
|
||||
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
func initConfig() (Config, string) {
|
||||
var conf Config
|
||||
pathConf, err := getConfigPath()
|
||||
if err != nil {
|
||||
return conf, ""
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(pathConf), 0755)
|
||||
if err != nil {
|
||||
return Config{}, ""
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pathConf); os.IsNotExist(err) {
|
||||
defaultConfig := []byte(`
|
||||
# Set this to "true" if you like ascii more than images
|
||||
ascii = false
|
||||
|
||||
# Stats layout: reorder or remove items to customize your fetch
|
||||
# Available: "dist", "host", "cpu", "kernel", "ram", "gpu", "de/wm", "pkgs", "shell", "terminal", "uptime"
|
||||
layout = [
|
||||
"host",
|
||||
"dist",
|
||||
"cpu",
|
||||
"kernel",
|
||||
"ram",
|
||||
"gpu",
|
||||
"de/wm",
|
||||
"pkgs",
|
||||
"shell",
|
||||
"terminal",
|
||||
"uptime",
|
||||
]
|
||||
|
||||
# Absolute path to a custom image or .txt file. Leave empty to use auto-detection.
|
||||
custom_logo = ""
|
||||
|
||||
# Position of the logo block relative to the stats block: "left" or "right".
|
||||
logo_position = "left"
|
||||
`)
|
||||
err := os.WriteFile(pathConf, defaultConfig, 0644)
|
||||
if err != nil {
|
||||
return Config{}, ""
|
||||
}
|
||||
}
|
||||
|
||||
md, err := toml.DecodeFile(pathConf, &conf)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading config at %s: %v\n", pathConf, err)
|
||||
return conf, pathConf
|
||||
}
|
||||
|
||||
if err := appendMissingConfigKeys(pathConf, md); err != nil {
|
||||
fmt.Printf("Warning: Could not update config at %s: %v\n", pathConf, err)
|
||||
} else {
|
||||
if _, err := toml.DecodeFile(pathConf, &conf); err != nil {
|
||||
fmt.Printf("Error reloading updated config at %s: %v\n", pathConf, err)
|
||||
}
|
||||
}
|
||||
|
||||
return conf, pathConf
|
||||
}
|
||||
|
||||
func getDist() string {
|
||||
f, _ := os.Open("/etc/os-release")
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Println("Error closing file:", err)
|
||||
}
|
||||
}(f)
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
t := s.Text()
|
||||
if strings.HasPrefix(t, "PRETTY_NAME") {
|
||||
distro := strings.TrimPrefix(t, "PRETTY_NAME=")
|
||||
dist = strings.Trim(distro, "\"")
|
||||
dist = strings.TrimSpace(dist)
|
||||
return dist
|
||||
}
|
||||
}
|
||||
return "no /etc/os-release file found"
|
||||
}
|
||||
|
||||
func getRam() string {
|
||||
f, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
var total, available int
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
_, err2 := fmt.Sscanf(line, "MemTotal: %d kB", &total)
|
||||
if err2 != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(line, "MemAvailable:") {
|
||||
_, err := fmt.Sscanf(line, "MemAvailable: %d kB", &available)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
totalMB := total / 1024
|
||||
availableMB := available / 1024
|
||||
usedMB := totalMB - availableMB
|
||||
|
||||
ram := fmt.Sprintf("%d / %d MiB (%d MiB available)", usedMB, totalMB, availableMB)
|
||||
ram = strings.TrimSpace(ram)
|
||||
return ram
|
||||
|
||||
}
|
||||
|
||||
func getCpu() string {
|
||||
f, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
var cpu string
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) > 1 {
|
||||
cpu := strings.TrimSpace(parts[1])
|
||||
return cpu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(cpu)
|
||||
}
|
||||
|
||||
func getGpu() string {
|
||||
out, err := exec.Command("sh", "-c", `lspci -mm | awk -F'"' '$2=="VGA compatible controller" || $2=="3D controller" || $2=="Display controller"'`).Output()
|
||||
if err != nil || len(out) == 0 {
|
||||
return "Unknown GPU"
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
var gpus []string
|
||||
|
||||
for _, line := range lines {
|
||||
re := regexp.MustCompile(`"([^"]+)"`)
|
||||
matches := re.FindAllStringSubmatch(line, -1)
|
||||
|
||||
if len(matches) >= 3 {
|
||||
vendor := matches[1][1]
|
||||
device := matches[2][1]
|
||||
|
||||
cleaned := cleanGpuString(vendor + " " + device)
|
||||
gpus = append(gpus, cleaned)
|
||||
}
|
||||
}
|
||||
|
||||
if len(gpus) > 0 {
|
||||
return strings.Join(gpus, " / ")
|
||||
}
|
||||
return "GPU not found"
|
||||
}
|
||||
|
||||
func cleanGpuString(raw string) string {
|
||||
prettyNameRe := regexp.MustCompile(`\[(.*?)]`)
|
||||
match := prettyNameRe.FindStringSubmatch(raw)
|
||||
|
||||
res := raw
|
||||
if len(match) > 1 {
|
||||
vendor := ""
|
||||
if strings.Contains(strings.ToLower(raw), "nvidia") {
|
||||
vendor = "NVIDIA "
|
||||
}
|
||||
if strings.Contains(strings.ToLower(raw), "intel") {
|
||||
vendor = "Intel "
|
||||
}
|
||||
if strings.Contains(strings.ToLower(raw), "amd") {
|
||||
vendor = "AMD "
|
||||
}
|
||||
|
||||
res = vendor + match[1]
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\(.*?\)|\[.*?]`)
|
||||
res = re.ReplaceAllString(res, "")
|
||||
|
||||
fluff := []string{"Corporation", "Controller", "VGA compatible", "3D", "Graphics", "Device"}
|
||||
for _, word := range fluff {
|
||||
res = strings.ReplaceAll(res, word, "")
|
||||
}
|
||||
|
||||
return strings.Join(strings.Fields(res), " ")
|
||||
}
|
||||
|
||||
func getKernel() string {
|
||||
out, err := exec.Command("sh", "-c", "uname -r").Output()
|
||||
if err != nil {
|
||||
return "Error getting kernel, how did we get there?"
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func getHostname() string {
|
||||
hostname, err := exec.Command("sh", "-c", "uname -n").Output()
|
||||
if err != nil {
|
||||
return "Error getting hostname, awful"
|
||||
}
|
||||
username, errUser := exec.Command("sh", "-c", "whoami").Output()
|
||||
if errUser != nil {
|
||||
return "Error getting username, nobody here but us chickens!"
|
||||
}
|
||||
fullHostname := fmt.Sprintf("%s@%s", strings.TrimSpace(string(username)), strings.TrimSpace(string(hostname)))
|
||||
return fullHostname
|
||||
}
|
||||
|
||||
func checkNix() bool {
|
||||
_, err := exec.Command("sh", "-c", "command -v nix-env >/dev/null 2>&1").Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func addNix() string {
|
||||
out, _ := exec.Command("sh", "-c", "nix-env -q | wc -l").Output()
|
||||
nixPkgs := strings.TrimSpace(string(out)) + " (nix)"
|
||||
return nixPkgs
|
||||
}
|
||||
|
||||
func getPkgs() string {
|
||||
var pkgs string
|
||||
haveNix := checkNix()
|
||||
if strings.HasPrefix(dist, "Arch") {
|
||||
out, _ := exec.Command("sh", "-c", "pacman -Qq | wc -l").Output()
|
||||
pkgs = strings.TrimSpace(string(out)) + " (pacman)"
|
||||
if haveNix {
|
||||
pkgs = strings.TrimSpace(pkgs + ", " + addNix())
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
if strings.HasPrefix(dist, "Debian") {
|
||||
out, _ := exec.Command("sh", "-c", "dpkg -l | grep ^ii | wc -l").Output()
|
||||
pkgs = strings.TrimSpace(string(out)) + " (apt)"
|
||||
if haveNix {
|
||||
pkgs = strings.TrimSpace(pkgs + ", " + addNix())
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
if strings.HasPrefix(dist, "NixOS") {
|
||||
entries, err := os.ReadDir("/run/current-system/sw/bin")
|
||||
if err != nil {
|
||||
return "can't get nix packages"
|
||||
}
|
||||
return strconv.Itoa(len(entries)) + " (nix)"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
func getLogo(dist string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(dist, "Arch"):
|
||||
return "logos/arch.png"
|
||||
case strings.HasPrefix(dist, "NixOS"):
|
||||
return "logos/nixos.png"
|
||||
case strings.HasPrefix(dist, "Debian"):
|
||||
return "logos/debian.png"
|
||||
default:
|
||||
return "logos/linux.png"
|
||||
}
|
||||
}
|
||||
|
||||
func getAscii(dist string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(dist, "Arch"):
|
||||
return "logos/arch.txt"
|
||||
case strings.HasPrefix(dist, "NixOS"):
|
||||
return "logos/nixos.txt"
|
||||
case strings.HasPrefix(dist, "Debian"):
|
||||
return "logos/debian.txt"
|
||||
default:
|
||||
return "logos/linux.txt"
|
||||
}
|
||||
}
|
||||
|
||||
func printAsciiLogo(path string) string {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: Could not read ASCII file at %s\n", path)
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
func renderLogoChafa(path string, width uint) []string {
|
||||
cols := width / 8
|
||||
if cols == 0 {
|
||||
cols = 1
|
||||
}
|
||||
sizeArg := fmt.Sprintf("--size=%dx%d", cols, cols/2)
|
||||
cmd := exec.Command("chafa", path, "--format", "symbols", sizeArg, "--symbols=half")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{fmt.Sprintf("Chafa error: %v", err)}
|
||||
}
|
||||
return splitLines(string(out))
|
||||
}
|
||||
|
||||
func normalizeLogoPosition(pos string) string {
|
||||
value := strings.ToLower(strings.TrimSpace(pos))
|
||||
if value == "left" || value == "right" {
|
||||
return value
|
||||
}
|
||||
return "right"
|
||||
}
|
||||
|
||||
func splitLines(content string) []string {
|
||||
normalized := strings.ReplaceAll(content, "\r\n", "\n")
|
||||
normalized = strings.TrimRight(normalized, "\n")
|
||||
if normalized == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(normalized, "\n")
|
||||
}
|
||||
|
||||
func maxLineLen(lines []string) int {
|
||||
maxLen := 0
|
||||
for _, line := range lines {
|
||||
lineLen := visibleWidth(line)
|
||||
if lineLen > maxLen {
|
||||
maxLen = lineLen
|
||||
}
|
||||
}
|
||||
return maxLen
|
||||
}
|
||||
|
||||
func visibleWidth(line string) int {
|
||||
stripped := ansiEscapeRe.ReplaceAllString(line, "")
|
||||
return utf8.RuneCountInString(stripped)
|
||||
}
|
||||
|
||||
func padRightVisible(line string, targetWidth int) string {
|
||||
pad := targetWidth - visibleWidth(line)
|
||||
if pad <= 0 {
|
||||
return line
|
||||
}
|
||||
return line + strings.Repeat(" ", pad)
|
||||
}
|
||||
|
||||
func renderSideBySide(statsLines []string, logoLines []string, position string) {
|
||||
const gap = " "
|
||||
statsWidth := maxLineLen(statsLines)
|
||||
logoWidth := maxLineLen(logoLines)
|
||||
|
||||
lineCount := len(statsLines)
|
||||
if len(logoLines) > lineCount {
|
||||
lineCount = len(logoLines)
|
||||
}
|
||||
|
||||
for i := 0; i < lineCount; i++ {
|
||||
stats := ""
|
||||
logo := ""
|
||||
|
||||
if i < len(statsLines) {
|
||||
stats = statsLines[i]
|
||||
}
|
||||
if i < len(logoLines) {
|
||||
logo = logoLines[i]
|
||||
}
|
||||
|
||||
if position == "left" {
|
||||
fmt.Printf("%s%s%s\n", padRightVisible(logo, logoWidth), gap, stats)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s%s%s\n", padRightVisible(stats, statsWidth), gap, logo)
|
||||
}
|
||||
}
|
||||
|
||||
func getAbsLogoPath(relativePath string) string {
|
||||
if _, err := os.Stat(relativePath); err == nil {
|
||||
return relativePath
|
||||
}
|
||||
|
||||
exePath, err := os.Executable()
|
||||
if err == nil {
|
||||
exeDir := filepath.Dir(exePath)
|
||||
|
||||
nixPath := filepath.Join(exeDir, "..", "share", "gogofetch", relativePath)
|
||||
|
||||
if _, err := os.Stat(nixPath); err == nil {
|
||||
return nixPath
|
||||
}
|
||||
}
|
||||
|
||||
systemPath := filepath.Join("/usr/share/gogofetch", relativePath)
|
||||
if _, err := os.Stat(systemPath); err == nil {
|
||||
return systemPath
|
||||
}
|
||||
|
||||
return relativePath
|
||||
}
|
||||
|
||||
func getShell() string {
|
||||
out, err := exec.Command("ps", "-p", strconv.Itoa(os.Getppid()), "-o", "comm=").Output()
|
||||
if err != nil {
|
||||
return path.Base(os.Getenv("SHELL"))
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func getTerminal() string {
|
||||
return os.Getenv("TERM")
|
||||
}
|
||||
|
||||
func getDE() string {
|
||||
de := os.Getenv("XDG_CURRENT_DESKTOP")
|
||||
if de == "" {
|
||||
return "Unknown or w/o GUI"
|
||||
}
|
||||
return strings.TrimSpace(de)
|
||||
}
|
||||
|
||||
func getUptime() string {
|
||||
data, err := os.ReadFile("/proc/uptime")
|
||||
if err != nil {
|
||||
return "0"
|
||||
}
|
||||
uptimeStr := strings.Fields(string(data))[0]
|
||||
seconds, err := strconv.ParseFloat(uptimeStr, 64)
|
||||
if err != nil {
|
||||
_, err := fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
if err != nil {
|
||||
return "error printing uptime"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
d := time.Duration(seconds) * time.Second
|
||||
formatted := d.Round(time.Second).String()
|
||||
spaced := strings.NewReplacer("h", "h ", "m", "m ", "s", "s ").Replace(formatted)
|
||||
return spaced
|
||||
}
|
||||
|
||||
func main() {
|
||||
conf, _ := initConfig()
|
||||
|
||||
if len(conf.Layout) == 0 {
|
||||
conf.Layout = []string{"host", "dist", "cpu", "ram", "shell", "uptime"}
|
||||
fmt.Println("Warning: No layout specified in config, using default")
|
||||
}
|
||||
|
||||
dist := getDist()
|
||||
var defaultLogo string
|
||||
|
||||
if conf.CustomLogo != "" {
|
||||
defaultLogo = conf.CustomLogo
|
||||
} else if conf.Ascii {
|
||||
defaultLogo = getAscii(dist)
|
||||
} else {
|
||||
defaultLogo = getLogo(dist)
|
||||
}
|
||||
|
||||
width := flag.Int("w", 190, "Logo width in lines")
|
||||
logoPath := flag.String("l", getAbsLogoPath(defaultLogo), "Path to the logo")
|
||||
logoPos := flag.String("p", normalizeLogoPosition(conf.LogoPosition), "Logo position: left or right")
|
||||
flag.Parse()
|
||||
position := normalizeLogoPosition(*logoPos)
|
||||
|
||||
type Info struct {
|
||||
Label string
|
||||
Value string
|
||||
}
|
||||
var stats []Info
|
||||
|
||||
for _, item := range conf.Layout {
|
||||
switch strings.ToLower(item) {
|
||||
case "host":
|
||||
stats = append(stats, Info{"host", getHostname()})
|
||||
case "dist":
|
||||
stats = append(stats, Info{"OS", dist})
|
||||
case "cpu":
|
||||
stats = append(stats, Info{"cpu", getCpu()})
|
||||
case "kernel", "krnl":
|
||||
stats = append(stats, Info{"krnl", getKernel()})
|
||||
case "ram":
|
||||
stats = append(stats, Info{"ram", getRam()})
|
||||
case "gpu":
|
||||
stats = append(stats, Info{"gpu", getGpu()})
|
||||
case "de/wm", "de", "wm":
|
||||
stats = append(stats, Info{"de/wm", getDE()})
|
||||
case "pkgs":
|
||||
stats = append(stats, Info{"pkgs", getPkgs()})
|
||||
case "shell":
|
||||
stats = append(stats, Info{"shell", getShell()})
|
||||
case "terminal", "term":
|
||||
stats = append(stats, Info{"term", getTerminal()})
|
||||
case "uptime":
|
||||
stats = append(stats, Info{"uptime", getUptime()})
|
||||
}
|
||||
}
|
||||
|
||||
var statsLines []string
|
||||
for _, info := range stats {
|
||||
statsLines = append(statsLines, fmt.Sprintf("-> %s%s%s: %s", Blue, info.Label, Reset, info.Value))
|
||||
}
|
||||
|
||||
var logoLines []string
|
||||
if _, err := os.Stat(*logoPath); err == nil {
|
||||
if conf.Ascii {
|
||||
logoLines = splitLines(printAsciiLogo(*logoPath))
|
||||
} else {
|
||||
logoLines = renderLogoChafa(*logoPath, uint(*width))
|
||||
}
|
||||
} else {
|
||||
logoLines = []string{fmt.Sprintf("Logo not found at: %s", *logoPath)}
|
||||
}
|
||||
|
||||
renderSideBySide(statsLines, logoLines, position)
|
||||
}
|
||||
@ -18,7 +18,7 @@ let
|
||||
goApp = pkgs.buildGoModule {
|
||||
pname = "goGoFetch";
|
||||
version = "1.0";
|
||||
src = ./.;
|
||||
src = ./src;
|
||||
vendorHash = null;
|
||||
};
|
||||
in
|
||||
|
||||
92
src/main.go
Normal file
92
src/main.go
Normal file
@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"goGoFetch/src/providers"
|
||||
_ "image/png"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
Reset = "\033[0m"
|
||||
Blue = "\033[1;34m"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf, _ := providers.InitConfig()
|
||||
|
||||
if len(conf.Layout) == 0 {
|
||||
conf.Layout = []string{"host", "dist", "cpu", "ram", "shell", "uptime"}
|
||||
fmt.Println("Warning: No layout specified in config, using default")
|
||||
}
|
||||
|
||||
distID, prettyName := providers.GetDist()
|
||||
var defaultLogo string
|
||||
|
||||
if conf.CustomLogo != "" {
|
||||
defaultLogo = conf.CustomLogo
|
||||
} else if conf.Ascii {
|
||||
defaultLogo = providers.GetAscii(distID)
|
||||
} else {
|
||||
defaultLogo = providers.GetLogo(distID)
|
||||
}
|
||||
|
||||
width := flag.Int("w", 190, "Logo width in lines")
|
||||
logoPath := flag.String("l", providers.GetAbsLogoPath(defaultLogo), "Path to the logo")
|
||||
logoPos := flag.String("p", providers.NormalizeLogoPosition(conf.LogoPosition), "Logo position: left or right")
|
||||
flag.Parse()
|
||||
position := providers.NormalizeLogoPosition(*logoPos)
|
||||
|
||||
type Info struct {
|
||||
Label string
|
||||
Value string
|
||||
}
|
||||
var stats []Info
|
||||
|
||||
for _, item := range conf.Layout {
|
||||
switch strings.ToLower(item) {
|
||||
case "host":
|
||||
stats = append(stats, Info{"host", providers.GetHostname()})
|
||||
case "dist":
|
||||
stats = append(stats, Info{"OS", prettyName})
|
||||
case "cpu":
|
||||
stats = append(stats, Info{"cpu", providers.GetCpu()})
|
||||
case "kernel", "krnl":
|
||||
stats = append(stats, Info{"krnl", providers.GetKernel()})
|
||||
case "ram":
|
||||
stats = append(stats, Info{"ram", providers.GetRam()})
|
||||
case "gpu":
|
||||
stats = append(stats, Info{"gpu", providers.GetGpu()})
|
||||
case "de/wm", "de", "wm":
|
||||
stats = append(stats, Info{"de/wm", providers.GetDE()})
|
||||
case "pkgs":
|
||||
stats = append(stats, Info{"pkgs", providers.GetPkgs()})
|
||||
case "shell":
|
||||
stats = append(stats, Info{"shell", providers.GetShell()})
|
||||
case "terminal", "term":
|
||||
stats = append(stats, Info{"term", providers.GetTerminal()})
|
||||
case "uptime":
|
||||
stats = append(stats, Info{"uptime", providers.GetUptime()})
|
||||
}
|
||||
}
|
||||
|
||||
var statsLines []string
|
||||
for _, info := range stats {
|
||||
statsLines = append(statsLines, fmt.Sprintf("-> %s%s%s: %s", Blue, info.Label, Reset, info.Value))
|
||||
}
|
||||
|
||||
var logoLines []string
|
||||
if _, err := os.Stat(*logoPath); err == nil {
|
||||
if conf.Ascii {
|
||||
logoLines = providers.SplitLines(providers.PrintAsciiLogo(*logoPath))
|
||||
} else {
|
||||
logoLines = providers.RenderLogoChafa(*logoPath, uint(*width))
|
||||
}
|
||||
} else {
|
||||
logoLines = []string{fmt.Sprintf("Logo not found at: %s", *logoPath)}
|
||||
}
|
||||
|
||||
providers.RenderSideBySide(statsLines, logoLines, position)
|
||||
}
|
||||
140
src/providers/config.go
Normal file
140
src/providers/config.go
Normal file
@ -0,0 +1,140 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Ascii bool `toml:"ascii"`
|
||||
Layout []string `toml:"layout"`
|
||||
CustomLogo string `toml:"custom_logo"`
|
||||
LogoPosition string `toml:"logo_position"`
|
||||
}
|
||||
|
||||
func appendMissingConfigKeys(pathConf string, md toml.MetaData) error {
|
||||
var missing []string
|
||||
|
||||
if !md.IsDefined("ascii") {
|
||||
missing = append(missing, "# Set this to \"true\" if you like ascii more than images\nascii = false")
|
||||
}
|
||||
|
||||
if !md.IsDefined("layout") {
|
||||
missing = append(missing, `# Stats layout: reorder or remove items to customize your fetch
|
||||
# Available: "dist", "host", "cpu", "kernel", "ram", "gpu", "de/wm", "pkgs", "shell", "terminal", "uptime"
|
||||
layout = [
|
||||
"host",
|
||||
"dist",
|
||||
"cpu",
|
||||
"kernel",
|
||||
"ram",
|
||||
"gpu",
|
||||
"de/wm",
|
||||
"pkgs",
|
||||
"shell",
|
||||
"terminal",
|
||||
"uptime",
|
||||
]`)
|
||||
}
|
||||
|
||||
if !md.IsDefined("custom_logo") {
|
||||
missing = append(missing, "# Absolute path to a custom image or .txt file. Leave empty to use auto-detection.\ncustom_logo = \"\"")
|
||||
}
|
||||
|
||||
if !md.IsDefined("logo_position") {
|
||||
missing = append(missing, "# Position of the logo block relative to the stats block: \"left\" or \"right\".\nlogo_position = \"left\"")
|
||||
}
|
||||
|
||||
if len(missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(pathConf, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
|
||||
block := "\n# Added automatically after update\n\n" + strings.Join(missing, "\n\n") + "\n"
|
||||
_, err = f.WriteString(block)
|
||||
return err
|
||||
}
|
||||
|
||||
func getConfigPath() (string, error) {
|
||||
configDir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
directoryName := "gogofetch"
|
||||
fullPath := filepath.Join(configDir, directoryName, "config.toml")
|
||||
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
func InitConfig() (Config, string) {
|
||||
var conf Config
|
||||
pathConf, err := getConfigPath()
|
||||
if err != nil {
|
||||
return conf, ""
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Dir(pathConf), 0755)
|
||||
if err != nil {
|
||||
return Config{}, ""
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pathConf); os.IsNotExist(err) {
|
||||
defaultConfig := []byte(`
|
||||
# Set this to "true" if you like ascii more than images
|
||||
ascii = false
|
||||
|
||||
# Stats layout: reorder or remove items to customize your fetch
|
||||
# Available: "dist", "host", "cpu", "kernel", "ram", "gpu", "de/wm", "pkgs", "shell", "terminal", "uptime"
|
||||
layout = [
|
||||
"host",
|
||||
"dist",
|
||||
"cpu",
|
||||
"kernel",
|
||||
"ram",
|
||||
"gpu",
|
||||
"de/wm",
|
||||
"pkgs",
|
||||
"shell",
|
||||
"terminal",
|
||||
"uptime",
|
||||
]
|
||||
|
||||
# Absolute path to a custom image or .txt file. Leave empty to use auto-detection.
|
||||
custom_logo = ""
|
||||
|
||||
# Position of the logo block relative to the stats block: "left" or "right".
|
||||
logo_position = "left"
|
||||
`)
|
||||
err := os.WriteFile(pathConf, defaultConfig, 0644)
|
||||
if err != nil {
|
||||
return Config{}, ""
|
||||
}
|
||||
}
|
||||
|
||||
md, err := toml.DecodeFile(pathConf, &conf)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading config at %s: %v\n", pathConf, err)
|
||||
return conf, pathConf
|
||||
}
|
||||
|
||||
if err := appendMissingConfigKeys(pathConf, md); err != nil {
|
||||
fmt.Printf("Warning: Could not update config at %s: %v\n", pathConf, err)
|
||||
} else {
|
||||
if _, err := toml.DecodeFile(pathConf, &conf); err != nil {
|
||||
fmt.Printf("Error reloading updated config at %s: %v\n", pathConf, err)
|
||||
}
|
||||
}
|
||||
|
||||
return conf, pathConf
|
||||
}
|
||||
156
src/providers/logo.go
Normal file
156
src/providers/logo.go
Normal file
@ -0,0 +1,156 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var ansiEscapeRe = regexp.MustCompile(`\x1b\[[0-9;?]*[ -/]*[@-~]`)
|
||||
|
||||
func GetLogo(id string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(id, "arch"):
|
||||
return "logos/arch.png"
|
||||
case strings.HasPrefix(id, "nixos"):
|
||||
return "logos/nixos.png"
|
||||
case strings.HasPrefix(id, "debian"):
|
||||
return "logos/debian.png"
|
||||
default:
|
||||
return "logos/linux.png"
|
||||
}
|
||||
}
|
||||
|
||||
func GetAscii(id string) string {
|
||||
switch {
|
||||
case strings.HasPrefix(id, "arch"):
|
||||
return "logos/arch.txt"
|
||||
case strings.HasPrefix(id, "nixos"):
|
||||
return "logos/nixos.txt"
|
||||
case strings.HasPrefix(id, "debian"):
|
||||
return "logos/debian.txt"
|
||||
default:
|
||||
return "logos/linux.txt"
|
||||
}
|
||||
}
|
||||
|
||||
func PrintAsciiLogo(path string) string {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Error: Could not read ASCII file at %s\n", path)
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
func RenderLogoChafa(path string, width uint) []string {
|
||||
cols := width / 8
|
||||
if cols == 0 {
|
||||
cols = 1
|
||||
}
|
||||
sizeArg := fmt.Sprintf("--size=%dx%d", cols, cols/2)
|
||||
cmd := exec.Command("chafa", path, "--format", "symbols", sizeArg, "--symbols=half")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return []string{fmt.Sprintf("Chafa error: %v", err)}
|
||||
}
|
||||
return SplitLines(string(out))
|
||||
}
|
||||
|
||||
func NormalizeLogoPosition(pos string) string {
|
||||
value := strings.ToLower(strings.TrimSpace(pos))
|
||||
if value == "left" || value == "right" {
|
||||
return value
|
||||
}
|
||||
return "right"
|
||||
}
|
||||
|
||||
func SplitLines(content string) []string {
|
||||
normalized := strings.ReplaceAll(content, "\r\n", "\n")
|
||||
normalized = strings.TrimRight(normalized, "\n")
|
||||
if normalized == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(normalized, "\n")
|
||||
}
|
||||
|
||||
func maxLineLen(lines []string) int {
|
||||
maxLen := 0
|
||||
for _, line := range lines {
|
||||
lineLen := visibleWidth(line)
|
||||
if lineLen > maxLen {
|
||||
maxLen = lineLen
|
||||
}
|
||||
}
|
||||
return maxLen
|
||||
}
|
||||
|
||||
func visibleWidth(line string) int {
|
||||
stripped := ansiEscapeRe.ReplaceAllString(line, "")
|
||||
return utf8.RuneCountInString(stripped)
|
||||
}
|
||||
|
||||
func padRightVisible(line string, targetWidth int) string {
|
||||
pad := targetWidth - visibleWidth(line)
|
||||
if pad <= 0 {
|
||||
return line
|
||||
}
|
||||
return line + strings.Repeat(" ", pad)
|
||||
}
|
||||
|
||||
func RenderSideBySide(statsLines []string, logoLines []string, position string) {
|
||||
const gap = " "
|
||||
statsWidth := maxLineLen(statsLines)
|
||||
logoWidth := maxLineLen(logoLines)
|
||||
|
||||
lineCount := len(statsLines)
|
||||
if len(logoLines) > lineCount {
|
||||
lineCount = len(logoLines)
|
||||
}
|
||||
|
||||
for i := 0; i < lineCount; i++ {
|
||||
stats := ""
|
||||
logo := ""
|
||||
|
||||
if i < len(statsLines) {
|
||||
stats = statsLines[i]
|
||||
}
|
||||
if i < len(logoLines) {
|
||||
logo = logoLines[i]
|
||||
}
|
||||
|
||||
if position == "left" {
|
||||
fmt.Printf("%s%s%s\n", padRightVisible(logo, logoWidth), gap, stats)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("%s%s%s\n", padRightVisible(stats, statsWidth), gap, logo)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAbsLogoPath(relativePath string) string {
|
||||
if _, err := os.Stat(relativePath); err == nil {
|
||||
return relativePath
|
||||
}
|
||||
|
||||
exePath, err := os.Executable()
|
||||
if err == nil {
|
||||
exeDir := filepath.Dir(exePath)
|
||||
|
||||
nixPath := filepath.Join(exeDir, "..", "share", "gogofetch", relativePath)
|
||||
|
||||
if _, err := os.Stat(nixPath); err == nil {
|
||||
return nixPath
|
||||
}
|
||||
}
|
||||
|
||||
systemPath := filepath.Join("/usr/share/gogofetch", relativePath)
|
||||
if _, err := os.Stat(systemPath); err == nil {
|
||||
return systemPath
|
||||
}
|
||||
|
||||
return relativePath
|
||||
}
|
||||
50
src/providers/packages.go
Normal file
50
src/providers/packages.go
Normal file
@ -0,0 +1,50 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func checkNix() bool {
|
||||
_, err := os.Stat("/nix")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func addNix() string {
|
||||
out, _ := exec.Command("sh", "-c", "nix-env -q | wc -l").Output()
|
||||
nixPkgs := strings.TrimSpace(string(out)) + " (nix)"
|
||||
return nixPkgs
|
||||
}
|
||||
|
||||
func GetPkgs() string {
|
||||
var pkgs string
|
||||
haveNix := checkNix()
|
||||
if strings.HasPrefix(id, "arch") {
|
||||
entries, err := os.ReadDir("/run/current-system/sw/bin")
|
||||
if err != nil {
|
||||
return "can't get nix packages"
|
||||
}
|
||||
return strconv.Itoa(len(entries)) + " (nix)"
|
||||
}
|
||||
if strings.HasPrefix(id, "debian") {
|
||||
out, _ := exec.Command("sh", "-c", "dpkg -l | grep ^ii | wc -l").Output()
|
||||
pkgs = strings.TrimSpace(string(out)) + " (apt)"
|
||||
if haveNix {
|
||||
pkgs = strings.TrimSpace(pkgs + ", " + addNix())
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
if strings.HasPrefix(id, "nixos") {
|
||||
entries, err := os.ReadDir("/run/current-system/sw/bin")
|
||||
if err != nil {
|
||||
return "can't get nix packages"
|
||||
}
|
||||
return strconv.Itoa(len(entries)) + " (nix)"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
215
src/providers/systemstats.go
Normal file
215
src/providers/systemstats.go
Normal file
@ -0,0 +1,215 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var id, prettyName string
|
||||
|
||||
func GetDist() (string, string) {
|
||||
f, _ := os.Open("/etc/os-release")
|
||||
defer func(f *os.File) {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Println("Error closing file:", err)
|
||||
}
|
||||
}(f)
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
t := s.Text()
|
||||
if strings.HasPrefix(t, "ID=") {
|
||||
id = strings.TrimPrefix(t, "ID=")
|
||||
}
|
||||
if strings.HasPrefix(t, "PRETTY_NAME=") {
|
||||
prettyName = strings.TrimPrefix(t, "PRETTY_NAME=")
|
||||
prettyName = strings.Trim(prettyName, "\"")
|
||||
}
|
||||
}
|
||||
if id == "" && prettyName == "" {
|
||||
return "unknown", "unknown"
|
||||
}
|
||||
return id, prettyName
|
||||
}
|
||||
|
||||
func GetRam() string {
|
||||
f, err := os.Open("/proc/meminfo")
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
var total, available int
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "MemTotal:") {
|
||||
_, err2 := fmt.Sscanf(line, "MemTotal: %d kB", &total)
|
||||
if err2 != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(line, "MemAvailable:") {
|
||||
_, err := fmt.Sscanf(line, "MemAvailable: %d kB", &available)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
totalMB := total / 1024
|
||||
availableMB := available / 1024
|
||||
usedMB := totalMB - availableMB
|
||||
|
||||
ram := fmt.Sprintf("%d / %d MiB (%d MiB available)", usedMB, totalMB, availableMB)
|
||||
ram = strings.TrimSpace(ram)
|
||||
return ram
|
||||
|
||||
}
|
||||
|
||||
func GetCpu() string {
|
||||
f, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
|
||||
var cpu string
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "model name") {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) > 1 {
|
||||
cpu := strings.TrimSpace(parts[1])
|
||||
return cpu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(cpu)
|
||||
}
|
||||
|
||||
func GetGpu() string {
|
||||
out, err := exec.Command("sh", "-c", `lspci -mm | awk -F'"' '$2=="VGA compatible controller" || $2=="3D controller" || $2=="Display controller"'`).Output()
|
||||
if err != nil || len(out) == 0 {
|
||||
return "Unknown GPU"
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
|
||||
var gpus []string
|
||||
|
||||
for _, line := range lines {
|
||||
re := regexp.MustCompile(`"([^"]+)"`)
|
||||
matches := re.FindAllStringSubmatch(line, -1)
|
||||
|
||||
if len(matches) >= 3 {
|
||||
vendor := matches[1][1]
|
||||
device := matches[2][1]
|
||||
|
||||
cleaned := cleanGpuString(vendor + " " + device)
|
||||
gpus = append(gpus, cleaned)
|
||||
}
|
||||
}
|
||||
|
||||
if len(gpus) > 0 {
|
||||
return strings.Join(gpus, " / ")
|
||||
}
|
||||
return "GPU not found"
|
||||
}
|
||||
|
||||
func cleanGpuString(raw string) string {
|
||||
prettyNameRe := regexp.MustCompile(`\[(.*?)]`)
|
||||
match := prettyNameRe.FindStringSubmatch(raw)
|
||||
|
||||
res := raw
|
||||
if len(match) > 1 {
|
||||
vendor := ""
|
||||
if strings.Contains(strings.ToLower(raw), "nvidia") {
|
||||
vendor = "NVIDIA "
|
||||
}
|
||||
if strings.Contains(strings.ToLower(raw), "intel") {
|
||||
vendor = "Intel "
|
||||
}
|
||||
if strings.Contains(strings.ToLower(raw), "amd") {
|
||||
vendor = "AMD "
|
||||
}
|
||||
|
||||
res = vendor + match[1]
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\(.*?\)|\[.*?]`)
|
||||
res = re.ReplaceAllString(res, "")
|
||||
|
||||
fluff := []string{"Corporation", "Controller", "VGA compatible", "3D", "Graphics", "Device"}
|
||||
for _, word := range fluff {
|
||||
res = strings.ReplaceAll(res, word, "")
|
||||
}
|
||||
|
||||
return strings.Join(strings.Fields(res), " ")
|
||||
}
|
||||
|
||||
func GetKernel() string {
|
||||
out, err := exec.Command("sh", "-c", "uname -r").Output()
|
||||
if err != nil {
|
||||
return "Error getting kernel, how did we get there?"
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func GetHostname() string {
|
||||
hostname, err := exec.Command("sh", "-c", "uname -n").Output()
|
||||
if err != nil {
|
||||
return "Error getting hostname, awful"
|
||||
}
|
||||
username, errUser := exec.Command("sh", "-c", "whoami").Output()
|
||||
if errUser != nil {
|
||||
return "Error getting username, nobody here but us chickens!"
|
||||
}
|
||||
fullHostname := fmt.Sprintf("%s@%s", strings.TrimSpace(string(username)), strings.TrimSpace(string(hostname)))
|
||||
return fullHostname
|
||||
}
|
||||
|
||||
func GetTerminal() string {
|
||||
return os.Getenv("TERM")
|
||||
}
|
||||
|
||||
func GetDE() string {
|
||||
de := os.Getenv("XDG_CURRENT_DESKTOP")
|
||||
if de == "" {
|
||||
return "Unknown or w/o GUI"
|
||||
}
|
||||
return strings.TrimSpace(de)
|
||||
}
|
||||
|
||||
func GetUptime() string {
|
||||
data, err := os.ReadFile("/proc/uptime")
|
||||
if err != nil {
|
||||
return "0"
|
||||
}
|
||||
uptimeStr := strings.Fields(string(data))[0]
|
||||
seconds, err := strconv.ParseFloat(uptimeStr, 64)
|
||||
if err != nil {
|
||||
_, err := fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
if err != nil {
|
||||
return "error printing uptime"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
d := time.Duration(seconds) * time.Second
|
||||
formatted := d.Round(time.Second).String()
|
||||
spaced := strings.NewReplacer("h", "h ", "m", "m ", "s", "s ").Replace(formatted)
|
||||
return spaced
|
||||
}
|
||||
|
||||
func GetShell() string {
|
||||
out, err := exec.Command("ps", "-p", strconv.Itoa(os.Getppid()), "-o", "comm=").Output()
|
||||
if err != nil {
|
||||
return path.Base(os.Getenv("SHELL"))
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user