1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/strings/pattern.h"
6
7 #include "base/third_party/icu/icu_utf.h"
8
9 namespace base {
10
11 namespace {
12
IsWildcard(base_icu::UChar32 character)13 static bool IsWildcard(base_icu::UChar32 character) {
14 return character == '*' || character == '?';
15 }
16
17 // Move the strings pointers to the point where they start to differ.
18 template <typename CHAR, typename NEXT>
EatSameChars(const CHAR ** pattern,const CHAR * pattern_end,const CHAR ** string,const CHAR * string_end,NEXT next)19 static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end,
20 const CHAR** string, const CHAR* string_end,
21 NEXT next) {
22 const CHAR* escape = NULL;
23 while (*pattern != pattern_end && *string != string_end) {
24 if (!escape && IsWildcard(**pattern)) {
25 // We don't want to match wildcard here, except if it's escaped.
26 return;
27 }
28
29 // Check if the escapement char is found. If so, skip it and move to the
30 // next character.
31 if (!escape && **pattern == '\\') {
32 escape = *pattern;
33 next(pattern, pattern_end);
34 continue;
35 }
36
37 // Check if the chars match, if so, increment the ptrs.
38 const CHAR* pattern_next = *pattern;
39 const CHAR* string_next = *string;
40 base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end);
41 if (pattern_char == next(&string_next, string_end) &&
42 pattern_char != CBU_SENTINEL) {
43 *pattern = pattern_next;
44 *string = string_next;
45 } else {
46 // Uh oh, it did not match, we are done. If the last char was an
47 // escapement, that means that it was an error to advance the ptr here,
48 // let's put it back where it was. This also mean that the MatchPattern
49 // function will return false because if we can't match an escape char
50 // here, then no one will.
51 if (escape) {
52 *pattern = escape;
53 }
54 return;
55 }
56
57 escape = NULL;
58 }
59 }
60
61 template <typename CHAR, typename NEXT>
EatWildcard(const CHAR ** pattern,const CHAR * end,NEXT next)62 static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) {
63 while (*pattern != end) {
64 if (!IsWildcard(**pattern))
65 return;
66 next(pattern, end);
67 }
68 }
69
70 template <typename CHAR, typename NEXT>
MatchPatternT(const CHAR * eval,const CHAR * eval_end,const CHAR * pattern,const CHAR * pattern_end,int depth,NEXT next)71 static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end,
72 const CHAR* pattern, const CHAR* pattern_end,
73 int depth,
74 NEXT next) {
75 const int kMaxDepth = 16;
76 if (depth > kMaxDepth)
77 return false;
78
79 // Eat all the matching chars.
80 EatSameChars(&pattern, pattern_end, &eval, eval_end, next);
81
82 // If the string is empty, then the pattern must be empty too, or contains
83 // only wildcards.
84 if (eval == eval_end) {
85 EatWildcard(&pattern, pattern_end, next);
86 return pattern == pattern_end;
87 }
88
89 // Pattern is empty but not string, this is not a match.
90 if (pattern == pattern_end)
91 return false;
92
93 // If this is a question mark, then we need to compare the rest with
94 // the current string or the string with one character eaten.
95 const CHAR* next_pattern = pattern;
96 next(&next_pattern, pattern_end);
97 if (pattern[0] == '?') {
98 if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
99 depth + 1, next))
100 return true;
101 const CHAR* next_eval = eval;
102 next(&next_eval, eval_end);
103 if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end,
104 depth + 1, next))
105 return true;
106 }
107
108 // This is a *, try to match all the possible substrings with the remainder
109 // of the pattern.
110 if (pattern[0] == '*') {
111 // Collapse duplicate wild cards (********** into *) so that the
112 // method does not recurse unnecessarily. http://crbug.com/52839
113 EatWildcard(&next_pattern, pattern_end, next);
114
115 while (eval != eval_end) {
116 if (MatchPatternT(eval, eval_end, next_pattern, pattern_end,
117 depth + 1, next))
118 return true;
119 eval++;
120 }
121
122 // We reached the end of the string, let see if the pattern contains only
123 // wildcards.
124 if (eval == eval_end) {
125 EatWildcard(&pattern, pattern_end, next);
126 if (pattern != pattern_end)
127 return false;
128 return true;
129 }
130 }
131
132 return false;
133 }
134
135 struct NextCharUTF8 {
operator ()base::__anon61ef0a090111::NextCharUTF8136 base_icu::UChar32 operator()(const char** p, const char* end) {
137 base_icu::UChar32 c;
138 int offset = 0;
139 CBU8_NEXT(*p, offset, end - *p, c);
140 *p += offset;
141 return c;
142 }
143 };
144
145 struct NextCharUTF16 {
operator ()base::__anon61ef0a090111::NextCharUTF16146 base_icu::UChar32 operator()(const char16** p, const char16* end) {
147 base_icu::UChar32 c;
148 int offset = 0;
149 CBU16_NEXT(*p, offset, end - *p, c);
150 *p += offset;
151 return c;
152 }
153 };
154
155 } // namespace
156
MatchPattern(const StringPiece & eval,const StringPiece & pattern)157 bool MatchPattern(const StringPiece& eval, const StringPiece& pattern) {
158 return MatchPatternT(eval.data(), eval.data() + eval.size(),
159 pattern.data(), pattern.data() + pattern.size(),
160 0, NextCharUTF8());
161 }
162
MatchPattern(const StringPiece16 & eval,const StringPiece16 & pattern)163 bool MatchPattern(const StringPiece16& eval, const StringPiece16& pattern) {
164 return MatchPatternT(eval.data(), eval.data() + eval.size(),
165 pattern.data(), pattern.data() + pattern.size(),
166 0, NextCharUTF16());
167 }
168
169 } // namespace base
170