Make a backup file: Difference between revisions

Go solution
(category file handling)
(Go solution)
Line 35:
(with-open-file (out file :direction :output)
(print data out))))</lang>
 
=={{header|Go}}==
===Rename===
This is the technique of the task description.
<lang go>package main
 
import (
"fmt"
"io/ioutil"
"os"
)
 
func main() {
fn := "myth"
bx := ".backup"
// see if it's a link
if tf, err := os.Readlink(fn); err == nil {
fn = tf
}
// default permissions, used if file doesn't exist
perm := os.FileMode(0666)
// now see if file exists.
if fi, err := os.Stat(fn); err == nil {
// it does. use existing permissions instead of default
// and rename the file, (losing any existing backup.)
perm = fi.Mode().Perm()
if err := os.Rename(fn, fn+bx); err != nil {
// if rename of known existing file fails, we don't continue.
fmt.Println(err)
return
}
} else {
// 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" {
fmt.Println(err)
return
}
}
// create new file
err := ioutil.WriteFile(fn, []byte("you too!\n"), perm)
if err != nil {
fmt.Println(err)
}
}</lang>
===Copy===
Alternative technique copies an existing file to make the backup copy, then updates the origial file. In an attempt to keep operations atomic, the original file is opened as the first operation and is not closed until all operations are complete. In an attempt to avoid data loss, the original file is not modified until the backup file is closed.
<lang go>package main
 
import (
"fmt"
"io"
"os"
)
 
func main() {
err := updateWithBackup("myth", ".backup", 0666,
func(f *os.File) (err error) {
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
return
}
if err = f.Truncate(0); err != nil {
return
}
_, err = f.WriteString("you too!\n")
return
})
if err != nil {
fmt.Println(err)
}
}
 
// updateWithBackup opens fn, creates a backup copy named fn+bx, then passes
// the still-open original file to the supplied function up. up should
// 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.
func updateWithBackup(fn, bx string, perm os.FileMode,
up func(*os.File) error) (err error) {
var f *os.File
if f, err = openWithBackup(fn, bx, perm); err != nil {
return
}
err = up(f)
if cErr := f.Close(); err == nil {
err = cErr
}
return
}
 
// openWithBackup opens fn, creates a backup copy, and returns fn still open.
// If fn is a symlink, the destination file is opened instead. The name of
// the backup file will be fn+bx, or if fn was a symlink, the name of the
// destination file + bx. Any error encountered is returned. tf will be
// an open file if and only if err == nil.
func openWithBackup(fn, bx string, perm os.FileMode) (tf *os.File, err error) {
// follow symlink.
if target, err := os.Readlink(fn); err == nil {
fn = target
}
// open the target file for exclusive access.
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
// during the backup operation.
defer func() {
if err != nil {
tf.Close()
}
}()
// stat to preserve permissions.
var fi os.FileInfo
if fi, err = tf.Stat(); err != nil {
return
}
// create backup file, silently droping any existing backup.
var bf *os.File
if bf, err = os.OpenFile(fn+bx, os.O_RDWR|os.O_CREATE|os.O_TRUNC,
fi.Mode().Perm()); err != nil {
return
}
// copy contents. hold on to any error.
_, err = io.Copy(bf, tf)
// do your best to close backup file whether copy worked or not.
if cErr := bf.Close(); err == nil {
err = cErr // return close error if there has been no other error.
}
// backup complete (as long as err == nil)
return
}</lang>
 
=={{header|Java}}==
1,707

edits