// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package toolchain import ( "io/fs" "os" "path/filepath" "strings" "sync" "cmd/go/internal/gover" ) var pathExts = sync.OnceValue(func() []string { x := os.Getenv(`PATHEXT`) if x == "" { return []string{".com", ".exe", ".bat", ".cmd"} } var exts []string for _, e := range strings.Split(strings.ToLower(x), `;`) { if e == "" { continue } if e[0] != '.' { e = "." + e } exts = append(exts, e) } return exts }) // pathDirs returns the directories in the system search path. func pathDirs() []string { return filepath.SplitList(os.Getenv("PATH")) } // pathVersion returns the Go version implemented by the file // described by de and info in directory dir. // The analysis only uses the name itself; it does not run the program. func pathVersion(dir string, de fs.DirEntry, info fs.FileInfo) (string, bool) { name, _, ok := cutExt(de.Name(), pathExts()) if !ok { return "", false } v := gover.FromToolchain(name) if v == "" { return "", false } return v, true } // cutExt looks for any of the known extensions at the end of file. // If one is found, cutExt returns the file name with the extension trimmed, // the extension itself, and true to signal that an extension was found. // Otherwise cutExt returns file, "", false. func cutExt(file string, exts []string) (name, ext string, found bool) { i := strings.LastIndex(file, ".") if i < 0 { return file, "", false } for _, x := range exts { if strings.EqualFold(file[i:], x) { return file[:i], file[i:], true } } return file, "", false }