PageRenderTime 36ms CodeModel.GetById 12ms app.highlight 18ms RepoModel.GetById 2ms 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
 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