Home | History | Annotate | Download | only in exec
      1 // Copyright 2010 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package exec
      6 
      7 import (
      8 	"errors"
      9 	"os"
     10 	"path/filepath"
     11 	"strings"
     12 )
     13 
     14 // ErrNotFound is the error resulting if a path search failed to find an executable file.
     15 var ErrNotFound = errors.New("executable file not found in %PATH%")
     16 
     17 func chkStat(file string) error {
     18 	d, err := os.Stat(file)
     19 	if err != nil {
     20 		return err
     21 	}
     22 	if d.IsDir() {
     23 		return os.ErrPermission
     24 	}
     25 	return nil
     26 }
     27 
     28 func hasExt(file string) bool {
     29 	i := strings.LastIndex(file, ".")
     30 	if i < 0 {
     31 		return false
     32 	}
     33 	return strings.LastIndexAny(file, `:\/`) < i
     34 }
     35 
     36 func findExecutable(file string, exts []string) (string, error) {
     37 	if len(exts) == 0 {
     38 		return file, chkStat(file)
     39 	}
     40 	if hasExt(file) {
     41 		if chkStat(file) == nil {
     42 			return file, nil
     43 		}
     44 	}
     45 	for _, e := range exts {
     46 		if f := file + e; chkStat(f) == nil {
     47 			return f, nil
     48 		}
     49 	}
     50 	return "", os.ErrNotExist
     51 }
     52 
     53 // LookPath searches for an executable binary named file
     54 // in the directories named by the PATH environment variable.
     55 // If file contains a slash, it is tried directly and the PATH is not consulted.
     56 // LookPath also uses PATHEXT environment variable to match
     57 // a suitable candidate.
     58 // The result may be an absolute path or a path relative to the current directory.
     59 func LookPath(file string) (string, error) {
     60 	var exts []string
     61 	x := os.Getenv(`PATHEXT`)
     62 	if x != "" {
     63 		for _, e := range strings.Split(strings.ToLower(x), `;`) {
     64 			if e == "" {
     65 				continue
     66 			}
     67 			if e[0] != '.' {
     68 				e = "." + e
     69 			}
     70 			exts = append(exts, e)
     71 		}
     72 	} else {
     73 		exts = []string{".com", ".exe", ".bat", ".cmd"}
     74 	}
     75 
     76 	if strings.ContainsAny(file, `:\/`) {
     77 		if f, err := findExecutable(file, exts); err == nil {
     78 			return f, nil
     79 		} else {
     80 			return "", &Error{file, err}
     81 		}
     82 	}
     83 	if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
     84 		return f, nil
     85 	}
     86 	path := os.Getenv("path")
     87 	for _, dir := range filepath.SplitList(path) {
     88 		if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
     89 			return f, nil
     90 		}
     91 	}
     92 	return "", &Error{file, ErrNotFound}
     93 }
     94