package main import ( "fmt" "os" "os/exec" "strings" "syscall" ) // clipTool is a detected clipboard backend and the command that writes stdin to // the clipboard (the same command with empty stdin clears it). type clipTool struct{ copy []string } func detectClipTool() *clipTool { candidates := [][]string{ {"wl-copy"}, {"xclip", "-selection", "clipboard"}, {"xsel", "-b", "-i"}, {"pbcopy"}, } for _, c := range candidates { if _, err := exec.LookPath(c[0]); err == nil { return &clipTool{copy: c} } } return nil } func clipTime() int { return atoiDefault(os.Getenv("PASSWORD_STORE_CLIP_TIME"), 45) } // copyClip places text on the clipboard and schedules a detached job to clear it // after PASSWORD_STORE_CLIP_TIME seconds. func copyClip(text string) error { tool := detectClipTool() if tool == nil { return errf("no clipboard tool found (install wl-clipboard, xclip, or xsel)") } c := exec.Command(tool.copy[0], tool.copy[1:]...) c.Stdin = strings.NewReader(text) if err := c.Run(); err != nil { return fmt.Errorf("copying to clipboard: %w", err) } // Detached clear: new process group so it outlives this CLI invocation. clearSh := fmt.Sprintf("sleep %d; printf '' | %s", clipTime(), strings.Join(tool.copy, " ")) clear := exec.Command("sh", "-c", clearSh) clear.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} return clear.Start() }