You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fnmatch.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package editorconfig
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. )
  8. var (
  9. // findLeftBrackets matches the opening left bracket {
  10. findLeftBrackets = regexp.MustCompile(`(^|[^\\])\{`)
  11. // findLeftBrackets matches the closing right bracket {
  12. findRightBrackets = regexp.MustCompile(`(^|[^\\])\}`)
  13. // findNumericRange matches a range of number, e.g. -2..5
  14. findNumericRange = regexp.MustCompile(`^([+-]?\d+)\.\.([+-]?\d+)$`)
  15. )
  16. // FnmatchCase tests whether the name matches the given pattern case included.
  17. func FnmatchCase(pattern, name string) (bool, error) {
  18. p, err := translate(pattern)
  19. if err != nil {
  20. return false, err
  21. }
  22. r, err := regexp.Compile(fmt.Sprintf("^%s$", p))
  23. if err != nil {
  24. return false, err
  25. }
  26. return r.MatchString(name), nil
  27. }
  28. func translate(pattern string) (string, error) {
  29. index := 0
  30. pat := []rune(pattern)
  31. length := len(pat)
  32. result := strings.Builder{}
  33. braceLevel := 0
  34. isEscaped := false
  35. inBrackets := false
  36. matchesBraces := len(findLeftBrackets.FindAllString(pattern, -1)) == len(findRightBrackets.FindAllString(pattern, -1))
  37. for index < length {
  38. r := pat[index]
  39. index++
  40. if r == '*' {
  41. p := index
  42. if p < length && pat[p] == '*' {
  43. result.WriteString(".*")
  44. index++
  45. } else {
  46. result.WriteString("[^/]*")
  47. }
  48. } else if r == '/' {
  49. p := index
  50. if p+2 < length && pat[p] == '*' && pat[p+1] == '*' && pat[p+2] == '/' {
  51. result.WriteString("(?:/|/.*/)")
  52. index += 3
  53. } else {
  54. result.WriteRune(r)
  55. }
  56. } else if r == '?' {
  57. result.WriteString("[^/]")
  58. } else if r == '[' {
  59. if inBrackets {
  60. result.WriteString("\\[")
  61. } else {
  62. hasSlash := false
  63. res := strings.Builder{}
  64. p := index
  65. for p < length {
  66. if pat[p] == ']' && pat[p-1] != '\\' {
  67. break
  68. }
  69. res.WriteRune(pat[p])
  70. if pat[p] == '/' && pat[p-1] != '\\' {
  71. hasSlash = true
  72. break
  73. }
  74. p++
  75. }
  76. if hasSlash {
  77. result.WriteString("\\[" + res.String())
  78. index = p + 1
  79. } else {
  80. inBrackets = true
  81. if index < length && pat[index] == '!' || pat[index] == '^' {
  82. index++
  83. result.WriteString("[^")
  84. } else {
  85. result.WriteRune('[')
  86. }
  87. }
  88. }
  89. } else if r == ']' {
  90. if inBrackets && pat[index-2] == '\\' {
  91. result.WriteString("\\]")
  92. } else {
  93. result.WriteRune(r)
  94. inBrackets = false
  95. }
  96. } else if r == '{' {
  97. hasComma := false
  98. p := index
  99. res := strings.Builder{}
  100. for p < length {
  101. if pat[p] == '}' && pat[p-1] != '\\' {
  102. break
  103. }
  104. res.WriteRune(pat[p])
  105. if pat[p] == ',' && pat[p-1] != '\\' {
  106. hasComma = true
  107. break
  108. }
  109. p++
  110. }
  111. if !hasComma && p < length {
  112. inner := res.String()
  113. sub := findNumericRange.FindStringSubmatch(inner)
  114. if len(sub) == 3 {
  115. from, _ := strconv.Atoi(sub[1])
  116. to, _ := strconv.Atoi(sub[2])
  117. result.WriteString("(?:")
  118. // XXX does not scale well
  119. for i := from; i < to; i++ {
  120. result.WriteString(strconv.Itoa(i))
  121. result.WriteRune('|')
  122. }
  123. result.WriteString(strconv.Itoa(to))
  124. result.WriteRune(')')
  125. } else {
  126. r, _ := translate(inner)
  127. result.WriteString(fmt.Sprintf("\\{%s\\}", r))
  128. }
  129. index = p + 1
  130. } else if matchesBraces {
  131. result.WriteString("(?:")
  132. braceLevel++
  133. } else {
  134. result.WriteString("\\{")
  135. }
  136. } else if r == '}' {
  137. if braceLevel > 0 {
  138. if isEscaped {
  139. result.WriteRune('}')
  140. isEscaped = false
  141. } else {
  142. result.WriteRune(')')
  143. braceLevel--
  144. }
  145. } else {
  146. result.WriteString("\\}")
  147. }
  148. } else if r == ',' {
  149. if braceLevel == 0 || isEscaped {
  150. result.WriteRune(r)
  151. } else {
  152. result.WriteRune('|')
  153. }
  154. } else if r != '\\' || isEscaped {
  155. result.WriteString(regexp.QuoteMeta(string(r)))
  156. isEscaped = false
  157. } else {
  158. isEscaped = true
  159. }
  160. }
  161. return result.String(), nil
  162. }