walkfs.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // This part copied directly from golang.org/src/path/filepath/path.go (Go
  5. // 1.6) and lightly modified to be methods on BasicFilesystem.
  6. // In our Walk() all paths given to a WalkFunc() are relative to the
  7. // filesystem root.
  8. package fs
  9. import (
  10. "errors"
  11. "path/filepath"
  12. )
  13. var ErrInfiniteRecursion = errors.New("infinite filesystem recursion detected")
  14. type ancestorDirList struct {
  15. list []FileInfo
  16. fs Filesystem
  17. }
  18. func (ancestors *ancestorDirList) Push(info FileInfo) {
  19. l.Debugf("ancestorDirList: Push '%s'", info.Name())
  20. ancestors.list = append(ancestors.list, info)
  21. }
  22. func (ancestors *ancestorDirList) Pop() FileInfo {
  23. aLen := len(ancestors.list)
  24. info := ancestors.list[aLen-1]
  25. l.Debugf("ancestorDirList: Pop '%s'", info.Name())
  26. ancestors.list = ancestors.list[:aLen-1]
  27. return info
  28. }
  29. func (ancestors *ancestorDirList) Contains(info FileInfo) bool {
  30. l.Debugf("ancestorDirList: Contains '%s'", info.Name())
  31. for _, ancestor := range ancestors.list {
  32. if ancestors.fs.SameFile(info, ancestor) {
  33. return true
  34. }
  35. }
  36. return false
  37. }
  38. // WalkFunc is the type of the function called for each file or directory
  39. // visited by Walk. The path argument contains the argument to Walk as a
  40. // prefix; that is, if Walk is called with "dir", which is a directory
  41. // containing the file "a", the walk function will be called with argument
  42. // "dir/a". The info argument is the FileInfo for the named path.
  43. //
  44. // If there was a problem walking to the file or directory named by path, the
  45. // incoming error will describe the problem and the function can decide how
  46. // to handle that error (and Walk will not descend into that directory). If
  47. // an error is returned, processing stops. The sole exception is when the function
  48. // returns the special value SkipDir. If the function returns SkipDir when invoked
  49. // on a directory, Walk skips the directory's contents entirely.
  50. // If the function returns SkipDir when invoked on a non-directory file,
  51. // Walk skips the remaining files in the containing directory.
  52. type WalkFunc func(path string, info FileInfo, err error) error
  53. type walkFilesystem struct {
  54. Filesystem
  55. }
  56. func NewWalkFilesystem(next Filesystem) Filesystem {
  57. return &walkFilesystem{next}
  58. }
  59. // walk recursively descends path, calling walkFn.
  60. func (f *walkFilesystem) walk(path string, info FileInfo, walkFn WalkFunc, ancestors *ancestorDirList) error {
  61. l.Debugf("walk: path=%s", path)
  62. path, err := Canonicalize(path)
  63. if err != nil {
  64. return err
  65. }
  66. err = walkFn(path, info, nil)
  67. if err != nil {
  68. if info.IsDir() && err == SkipDir {
  69. return nil
  70. }
  71. return err
  72. }
  73. if !info.IsDir() && path != "." {
  74. return nil
  75. }
  76. if !ancestors.Contains(info) {
  77. ancestors.Push(info)
  78. defer ancestors.Pop()
  79. } else {
  80. return walkFn(path, info, ErrInfiniteRecursion)
  81. }
  82. names, err := f.DirNames(path)
  83. if err != nil {
  84. return walkFn(path, info, err)
  85. }
  86. for _, name := range names {
  87. filename := filepath.Join(path, name)
  88. fileInfo, err := f.Lstat(filename)
  89. if err != nil {
  90. if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
  91. return err
  92. }
  93. } else {
  94. err = f.walk(filename, fileInfo, walkFn, ancestors)
  95. if err != nil {
  96. if !fileInfo.IsDir() || err != SkipDir {
  97. return err
  98. }
  99. }
  100. }
  101. }
  102. return nil
  103. }
  104. // Walk walks the file tree rooted at root, calling walkFn for each file or
  105. // directory in the tree, including root. All errors that arise visiting files
  106. // and directories are filtered by walkFn. The files are walked in lexical
  107. // order, which makes the output deterministic but means that for very
  108. // large directories Walk can be inefficient.
  109. // Walk does not follow symbolic links.
  110. func (f *walkFilesystem) Walk(root string, walkFn WalkFunc) error {
  111. info, err := f.Lstat(root)
  112. if err != nil {
  113. return walkFn(root, nil, err)
  114. }
  115. ancestors := &ancestorDirList{fs: f.Filesystem}
  116. return f.walk(root, info, walkFn, ancestors)
  117. }