github.com/MRtecno98/afero@v1.9.3/util.go (about)

     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, 0777) // 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, 0777) // 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, 0777)
   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  
   201  	if r == nil || len(subslices) == 0 {
   202  		return false
   203  	}
   204  
   205  	largestSlice := 0
   206  
   207  	for _, sl := range subslices {
   208  		if len(sl) > largestSlice {
   209  			largestSlice = len(sl)
   210  		}
   211  	}
   212  
   213  	if largestSlice == 0 {
   214  		return false
   215  	}
   216  
   217  	bufflen := largestSlice * 4
   218  	halflen := bufflen / 2
   219  	buff := make([]byte, bufflen)
   220  	var err error
   221  	var n, i int
   222  
   223  	for {
   224  		i++
   225  		if i == 1 {
   226  			n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
   227  		} else {
   228  			if i != 2 {
   229  				// shift left to catch overlapping matches
   230  				copy(buff[:], buff[halflen:])
   231  			}
   232  			n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
   233  		}
   234  
   235  		if n > 0 {
   236  			for _, sl := range subslices {
   237  				if bytes.Contains(buff, sl) {
   238  					return true
   239  				}
   240  			}
   241  		}
   242  
   243  		if err != nil {
   244  			break
   245  		}
   246  	}
   247  	return false
   248  }
   249  
   250  func (a Afero) DirExists(path string) (bool, error) {
   251  	return DirExists(a.Fs, path)
   252  }
   253  
   254  // DirExists checks if a path exists and is a directory.
   255  func DirExists(fs Fs, path string) (bool, error) {
   256  	fi, err := fs.Stat(path)
   257  	if err == nil && fi.IsDir() {
   258  		return true, nil
   259  	}
   260  	if os.IsNotExist(err) {
   261  		return false, nil
   262  	}
   263  	return false, err
   264  }
   265  
   266  func (a Afero) IsDir(path string) (bool, error) {
   267  	return IsDir(a.Fs, path)
   268  }
   269  
   270  // IsDir checks if a given path is a directory.
   271  func IsDir(fs Fs, path string) (bool, error) {
   272  	fi, err := fs.Stat(path)
   273  	if err != nil {
   274  		return false, err
   275  	}
   276  	return fi.IsDir(), nil
   277  }
   278  
   279  func (a Afero) IsEmpty(path string) (bool, error) {
   280  	return IsEmpty(a.Fs, path)
   281  }
   282  
   283  // IsEmpty checks if a given file or directory is empty.
   284  func IsEmpty(fs Fs, path string) (bool, error) {
   285  	if b, _ := Exists(fs, path); !b {
   286  		return false, fmt.Errorf("%q path does not exist", path)
   287  	}
   288  	fi, err := fs.Stat(path)
   289  	if err != nil {
   290  		return false, err
   291  	}
   292  	if fi.IsDir() {
   293  		f, err := fs.Open(path)
   294  		if err != nil {
   295  			return false, err
   296  		}
   297  		defer f.Close()
   298  		list, err := f.Readdir(-1)
   299  		if err != nil {
   300  			return false, err
   301  		}
   302  		return len(list) == 0, nil
   303  	}
   304  	return fi.Size() == 0, nil
   305  }
   306  
   307  func (a Afero) Exists(path string) (bool, error) {
   308  	return Exists(a.Fs, path)
   309  }
   310  
   311  // Check if a file or directory exists.
   312  func Exists(fs Fs, path string) (bool, error) {
   313  	_, err := fs.Stat(path)
   314  	if err == nil {
   315  		return true, nil
   316  	}
   317  	if os.IsNotExist(err) {
   318  		return false, nil
   319  	}
   320  	return false, err
   321  }
   322  
   323  func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {
   324  	combinedPath := filepath.Join(basePathFs.path, relativePath)
   325  	if parent, ok := basePathFs.source.(*BasePathFs); ok {
   326  		return FullBaseFsPath(parent, combinedPath)
   327  	}
   328  
   329  	return combinedPath
   330  }