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  }