Make a backup file: Difference between revisions
Content added Content deleted
(Go solution) |
(→{{header|Go}}: modify to require existing file) |
||
Line 51: | Line 51: | ||
bx := ".backup" |
bx := ".backup" |
||
// see if it's a link |
// see if it's a link |
||
var err error |
|||
if tf, err := os.Readlink(fn); err == nil { |
if tf, err := os.Readlink(fn); err == nil { |
||
fn = tf |
fn = tf |
||
} |
} |
||
// stat to preserve permissions. |
|||
// default permissions, used if file doesn't exist |
|||
var fi os.FileInfo |
|||
⚫ | |||
// now see if file exists. |
|||
fmt.Println(err) |
|||
⚫ | |||
// it does. use existing permissions instead of default |
|||
⚫ | |||
// and rename the file, (losing any existing backup.) |
|||
// attemp rename |
|||
perm = fi.Mode().Perm() |
|||
if err = os.Rename(fn, fn+bx); err != nil { |
|||
⚫ | |||
// if rename of known existing file fails, we don't continue. |
|||
return |
|||
⚫ | |||
⚫ | |||
⚫ | |||
// stat failed. only allowable error is "no such file..." |
|||
// any other error is fatal. |
|||
pErr, ok := err.(*os.PathError) |
|||
if !ok || pErr.Err.Error() != "no such file or directory" { |
|||
⚫ | |||
⚫ | |||
⚫ | |||
} |
} |
||
// create new file |
// create new file |
||
err |
err = ioutil.WriteFile(fn, []byte("you too!\n"), fi.Mode().Perm()) |
||
if err != nil { |
if err != nil { |
||
fmt.Println(err) |
fmt.Println(err) |
||
Line 92: | Line 83: | ||
func main() { |
func main() { |
||
err := updateWithBackup("myth", ".backup", |
err := updateWithBackup("myth", ".backup", func(f *os.File) (err error) { |
||
if _, err = f.Seek(0, os.SEEK_SET); err != nil { |
|||
⚫ | |||
⚫ | |||
} |
|||
⚫ | |||
return |
|||
} |
|||
⚫ | |||
return |
return |
||
} |
} |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
if err != nil { |
if err != nil { |
||
fmt.Println(err) |
fmt.Println(err) |
||
Line 112: | Line 102: | ||
// update the file as needed and return any error, but should not close |
// update the file as needed and return any error, but should not close |
||
// the file. updateWithBackup will then close the file and return any error. |
// the file. updateWithBackup will then close the file and return any error. |
||
func updateWithBackup(fn, bx string, |
func updateWithBackup(fn, bx string, up func(*os.File) error) (err error) { |
||
up func(*os.File) error) (err error) { |
|||
var f *os.File |
var f *os.File |
||
if f, err = openWithBackup(fn, bx |
if f, err = openWithBackup(fn, bx); err != nil { |
||
return |
return |
||
} |
} |
||
Line 130: | Line 119: | ||
// destination file + bx. Any error encountered is returned. tf will be |
// destination file + bx. Any error encountered is returned. tf will be |
||
// an open file if and only if err == nil. |
// an open file if and only if err == nil. |
||
func openWithBackup(fn, bx string |
func openWithBackup(fn, bx string) (tf *os.File, err error) { |
||
// follow symlink. |
// follow symlink. |
||
if target, err := os.Readlink(fn); err == nil { |
if target, err := os.Readlink(fn); err == nil { |
||
Line 137: | Line 126: | ||
// open the target file for exclusive access. |
// open the target file for exclusive access. |
||
if tf, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil { |
if tf, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil { |
||
⚫ | |||
// oops, that didn't work. see if it's simply missing. |
|||
pErr, ok := err.(*os.PathError) |
|||
if ok && pErr.Err.Error() == "no such file or directory" { |
|||
// yep, that's all it was. return a new file. |
|||
return os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm) |
|||
⚫ | |||
return // no, it was some other error. fail. |
|||
} |
} |
||
// at this point an existing target file has been opened. |
|||
// deferred function closes target file if an error happens |
// deferred function closes target file if an error happens |
||
// during the backup operation. |
// during the backup operation. |