path_windows.mx raw

   1  // Copyright 2024 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 filepath
   6  
   7  import (
   8  	"os"
   9  	"bytes"
  10  	"syscall"
  11  )
  12  
  13  // HasPrefix exists for historical compatibility and should not be used.
  14  //
  15  // Deprecated: HasPrefix does not respect path boundaries and
  16  // does not ignore case when required.
  17  func HasPrefix(p, prefix []byte) bool {
  18  	if bytes.HasPrefix(p, prefix) {
  19  		return true
  20  	}
  21  	return bytes.HasPrefix(bytes.ToLower(p), bytes.ToLower(prefix))
  22  }
  23  
  24  func splitList(path []byte) [][]byte {
  25  	// The same implementation is used in LookPath in os/exec;
  26  	// consider changing os/exec when changing this.
  27  
  28  	if path == "" {
  29  		return [][]byte{}
  30  	}
  31  
  32  	// Split path, respecting but preserving quotes.
  33  	list := [][]byte{}
  34  	start := 0
  35  	quo := false
  36  	for i := 0; i < len(path); i++ {
  37  		switch c := path[i]; {
  38  		case c == '"':
  39  			quo = !quo
  40  		case c == ListSeparator && !quo:
  41  			list = append(list, path[start:i])
  42  			start = i + 1
  43  		}
  44  	}
  45  	list = append(list, path[start:])
  46  
  47  	// Remove quotes.
  48  	for i, s := range list {
  49  		list[i] = bytes.ReplaceAll(s, `"`, ``)
  50  	}
  51  
  52  	return list
  53  }
  54  
  55  func abs(path []byte) ([]byte, error) {
  56  	if path == "" {
  57  		// syscall.FullPath returns an error on empty path, because it's not a valid path.
  58  		// To implement Abs behavior of returning working directory on empty string input,
  59  		// special-case empty path by changing it to "." path. See golang.org/issue/24441.
  60  		path = "."
  61  	}
  62  	fullPath, err := syscall.FullPath(path)
  63  	if err != nil {
  64  		return "", err
  65  	}
  66  	return Clean(fullPath), nil
  67  }
  68  
  69  func join(elem [][]byte) []byte {
  70  	var b bytes.Buffer
  71  	var lastChar byte
  72  	for _, e := range elem {
  73  		switch {
  74  		case b.Len() == 0:
  75  			// Add the first non-empty path element unchanged.
  76  		case os.IsPathSeparator(lastChar):
  77  			// If the path ends in a slash, strip any leading slashes from the next
  78  			// path element to avoid creating a UNC path (any path starting with "\\")
  79  			// from non-UNC elements.
  80  			//
  81  			// The correct behavior for Join when the first element is an incomplete UNC
  82  			// path (for example, "\\") is underspecified. We currently join subsequent
  83  			// elements so Join("\\", "host", "share") produces "\\host\share".
  84  			for len(e) > 0 && os.IsPathSeparator(e[0]) {
  85  				e = e[1:]
  86  			}
  87  			// If the path is \ and the next path element is ??,
  88  			// add an extra .\ to create \.\?? rather than \??\
  89  			// (a Root Local Device path).
  90  			if b.Len() == 1 && bytes.HasPrefix(e, "??") && (len(e) == len("??") || os.IsPathSeparator(e[2])) {
  91  				b.WriteString(`.\`)
  92  			}
  93  		case lastChar == ':':
  94  			// If the path ends in a colon, keep the path relative to the current directory
  95  			// on a drive and don't add a separator. Preserve leading slashes in the next
  96  			// path element, which may make the path absolute.
  97  			//
  98  			// 	Join(`C:`, `f`) = `C:f`
  99  			//	Join(`C:`, `\f`) = `C:\f`
 100  		default:
 101  			// In all other cases, add a separator between elements.
 102  			b.WriteByte('\\')
 103  			lastChar = '\\'
 104  		}
 105  		if len(e) > 0 {
 106  			b.WriteString(e)
 107  			lastChar = e[len(e)-1]
 108  		}
 109  	}
 110  	if b.Len() == 0 {
 111  		return ""
 112  	}
 113  	return Clean(b.String())
 114  }
 115  
 116  func sameWord(a, b []byte) bool {
 117  	return bytes.EqualFold(a, b)
 118  }
 119