util.go - afero - [fork] go afero port for 9front
(HTM) git clone https://git.drkhsh.at/afero.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
util.go (7335B)
---
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
17 package afero
18
19 import (
20 "bytes"
21 "fmt"
22 "io"
23 "os"
24 "path/filepath"
25 "strings"
26 "unicode"
27
28 "golang.org/x/text/runes"
29 "golang.org/x/text/transform"
30 "golang.org/x/text/unicode/norm"
31 )
32
33 // Filepath separator defined by os.Separator.
34 const FilePathSeparator = string(filepath.Separator)
35
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)
39 }
40
41 func WriteReader(fs Fs, path string, r io.Reader) (err error) {
42 dir, _ := filepath.Split(path)
43 ospath := filepath.FromSlash(dir)
44
45 if ospath != "" {
46 err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r
47 if err != nil {
48 if err != os.ErrExist {
49 return err
50 }
51 }
52 }
53
54 file, err := fs.Create(path)
55 if err != nil {
56 return
57 }
58 defer file.Close()
59
60 _, err = io.Copy(file, r)
61 return
62 }
63
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)
67 }
68
69 func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
70 dir, _ := filepath.Split(path)
71 ospath := filepath.FromSlash(dir)
72
73 if ospath != "" {
74 err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r
75 if err != nil {
76 return
77 }
78 }
79
80 exists, err := Exists(fs, path)
81 if err != nil {
82 return
83 }
84 if exists {
85 return fmt.Errorf("%v already exists", path)
86 }
87
88 file, err := fs.Create(path)
89 if err != nil {
90 return
91 }
92 defer file.Close()
93
94 _, err = io.Copy(file, r)
95 return
96 }
97
98 func (a Afero) GetTempDir(subPath string) string {
99 return GetTempDir(a.Fs, subPath)
100 }
101
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
108 }
109 return p
110 }
111 dir := addSlash(os.TempDir())
112
113 if subPath != "" {
114 // preserve windows backslash :-(
115 if FilePathSeparator == "\\" {
116 subPath = strings.Replace(subPath, "\\", "____", -1)
117 }
118 dir = dir + UnicodeSanitize((subPath))
119 if FilePathSeparator == "\\" {
120 dir = strings.Replace(dir, "____", "\\", -1)
121 }
122
123 if exists, _ := Exists(fs, dir); exists {
124 return addSlash(dir)
125 }
126
127 err := fs.MkdirAll(dir, 0o777)
128 if err != nil {
129 panic(err)
130 }
131 dir = addSlash(dir)
132 }
133 return dir
134 }
135
136 // Rewrite string to remove non-standard path characters
137 func UnicodeSanitize(s string) string {
138 source := []rune(s)
139 target := make([]rune, 0, len(source))
140
141 for _, r := range source {
142 if unicode.IsLetter(r) ||
143 unicode.IsDigit(r) ||
144 unicode.IsMark(r) ||
145 r == '.' ||
146 r == '/' ||
147 r == '\\' ||
148 r == '_' ||
149 r == '-' ||
150 r == '%' ||
151 r == ' ' ||
152 r == '#' {
153 target = append(target, r)
154 }
155 }
156
157 return string(target)
158 }
159
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))
164
165 return result
166 }
167
168 func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
169 return FileContainsBytes(a.Fs, filename, subslice)
170 }
171
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)
175 if err != nil {
176 return false, err
177 }
178 defer f.Close()
179
180 return readerContainsAny(f, subslice), nil
181 }
182
183 func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
184 return FileContainsAnyBytes(a.Fs, filename, subslices)
185 }
186
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)
190 if err != nil {
191 return false, err
192 }
193 defer f.Close()
194
195 return readerContainsAny(f, subslices...), nil
196 }
197
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 {
201 return false
202 }
203
204 largestSlice := 0
205
206 for _, sl := range subslices {
207 if len(sl) > largestSlice {
208 largestSlice = len(sl)
209 }
210 }
211
212 if largestSlice == 0 {
213 return false
214 }
215
216 bufflen := largestSlice * 4
217 halflen := bufflen / 2
218 buff := make([]byte, bufflen)
219 var err error
220 var n, i int
221
222 for {
223 i++
224 if i == 1 {
225 n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
226 } else {
227 if i != 2 {
228 // shift left to catch overlapping matches
229 copy(buff[:], buff[halflen:])
230 }
231 n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
232 }
233
234 if n > 0 {
235 for _, sl := range subslices {
236 if bytes.Contains(buff, sl) {
237 return true
238 }
239 }
240 }
241
242 if err != nil {
243 break
244 }
245 }
246 return false
247 }
248
249 func (a Afero) DirExists(path string) (bool, error) {
250 return DirExists(a.Fs, path)
251 }
252
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() {
257 return true, nil
258 }
259 if os.IsNotExist(err) {
260 return false, nil
261 }
262 return false, err
263 }
264
265 func (a Afero) IsDir(path string) (bool, error) {
266 return IsDir(a.Fs, path)
267 }
268
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)
272 if err != nil {
273 return false, err
274 }
275 return fi.IsDir(), nil
276 }
277
278 func (a Afero) IsEmpty(path string) (bool, error) {
279 return IsEmpty(a.Fs, path)
280 }
281
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)
286 }
287 fi, err := fs.Stat(path)
288 if err != nil {
289 return false, err
290 }
291 if fi.IsDir() {
292 f, err := fs.Open(path)
293 if err != nil {
294 return false, err
295 }
296 defer f.Close()
297 list, err := f.Readdir(-1)
298 if err != nil {
299 return false, err
300 }
301 return len(list) == 0, nil
302 }
303 return fi.Size() == 0, nil
304 }
305
306 func (a Afero) Exists(path string) (bool, error) {
307 return Exists(a.Fs, path)
308 }
309
310 // Check if a file or directory exists.
311 func Exists(fs Fs, path string) (bool, error) {
312 _, err := fs.Stat(path)
313 if err == nil {
314 return true, nil
315 }
316 if os.IsNotExist(err) {
317 return false, nil
318 }
319 return false, err
320 }
321
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)
326 }
327
328 return combinedPath
329 }