github.com/v2fly/tools@v0.100.0/internal/lsp/cache/os_windows.go (about)

     1  // Copyright 2020 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  package cache
     5  
     6  import (
     7  	"fmt"
     8  	"path/filepath"
     9  	"syscall"
    10  )
    11  
    12  func init() {
    13  	checkPathCase = windowsCheckPathCase
    14  }
    15  
    16  func windowsCheckPathCase(path string) error {
    17  	// Back in the day, Windows used to have short and long filenames, and
    18  	// it still supports those APIs. GetLongPathName gets the real case for a
    19  	// path, so we can use it here. Inspired by
    20  	// http://stackoverflow.com/q/2113822.
    21  
    22  	// Short paths can be longer than long paths, and unicode, so be generous.
    23  	buflen := 4 * len(path)
    24  	namep, err := syscall.UTF16PtrFromString(path)
    25  	if err != nil {
    26  		return err
    27  	}
    28  	short := make([]uint16, buflen)
    29  	n, err := syscall.GetShortPathName(namep, &short[0], uint32(len(short)*2)) // buflen is in bytes.
    30  	if err != nil {
    31  		return err
    32  	}
    33  	if int(n) > len(short)*2 {
    34  		return fmt.Errorf("short buffer too short: %v vs %v*2", n, len(short))
    35  	}
    36  	long := make([]uint16, buflen)
    37  	n, err = syscall.GetLongPathName(&short[0], &long[0], uint32(len(long)*2))
    38  	if err != nil {
    39  		return err
    40  	}
    41  	if int(n) > len(long)*2 {
    42  		return fmt.Errorf("long buffer too short: %v vs %v*2", n, len(long))
    43  	}
    44  	longstr := syscall.UTF16ToString(long)
    45  
    46  	isRoot := func(p string) bool {
    47  		return p[len(p)-1] == filepath.Separator
    48  	}
    49  	for got, want := path, longstr; !isRoot(got) && !isRoot(want); got, want = filepath.Dir(got), filepath.Dir(want) {
    50  		if g, w := filepath.Base(got), filepath.Base(want); g != w {
    51  			return fmt.Errorf("case mismatch in path %q: component %q should be %q", path, g, w)
    52  		}
    53  	}
    54  	return nil
    55  }