pagr

A 'static site generator', built using dati.
Log | Files | Refs | Atom

copy.go (2587B)


      1 package main
      2 
      3 /*
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 
      7 // copyf copies data from file at `src` to the file at `dst`
      8 // in 4kb chunks`.
      9 // The file @ `src` *must* be **less than 2GB**. This is a
     10 // limitation of libc (staying portable).
     11 // Any existing file @ `dst` will be overwritten.
     12 // returns EXIT_SUCCESS or EXIT_FAILURE.
     13 int copyf(const char *src, const char *dst)
     14 {
     15 	int ret = EXIT_FAILURE;
     16 
     17 	FILE *srcf = fopen(src, "rb"), *dstf = fopen(dst, "wb");
     18 	if (!src || !dst) goto ABORT;
     19 
     20 	fseek(srcf, 0, SEEK_END);
     21 	size_t siz = ftell(srcf); // 2GB limit, returns long int
     22 	rewind(srcf);
     23 
     24 	char buf[4096]; // 4kb chunks
     25 	size_t r, w, total = 0;
     26 	while ((r = fread(buf, sizeof(char), sizeof(buf), srcf)) > 0) {
     27 		if (ferror(srcf)) goto ABORT;
     28 		w = fwrite(buf, sizeof(char), r, dstf);
     29 		if (ferror(dstf)) goto ABORT;
     30 		total += w;
     31 	}
     32 
     33 	if (total == siz) ret = EXIT_SUCCESS;
     34 
     35 ABORT:
     36 	if (srcf) fclose(srcf);
     37 	if (dstf) fclose(dstf);
     38 	return ret;
     39 }
     40 */
     41 import "C"
     42 import (
     43 	"fmt"
     44 	"io"
     45 	"os"
     46 	"path/filepath"
     47 	"unsafe"
     48 )
     49 
     50 func copyFile(src, dst string) (err error) {
     51 	var srcf, dstf *os.File
     52 	if srcf, err = os.Open(src); err != nil {
     53 		return err
     54 	}
     55 	defer srcf.Close()
     56 	if dstf, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0644); err != nil {
     57 		return err
     58 	}
     59 	defer dstf.Close()
     60 
     61 	if _, err = io.Copy(dstf, srcf); err != nil {
     62 		return err
     63 	}
     64 	return dstf.Sync()
     65 }
     66 
     67 func CopyFile(src, dst string) (err error) {
     68 	var srcfi, dstfi os.FileInfo
     69 
     70 	if srcfi, err = os.Stat(src); err != nil {
     71 		return err
     72 	} else if !srcfi.Mode().IsRegular() {
     73 		return fmt.Errorf("cannot copy from non-regular source file %s (%q)",
     74 			srcfi.Name(), srcfi.Mode().String())
     75 	}
     76 
     77 	if dstfi, err = os.Stat(dst); err != nil && !os.IsNotExist(err) {
     78 		return err
     79 	} else if dstfi != nil && !dstfi.Mode().IsRegular() {
     80 		return fmt.Errorf("cannot copy to non-regular destination file %s (%q)",
     81 			dstfi.Name(), dstfi.Mode().String())
     82 	} else if os.SameFile(srcfi, dstfi) {
     83 		return nil
     84 	}
     85 
     86 	if err = os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
     87 		return err
     88 	}
     89 
     90 	// only copy if dst doesnt exist or has different name/size/modtime
     91 	// and has a size less than 2GB (libc limit)
     92 	if dstfi == nil || srcfi.Name() != dstfi.Name() ||
     93 		srcfi.Size() != dstfi.Size() || srcfi.ModTime() != dstfi.ModTime() {
     94 		if srcfi.Size() > 2000000000 {
     95 			copyFile(src, dst)
     96 		} else {
     97 			cSrc := C.CString(src)
     98 			cDst := C.CString(dst)
     99 			if uint32(C.copyf(cSrc, cDst)) != 0 {
    100 				err = fmt.Errorf("copyf failed ('%s' -> '%s')", src, dst)
    101 			}
    102 			C.free(unsafe.Pointer(cSrc))
    103 			C.free(unsafe.Pointer(cDst))
    104 		}
    105 	}
    106 
    107 	return
    108 }