1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-12-02 14:57:50 +01:00
gojekyll/utils/ioutil.go

125 lines
3.0 KiB
Go
Raw Permalink Normal View History

2017-07-09 22:17:20 +02:00
package utils
import (
"errors"
"io"
"os"
"path/filepath"
"syscall"
)
// 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 {
2017-06-18 22:54:36 +02:00
// nolint: gas
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
return err
}
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-30 18:20:57 +02:00
func ReadFileMagic(filename string) ([]byte, error) {
2017-06-23 04:03:18 +02:00
f, err := os.Open(filename)
if err != nil {
2017-06-23 04:03:18 +02:00
return nil, err
}
defer f.Close() // nolint: errcheck
2017-06-23 04:03:18 +02:00
b := make([]byte, 4)
_, err = f.Read(b)
if err != nil && err != io.EOF {
return nil, err
2017-06-17 19:08:49 +02:00
}
// Normalize windows linefeeds. This function is used to
// recognize frontmatter, so we only need to look at the fourth byte.
2017-06-23 04:03:18 +02:00
if b[3] == '\r' {
b[3] = '\n'
}
2017-06-23 04:03:18 +02:00
return b, nil
}
// 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 {
2023-03-03 21:58:41 +01:00
if files, err := os.ReadDir(root); err == nil {
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 {
return err
}
}
}
}
2017-06-17 02:06:55 +02:00
info, err := os.Stat(root)
return walkFn(root, info, err)
}
// 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
}
// NewPathError returns an os.PathError that formats as the given text.
func NewPathError(op, name, text string) *os.PathError {
return &os.PathError{Op: op, Path: name, Err: errors.New(text)}
}
// 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 {
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)
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-21 15:11:09 +02:00
// VisitCreatedFile calls os.Create to create a file, and applies w to it.
2017-07-10 20:14:42 +02:00
func VisitCreatedFile(filename string, w func(io.Writer) error) (err error) {
2017-06-23 04:03:18 +02:00
f, err := os.Create(filename)
2017-06-21 15:11:09 +02:00
if err != nil {
2017-07-10 20:14:42 +02:00
return
2017-06-21 15:11:09 +02:00
}
defer func() {
2017-07-10 20:14:42 +02:00
if e := f.Close(); e != nil && err == nil {
err = e
2017-06-21 15:11:09 +02:00
}
}()
2017-07-10 20:14:42 +02:00
err = w(f)
return
2017-06-21 15:11:09 +02:00
}