PageRenderTime 16ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/gogaedev/toolbox_walk.go

https://code.google.com/
Go | 81 lines | 58 code | 6 blank | 17 comment | 30 complexity | fa66b815b7692b7864a32843b4e084f8 MD5 | raw file
  1. package main
  2. import (
  3. "os"
  4. "fmt"
  5. "io/ioutil"
  6. "path/filepath"
  7. )
  8. //WalkSkipDir is the Error returned when we want to skip descending into a directory
  9. var WalkSkipDir = os.NewError("skip this directory")
  10. //WalkFunc is a callback function called for each path as a directory is walked
  11. //If resolvedPath != "", then we are following symbolic links.
  12. type WalkFunc func(path string, resolvedPath string, info *os.FileInfo, err os.Error) os.Error
  13. //Walk walks a path, optionally following symbolic links, and for each path,
  14. //it calls the walkFn passed.
  15. //
  16. //It is similar to filepath.Walk, except that it supports symbolic links and
  17. //can detect infinite loops while following sym links.
  18. //It solves the issue where your WalkFunc needs a path relative to the symbolic link
  19. //(resolving links within walkfunc loses the path to the symbolic link for each traversal).
  20. func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) (
  21. os.Error) {
  22. info, err := os.Lstat(path)
  23. if err != nil { return err }
  24. var symlinkPathsFollowed map[string]bool
  25. var resolvedPath string
  26. if followSymlinks {
  27. resolvedPath = path
  28. if detectSymlinkInfiniteLoop { symlinkPathsFollowed = make(map[string]bool, 8) }
  29. }
  30. return walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
  31. }
  32. //walk walks the path. It is a helper/sibling function to Walk.
  33. //It takes a resolvedPath into consideration. This way, paths being walked are
  34. //always relative to the path argument, even if symbolic links were resolved).
  35. //
  36. //If resolvedPath is "", then we are not following symbolic links.
  37. //If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
  38. func walk(path string, info *os.FileInfo, resolvedPath string,
  39. symlinkPathsFollowed map[string]bool, walkFn WalkFunc) os.Error {
  40. if info == nil { return os.NewError("Walk: Nil FileInfo passed") }
  41. err := walkFn(path, resolvedPath, info, nil)
  42. if err != nil {
  43. if info.IsDirectory() && err == WalkSkipDir { err = nil }
  44. return err
  45. }
  46. if resolvedPath != "" && info.IsSymlink() {
  47. path2, err := os.Readlink(resolvedPath)
  48. if err != nil { return err }
  49. //vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
  50. if symlinkPathsFollowed != nil {
  51. if _, ok := symlinkPathsFollowed[path2]; ok {
  52. errMsg := "Potential SymLink Infinite Loop. Path: %v, Link To: %v"
  53. return fmt.Errorf(errMsg, resolvedPath, path2)
  54. } else {
  55. symlinkPathsFollowed[path2] = true
  56. }
  57. }
  58. info2, err := os.Lstat(path2)
  59. if err != nil { return err }
  60. return walk(path, info2, path2, symlinkPathsFollowed, walkFn)
  61. }
  62. if info.IsDirectory() {
  63. list, err := ioutil.ReadDir(path)
  64. if err != nil { return walkFn(path, resolvedPath, info, err) }
  65. for _, fileInfo := range list {
  66. path2 := filepath.Join(path, fileInfo.Name)
  67. var resolvedPath2 string
  68. if resolvedPath != "" { resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name) }
  69. err = walk(path2, fileInfo, resolvedPath2, symlinkPathsFollowed, walkFn)
  70. if err != nil { return err }
  71. }
  72. return nil
  73. }
  74. return nil
  75. }