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.
28
"golang.org/x/text/runes"
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, 0o777) // 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, 0o777) // 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, 0o777)
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, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
163
result, _, _ := transform.String(t, string(s))
168
func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
169
return FileContainsBytes(a.Fs, filename, subslice)
172
// Check if a file contains a specified byte slice.
173
func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
174
f, err := fs.Open(filename)
180
return readerContainsAny(f, subslice), nil
183
func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
184
return FileContainsAnyBytes(a.Fs, filename, subslices)
187
// Check if a file contains any of the specified byte slices.
188
func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
189
f, err := fs.Open(filename)
195
return readerContainsAny(f, subslices...), nil
198
// readerContains reports whether any of the subslices is within r.
199
func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
200
if r == nil || len(subslices) == 0 {
206
for _, sl := range subslices {
207
if len(sl) > largestSlice {
208
largestSlice = len(sl)
212
if largestSlice == 0 {
216
bufflen := largestSlice * 4
217
halflen := bufflen / 2
218
buff := make([]byte, bufflen)
225
n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
228
// shift left to catch overlapping matches
229
copy(buff[:], buff[halflen:])
231
n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
235
for _, sl := range subslices {
236
if bytes.Contains(buff, sl) {
249
func (a Afero) DirExists(path string) (bool, error) {
250
return DirExists(a.Fs, path)
253
// DirExists checks if a path exists and is a directory.
254
func DirExists(fs Fs, path string) (bool, error) {
255
fi, err := fs.Stat(path)
256
if err == nil && fi.IsDir() {
259
if os.IsNotExist(err) {
265
func (a Afero) IsDir(path string) (bool, error) {
266
return IsDir(a.Fs, path)
269
// IsDir checks if a given path is a directory.
270
func IsDir(fs Fs, path string) (bool, error) {
271
fi, err := fs.Stat(path)
275
return fi.IsDir(), nil
278
func (a Afero) IsEmpty(path string) (bool, error) {
279
return IsEmpty(a.Fs, path)
282
// IsEmpty checks if a given file or directory is empty.
283
func IsEmpty(fs Fs, path string) (bool, error) {
284
if b, _ := Exists(fs, path); !b {
285
return false, fmt.Errorf("%q path does not exist", path)
287
fi, err := fs.Stat(path)
292
f, err := fs.Open(path)
297
list, err := f.Readdir(-1)
301
return len(list) == 0, nil
303
return fi.Size() == 0, nil
306
func (a Afero) Exists(path string) (bool, error) {
307
return Exists(a.Fs, path)
310
// Check if a file or directory exists.
311
func Exists(fs Fs, path string) (bool, error) {
312
_, err := fs.Stat(path)
316
if os.IsNotExist(err) {
322
func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
323
combinedPath := filepath.Join(basePathFs.path, relativePath)
324
if parent, ok := basePathFs.source.(*BasePathFs); ok {
325
return FullBaseFsPath(parent, combinedPath)