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>
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
29 "golang.org/x/text/transform"
30 "golang.org/x/text/unicode/norm"
33 // Filepath separator defined by os.Separator.
34 const FilePathSeparator = string(filepath.Separator)
36 // Takes a reader and a path and writes the content
37 func (a Afero) WriteReader(path string, r io.Reader) (err error) {
38 return WriteReader(a.Fs, path, r)
41 func WriteReader(fs Fs, path string, r io.Reader) (err error) {
42 dir, _ := filepath.Split(path)
43 ospath := filepath.FromSlash(dir)
46 err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
48 if err != os.ErrExist {
54 file, err := fs.Create(path)
60 _, err = io.Copy(file, r)
64 // Same as WriteReader but checks to see if file/directory already exists.
65 func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
66 return SafeWriteReader(a.Fs, path, r)
69 func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
70 dir, _ := filepath.Split(path)
71 ospath := filepath.FromSlash(dir)
74 err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
80 exists, err := Exists(fs, path)
85 return fmt.Errorf("%v already exists", path)
88 file, err := fs.Create(path)
94 _, err = io.Copy(file, r)
98 func (a Afero) GetTempDir(subPath string) string {
99 return GetTempDir(a.Fs, subPath)
102 // GetTempDir returns the default temp directory with trailing slash
103 // if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
104 func GetTempDir(fs Fs, subPath string) string {
105 addSlash := func(p string) string {
106 if FilePathSeparator != p[len(p)-1:] {
107 p = p + FilePathSeparator
111 dir := addSlash(os.TempDir())
114 // preserve windows backslash :-(
115 if FilePathSeparator == "\\" {
116 subPath = strings.Replace(subPath, "\\", "____", -1)
118 dir = dir + UnicodeSanitize((subPath))
119 if FilePathSeparator == "\\" {
120 dir = strings.Replace(dir, "____", "\\", -1)
123 if exists, _ := Exists(fs, dir); exists {
127 err := fs.MkdirAll(dir, 0777)
136 // Rewrite string to remove non-standard path characters
137 func UnicodeSanitize(s string) string {
139 target := make([]rune, 0, len(source))
141 for _, r := range source {
142 if unicode.IsLetter(r) ||
143 unicode.IsDigit(r) ||
153 target = append(target, r)
157 return string(target)
160 // Transform characters with accents into plain forms.
161 func NeuterAccents(s string) string {
162 t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
163 result, _, _ := transform.String(t, string(s))
168 func isMn(r rune) bool {
169 return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
172 func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
173 return FileContainsBytes(a.Fs, filename, subslice)
176 // Check if a file contains a specified byte slice.
177 func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
178 f, err := fs.Open(filename)
184 return readerContainsAny(f, subslice), nil
187 func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
188 return FileContainsAnyBytes(a.Fs, filename, subslices)
191 // Check if a file contains any of the specified byte slices.
192 func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
193 f, err := fs.Open(filename)
199 return readerContainsAny(f, subslices...), nil
202 // readerContains reports whether any of the subslices is within r.
203 func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
205 if r == nil || len(subslices) == 0 {
211 for _, sl := range subslices {
212 if len(sl) > largestSlice {
213 largestSlice = len(sl)
217 if largestSlice == 0 {
221 bufflen := largestSlice * 4
222 halflen := bufflen / 2
223 buff := make([]byte, bufflen)
230 n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
233 // shift left to catch overlapping matches
234 copy(buff[:], buff[halflen:])
236 n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
240 for _, sl := range subslices {
241 if bytes.Contains(buff, sl) {
254 func (a Afero) DirExists(path string) (bool, error) {
255 return DirExists(a.Fs, path)
258 // DirExists checks if a path exists and is a directory.
259 func DirExists(fs Fs, path string) (bool, error) {
260 fi, err := fs.Stat(path)
261 if err == nil && fi.IsDir() {
264 if os.IsNotExist(err) {
270 func (a Afero) IsDir(path string) (bool, error) {
271 return IsDir(a.Fs, path)
274 // IsDir checks if a given path is a directory.
275 func IsDir(fs Fs, path string) (bool, error) {
276 fi, err := fs.Stat(path)
280 return fi.IsDir(), nil
283 func (a Afero) IsEmpty(path string) (bool, error) {
284 return IsEmpty(a.Fs, path)
287 // IsEmpty checks if a given file or directory is empty.
288 func IsEmpty(fs Fs, path string) (bool, error) {
289 if b, _ := Exists(fs, path); !b {
290 return false, fmt.Errorf("%q path does not exist", path)
292 fi, err := fs.Stat(path)
297 f, err := fs.Open(path)
302 list, err := f.Readdir(-1)
303 return len(list) == 0, nil
305 return fi.Size() == 0, nil
308 func (a Afero) Exists(path string) (bool, error) {
309 return Exists(a.Fs, path)
312 // Check if a file or directory exists.
313 func Exists(fs Fs, path string) (bool, error) {
314 _, err := fs.Stat(path)
318 if os.IsNotExist(err) {
324 func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
325 combinedPath := filepath.Join(basePathFs.path, relativePath)
326 if parent, ok := basePathFs.source.(*BasePathFs); ok {
327 return FullBaseFsPath(parent, combinedPath)