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 }