util.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright ©2015 Steve Francia <spf@spf13.com>
  2. // Portions Copyright ©2015 The Hugo Authors
  3. // Portions Copyright 2016-present Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. package afero
  17. import (
  18. "bytes"
  19. "fmt"
  20. "io"
  21. "log"
  22. "os"
  23. "path/filepath"
  24. "strings"
  25. "unicode"
  26. "golang.org/x/text/transform"
  27. "golang.org/x/text/unicode/norm"
  28. )
  29. // Filepath separator defined by os.Separator.
  30. const FilePathSeparator = string(filepath.Separator)
  31. // Takes a reader and a path and writes the content
  32. func (a Afero) WriteReader(path string, r io.Reader) (err error) {
  33. return WriteReader(a.Fs, path, r)
  34. }
  35. func WriteReader(fs Fs, path string, r io.Reader) (err error) {
  36. dir, _ := filepath.Split(path)
  37. ospath := filepath.FromSlash(dir)
  38. if ospath != "" {
  39. err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
  40. if err != nil {
  41. if err != os.ErrExist {
  42. log.Panicln(err)
  43. }
  44. }
  45. }
  46. file, err := fs.Create(path)
  47. if err != nil {
  48. return
  49. }
  50. defer file.Close()
  51. _, err = io.Copy(file, r)
  52. return
  53. }
  54. // Same as WriteReader but checks to see if file/directory already exists.
  55. func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
  56. return SafeWriteReader(a.Fs, path, r)
  57. }
  58. func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
  59. dir, _ := filepath.Split(path)
  60. ospath := filepath.FromSlash(dir)
  61. if ospath != "" {
  62. err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
  63. if err != nil {
  64. return
  65. }
  66. }
  67. exists, err := Exists(fs, path)
  68. if err != nil {
  69. return
  70. }
  71. if exists {
  72. return fmt.Errorf("%v already exists", path)
  73. }
  74. file, err := fs.Create(path)
  75. if err != nil {
  76. return
  77. }
  78. defer file.Close()
  79. _, err = io.Copy(file, r)
  80. return
  81. }
  82. func (a Afero) GetTempDir(subPath string) string {
  83. return GetTempDir(a.Fs, subPath)
  84. }
  85. // GetTempDir returns the default temp directory with trailing slash
  86. // if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
  87. func GetTempDir(fs Fs, subPath string) string {
  88. addSlash := func(p string) string {
  89. if FilePathSeparator != p[len(p)-1:] {
  90. p = p + FilePathSeparator
  91. }
  92. return p
  93. }
  94. dir := addSlash(os.TempDir())
  95. if subPath != "" {
  96. // preserve windows backslash :-(
  97. if FilePathSeparator == "\\" {
  98. subPath = strings.Replace(subPath, "\\", "____", -1)
  99. }
  100. dir = dir + UnicodeSanitize((subPath))
  101. if FilePathSeparator == "\\" {
  102. dir = strings.Replace(dir, "____", "\\", -1)
  103. }
  104. if exists, _ := Exists(fs, dir); exists {
  105. return addSlash(dir)
  106. }
  107. err := fs.MkdirAll(dir, 0777)
  108. if err != nil {
  109. panic(err)
  110. }
  111. dir = addSlash(dir)
  112. }
  113. return dir
  114. }
  115. // Rewrite string to remove non-standard path characters
  116. func UnicodeSanitize(s string) string {
  117. source := []rune(s)
  118. target := make([]rune, 0, len(source))
  119. for _, r := range source {
  120. if unicode.IsLetter(r) ||
  121. unicode.IsDigit(r) ||
  122. unicode.IsMark(r) ||
  123. r == '.' ||
  124. r == '/' ||
  125. r == '\\' ||
  126. r == '_' ||
  127. r == '-' ||
  128. r == '%' ||
  129. r == ' ' ||
  130. r == '#' {
  131. target = append(target, r)
  132. }
  133. }
  134. return string(target)
  135. }
  136. // Transform characters with accents into plan forms
  137. func NeuterAccents(s string) string {
  138. t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
  139. result, _, _ := transform.String(t, string(s))
  140. return result
  141. }
  142. func isMn(r rune) bool {
  143. return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
  144. }
  145. func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
  146. return FileContainsBytes(a.Fs, filename, subslice)
  147. }
  148. // Check if a file contains a specified byte slice.
  149. func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
  150. f, err := fs.Open(filename)
  151. if err != nil {
  152. return false, err
  153. }
  154. defer f.Close()
  155. return readerContainsAny(f, subslice), nil
  156. }
  157. func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
  158. return FileContainsAnyBytes(a.Fs, filename, subslices)
  159. }
  160. // Check if a file contains any of the specified byte slices.
  161. func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
  162. f, err := fs.Open(filename)
  163. if err != nil {
  164. return false, err
  165. }
  166. defer f.Close()
  167. return readerContainsAny(f, subslices...), nil
  168. }
  169. // readerContains reports whether any of the subslices is within r.
  170. func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
  171. if r == nil || len(subslices) == 0 {
  172. return false
  173. }
  174. largestSlice := 0
  175. for _, sl := range subslices {
  176. if len(sl) > largestSlice {
  177. largestSlice = len(sl)
  178. }
  179. }
  180. if largestSlice == 0 {
  181. return false
  182. }
  183. bufflen := largestSlice * 4
  184. halflen := bufflen / 2
  185. buff := make([]byte, bufflen)
  186. var err error
  187. var n, i int
  188. for {
  189. i++
  190. if i == 1 {
  191. n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
  192. } else {
  193. if i != 2 {
  194. // shift left to catch overlapping matches
  195. copy(buff[:], buff[halflen:])
  196. }
  197. n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
  198. }
  199. if n > 0 {
  200. for _, sl := range subslices {
  201. if bytes.Contains(buff, sl) {
  202. return true
  203. }
  204. }
  205. }
  206. if err != nil {
  207. break
  208. }
  209. }
  210. return false
  211. }
  212. func (a Afero) DirExists(path string) (bool, error) {
  213. return DirExists(a.Fs, path)
  214. }
  215. // DirExists checks if a path exists and is a directory.
  216. func DirExists(fs Fs, path string) (bool, error) {
  217. fi, err := fs.Stat(path)
  218. if err == nil && fi.IsDir() {
  219. return true, nil
  220. }
  221. if os.IsNotExist(err) {
  222. return false, nil
  223. }
  224. return false, err
  225. }
  226. func (a Afero) IsDir(path string) (bool, error) {
  227. return IsDir(a.Fs, path)
  228. }
  229. // IsDir checks if a given path is a directory.
  230. func IsDir(fs Fs, path string) (bool, error) {
  231. fi, err := fs.Stat(path)
  232. if err != nil {
  233. return false, err
  234. }
  235. return fi.IsDir(), nil
  236. }
  237. func (a Afero) IsEmpty(path string) (bool, error) {
  238. return IsEmpty(a.Fs, path)
  239. }
  240. // IsEmpty checks if a given file or directory is empty.
  241. func IsEmpty(fs Fs, path string) (bool, error) {
  242. if b, _ := Exists(fs, path); !b {
  243. return false, fmt.Errorf("%q path does not exist", path)
  244. }
  245. fi, err := fs.Stat(path)
  246. if err != nil {
  247. return false, err
  248. }
  249. if fi.IsDir() {
  250. f, err := fs.Open(path)
  251. defer f.Close()
  252. if err != nil {
  253. return false, err
  254. }
  255. list, err := f.Readdir(-1)
  256. return len(list) == 0, nil
  257. }
  258. return fi.Size() == 0, nil
  259. }
  260. func (a Afero) Exists(path string) (bool, error) {
  261. return Exists(a.Fs, path)
  262. }
  263. // Check if a file or directory exists.
  264. func Exists(fs Fs, path string) (bool, error) {
  265. _, err := fs.Stat(path)
  266. if err == nil {
  267. return true, nil
  268. }
  269. if os.IsNotExist(err) {
  270. return false, nil
  271. }
  272. return false, err
  273. }
  274. func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
  275. combinedPath := filepath.Join(basePathFs.path, relativePath)
  276. if parent, ok := basePathFs.source.(*BasePathFs); ok {
  277. return FullBaseFsPath(parent, combinedPath)
  278. }
  279. return combinedPath
  280. }