Source file src/cmd/go/internal/base/path.go

     1  // Copyright 2017 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 base
     6  
     7  import (
     8  	"errors"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  
    15  	"cmd/go/internal/str"
    16  )
    17  
    18  // UncachedCwd returns the current working directory.
    19  // Most callers should use Cwd, which caches the result for future use.
    20  // UncachedCwd is appropriate to call early in program startup before flag parsing,
    21  // because the -C flag may change the current directory.
    22  func UncachedCwd() string {
    23  	wd, err := os.Getwd()
    24  	if err != nil {
    25  		Fatalf("cannot determine current directory: %v", err)
    26  	}
    27  	return wd
    28  }
    29  
    30  var cwdOnce = sync.OnceValue(UncachedCwd)
    31  
    32  // Cwd returns the current working directory at the time of the first call.
    33  func Cwd() string {
    34  	return cwdOnce()
    35  }
    36  
    37  // ShortPath returns an absolute or relative name for path, whatever is shorter.
    38  // There are rare cases where the path produced by ShortPath could be incorrect
    39  // so it should only be used when formatting paths for error messages, not to read
    40  // a file.
    41  func ShortPath(path string) string {
    42  	if rel, err := filepath.Rel(Cwd(), path); err == nil && len(rel) < len(path) {
    43  		return rel
    44  	}
    45  	return path
    46  }
    47  
    48  // ShortPathConservative is similar to ShortPath, but returns the input if the result of ShortPath
    49  // would meet conditions that could make it invalid. If the short path would reach into a
    50  // parent directory and the base path contains a symlink, a ".." component can
    51  // cross a symlink boundary. That could be a problem because the symlinks could be evaluated,
    52  // changing the relative location of the boundary, before the ".." terms are applied to
    53  // go to parents. The check here is a little more conservative: it checks
    54  // whether the path starts with a ../ or ..\ component, and if any of the parent directories
    55  // of the working directory are symlinks.
    56  // See #68383 for a case where this could happen.
    57  func ShortPathConservative(path string) string {
    58  	if rel, err := relConservative(Cwd(), path); err == nil && len(rel) < len(path) {
    59  		return rel
    60  	}
    61  	return path
    62  }
    63  
    64  func relConservative(basepath, targpath string) (string, error) {
    65  	relpath, err := filepath.Rel(basepath, targpath)
    66  	if err != nil {
    67  		return "", err
    68  	}
    69  	if strings.HasPrefix(relpath, str.WithFilePathSeparator("..")) {
    70  		expanded, err := filepath.EvalSymlinks(basepath)
    71  		if err != nil || expanded != basepath { // The basepath contains a symlink. Be conservative and reject it.
    72  			return "", errors.New("conservatively rejecting relative path that may be invalid")
    73  		}
    74  	}
    75  	return relpath, nil
    76  }
    77  
    78  // RelPaths returns a copy of paths with absolute paths
    79  // made relative to the current directory if they would be shorter.
    80  func RelPaths(paths []string) []string {
    81  	out := make([]string, 0, len(paths))
    82  	for _, p := range paths {
    83  		rel, err := relConservative(Cwd(), p)
    84  		if err == nil && len(rel) < len(p) {
    85  			p = rel
    86  		}
    87  		out = append(out, p)
    88  	}
    89  	return out
    90  }
    91  
    92  // IsTestFile reports whether the source file is a set of tests and should therefore
    93  // be excluded from coverage analysis.
    94  func IsTestFile(file string) bool {
    95  	// We don't cover tests, only the code they test.
    96  	return strings.HasSuffix(file, "_test.go")
    97  }
    98  
    99  // IsNull reports whether the path is a common name for the null device.
   100  // It returns true for /dev/null on Unix, or NUL (case-insensitive) on Windows.
   101  func IsNull(path string) bool {
   102  	if path == os.DevNull {
   103  		return true
   104  	}
   105  	if runtime.GOOS == "windows" {
   106  		if strings.EqualFold(path, "NUL") {
   107  			return true
   108  		}
   109  	}
   110  	return false
   111  }
   112  

View as plain text