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