glob_windows.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. // Copyright (C) 2015 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // +build windows
  7. package osutil
  8. import (
  9. "os"
  10. "path/filepath"
  11. "runtime"
  12. "sort"
  13. "strings"
  14. )
  15. // Glob implements filepath.Glob, but works with Windows long path prefixes.
  16. // Deals with https://github.com/golang/go/issues/10577
  17. func Glob(pattern string) (matches []string, err error) {
  18. if !hasMeta(pattern) {
  19. if _, err = os.Lstat(pattern); err != nil {
  20. return nil, nil
  21. }
  22. return []string{pattern}, nil
  23. }
  24. dir, file := filepath.Split(filepath.Clean(pattern))
  25. switch dir {
  26. case "":
  27. dir = "."
  28. case string(filepath.Separator):
  29. // nothing
  30. default:
  31. if runtime.GOOS != "windows" || len(dir) < 2 || dir[len(dir)-2] != ':' {
  32. dir = dir[0 : len(dir)-1] // chop off trailing separator, if it's not after the drive letter
  33. }
  34. }
  35. if !hasMeta(dir) {
  36. return glob(dir, file, nil)
  37. }
  38. var m []string
  39. m, err = Glob(dir)
  40. if err != nil {
  41. return
  42. }
  43. for _, d := range m {
  44. matches, err = glob(d, file, matches)
  45. if err != nil {
  46. return
  47. }
  48. }
  49. return
  50. }
  51. func hasMeta(path string) bool {
  52. // Strip off Windows long path prefix if it exists.
  53. if strings.HasPrefix(path, "\\\\?\\") {
  54. path = path[4:]
  55. }
  56. // TODO(niemeyer): Should other magic characters be added here?
  57. return strings.IndexAny(path, "*?[") >= 0
  58. }
  59. func glob(dir, pattern string, matches []string) (m []string, e error) {
  60. m = matches
  61. fi, err := os.Stat(dir)
  62. if err != nil {
  63. return
  64. }
  65. if !fi.IsDir() {
  66. return
  67. }
  68. d, err := os.Open(dir)
  69. if err != nil {
  70. return
  71. }
  72. defer d.Close()
  73. names, _ := d.Readdirnames(-1)
  74. sort.Strings(names)
  75. for _, n := range names {
  76. matched, err := filepath.Match(pattern, n)
  77. if err != nil {
  78. return m, err
  79. }
  80. if matched {
  81. m = append(m, filepath.Join(dir, n))
  82. }
  83. }
  84. return
  85. }