diff --git a/internal/killist/killist.go b/internal/killist/killist.go index 2c901a3..7b8c18d 100644 --- a/internal/killist/killist.go +++ b/internal/killist/killist.go @@ -6,6 +6,7 @@ package killist import ( "bufio" "os" + "path/filepath" "strings" "sync" ) @@ -79,15 +80,33 @@ func (kl *KillList) List() []string { return out } +// save writes the kill list atomically: it writes to a temp file in the same +// directory, flushes, closes, then renames over the target. This ensures the +// existing file is never truncated on a partial write or flush error. func (kl *KillList) save() error { - f, err := os.Create(kl.filepath) + dir := filepath.Dir(kl.filepath) + tmp, err := os.CreateTemp(dir, ".killist-*.tmp") if err != nil { return err } - defer f.Close() - w := bufio.NewWriter(f) + tmpName := tmp.Name() + + w := bufio.NewWriter(tmp) for t := range kl.titles { - w.WriteString(t + "\n") + if _, err := w.WriteString(t + "\n"); err != nil { + tmp.Close() + os.Remove(tmpName) + return err + } + } + if err := w.Flush(); err != nil { + tmp.Close() + os.Remove(tmpName) + return err + } + if err := tmp.Close(); err != nil { + os.Remove(tmpName) + return err } - return w.Flush() + return os.Rename(tmpName, kl.filepath) }