match.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright 2010 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 path
  5. import (
  6. "errors"
  7. "strings"
  8. "unicode/utf8"
  9. )
  10. // ErrBadPattern indicates a globbing pattern was malformed.
  11. var ErrBadPattern = errors.New("syntax error in pattern")
  12. // Match returns true if name matches the shell file name pattern.
  13. // The pattern syntax is:
  14. //
  15. // pattern:
  16. // { term }
  17. // term:
  18. // '*' matches any sequence of non-/ characters
  19. // '?' matches any single non-/ character
  20. // '[' [ '^' ] { character-range } ']'
  21. // character class (must be non-empty)
  22. // c matches character c (c != '*', '?', '\\', '[')
  23. // '\\' c matches character c
  24. //
  25. // character-range:
  26. // c matches character c (c != '\\', '-', ']')
  27. // '\\' c matches character c
  28. // lo '-' hi matches character c for lo <= c <= hi
  29. //
  30. // Match requires pattern to match all of name, not just a substring.
  31. // The only possible returned error is ErrBadPattern, when pattern
  32. // is malformed.
  33. //
  34. func Match(pattern, name string) (matched bool, err error) {
  35. Pattern:
  36. for len(pattern) > 0 {
  37. var star bool
  38. var chunk string
  39. star, chunk, pattern = scanChunk(pattern)
  40. if star && chunk == "" {
  41. // Trailing * matches rest of string unless it has a /.
  42. return strings.Index(name, "/") < 0, nil
  43. }
  44. // Look for match at current position.
  45. t, ok, err := matchChunk(chunk, name)
  46. // if we're the last chunk, make sure we've exhausted the name
  47. // otherwise we'll give a false result even if we could still match
  48. // using the star
  49. if ok && (len(t) == 0 || len(pattern) > 0) {
  50. name = t
  51. continue
  52. }
  53. if err != nil {
  54. return false, err
  55. }
  56. if star {
  57. // Look for match skipping i+1 bytes.
  58. // Cannot skip /.
  59. for i := 0; i < len(name) && name[i] != '/'; i++ {
  60. t, ok, err := matchChunk(chunk, name[i+1:])
  61. if ok {
  62. // if we're the last chunk, make sure we exhausted the name
  63. if len(pattern) == 0 && len(t) > 0 {
  64. continue
  65. }
  66. name = t
  67. continue Pattern
  68. }
  69. if err != nil {
  70. return false, err
  71. }
  72. }
  73. }
  74. return false, nil
  75. }
  76. return len(name) == 0, nil
  77. }
  78. // scanChunk gets the next segment of pattern, which is a non-star string
  79. // possibly preceded by a star.
  80. func scanChunk(pattern string) (star bool, chunk, rest string) {
  81. for len(pattern) > 0 && pattern[0] == '*' {
  82. pattern = pattern[1:]
  83. star = true
  84. }
  85. inrange := false
  86. var i int
  87. Scan:
  88. for i = 0; i < len(pattern); i++ {
  89. switch pattern[i] {
  90. case '\\':
  91. // error check handled in matchChunk: bad pattern.
  92. if i+1 < len(pattern) {
  93. i++
  94. }
  95. case '[':
  96. inrange = true
  97. case ']':
  98. inrange = false
  99. case '*':
  100. if !inrange {
  101. break Scan
  102. }
  103. }
  104. }
  105. return star, pattern[0:i], pattern[i:]
  106. }
  107. // matchChunk checks whether chunk matches the beginning of s.
  108. // If so, it returns the remainder of s (after the match).
  109. // Chunk is all single-character operators: literals, char classes, and ?.
  110. func matchChunk(chunk, s string) (rest string, ok bool, err error) {
  111. for len(chunk) > 0 {
  112. if len(s) == 0 {
  113. return
  114. }
  115. switch chunk[0] {
  116. case '[':
  117. // character class
  118. r, n := utf8.DecodeRuneInString(s)
  119. s = s[n:]
  120. chunk = chunk[1:]
  121. // possibly negated
  122. notNegated := true
  123. if len(chunk) > 0 && chunk[0] == '^' {
  124. notNegated = false
  125. chunk = chunk[1:]
  126. }
  127. // parse all ranges
  128. match := false
  129. nrange := 0
  130. for {
  131. if len(chunk) > 0 && chunk[0] == ']' && nrange > 0 {
  132. chunk = chunk[1:]
  133. break
  134. }
  135. var lo, hi rune
  136. if lo, chunk, err = getEsc(chunk); err != nil {
  137. return
  138. }
  139. hi = lo
  140. if chunk[0] == '-' {
  141. if hi, chunk, err = getEsc(chunk[1:]); err != nil {
  142. return
  143. }
  144. }
  145. if lo <= r && r <= hi {
  146. match = true
  147. }
  148. nrange++
  149. }
  150. if match != notNegated {
  151. return
  152. }
  153. case '?':
  154. if s[0] == '/' {
  155. return
  156. }
  157. _, n := utf8.DecodeRuneInString(s)
  158. s = s[n:]
  159. chunk = chunk[1:]
  160. case '\\':
  161. chunk = chunk[1:]
  162. if len(chunk) == 0 {
  163. err = ErrBadPattern
  164. return
  165. }
  166. fallthrough
  167. default:
  168. if chunk[0] != s[0] {
  169. return
  170. }
  171. s = s[1:]
  172. chunk = chunk[1:]
  173. }
  174. }
  175. return s, true, nil
  176. }
  177. // getEsc gets a possibly-escaped character from chunk, for a character class.
  178. func getEsc(chunk string) (r rune, nchunk string, err error) {
  179. if len(chunk) == 0 || chunk[0] == '-' || chunk[0] == ']' {
  180. err = ErrBadPattern
  181. return
  182. }
  183. if chunk[0] == '\\' {
  184. chunk = chunk[1:]
  185. if len(chunk) == 0 {
  186. err = ErrBadPattern
  187. return
  188. }
  189. }
  190. r, n := utf8.DecodeRuneInString(chunk)
  191. if r == utf8.RuneError && n == 1 {
  192. err = ErrBadPattern
  193. }
  194. nchunk = chunk[n:]
  195. if len(nchunk) == 0 {
  196. err = ErrBadPattern
  197. }
  198. return
  199. }