package main import ( "bytes" "net" "os" "path/filepath" "strings" ) func word(words []string, i int) string { if i < len(words) { return words[i] } return "" } // splitLines splits s on newlines, dropping a trailing newline and returning // nil for empty input. func splitLines(s string) []string { s = strings.TrimRight(s, "\n") if s == "" { return nil } return strings.Split(s, "\n") } func isDir(p string) bool { fi, err := os.Stat(p) return err == nil && fi.IsDir() } // truthy reports whether a config string means "on" (1/true/yes/on). func truthy(s string) bool { switch strings.ToLower(strings.TrimSpace(s)) { case "1", "true", "yes", "on": return true } return false } // falsy reports whether a config string explicitly means "off". It is not the // negation of truthy: for a setting that defaults to on, an unset value or a // typo must leave it on, and only a deliberate "off" may switch it off. func falsy(s string) bool { switch strings.ToLower(strings.TrimSpace(s)) { case "0", "false", "no", "off": return true } return false } func fileExists(p string) bool { fi, err := os.Stat(p) return err == nil && !fi.IsDir() } func filesEqual(a, b string) bool { da, ea := os.ReadFile(a) db, eb := os.ReadFile(b) if ea != nil || eb != nil { return false } return bytes.Equal(da, db) } func copyFile(src, dst string) error { data, err := os.ReadFile(src) if err != nil { return err } if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { return err } mode := os.FileMode(0644) if fi, err := os.Stat(src); err == nil { mode = fi.Mode() } return os.WriteFile(dst, data, mode) } func resolveIP() string { h, err := os.Hostname() if err != nil { return "" } addrs, err := net.LookupHost(h) if err != nil || len(addrs) == 0 { return "" } for _, a := range addrs { if ip := net.ParseIP(a); ip != nil && ip.To4() != nil { return a } } return addrs[0] } func shortHostname() string { h, _ := os.Hostname() if i := strings.IndexByte(h, '.'); i >= 0 { h = h[:i] } return h }