github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/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 // EachExecutable calls f for each executable file in paths. 34 func EachExecutable(paths []string, f func(string)) { 35 for _, dir := range paths { 36 // XXX Ignore error 37 infos, _ := ioutil.ReadDir(dir) 38 for _, info := range infos { 39 if !info.IsDir() && (info.Mode()&0111 != 0) { 40 f(info.Name()) 41 } 42 } 43 } 44 } 45 46 // DontSearch determines whether the path to an external command should be 47 // taken literally and not searched. 48 func DontSearch(exe string) bool { 49 return exe == ".." || strings.ContainsRune(exe, '/') 50 } 51 52 // IsExecutable determines whether path refers to an executable file. 53 func IsExecutable(path string) bool { 54 fi, err := os.Stat(path) 55 if err != nil { 56 return false 57 } 58 fm := fi.Mode() 59 return !fm.IsDir() && (fm&0111 != 0) 60 }