util.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. "os"
  22. "path/filepath"
  23. "strings"
  24. "unicode"
  25. "golang.org/x/text/runes"
  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. return 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 plain forms.
  137. func NeuterAccents(s string) string {
  138. t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
  139. result, _, _ := transform.String(t, string(s))
  140. return result
  141. }
  142. func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
  143. return FileContainsBytes(a.Fs, filename, subslice)
  144. }
  145. // Check if a file contains a specified byte slice.
  146. func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
  147. f, err := fs.Open(filename)
  148. if err != nil {
  149. return false, err
  150. }
  151. defer f.Close()
  152. return readerContainsAny(f, subslice), nil
  153. }
  154. func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
  155. return FileContainsAnyBytes(a.Fs, filename, subslices)
  156. }
  157. // Check if a file contains any of the specified byte slices.
  158. func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
  159. f, err := fs.Open(filename)
  160. if err != nil {
  161. return false, err
  162. }
  163. defer f.Close()
  164. return readerContainsAny(f, subslices...), nil
  165. }
  166. // readerContains reports whether any of the subslices is within r.
  167. func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
  168. if r == nil || len(subslices) == 0 {
  169. return false
  170. }
  171. largestSlice := 0
  172. for _, sl := range subslices {
  173. if len(sl) > largestSlice {
  174. largestSlice = len(sl)
  175. }
  176. }
  177. if largestSlice == 0 {
  178. return false
  179. }
  180. bufflen := largestSlice * 4
  181. halflen := bufflen / 2
  182. buff := make([]byte, bufflen)
  183. var err error
  184. var n, i int
  185. for {
  186. i++
  187. if i == 1 {
  188. n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
  189. } else {
  190. if i != 2 {
  191. // shift left to catch overlapping matches
  192. copy(buff[:], buff[halflen:])
  193. }
  194. n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
  195. }
  196. if n > 0 {
  197. for _, sl := range subslices {
  198. if bytes.Contains(buff, sl) {
  199. return true
  200. }
  201. }
  202. }
  203. if err != nil {
  204. break
  205. }
  206. }
  207. return false
  208. }
  209. func (a Afero) DirExists(path string) (bool, error) {
  210. return DirExists(a.Fs, path)
  211. }
  212. // DirExists checks if a path exists and is a directory.
  213. func DirExists(fs Fs, path string) (bool, error) {
  214. fi, err := fs.Stat(path)
  215. if err == nil && fi.IsDir() {
  216. return true, nil
  217. }
  218. if os.IsNotExist(err) {
  219. return false, nil
  220. }
  221. return false, err
  222. }
  223. func (a Afero) IsDir(path string) (bool, error) {
  224. return IsDir(a.Fs, path)
  225. }
  226. // IsDir checks if a given path is a directory.
  227. func IsDir(fs Fs, path string) (bool, error) {
  228. fi, err := fs.Stat(path)
  229. if err != nil {
  230. return false, err
  231. }
  232. return fi.IsDir(), nil
  233. }
  234. func (a Afero) IsEmpty(path string) (bool, error) {
  235. return IsEmpty(a.Fs, path)
  236. }
  237. // IsEmpty checks if a given file or directory is empty.
  238. func IsEmpty(fs Fs, path string) (bool, error) {
  239. if b, _ := Exists(fs, path); !b {
  240. return false, fmt.Errorf("%q path does not exist", path)
  241. }
  242. fi, err := fs.Stat(path)
  243. if err != nil {
  244. return false, err
  245. }
  246. if fi.IsDir() {
  247. f, err := fs.Open(path)
  248. if err != nil {
  249. return false, err
  250. }
  251. defer f.Close()
  252. list, err := f.Readdir(-1)
  253. if err != nil {
  254. return false, err
  255. }
  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. }