github.com/hoop33/elvish@v0.0.0-20160801152013-6d25485beab4/util/search.go (about) 1 package util 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "strings" 8 ) 9 10 var ( 11 ErrNotExecutable = errors.New("not executable") 12 ErrNotFound = errors.New("not found") 13 ) 14 15 // Search tries to resolve an external command and return the full (possibly 16 // relative) path. 17 func Search(paths []string, exe string) (string, error) { 18 if DontSearch(exe) { 19 if IsExecutable(exe) { 20 return exe, nil 21 } 22 return "", ErrNotExecutable 23 } 24 for _, p := range paths { 25 full := p + "/" + exe 26 if IsExecutable(full) { 27 return full, nil 28 } 29 } 30 return "", ErrNotFound 31 } 32 33 // AllExecutables writes the names of all executable files in the search path 34 // to a channel. 35 func AllExecutables(paths []string, names chan<- string) { 36 for _, dir := range paths { 37 // XXX Ignore error 38 infos, _ := ioutil.ReadDir(dir) 39 for _, info := range infos { 40 if !info.IsDir() && (info.Mode()&0111 != 0) { 41 names <- info.Name() 42 } 43 } 44 } 45 } 46 47 // DontSearch determines whether the path to an external command should be 48 // taken literally and not searched. 49 func DontSearch(exe string) bool { 50 return exe == ".." || strings.ContainsRune(exe, '/') 51 } 52 53 // IsExecutable determines whether path refers to an executable file. 54 func IsExecutable(path string) bool { 55 fi, err := os.Stat(path) 56 if err != nil { 57 return false 58 } 59 fm := fi.Mode() 60 return !fm.IsDir() && (fm&0111 != 0) 61 }