2017-06-10 21:38:09 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-06-12 23:12:40 +02:00
|
|
|
"io"
|
2017-06-10 21:38:09 +02:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-06-13 17:00:24 +02:00
|
|
|
"syscall"
|
2017-06-10 21:38:09 +02:00
|
|
|
)
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// VariableMap is a map of strings to interface values, for use in template processing.
|
|
|
|
type VariableMap map[string]interface{}
|
|
|
|
|
|
|
|
// copyFile copies from file src to dst. It's not atomic and doesn't copy permissions or metadata.
|
2017-06-12 23:12:40 +02:00
|
|
|
// This is sufficient for its use within this package.
|
|
|
|
func copyFile(dst, src string, perm os.FileMode) error {
|
|
|
|
inf, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-06-13 23:19:05 +02:00
|
|
|
defer inf.Close() // nolint: errcheck, gas
|
2017-06-12 23:12:40 +02:00
|
|
|
outf, err := os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if _, err = io.Copy(outf, inf); err != nil {
|
2017-06-13 23:19:05 +02:00
|
|
|
_ = os.Remove(dst) // nolint: gas
|
2017-06-12 23:12:40 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return outf.Close()
|
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// Bool returns m[k] if it's a bool; else defaultValue.
|
2017-06-14 23:41:15 +02:00
|
|
|
func (m VariableMap) Bool(k string, defaultValue bool) bool {
|
2017-06-11 01:32:39 +02:00
|
|
|
if val, found := m[k]; found {
|
|
|
|
if v, ok := val.(bool); ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// String returns m[k] if it's a string; else defaultValue.
|
2017-06-14 23:41:15 +02:00
|
|
|
func (m VariableMap) String(k string, defaultValue string) string {
|
2017-06-11 17:51:25 +02:00
|
|
|
if val, found := m[k]; found {
|
|
|
|
if v, ok := val.(string); ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return defaultValue
|
|
|
|
}
|
|
|
|
|
2017-06-15 15:07:06 +02:00
|
|
|
// HyphenateNonAlphaSequence replaces each sequence of non-alphanumerics by a single hyphen
|
|
|
|
func HyphenateNonAlphaSequence(s string) string {
|
|
|
|
return nonAlphanumericSequenceMatcher.ReplaceAllString(s, "-")
|
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// LeftPad left-pads s with spaces to n wide. It's an alternative to http://left-pad.io.
|
2017-06-13 23:19:05 +02:00
|
|
|
func LeftPad(s string, n int) string {
|
2017-06-11 22:00:03 +02:00
|
|
|
if n <= len(s) {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
ws := make([]byte, n-len(s))
|
2017-06-10 21:38:09 +02:00
|
|
|
for i := range ws {
|
|
|
|
ws[i] = ' '
|
|
|
|
}
|
|
|
|
return string(ws) + s
|
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
func mergeVariableMaps(ms ...VariableMap) VariableMap {
|
2017-06-14 23:41:15 +02:00
|
|
|
result := VariableMap{}
|
2017-06-15 13:19:49 +02:00
|
|
|
for _, m := range ms {
|
|
|
|
for k, v := range m {
|
|
|
|
result[k] = v
|
2017-06-10 23:51:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// PostfixWalk is like filepath.Walk, but visits each directory after visiting its children instead of before.
|
|
|
|
// It does not implement SkipDir.
|
2017-06-13 23:19:05 +02:00
|
|
|
func PostfixWalk(path string, walkFn filepath.WalkFunc) error {
|
2017-06-12 00:12:33 +02:00
|
|
|
if files, err := ioutil.ReadDir(path); err == nil {
|
|
|
|
for _, stat := range files {
|
|
|
|
if stat.IsDir() {
|
2017-06-13 23:19:05 +02:00
|
|
|
if err = PostfixWalk(filepath.Join(path, stat.Name()), walkFn); err != nil {
|
2017-06-12 00:12:33 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-06-10 21:38:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err := os.Stat(path)
|
2017-06-12 02:05:17 +02:00
|
|
|
return walkFn(path, info, err)
|
2017-06-10 21:38:09 +02:00
|
|
|
}
|
|
|
|
|
2017-06-15 13:19:49 +02:00
|
|
|
// IsNotEmpty returns a boolean indicating whether the error is known to report that a directory is not empty.
|
2017-06-13 17:00:24 +02:00
|
|
|
func IsNotEmpty(err error) bool {
|
|
|
|
if err, ok := err.(*os.PathError); ok {
|
|
|
|
return err.Err.(syscall.Errno) == syscall.ENOTEMPTY
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveEmptyDirectories recursively removes empty directories.
|
|
|
|
func RemoveEmptyDirectories(path string) error {
|
2017-06-10 21:38:09 +02:00
|
|
|
walkFn := func(path string, info os.FileInfo, err error) error {
|
2017-06-13 17:00:24 +02:00
|
|
|
switch {
|
|
|
|
case err != nil && os.IsNotExist(err):
|
2017-06-15 13:19:49 +02:00
|
|
|
// It's okay to call this on a directory that doesn't exist.
|
|
|
|
// It's also okay if another process removed a file during traversal.
|
2017-06-13 17:00:24 +02:00
|
|
|
return nil
|
|
|
|
case err != nil:
|
2017-06-10 21:38:09 +02:00
|
|
|
return err
|
2017-06-13 17:00:24 +02:00
|
|
|
case info.IsDir():
|
|
|
|
err := os.Remove(path)
|
|
|
|
switch {
|
|
|
|
case err == nil:
|
|
|
|
return nil
|
|
|
|
case os.IsNotExist(err):
|
|
|
|
return nil
|
|
|
|
case IsNotEmpty(err):
|
|
|
|
return nil
|
|
|
|
default:
|
2017-06-12 00:12:33 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-06-10 21:38:09 +02:00
|
|
|
}
|
2017-06-12 00:12:33 +02:00
|
|
|
return nil
|
2017-06-10 21:38:09 +02:00
|
|
|
}
|
2017-06-13 23:19:05 +02:00
|
|
|
return PostfixWalk(path, walkFn)
|
2017-06-10 21:38:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func stringArrayToMap(strings []string) map[string]bool {
|
|
|
|
stringMap := map[string]bool{}
|
|
|
|
for _, s := range strings {
|
|
|
|
stringMap[s] = true
|
|
|
|
}
|
|
|
|
return stringMap
|
|
|
|
}
|