5d6b8141ec
As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code.
136 lines
2.8 KiB
Go
136 lines
2.8 KiB
Go
// Package bilib provides common stuff for bisync and bisync_test
|
|
// Here it's got local file/directory helpers (nice to have in lib/file)
|
|
package bilib
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
)
|
|
|
|
// PermSecure is a Unix permission for a file accessible only by its owner
|
|
const PermSecure = 0600
|
|
|
|
var (
|
|
regexLocalPath = regexp.MustCompile(`^[./\\]`)
|
|
regexWindowsPath = regexp.MustCompile(`^[a-zA-Z]:`)
|
|
regexRemotePath = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*:`)
|
|
)
|
|
|
|
// IsLocalPath returns true if its argument is a non-remote path.
|
|
// Empty string or a relative path will be considered local.
|
|
// Note: `c:dir` will be considered local on Windows but remote on Linux.
|
|
func IsLocalPath(path string) bool {
|
|
if path == "" || regexLocalPath.MatchString(path) {
|
|
return true
|
|
}
|
|
if runtime.GOOS == "windows" && regexWindowsPath.MatchString(path) {
|
|
return true
|
|
}
|
|
return !regexRemotePath.MatchString(path)
|
|
}
|
|
|
|
// FileExists returns true if the local file exists
|
|
func FileExists(file string) bool {
|
|
_, err := os.Stat(file)
|
|
return !os.IsNotExist(err)
|
|
}
|
|
|
|
// CopyFileIfExists is like CopyFile but does to fail if source does not exist
|
|
func CopyFileIfExists(srcFile, dstFile string) error {
|
|
if !FileExists(srcFile) {
|
|
return nil
|
|
}
|
|
return CopyFile(srcFile, dstFile)
|
|
}
|
|
|
|
// CopyFile copies a local file
|
|
func CopyFile(src, dst string) (err error) {
|
|
var (
|
|
rd io.ReadCloser
|
|
wr io.WriteCloser
|
|
info os.FileInfo
|
|
)
|
|
if info, err = os.Stat(src); err != nil {
|
|
return
|
|
}
|
|
if rd, err = os.Open(src); err != nil {
|
|
return
|
|
}
|
|
defer func() {
|
|
_ = rd.Close()
|
|
}()
|
|
if wr, err = os.Create(dst); err != nil {
|
|
return
|
|
}
|
|
_, err = io.Copy(wr, rd)
|
|
if e := wr.Close(); err == nil {
|
|
err = e
|
|
}
|
|
if e := os.Chmod(dst, info.Mode()); err == nil {
|
|
err = e
|
|
}
|
|
if e := os.Chtimes(dst, info.ModTime(), info.ModTime()); err == nil {
|
|
err = e
|
|
}
|
|
return
|
|
}
|
|
|
|
// CopyDir copies a local directory
|
|
func CopyDir(src string, dst string) (err error) {
|
|
src = filepath.Clean(src)
|
|
dst = filepath.Clean(dst)
|
|
|
|
si, err := os.Stat(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !si.IsDir() {
|
|
return fmt.Errorf("source is not a directory")
|
|
}
|
|
|
|
_, err = os.Stat(dst)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return
|
|
}
|
|
if err == nil {
|
|
return fmt.Errorf("destination already exists")
|
|
}
|
|
|
|
err = os.MkdirAll(dst, si.Mode())
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
entries, err := os.ReadDir(src)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
srcPath := filepath.Join(src, entry.Name())
|
|
dstPath := filepath.Join(dst, entry.Name())
|
|
|
|
if entry.IsDir() {
|
|
err = CopyDir(srcPath, dstPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
// Skip symlinks.
|
|
if entry.Type()&os.ModeSymlink != 0 {
|
|
continue
|
|
}
|
|
|
|
err = CopyFile(srcPath, dstPath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|