github.com/cli/safeexec@v1.0.1/lookpath_windows.go (about)

     1  // Copyright (c) 2009 The Go Authors. All rights reserved.
     2  //
     3  // Redistribution and use in source and binary forms, with or without
     4  // modification, are permitted provided that the following conditions are
     5  // met:
     6  //
     7  //    * Redistributions of source code must retain the above copyright
     8  // notice, this list of conditions and the following disclaimer.
     9  //    * Redistributions in binary form must reproduce the above
    10  // copyright notice, this list of conditions and the following disclaimer
    11  // in the documentation and/or other materials provided with the
    12  // distribution.
    13  //    * Neither the name of Google Inc. nor the names of its
    14  // contributors may be used to endorse or promote products derived from
    15  // this software without specific prior written permission.
    16  //
    17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    28  
    29  // Package safeexec provides alternatives for exec package functions to avoid
    30  // accidentally executing binaries found in the current working directory on
    31  // Windows.
    32  package safeexec
    33  
    34  import (
    35  	"os"
    36  	"os/exec"
    37  	"path/filepath"
    38  	"strings"
    39  )
    40  
    41  func chkStat(file string) error {
    42  	d, err := os.Stat(file)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	if d.IsDir() {
    47  		return os.ErrPermission
    48  	}
    49  	return nil
    50  }
    51  
    52  func hasExt(file string) bool {
    53  	i := strings.LastIndex(file, ".")
    54  	if i < 0 {
    55  		return false
    56  	}
    57  	return strings.LastIndexAny(file, `:\/`) < i
    58  }
    59  
    60  func findExecutable(file string, exts []string) (string, error) {
    61  	if len(exts) == 0 {
    62  		return file, chkStat(file)
    63  	}
    64  	if hasExt(file) {
    65  		if chkStat(file) == nil {
    66  			return file, nil
    67  		}
    68  	}
    69  	for _, e := range exts {
    70  		if f := file + e; chkStat(f) == nil {
    71  			return f, nil
    72  		}
    73  	}
    74  	return "", os.ErrNotExist
    75  }
    76  
    77  // LookPath searches for an executable named file in the
    78  // directories named by the PATH environment variable.
    79  // If file contains a slash, it is tried directly and the PATH is not consulted.
    80  // LookPath also uses PATHEXT environment variable to match
    81  // a suitable candidate.
    82  // The result may be an absolute path or a path relative to the current directory.
    83  func LookPath(file string) (string, error) {
    84  	var exts []string
    85  	x := os.Getenv(`PATHEXT`)
    86  	if x != "" {
    87  		for _, e := range strings.Split(strings.ToLower(x), `;`) {
    88  			if e == "" {
    89  				continue
    90  			}
    91  			if e[0] != '.' {
    92  				e = "." + e
    93  			}
    94  			exts = append(exts, e)
    95  		}
    96  	} else {
    97  		exts = []string{".com", ".exe", ".bat", ".cmd"}
    98  	}
    99  
   100  	if strings.ContainsAny(file, `:\/`) {
   101  		if f, err := findExecutable(file, exts); err == nil {
   102  			return f, nil
   103  		} else {
   104  			return "", &exec.Error{file, err}
   105  		}
   106  	}
   107  
   108  	// https://github.com/golang/go/issues/38736
   109  	// if f, err := findExecutable(filepath.Join(".", file), exts); err == nil {
   110  	// 	return f, nil
   111  	// }
   112  
   113  	path := os.Getenv("path")
   114  	for _, dir := range filepath.SplitList(path) {
   115  		if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil {
   116  			return f, nil
   117  		}
   118  	}
   119  	return "", &exec.Error{file, exec.ErrNotFound}
   120  }