github.com/IBM/fsgo@v0.0.0-20220920202152-e16fd2119d49/path.go (about) 1 // Copyright 2022 IBM Inc. All rights reserved 2 // Copyright © 2014 Steve Francia <spf@spf13.com> 3 // 4 // SPDX-License-Identifier: Apache2.0 5 package fsgo 6 7 import ( 8 "os" 9 "path/filepath" 10 "sort" 11 ) 12 13 // readDirNames reads the directory named by dirname and returns 14 // a sorted list of directory entries. 15 // adapted from https://golang.org/src/path/filepath/path.go 16 func readDirNames(fs Fs, dirname string) ([]string, error) { 17 f, err := fs.Open(dirname) 18 if err != nil { 19 return nil, err 20 } 21 names, err := f.Readdirnames(-1) 22 f.Close() 23 if err != nil { 24 return nil, err 25 } 26 sort.Strings(names) 27 return names, nil 28 } 29 30 // walk recursively descends path, calling walkFn 31 // adapted from https://golang.org/src/path/filepath/path.go 32 func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 33 err := walkFn(path, info, nil) 34 if err != nil { 35 if info.IsDir() && err == filepath.SkipDir { 36 return nil 37 } 38 return err 39 } 40 41 if !info.IsDir() { 42 return nil 43 } 44 45 names, err := readDirNames(fs, path) 46 if err != nil { 47 return walkFn(path, info, err) 48 } 49 50 for _, name := range names { 51 filename := filepath.Join(path, name) 52 fileInfo, err := lstatIfPossible(fs, filename) 53 if err != nil { 54 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 55 return err 56 } 57 } else { 58 err = walk(fs, filename, fileInfo, walkFn) 59 if err != nil { 60 if !fileInfo.IsDir() || err != filepath.SkipDir { 61 return err 62 } 63 } 64 } 65 } 66 return nil 67 } 68 69 // if the filesystem supports it, use Lstat, else use fs.Stat 70 func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) { 71 if lfs, ok := fs.(Lstater); ok { 72 fi, _, err := lfs.LstatIfPossible(path) 73 return fi, err 74 } 75 return fs.Stat(path) 76 } 77 78 // Walk walks the file tree rooted at root, calling walkFn for each file or 79 // directory in the tree, including root. All errors that arise visiting files 80 // and directories are filtered by walkFn. The files are walked in lexical 81 // order, which makes the output deterministic but means that for very 82 // large directories Walk can be inefficient. 83 // Walk does not follow symbolic links. 84 85 func (a FsGo) Walk(root string, walkFn filepath.WalkFunc) error { 86 return Walk(a.Fs, root, walkFn) 87 } 88 89 func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error { 90 info, err := lstatIfPossible(fs, root) 91 if err != nil { 92 return walkFn(root, nil, err) 93 } 94 return walk(fs, root, info, walkFn) 95 }