1+ package utils
2+
3+ import (
4+ "fmt"
5+ "io"
6+ "os"
7+ )
8+
9+ // Credit to https://stackoverflow.com/a/21067803
10+ // CopyFile copies a file from src to dst. If src and dst files exist, and are
11+ // the same, then return success. Otherise, attempt to create a hard link
12+ // between the two files. If that fail, copy the file contents from src to dst.
13+ func CopyFile (src , dst string ) (err error ) {
14+ sfi , err := os .Stat (src )
15+ if err != nil {
16+ return
17+ }
18+ if ! sfi .Mode ().IsRegular () {
19+ // cannot copy non-regular files (e.g., directories,
20+ // symlinks, devices, etc.)
21+ return fmt .Errorf ("CopyFile: non-regular source file %s (%q)" , sfi .Name (), sfi .Mode ().String ())
22+ }
23+ dfi , err := os .Stat (dst )
24+ if err != nil {
25+ if ! os .IsNotExist (err ) {
26+ return
27+ }
28+ } else {
29+ if ! (dfi .Mode ().IsRegular ()) {
30+ return fmt .Errorf ("CopyFile: non-regular destination file %s (%q)" , dfi .Name (), dfi .Mode ().String ())
31+ }
32+ }
33+
34+ err = copyFileContents (src , dst )
35+ return
36+ }
37+
38+ // copyFileContents copies the contents of the file named src to the file named
39+ // by dst. The file will be created if it does not already exist. If the
40+ // destination file exists, all it's contents will be replaced by the contents
41+ // of the source file.
42+ func copyFileContents (src , dst string ) (err error ) {
43+ in , err := os .Open (src )
44+ if err != nil {
45+ return
46+ }
47+ defer in .Close ()
48+ out , err := os .Create (dst )
49+ if err != nil {
50+ return
51+ }
52+ defer func () {
53+ cerr := out .Close ()
54+ if err == nil {
55+ err = cerr
56+ }
57+ }()
58+ if _ , err = io .Copy (out , in ); err != nil {
59+ return
60+ }
61+ err = out .Sync ()
62+ return
63+ }
0 commit comments