2017-06-17 01:17:22 +02:00
|
|
|
package helpers
|
2017-06-16 21:47:41 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
)
|
|
|
|
|
|
|
|
// VisitCreatedFile calls os.Create to create a file, and applies w to it.
|
|
|
|
func VisitCreatedFile(name string, w func(io.Writer) error) error {
|
|
|
|
f, err := os.Create(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
close := true
|
|
|
|
defer func() {
|
|
|
|
if close {
|
|
|
|
_ = f.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if err := w(f); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
close = false
|
|
|
|
return f.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopyFileContents copies the file contents from src to dst.
|
|
|
|
// It's not atomic and doesn't copy permissions or metadata.
|
|
|
|
func CopyFileContents(dst, src string, perm os.FileMode) error {
|
|
|
|
in, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer in.Close() // nolint: errcheck, gas
|
|
|
|
out, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(out, in); err != nil {
|
|
|
|
_ = os.Remove(dst) // nolint: gas
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return out.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadFileMagic returns the first four bytes of the file, with final '\r' replaced by '\n'.
|
2017-06-17 02:06:55 +02:00
|
|
|
func ReadFileMagic(name string) (data []byte, err error) {
|
|
|
|
f, err := os.Open(name)
|
2017-06-16 21:47:41 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close() // nolint: errcheck
|
|
|
|
data = make([]byte, 4)
|
|
|
|
_, err = f.Read(data)
|
|
|
|
if data[3] == '\r' {
|
|
|
|
data[3] = '\n'
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// PostfixWalk is like filepath.Walk, but visits each directory after visiting its children instead of before.
|
|
|
|
// It does not implement SkipDir.
|
2017-06-17 02:06:55 +02:00
|
|
|
func PostfixWalk(root string, walkFn filepath.WalkFunc) error {
|
|
|
|
if files, err := ioutil.ReadDir(root); err == nil {
|
2017-06-16 21:47:41 +02:00
|
|
|
for _, stat := range files {
|
|
|
|
if stat.IsDir() {
|
2017-06-17 02:06:55 +02:00
|
|
|
if err = PostfixWalk(filepath.Join(root, stat.Name()), walkFn); err != nil {
|
2017-06-16 21:47:41 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-17 02:06:55 +02:00
|
|
|
info, err := os.Stat(root)
|
|
|
|
return walkFn(root, info, err)
|
2017-06-16 21:47:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsNotEmpty returns a boolean indicating whether the error is known to report that a directory is not empty.
|
|
|
|
func IsNotEmpty(err error) bool {
|
|
|
|
if err, ok := err.(*os.PathError); ok {
|
|
|
|
return err.Err.(syscall.Errno) == syscall.ENOTEMPTY
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveEmptyDirectories recursively removes empty directories.
|
2017-06-17 02:06:55 +02:00
|
|
|
func RemoveEmptyDirectories(root string) error {
|
|
|
|
walkFn := func(name string, info os.FileInfo, err error) error {
|
2017-06-16 21:47:41 +02:00
|
|
|
switch {
|
|
|
|
case err != nil && os.IsNotExist(err):
|
|
|
|
// It's okay to call this on a directory that doesn't exist.
|
|
|
|
// It's also okay if another process removed a file during traversal.
|
|
|
|
return nil
|
|
|
|
case err != nil:
|
|
|
|
return err
|
|
|
|
case info.IsDir():
|
2017-06-17 02:06:55 +02:00
|
|
|
err := os.Remove(name)
|
2017-06-16 21:47:41 +02:00
|
|
|
switch {
|
|
|
|
case err == nil:
|
|
|
|
return nil
|
|
|
|
case os.IsNotExist(err):
|
|
|
|
return nil
|
|
|
|
case IsNotEmpty(err):
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-06-17 02:06:55 +02:00
|
|
|
return PostfixWalk(root, walkFn)
|
2017-06-16 21:47:41 +02:00
|
|
|
}
|