2017-10-20 15:25:48 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/md5"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-10-21 02:26:04 +03:00
|
|
|
"math"
|
2017-10-20 15:25:48 +03:00
|
|
|
"os"
|
2017-10-21 02:26:04 +03:00
|
|
|
"strconv"
|
2017-10-21 23:34:59 +03:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2017-10-20 15:25:48 +03:00
|
|
|
)
|
|
|
|
|
2017-10-24 12:12:33 +03:00
|
|
|
// CatFiles copies data from n files to a single one and removes source files
|
|
|
|
// if Debug mode is set to false
|
|
|
|
func CatFiles(files []string, outfile string, remove bool) error {
|
2017-10-21 03:49:19 +03:00
|
|
|
finished, err := os.Create(outfile)
|
|
|
|
if err != nil {
|
2017-10-21 23:34:59 +03:00
|
|
|
return errors.Wrap(err, "CatFiles create: ")
|
2017-10-21 03:49:19 +03:00
|
|
|
}
|
2017-10-24 12:12:33 +03:00
|
|
|
defer finished.Close()
|
|
|
|
for _, file := range files {
|
|
|
|
fh, err := os.Open(file)
|
2017-10-21 03:49:19 +03:00
|
|
|
if err != nil {
|
2017-10-24 12:12:33 +03:00
|
|
|
return errors.Wrap(err, fmt.Sprintf("CatFiles open %v: ", file))
|
2017-10-21 03:49:19 +03:00
|
|
|
}
|
2017-10-24 12:12:33 +03:00
|
|
|
defer fh.Close()
|
2017-10-21 03:49:19 +03:00
|
|
|
_, err = io.Copy(finished, fh)
|
|
|
|
if err != nil {
|
2017-10-21 23:34:59 +03:00
|
|
|
return errors.Wrap(err, "CatFiles copy: ")
|
2017-10-21 03:49:19 +03:00
|
|
|
}
|
2017-10-24 12:12:33 +03:00
|
|
|
if remove {
|
|
|
|
os.Remove(file)
|
2017-10-21 03:49:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-24 12:56:50 +03:00
|
|
|
// SplitFile creates a bunch of smaller files with the data from source splited into them
|
2017-10-21 02:26:04 +03:00
|
|
|
func SplitFile(fileName string, numPieces int) (err error) {
|
|
|
|
file, err := os.Open(fileName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
fi, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesPerPiece := int(math.Ceil(float64(fi.Size()) / float64(numPieces)))
|
|
|
|
bytesRead := 0
|
|
|
|
i := 0
|
|
|
|
out, err := os.Create(fileName + "." + strconv.Itoa(i))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
buf := make([]byte, 4096)
|
|
|
|
if bytesPerPiece < 4096/numPieces {
|
|
|
|
buf = make([]byte, bytesPerPiece)
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
n, err := file.Read(buf)
|
|
|
|
out.Write(buf[:n])
|
2017-10-24 12:56:50 +03:00
|
|
|
// If written bytes count is smaller than lenght of buffer
|
|
|
|
// then we don't create one more empty file
|
|
|
|
if err == io.EOF || n < len(buf) {
|
2017-10-21 02:26:04 +03:00
|
|
|
break
|
|
|
|
}
|
2017-10-24 12:56:50 +03:00
|
|
|
bytesRead += n
|
|
|
|
|
2017-10-21 02:26:04 +03:00
|
|
|
if bytesRead >= bytesPerPiece {
|
|
|
|
// Close file and open a new one
|
|
|
|
out.Close()
|
|
|
|
i++
|
|
|
|
out, err = os.Create(fileName + "." + strconv.Itoa(i))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
bytesRead = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-20 15:25:48 +03:00
|
|
|
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
|
|
|
// the same, then return success. Otherise, attempt to create a hard link
|
|
|
|
// between the two files. If that fail, copy the file contents from src to dst.
|
|
|
|
func CopyFile(src, dst string) (err error) {
|
|
|
|
sfi, err := os.Stat(src)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !sfi.Mode().IsRegular() {
|
|
|
|
// cannot copy non-regular files (e.g., directories,
|
|
|
|
// symlinks, devices, etc.)
|
|
|
|
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
|
|
|
|
}
|
|
|
|
dfi, err := os.Stat(dst)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if !(dfi.Mode().IsRegular()) {
|
|
|
|
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
|
|
|
|
}
|
|
|
|
if os.SameFile(sfi, dfi) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err = os.Link(src, dst); err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = copyFileContents(src, dst)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// copyFileContents copies the contents of the file named src to the file named
|
|
|
|
// by dst. The file will be created if it does not already exist. If the
|
|
|
|
// destination file exists, all it's contents will be replaced by the contents
|
|
|
|
// of the source file.
|
|
|
|
func copyFileContents(src, dst string) (err error) {
|
|
|
|
in, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
out, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
cerr := out.Close()
|
|
|
|
if err == nil {
|
|
|
|
err = cerr
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if _, err = io.Copy(out, in); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = out.Sync()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// HashFile does a md5 hash on the file
|
|
|
|
// from https://golang.org/pkg/crypto/md5/#example_New_file
|
|
|
|
func HashFile(filename string) (hash string, err error) {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
h := md5.New()
|
|
|
|
if _, err = io.Copy(h, f); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
hash = fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// FileSize returns the size of a file
|
|
|
|
func FileSize(filename string) (int, error) {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
fi, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return -1, err
|
|
|
|
}
|
|
|
|
size := int(fi.Size())
|
|
|
|
return size, nil
|
|
|
|
}
|