github.com/ddosakura/afero@v1.1.1/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/transform"
    29  	"golang.org/x/text/unicode/norm"
    30  )
    31  
    32  // Filepath separator defined by os.Separator.
    33  const FilePathSeparator = string(filepath.Separator)
    34  
    35  // Takes a reader and a path and writes the content
    36  func (a Afero) WriteReader(path string, r io.Reader) (err error) {
    37  	return WriteReader(a.Fs, path, r)
    38  }
    39  
    40  func WriteReader(fs Fs, path string, r io.Reader) (err error) {
    41  	dir, _ := filepath.Split(path)
    42  	ospath := filepath.FromSlash(dir)
    43  
    44  	if ospath != "" {
    45  		err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
    46  		if err != nil {
    47  			if err != os.ErrExist {
    48  				return err
    49  			}
    50  		}
    51  	}
    52  
    53  	file, err := fs.Create(path)
    54  	if err != nil {
    55  		return
    56  	}
    57  	defer file.Close()
    58  
    59  	_, err = io.Copy(file, r)
    60  	return
    61  }
    62  
    63  // Same as WriteReader but checks to see if file/directory already exists.
    64  func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {
    65  	return SafeWriteReader(a.Fs, path, r)
    66  }
    67  
    68  func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {
    69  	dir, _ := filepath.Split(path)
    70  	ospath := filepath.FromSlash(dir)
    71  
    72  	if ospath != "" {
    73  		err = fs.MkdirAll(ospath, 0777) // rwx, rw, r
    74  		if err != nil {
    75  			return
    76  		}
    77  	}
    78  
    79  	exists, err := Exists(fs, path)
    80  	if err != nil {
    81  		return
    82  	}
    83  	if exists {
    84  		return fmt.Errorf("%v already exists", path)
    85  	}
    86  
    87  	file, err := fs.Create(path)
    88  	if err != nil {
    89  		return
    90  	}
    91  	defer file.Close()
    92  
    93  	_, err = io.Copy(file, r)
    94  	return
    95  }
    96  
    97  func (a Afero) GetTempDir(subPath string) string {
    98  	return GetTempDir(a.Fs, subPath)
    99  }
   100  
   101  // GetTempDir returns the default temp directory with trailing slash
   102  // if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx
   103  func GetTempDir(fs Fs, subPath string) string {
   104  	addSlash := func(p string) string {
   105  		if FilePathSeparator != p[len(p)-1:] {
   106  			p = p + FilePathSeparator
   107  		}
   108  		return p
   109  	}
   110  	dir := addSlash(os.TempDir())
   111  
   112  	if subPath != "" {
   113  		// preserve windows backslash :-(
   114  		if FilePathSeparator == "\\" {
   115  			subPath = strings.Replace(subPath, "\\", "____", -1)
   116  		}
   117  		dir = dir + UnicodeSanitize((subPath))
   118  		if FilePathSeparator == "\\" {
   119  			dir = strings.Replace(dir, "____", "\\", -1)
   120  		}
   121  
   122  		if exists, _ := Exists(fs, dir); exists {
   123  			return addSlash(dir)
   124  		}
   125  
   126  		err := fs.MkdirAll(dir, 0777)
   127  		if err != nil {
   128  			panic(err)
   129  		}
   130  		dir = addSlash(dir)
   131  	}
   132  	return dir
   133  }
   134  
   135  // Rewrite string to remove non-standard path characters
   136  func UnicodeSanitize(s string) string {
   137  	source := []rune(s)
   138  	target := make([]rune, 0, len(source))
   139  
   140  	for _, r := range source {
   141  		if unicode.IsLetter(r) ||
   142  			unicode.IsDigit(r) ||
   143  			unicode.IsMark(r) ||
   144  			r == '.' ||
   145  			r == '/' ||
   146  			r == '\\' ||
   147  			r == '_' ||
   148  			r == '-' ||
   149  			r == '%' ||
   150  			r == ' ' ||
   151  			r == '#' {
   152  			target = append(target, r)
   153  		}
   154  	}
   155  
   156  	return string(target)
   157  }
   158  
   159  // Transform characters with accents into plain forms.
   160  func NeuterAccents(s string) string {
   161  	t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
   162  	result, _, _ := transform.String(t, string(s))
   163  
   164  	return result
   165  }
   166  
   167  func isMn(r rune) bool {
   168  	return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
   169  }
   170  
   171  func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {
   172  	return FileContainsBytes(a.Fs, filename, subslice)
   173  }
   174  
   175  // Check if a file contains a specified byte slice.
   176  func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {
   177  	f, err := fs.Open(filename)
   178  	if err != nil {
   179  		return false, err
   180  	}
   181  	defer f.Close()
   182  
   183  	return readerContainsAny(f, subslice), nil
   184  }
   185  
   186  func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {
   187  	return FileContainsAnyBytes(a.Fs, filename, subslices)
   188  }
   189  
   190  // Check if a file contains any of the specified byte slices.
   191  func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {
   192  	f, err := fs.Open(filename)
   193  	if err != nil {
   194  		return false, err
   195  	}
   196  	defer f.Close()
   197  
   198  	return readerContainsAny(f, subslices...), nil
   199  }
   200  
   201  // readerContains reports whether any of the subslices is within r.
   202  func readerContainsAny(r io.Reader, subslices ...[]byte) bool {
   203  
   204  	if r == nil || len(subslices) == 0 {
   205  		return false
   206  	}
   207  
   208  	largestSlice := 0
   209  
   210  	for _, sl := range subslices {
   211  		if len(sl) > largestSlice {
   212  			largestSlice = len(sl)
   213  		}
   214  	}
   215  
   216  	if largestSlice == 0 {
   217  		return false
   218  	}
   219  
   220  	bufflen := largestSlice * 4
   221  	halflen := bufflen / 2
   222  	buff := make([]byte, bufflen)
   223  	var err error
   224  	var n, i int
   225  
   226  	for {
   227  		i++
   228  		if i == 1 {
   229  			n, err = io.ReadAtLeast(r, buff[:halflen], halflen)
   230  		} else {
   231  			if i != 2 {
   232  				// shift left to catch overlapping matches
   233  				copy(buff[:], buff[halflen:])
   234  			}
   235  			n, err = io.ReadAtLeast(r, buff[halflen:], halflen)
   236  		}
   237  
   238  		if n > 0 {
   239  			for _, sl := range subslices {
   240  				if bytes.Contains(buff, sl) {
   241  					return true
   242  				}
   243  			}
   244  		}
   245  
   246  		if err != nil {
   247  			break
   248  		}
   249  	}
   250  	return false
   251  }
   252  
   253  func (a Afero) DirExists(path string) (bool, error) {
   254  	return DirExists(a.Fs, path)
   255  }
   256  
   257  // DirExists checks if a path exists and is a directory.
   258  func DirExists(fs Fs, path string) (bool, error) {
   259  	fi, err := fs.Stat(path)
   260  	if err == nil && fi.IsDir() {
   261  		return true, nil
   262  	}
   263  	if os.IsNotExist(err) {
   264  		return false, nil
   265  	}
   266  	return false, err
   267  }
   268  
   269  func (a Afero) IsDir(path string) (bool, error) {
   270  	return IsDir(a.Fs, path)
   271  }
   272  
   273  // IsDir checks if a given path is a directory.
   274  func IsDir(fs Fs, path string) (bool, error) {
   275  	fi, err := fs.Stat(path)
   276  	if err != nil {
   277  		return false, err
   278  	}
   279  	return fi.IsDir(), nil
   280  }
   281  
   282  func (a Afero) IsEmpty(path string) (bool, error) {
   283  	return IsEmpty(a.Fs, path)
   284  }
   285  
   286  // IsEmpty checks if a given file or directory is empty.
   287  func IsEmpty(fs Fs, path string) (bool, error) {
   288  	if b, _ := Exists(fs, path); !b {
   289  		return false, fmt.Errorf("%q path does not exist", path)
   290  	}
   291  	fi, err := fs.Stat(path)
   292  	if err != nil {
   293  		return false, err
   294  	}
   295  	if fi.IsDir() {
   296  		f, err := fs.Open(path)
   297  		if err != nil {
   298  			return false, err
   299  		}
   300  		defer f.Close()
   301  		list, err := f.Readdir(-1)
   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  }