| // package fileutils provides utililty methods to copy and move files and directories. | 
 | package fileutils | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"io/ioutil" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"runtime" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | // https://golang.org/cmd/go/#hdr-File_types | 
 | var goFileTypes = []string{ | 
 | 	".go", | 
 | 	".c", ".h", | 
 | 	".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx", | 
 | 	".m", | 
 | 	".s", ".S", | 
 | 	".swig", ".swigcxx", | 
 | 	".syso", | 
 | } | 
 |  | 
 | var licenseFiles = []string{ | 
 | 	"LICENSE", "LICENCE", "UNLICENSE", "COPYING", "COPYRIGHT", | 
 | } | 
 |  | 
 | func ShouldSkip(path string, info os.FileInfo, tests, all bool) bool { | 
 | 	name := filepath.Base(path) | 
 |  | 
 | 	relevantFile := false | 
 | 	for _, ext := range goFileTypes { | 
 | 		if strings.HasSuffix(name, ext) { | 
 | 			relevantFile = true | 
 | 			break | 
 | 		} | 
 | 	} | 
 |  | 
 | 	testdata := false | 
 | 	for _, n := range strings.Split(filepath.Dir(path), string(filepath.Separator)) { | 
 | 		if n == "testdata" || n == "_testdata" { | 
 | 			testdata = true | 
 | 		} | 
 | 	} | 
 |  | 
 | 	skip := false | 
 | 	switch { | 
 | 	case all && !(name == ".git" && info.IsDir()) && name != ".bzr" && name != ".hg": | 
 | 		skip = false | 
 |  | 
 | 	// Include all files in a testdata folder | 
 | 	case tests && testdata: | 
 | 		skip = false | 
 |  | 
 | 	// https://golang.org/cmd/go/#hdr-Description_of_package_lists | 
 | 	case strings.HasPrefix(name, "."): | 
 | 		skip = true | 
 | 	case strings.HasPrefix(name, "_") && name != "_testdata": | 
 | 		skip = true | 
 |  | 
 | 	case !tests && name == "_testdata" && info.IsDir(): | 
 | 		skip = true | 
 | 	case !tests && name == "testdata" && info.IsDir(): | 
 | 		skip = true | 
 | 	case !tests && strings.HasSuffix(name, "_test.go") && !info.IsDir(): | 
 | 		skip = true | 
 |  | 
 | 	case !relevantFile && !info.IsDir(): | 
 | 		skip = true | 
 | 	} | 
 |  | 
 | 	return skip | 
 | } | 
 |  | 
 | // Copypath copies the contents of src to dst, excluding any file that is not | 
 | // relevant to the Go compiler. | 
 | func Copypath(dst string, src string, tests, all bool) error { | 
 | 	err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { | 
 | 		if err != nil { | 
 | 			return err | 
 | 		} | 
 |  | 
 | 		skip := ShouldSkip(path, info, tests, all) | 
 |  | 
 | 		if skip { | 
 | 			if info.IsDir() { | 
 | 				return filepath.SkipDir | 
 | 			} | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		if info.IsDir() { | 
 | 			return nil | 
 | 		} | 
 |  | 
 | 		dst := filepath.Join(dst, path[len(src):]) | 
 |  | 
 | 		if info.Mode()&os.ModeSymlink != 0 { | 
 | 			return Copylink(dst, path) | 
 | 		} | 
 |  | 
 | 		return Copyfile(dst, path) | 
 | 	}) | 
 | 	if err != nil { | 
 | 		// if there was an error during copying, remove the partial copy. | 
 | 		RemoveAll(dst) | 
 | 	} | 
 | 	return err | 
 | } | 
 |  | 
 | func Copyfile(dst, src string) error { | 
 | 	err := mkdir(filepath.Dir(dst)) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("copyfile: mkdirall: %v", err) | 
 | 	} | 
 | 	r, err := os.Open(src) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("copyfile: open(%q): %v", src, err) | 
 | 	} | 
 | 	defer r.Close() | 
 | 	w, err := os.Create(dst) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("copyfile: create(%q): %v", dst, err) | 
 | 	} | 
 | 	defer w.Close() | 
 | 	_, err = io.Copy(w, r) | 
 | 	return err | 
 | } | 
 |  | 
 | func Copylink(dst, src string) error { | 
 | 	target, err := os.Readlink(src) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("copylink: readlink: %v", err) | 
 | 	} | 
 | 	if err := mkdir(filepath.Dir(dst)); err != nil { | 
 | 		return fmt.Errorf("copylink: mkdirall: %v", err) | 
 | 	} | 
 | 	if err := os.Symlink(target, dst); err != nil { | 
 | 		return fmt.Errorf("copylink: symlink: %v", err) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // RemoveAll removes path and any children it contains. Unlike os.RemoveAll it | 
 | // deletes read only files on Windows. | 
 | func RemoveAll(path string) error { | 
 | 	if runtime.GOOS == "windows" { | 
 | 		// Simple case: if Remove works, we're done. | 
 | 		err := os.Remove(path) | 
 | 		if err == nil || os.IsNotExist(err) { | 
 | 			return nil | 
 | 		} | 
 | 		// make sure all files are writable so we can delete them | 
 | 		filepath.Walk(path, func(path string, info os.FileInfo, err error) error { | 
 | 			if err != nil { | 
 | 				// walk gave us some error, give it back. | 
 | 				return err | 
 | 			} | 
 | 			mode := info.Mode() | 
 | 			if mode|0200 == mode { | 
 | 				return nil | 
 | 			} | 
 | 			return os.Chmod(path, mode|0200) | 
 | 		}) | 
 | 	} | 
 | 	return os.RemoveAll(path) | 
 | } | 
 |  | 
 | // CopyLicense copies the license file from folder src to folder dst. | 
 | func CopyLicense(dst, src string) error { | 
 | 	files, err := ioutil.ReadDir(src) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 | 	for _, f := range files { | 
 | 		if f.IsDir() { | 
 | 			continue | 
 | 		} | 
 | 		for _, candidate := range licenseFiles { | 
 | 			if strings.ToLower(candidate) == strings.TrimSuffix( | 
 | 				strings.TrimSuffix(strings.ToLower(f.Name()), ".md"), ".txt") { | 
 | 				if err := Copyfile(filepath.Join(dst, f.Name()), | 
 | 					filepath.Join(src, f.Name())); err != nil { | 
 | 					return err | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func mkdir(path string) error { | 
 | 	return os.MkdirAll(path, 0755) | 
 | } |