Path: blob/main/vendor/github.com/spf13/afero/util.go
2875 views
// Copyright ©2015 Steve Francia <[email protected]>1// Portions Copyright ©2015 The Hugo Authors2// Portions Copyright 2016-present Bjørn Erik Pedersen <[email protected]>3//4// Licensed under the Apache License, Version 2.0 (the "License");5// you may not use this file except in compliance with the License.6// You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing, software11// distributed under the License is distributed on an "AS IS" BASIS,12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13// See the License for the specific language governing permissions and14// limitations under the License.1516package afero1718import (19"bytes"20"fmt"21"io"22"os"23"path/filepath"24"strings"25"unicode"2627"golang.org/x/text/runes"28"golang.org/x/text/transform"29"golang.org/x/text/unicode/norm"30)3132// Filepath separator defined by os.Separator.33const FilePathSeparator = string(filepath.Separator)3435// Takes a reader and a path and writes the content36func (a Afero) WriteReader(path string, r io.Reader) (err error) {37return WriteReader(a.Fs, path, r)38}3940func WriteReader(fs Fs, path string, r io.Reader) (err error) {41dir, _ := filepath.Split(path)42ospath := filepath.FromSlash(dir)4344if ospath != "" {45err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r46if err != nil {47if err != os.ErrExist {48return err49}50}51}5253file, err := fs.Create(path)54if err != nil {55return56}57defer file.Close()5859_, err = io.Copy(file, r)60return61}6263// Same as WriteReader but checks to see if file/directory already exists.64func (a Afero) SafeWriteReader(path string, r io.Reader) (err error) {65return SafeWriteReader(a.Fs, path, r)66}6768func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) {69dir, _ := filepath.Split(path)70ospath := filepath.FromSlash(dir)7172if ospath != "" {73err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r74if err != nil {75return76}77}7879exists, err := Exists(fs, path)80if err != nil {81return82}83if exists {84return fmt.Errorf("%v already exists", path)85}8687file, err := fs.Create(path)88if err != nil {89return90}91defer file.Close()9293_, err = io.Copy(file, r)94return95}9697func (a Afero) GetTempDir(subPath string) string {98return GetTempDir(a.Fs, subPath)99}100101// GetTempDir returns the default temp directory with trailing slash102// if subPath is not empty then it will be created recursively with mode 777 rwx rwx rwx103func GetTempDir(fs Fs, subPath string) string {104addSlash := func(p string) string {105if FilePathSeparator != p[len(p)-1:] {106p = p + FilePathSeparator107}108return p109}110dir := addSlash(os.TempDir())111112if subPath != "" {113// preserve windows backslash :-(114if FilePathSeparator == "\\" {115subPath = strings.ReplaceAll(subPath, "\\", "____")116}117dir = dir + UnicodeSanitize((subPath))118if FilePathSeparator == "\\" {119dir = strings.ReplaceAll(dir, "____", "\\")120}121122if exists, _ := Exists(fs, dir); exists {123return addSlash(dir)124}125126err := fs.MkdirAll(dir, 0o777)127if err != nil {128panic(err)129}130dir = addSlash(dir)131}132return dir133}134135// Rewrite string to remove non-standard path characters136func UnicodeSanitize(s string) string {137source := []rune(s)138target := make([]rune, 0, len(source))139140for _, r := range source {141if unicode.IsLetter(r) ||142unicode.IsDigit(r) ||143unicode.IsMark(r) ||144r == '.' ||145r == '/' ||146r == '\\' ||147r == '_' ||148r == '-' ||149r == '%' ||150r == ' ' ||151r == '#' {152target = append(target, r)153}154}155156return string(target)157}158159// Transform characters with accents into plain forms.160func NeuterAccents(s string) string {161t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)162result, _, _ := transform.String(t, string(s))163164return result165}166167func (a Afero) FileContainsBytes(filename string, subslice []byte) (bool, error) {168return FileContainsBytes(a.Fs, filename, subslice)169}170171// Check if a file contains a specified byte slice.172func FileContainsBytes(fs Fs, filename string, subslice []byte) (bool, error) {173f, err := fs.Open(filename)174if err != nil {175return false, err176}177defer f.Close()178179return readerContainsAny(f, subslice), nil180}181182func (a Afero) FileContainsAnyBytes(filename string, subslices [][]byte) (bool, error) {183return FileContainsAnyBytes(a.Fs, filename, subslices)184}185186// Check if a file contains any of the specified byte slices.187func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, error) {188f, err := fs.Open(filename)189if err != nil {190return false, err191}192defer f.Close()193194return readerContainsAny(f, subslices...), nil195}196197// readerContains reports whether any of the subslices is within r.198func readerContainsAny(r io.Reader, subslices ...[]byte) bool {199if r == nil || len(subslices) == 0 {200return false201}202203largestSlice := 0204205for _, sl := range subslices {206if len(sl) > largestSlice {207largestSlice = len(sl)208}209}210211if largestSlice == 0 {212return false213}214215bufflen := largestSlice * 4216halflen := bufflen / 2217buff := make([]byte, bufflen)218var err error219var n, i int220221for {222i++223if i == 1 {224n, err = io.ReadAtLeast(r, buff[:halflen], halflen)225} else {226if i != 2 {227// shift left to catch overlapping matches228copy(buff[:], buff[halflen:])229}230n, err = io.ReadAtLeast(r, buff[halflen:], halflen)231}232233if n > 0 {234for _, sl := range subslices {235if bytes.Contains(buff, sl) {236return true237}238}239}240241if err != nil {242break243}244}245return false246}247248func (a Afero) DirExists(path string) (bool, error) {249return DirExists(a.Fs, path)250}251252// DirExists checks if a path exists and is a directory.253func DirExists(fs Fs, path string) (bool, error) {254fi, err := fs.Stat(path)255if err == nil && fi.IsDir() {256return true, nil257}258if os.IsNotExist(err) {259return false, nil260}261return false, err262}263264func (a Afero) IsDir(path string) (bool, error) {265return IsDir(a.Fs, path)266}267268// IsDir checks if a given path is a directory.269func IsDir(fs Fs, path string) (bool, error) {270fi, err := fs.Stat(path)271if err != nil {272return false, err273}274return fi.IsDir(), nil275}276277func (a Afero) IsEmpty(path string) (bool, error) {278return IsEmpty(a.Fs, path)279}280281// IsEmpty checks if a given file or directory is empty.282func IsEmpty(fs Fs, path string) (bool, error) {283if b, _ := Exists(fs, path); !b {284return false, fmt.Errorf("%q path does not exist", path)285}286fi, err := fs.Stat(path)287if err != nil {288return false, err289}290if fi.IsDir() {291f, err := fs.Open(path)292if err != nil {293return false, err294}295defer f.Close()296list, err := f.Readdir(-1)297if err != nil {298return false, err299}300return len(list) == 0, nil301}302return fi.Size() == 0, nil303}304305func (a Afero) Exists(path string) (bool, error) {306return Exists(a.Fs, path)307}308309// Check if a file or directory exists.310func Exists(fs Fs, path string) (bool, error) {311_, err := fs.Stat(path)312if err == nil {313return true, nil314}315if os.IsNotExist(err) {316return false, nil317}318return false, err319}320321func FullBaseFsPath(basePathFs *BasePathFs, relativePath string) string {322combinedPath := filepath.Join(basePathFs.path, relativePath)323if parent, ok := basePathFs.source.(*BasePathFs); ok {324return FullBaseFsPath(parent, combinedPath)325}326327return combinedPath328}329330331