github.com/rogpeppe/go-internal@v1.12.1-0.20240509064211-c8567cf8e95f/internal/os/execpath/lp_windows.go (about)

     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 execpath
     6  
     7  import (
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  )
    12  
    13  func chkStat(file string) error {
    14  	d, err := os.Stat(file)
    15  	if err != nil {
    16  		return err
    17  	}
    18  	if d.IsDir() {
    19  		return os.ErrPermission
    20  	}
    21  	return nil
    22  }
    23  
    24  func hasExt(file string) bool {
    25  	i := strings.LastIndex(file, ".")
    26  	if i < 0 {
    27  		return false
    28  	}
    29  	return strings.LastIndexAny(file, `:\/`) < i
    30  }
    31  
    32  func findExecutable(file string, exts []string) (string, error) {
    33  	if len(exts) == 0 {
    34  		return file, chkStat(file)
    35  	}
    36  	if hasExt(file) {
    37  		if chkStat(file) == nil {
    38  			return file, nil
    39  		}
    40  	}
    41  	for _, e := range exts {
    42  		if f := file + e; chkStat(f) == nil {
    43  			return f, nil
    44  		}
    45  	}
    46  	return "", os.ErrNotExist
    47  }
    48  
    49  // Look searches for an executable named file, using getenv to look up
    50  // environment variables. If getenv is nil, os.Getenv will be used. If file
    51  // contains a slash, it is tried directly and getenv will not be called. The
    52  // result may be an absolute path or a path relative to the current directory.
    53  // Look also uses PATHEXT environment variable to match
    54  // a suitable candidate.
    55  func Look(file string, getenv func(string) string) (string, error) {
    56  	if getenv == nil {
    57  		getenv = os.Getenv
    58  	}
    59  	var exts []string
    60  	x := getenv(`PATHEXT`)
    61  	if x != "" {
    62  		for _, e := range strings.Split(strings.ToLower(x), `;`) {
    63  			if e == "" {
    64  				continue
    65  			}
    66  			if e[0] != '.' {
    67  				e = "." + e
    68  			}
    69  			exts = append(exts, e)
    70  		}
    71  	} else {
    72  		exts = []string{".com", ".exe", ".bat", ".cmd"}
    73  	}
    74  
    75  	if strings.ContainsAny(file, `:\/`) {
    76  		if f, err := findExecutable(file, exts); err == nil {
    77  			return f, nil
    78  		} else {
    79  			return "", &Error{file, err}
    80  		}
    81  	}
    82  	if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
    83  		return f, nil
    84  	}
    85  	path := getenv("path")
    86  	for _, dir := range filepath.SplitList(path) {
    87  		if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
    88  			return f, nil
    89  		}
    90  	}
    91  	return "", &Error{file, ErrNotFound}
    92  }