path.go (2928B)
1 // Copyright ©2015 The Go Authors 2 // Copyright ©2015 Steve Francia <spf@spf13.com> 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 at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // 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 and 14 // limitations under the License. 15 16 package afero 17 18 import ( 19 "os" 20 "path/filepath" 21 "sort" 22 ) 23 24 // readDirNames reads the directory named by dirname and returns 25 // a sorted list of directory entries. 26 // adapted from https://golang.org/src/path/filepath/path.go 27 func readDirNames(fs Fs, dirname string) ([]string, error) { 28 f, err := fs.Open(dirname) 29 if err != nil { 30 return nil, err 31 } 32 names, err := f.Readdirnames(-1) 33 f.Close() 34 if err != nil { 35 return nil, err 36 } 37 sort.Strings(names) 38 return names, nil 39 } 40 41 // walk recursively descends path, calling walkFn 42 // adapted from https://golang.org/src/path/filepath/path.go 43 func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 44 err := walkFn(path, info, nil) 45 if err != nil { 46 if info.IsDir() && err == filepath.SkipDir { 47 return nil 48 } 49 return err 50 } 51 52 if !info.IsDir() { 53 return nil 54 } 55 56 names, err := readDirNames(fs, path) 57 if err != nil { 58 return walkFn(path, info, err) 59 } 60 61 for _, name := range names { 62 filename := filepath.Join(path, name) 63 fileInfo, err := lstatIfPossible(fs, filename) 64 if err != nil { 65 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 66 return err 67 } 68 } else { 69 err = walk(fs, filename, fileInfo, walkFn) 70 if err != nil { 71 if !fileInfo.IsDir() || err != filepath.SkipDir { 72 return err 73 } 74 } 75 } 76 } 77 return nil 78 } 79 80 // if the filesystem supports it, use Lstat, else use fs.Stat 81 func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) { 82 if lfs, ok := fs.(Lstater); ok { 83 fi, _, err := lfs.LstatIfPossible(path) 84 return fi, err 85 } 86 return fs.Stat(path) 87 } 88 89 // Walk walks the file tree rooted at root, calling walkFn for each file or 90 // directory in the tree, including root. All errors that arise visiting files 91 // and directories are filtered by walkFn. The files are walked in lexical 92 // order, which makes the output deterministic but means that for very 93 // large directories Walk can be inefficient. 94 // Walk does not follow symbolic links. 95 96 func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error { 97 return Walk(a.Fs, root, walkFn) 98 } 99 100 func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error { 101 info, err := lstatIfPossible(fs, root) 102 if err != nil { 103 return walkFn(root, nil, err) 104 } 105 return walk(fs, root, info, walkFn) 106 }