package main import ( "fmt" "os/exec" "strings" "sync" ) // runCmd runs name with args, returning trimmed combined output. func runCmd(name string, args ...string) (string, error) { out, err := exec.Command(name, args...).CombinedOutput() return strings.TrimSpace(string(out)), err } // runCmdStdin is runCmd with a fixed stdin string (for tools that prompt). func runCmdStdin(stdin, name string, args ...string) (string, error) { c := exec.Command(name, args...) c.Stdin = strings.NewReader(stdin) out, err := c.CombinedOutput() return strings.TrimSpace(string(out)), err } // limitedBuffer keeps only the last maxLen bytes written to it — used to // capture a bounded tail of a subprocess's stderr for error messages // without risking unbounded memory growth from a noisy remote command. type limitedBuffer struct { mu sync.Mutex buf []byte maxLen int } func newLimitedBuffer(maxLen int) *limitedBuffer { return &limitedBuffer{maxLen: maxLen} } func (b *limitedBuffer) Write(p []byte) (int, error) { b.mu.Lock() defer b.mu.Unlock() b.buf = append(b.buf, p...) if len(b.buf) > b.maxLen { b.buf = b.buf[len(b.buf)-b.maxLen:] } return len(p), nil } func (b *limitedBuffer) String() string { b.mu.Lock() defer b.mu.Unlock() return string(b.buf) } // roundUp rounds n up to the next multiple of to (to <= 1 is a no-op). func roundUp(n, to int64) int64 { if to <= 1 { return n } return (n + to - 1) / to * to } func humanBytes(n int64) string { const unit = 1024 if n < unit { return fmt.Sprintf("%d B", n) } div, exp := int64(unit), 0 for m := n / unit; m >= unit; m /= unit { div *= unit exp++ } units := "KMGTPE" return fmt.Sprintf("%.1f%ciB", float64(n)/float64(div), units[exp]) }