glob_windows.go 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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 http://mozilla.org/MPL/2.0/.
  6. // +build windows
  7. package osutil
  8. import (
  9. "os"
  10. "path/filepath"
  11. "sort"
  12. "strings"
  13. )
  14. // Glob implements filepath.Glob, but works with Windows long path prefixes.
  15. // Deals with https://github.com/golang/go/issues/10577
  16. func Glob(pattern string) (matches []string, err error) {
  17. if !hasMeta(pattern) {
  18. if _, err = os.Lstat(pattern); err != nil {
  19. return nil, nil
  20. }
  21. return []string{pattern}, nil
  22. }
  23. dir, file := filepath.Split(pattern)
  24. switch dir {
  25. case "":
  26. dir = "."
  27. case string(filepath.Separator):
  28. // nothing
  29. default:
  30. dir = dir[0 : len(dir)-1] // chop off trailing separator
  31. }
  32. if !hasMeta(dir) {
  33. return glob(dir, file, nil)
  34. }
  35. var m []string
  36. m, err = Glob(dir)
  37. if err != nil {
  38. return
  39. }
  40. for _, d := range m {
  41. matches, err = glob(d, file, matches)
  42. if err != nil {
  43. return
  44. }
  45. }
  46. return
  47. }
  48. func hasMeta(path string) bool {
  49. // Strip off Windows long path prefix if it exists.
  50. if strings.HasPrefix(path, "\\\\?\\") {
  51. path = path[4:]
  52. }
  53. // TODO(niemeyer): Should other magic characters be added here?
  54. return strings.IndexAny(path, "*?[") >= 0
  55. }
  56. func glob(dir, pattern string, matches []string) (m []string, e error) {
  57. m = matches
  58. fi, err := os.Stat(dir)
  59. if err != nil {
  60. return
  61. }
  62. if !fi.IsDir() {
  63. return
  64. }
  65. d, err := os.Open(dir)
  66. if err != nil {
  67. return
  68. }
  69. defer d.Close()
  70. names, _ := d.Readdirnames(-1)
  71. sort.Strings(names)
  72. for _, n := range names {
  73. matched, err := filepath.Match(pattern, n)
  74. if err != nil {
  75. return m, err
  76. }
  77. if matched {
  78. m = append(m, filepath.Join(dir, n))
  79. }
  80. }
  81. return
  82. }