/src/gogaedev/toolbox_walk.go
Go | 81 lines | 58 code | 6 blank | 17 comment | 30 complexity | fa66b815b7692b7864a32843b4e084f8 MD5 | raw file
- package main
- import (
- "os"
- "fmt"
- "io/ioutil"
- "path/filepath"
- )
- //WalkSkipDir is the Error returned when we want to skip descending into a directory
- var WalkSkipDir = os.NewError("skip this directory")
- //WalkFunc is a callback function called for each path as a directory is walked
- //If resolvedPath != "", then we are following symbolic links.
- type WalkFunc func(path string, resolvedPath string, info *os.FileInfo, err os.Error) os.Error
- //Walk walks a path, optionally following symbolic links, and for each path,
- //it calls the walkFn passed.
- //
- //It is similar to filepath.Walk, except that it supports symbolic links and
- //can detect infinite loops while following sym links.
- //It solves the issue where your WalkFunc needs a path relative to the symbolic link
- //(resolving links within walkfunc loses the path to the symbolic link for each traversal).
- func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) (
- os.Error) {
- info, err := os.Lstat(path)
- if err != nil { return err }
- var symlinkPathsFollowed map[string]bool
- var resolvedPath string
- if followSymlinks {
- resolvedPath = path
- if detectSymlinkInfiniteLoop { symlinkPathsFollowed = make(map[string]bool, 8) }
- }
- return walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
- }
- //walk walks the path. It is a helper/sibling function to Walk.
- //It takes a resolvedPath into consideration. This way, paths being walked are
- //always relative to the path argument, even if symbolic links were resolved).
- //
- //If resolvedPath is "", then we are not following symbolic links.
- //If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
- func walk(path string, info *os.FileInfo, resolvedPath string,
- symlinkPathsFollowed map[string]bool, walkFn WalkFunc) os.Error {
- if info == nil { return os.NewError("Walk: Nil FileInfo passed") }
- err := walkFn(path, resolvedPath, info, nil)
- if err != nil {
- if info.IsDirectory() && err == WalkSkipDir { err = nil }
- return err
- }
- if resolvedPath != "" && info.IsSymlink() {
- path2, err := os.Readlink(resolvedPath)
- if err != nil { return err }
- //vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
- if symlinkPathsFollowed != nil {
- if _, ok := symlinkPathsFollowed[path2]; ok {
- errMsg := "Potential SymLink Infinite Loop. Path: %v, Link To: %v"
- return fmt.Errorf(errMsg, resolvedPath, path2)
- } else {
- symlinkPathsFollowed[path2] = true
- }
- }
- info2, err := os.Lstat(path2)
- if err != nil { return err }
- return walk(path, info2, path2, symlinkPathsFollowed, walkFn)
- }
- if info.IsDirectory() {
- list, err := ioutil.ReadDir(path)
- if err != nil { return walkFn(path, resolvedPath, info, err) }
- for _, fileInfo := range list {
- path2 := filepath.Join(path, fileInfo.Name)
- var resolvedPath2 string
- if resolvedPath != "" { resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name) }
- err = walk(path2, fileInfo, resolvedPath2, symlinkPathsFollowed, walkFn)
- if err != nil { return err }
- }
- return nil
- }
- return nil
- }