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  }