micro/internal/buffer/backup.go

154 lines
3.6 KiB
Go
Raw Normal View History

2019-12-21 21:35:09 +00:00
package buffer
import (
"errors"
2019-12-22 23:05:23 +00:00
"fmt"
"io/fs"
2019-12-22 00:55:23 +00:00
"os"
2020-02-11 18:09:17 +00:00
"path/filepath"
2020-09-04 17:36:23 +00:00
"sync/atomic"
2019-12-22 00:55:23 +00:00
"time"
2019-12-21 21:35:09 +00:00
2020-05-04 14:16:15 +00:00
"github.com/zyedidia/micro/v2/internal/config"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
2019-12-21 21:35:09 +00:00
)
const BackupMsg = `A backup was detected for this file. This likely means that micro
2019-12-22 00:55:23 +00:00
crashed while editing this file, or another instance of micro is currently
editing this file.
The backup was created on %s, and the file is
%s
2019-12-22 00:55:23 +00:00
* 'recover' will apply the backup as unsaved changes to the current buffer.
When the buffer is closed, the backup will be removed.
* 'ignore' will ignore the backup, discarding its changes. The backup file
will be removed.
* 'abort' will abort the open operation, and instead open an empty buffer.
2019-12-22 00:55:23 +00:00
Options: [r]ecover, [i]gnore, [a]bort: `
2019-12-22 00:55:23 +00:00
var backupRequestChan chan *Buffer
func backupThread() {
for {
time.Sleep(time.Second * 8)
for len(backupRequestChan) > 0 {
b := <-backupRequestChan
2020-09-04 17:36:23 +00:00
bfini := atomic.LoadInt32(&(b.fini)) != 0
if !bfini {
b.Backup()
}
}
2019-12-21 21:35:09 +00:00
}
}
func init() {
backupRequestChan = make(chan *Buffer, 10)
go backupThread()
}
2019-12-21 21:35:09 +00:00
func (b *Buffer) RequestBackup() {
if !b.requestedBackup {
select {
case backupRequestChan <- b:
default:
// channel is full
2019-12-22 04:26:53 +00:00
}
b.requestedBackup = true
2019-12-22 00:55:23 +00:00
}
}
2019-12-22 00:55:23 +00:00
2024-05-29 20:33:33 +00:00
func (b *Buffer) backupDir() string {
backupdir, err := util.ReplaceHome(b.Settings["backupdir"].(string))
if backupdir == "" || err != nil {
backupdir = filepath.Join(config.ConfigDir, "backups")
}
return backupdir
}
func (b *Buffer) keepBackup() bool {
return b.forceKeepBackup || b.Settings["permbackup"].(bool)
}
2024-05-31 18:44:38 +00:00
// Backup saves the current buffer to the backups directory
func (b *Buffer) Backup() error {
if !b.Settings["backup"].(bool) || b.Path == "" || b.Type != BTDefault {
return nil
}
2019-12-22 00:55:23 +00:00
2024-05-29 20:33:33 +00:00
backupdir := b.backupDir()
if _, err := os.Stat(backupdir); errors.Is(err, fs.ErrNotExist) {
2019-12-22 00:55:23 +00:00
os.Mkdir(backupdir, os.ModePerm)
}
name := util.DetermineEscapePath(backupdir, b.AbsPath)
2024-05-31 18:44:38 +00:00
if _, err := os.Stat(name); errors.Is(err, fs.ErrNotExist) {
_, err = b.overwriteFile(name)
2024-05-31 18:44:38 +00:00
if err == nil {
b.requestedBackup = false
2019-12-21 21:35:09 +00:00
}
2024-05-31 18:44:38 +00:00
return err
}
2019-12-21 21:35:09 +00:00
2024-05-31 18:44:38 +00:00
tmp := util.AppendBackupSuffix(name)
_, err := b.overwriteFile(tmp)
2024-05-31 18:44:38 +00:00
if err != nil {
os.Remove(tmp)
return err
}
err = os.Rename(tmp, name)
if err != nil {
os.Remove(tmp)
return err
}
2019-12-21 21:35:09 +00:00
b.requestedBackup = false
2019-12-21 21:35:09 +00:00
return err
}
2019-12-22 00:55:23 +00:00
// RemoveBackup removes any backup file associated with this buffer
func (b *Buffer) RemoveBackup() {
2024-05-29 20:33:33 +00:00
if !b.Settings["backup"].(bool) || b.keepBackup() || b.Path == "" || b.Type != BTDefault {
2019-12-22 00:55:23 +00:00
return
}
2024-05-31 18:44:38 +00:00
f := util.DetermineEscapePath(b.backupDir(), b.AbsPath)
2019-12-22 00:55:23 +00:00
os.Remove(f)
}
2019-12-21 21:35:09 +00:00
// ApplyBackup applies the corresponding backup file to this buffer (if one exists)
2019-12-22 23:05:23 +00:00
// Returns true if a backup was applied
func (b *Buffer) ApplyBackup(fsize int64) (bool, bool) {
if b.Settings["backup"].(bool) && !b.Settings["permbackup"].(bool) && len(b.Path) > 0 && b.Type == BTDefault {
2024-05-31 18:44:38 +00:00
backupfile := util.DetermineEscapePath(b.backupDir(), b.AbsPath)
2019-12-22 23:05:23 +00:00
if info, err := os.Stat(backupfile); err == nil {
backup, err := os.Open(backupfile)
if err == nil {
defer backup.Close()
t := info.ModTime()
msg := fmt.Sprintf(BackupMsg, t.Format("Mon Jan _2 at 15:04, 2006"), backupfile)
choice := screen.TermPrompt(msg, []string{"r", "i", "a", "recover", "ignore", "abort"}, true)
2019-12-22 23:05:23 +00:00
if choice%3 == 0 {
2019-12-22 23:05:23 +00:00
// recover
b.LineArray = NewLineArray(uint64(fsize), FFAuto, backup)
b.isModified = true
return true, true
} else if choice%3 == 1 {
2019-12-22 23:05:23 +00:00
// delete
os.Remove(backupfile)
} else if choice%3 == 2 {
return false, false
2019-12-22 23:05:23 +00:00
}
}
}
}
return false, true
2019-12-21 21:35:09 +00:00
}