1 /*=============================================================================
2 Copyright (c) 2013 Daniel James
4 Use, modification and distribution is subject to the Boost Software
5 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 http://www.boost.org/LICENSE_1_0.txt)
7 =============================================================================*/
14 typedef string_iterator glob_iterator
;
16 void check_glob_range(glob_iterator
&, glob_iterator
);
17 void check_glob_escape(glob_iterator
&, glob_iterator
);
20 glob_iterator
& pattern_begin
,
21 glob_iterator pattern_end
,
22 glob_iterator
& filename_begin
,
23 glob_iterator
& filename_end
);
25 glob_iterator
& pattern_begin
, glob_iterator pattern_end
, char x
);
27 // Is pattern a glob or a plain file name?
28 // Throws glob_error if pattern is an invalid glob.
29 bool check_glob(quickbook::string_view pattern
)
34 glob_iterator begin
= pattern
.begin();
35 glob_iterator end
= pattern
.end();
37 while (begin
!= end
) {
38 if (*begin
< 32 || (*begin
& 0x80)) is_ascii
= false;
42 check_glob_escape(begin
, end
);
46 check_glob_range(begin
, end
);
51 throw glob_error("uneven square brackets");
62 if (begin
!= end
&& *begin
== '*') {
63 throw glob_error("'**' not supported");
72 if (is_glob
&& !is_ascii
)
73 throw glob_error("invalid character, globs are ascii only");
78 void check_glob_range(glob_iterator
& begin
, glob_iterator end
)
80 assert(begin
!= end
&& *begin
== '[');
83 if (*begin
== ']') throw glob_error("empty range");
85 while (begin
!= end
) {
91 throw glob_error("trailing escape");
93 else if (*begin
== '\\' || *begin
== '/') {
94 throw glob_error("contains escaped slash");
100 throw glob_error("nested square brackets");
105 throw glob_error("slash in square brackets");
111 throw glob_error("uneven square brackets");
114 void check_glob_escape(glob_iterator
& begin
, glob_iterator end
)
116 assert(begin
!= end
&& *begin
== '\\');
121 throw glob_error("trailing escape");
123 else if (*begin
== '\\' || *begin
== '/') {
124 throw glob_error("contains escaped slash");
130 // Does filename match pattern?
131 // Might throw glob_error if pattern is an invalid glob,
132 // but should call check_glob first to validate the glob.
134 quickbook::string_view
const& pattern
,
135 quickbook::string_view
const& filename
)
137 // If there wasn't this special case then '*' would match an
139 if (filename
.empty()) return pattern
.empty();
141 glob_iterator pattern_it
= pattern
.begin();
142 glob_iterator pattern_end
= pattern
.end();
144 glob_iterator filename_it
= filename
.begin();
145 glob_iterator filename_end
= filename
.end();
147 if (!match_section(pattern_it
, pattern_end
, filename_it
, filename_end
))
150 while (pattern_it
!= pattern_end
) {
151 assert(*pattern_it
== '*');
154 if (pattern_it
== pattern_end
) return true;
156 if (*pattern_it
== '*') {
157 throw glob_error("'**' not supported");
161 if (filename_it
== filename_end
) return false;
163 pattern_it
, pattern_end
, filename_it
, filename_end
))
169 return filename_it
== filename_end
;
173 glob_iterator
& pattern_begin
,
174 glob_iterator pattern_end
,
175 glob_iterator
& filename_begin
,
176 glob_iterator
& filename_end
)
178 glob_iterator pattern_it
= pattern_begin
;
179 glob_iterator filename_it
= filename_begin
;
181 while (pattern_it
!= pattern_end
&& *pattern_it
!= '*') {
182 if (filename_it
== filename_end
) return false;
184 switch (*pattern_it
) {
187 throw new glob_error("Internal error");
189 if (!match_range(pattern_it
, pattern_end
, *filename_it
))
194 throw glob_error("uneven square brackets");
201 if (pattern_it
== pattern_end
) {
202 throw glob_error("trailing escape");
204 else if (*pattern_it
== '\\' || *pattern_it
== '/') {
205 throw glob_error("contains escaped slash");
209 if (*pattern_it
!= *filename_it
) return false;
215 if (pattern_it
== pattern_end
&& filename_it
!= filename_end
)
218 pattern_begin
= pattern_it
;
219 filename_begin
= filename_it
;
224 glob_iterator
& pattern_begin
, glob_iterator pattern_end
, char x
)
226 assert(pattern_begin
!= pattern_end
&& *pattern_begin
== '[');
228 if (pattern_begin
== pattern_end
) {
229 throw glob_error("uneven square brackets");
232 bool invert_match
= false;
233 bool matched
= false;
235 if (*pattern_begin
== '^') {
238 if (pattern_begin
== pattern_end
) {
239 throw glob_error("uneven square brackets");
242 else if (*pattern_begin
== ']') {
243 throw glob_error("empty range");
246 // Search for a match
248 unsigned char first
= *pattern_begin
;
250 if (first
== ']') break;
252 throw glob_error("nested square brackets");
254 if (pattern_begin
== pattern_end
) {
255 throw glob_error("uneven square brackets");
259 first
= *pattern_begin
;
260 if (first
== '\\' || first
== '/') {
261 throw glob_error("contains escaped slash");
264 if (pattern_begin
== pattern_end
) {
265 throw glob_error("uneven square brackets");
268 else if (first
== '/') {
269 throw glob_error("slash in square brackets");
272 if (*pattern_begin
!= '-') {
273 matched
= matched
|| (first
== x
);
277 if (pattern_begin
== pattern_end
) {
278 throw glob_error("uneven square brackets");
281 unsigned char second
= *pattern_begin
;
284 matched
= matched
|| (first
== x
) || (x
== '-');
287 if (pattern_begin
== pattern_end
) {
288 throw glob_error("uneven square brackets");
291 if (second
== '\\') {
292 second
= *pattern_begin
;
293 if (second
== '\\' || second
== '/') {
294 throw glob_error("contains escaped slash");
297 if (pattern_begin
== pattern_end
) {
298 throw glob_error("uneven square brackets");
301 else if (second
== '/') {
302 throw glob_error("slash in square brackets");
305 matched
= matched
|| (first
<= x
&& x
<= second
);
309 return invert_match
!= matched
;
312 std::size_t find_glob_char(quickbook::string_view pattern
, std::size_t pos
)
314 // Weird style is because quickbook::string_view's find_first_of
315 // doesn't take a position argument.
316 std::size_t removed
= 0;
319 pos
= pattern
.find_first_of("[]?*\\");
320 if (pos
== quickbook::string_view::npos
) return pos
;
321 if (pattern
[pos
] != '\\') return pos
+ removed
;
322 pattern
.remove_prefix(pos
+ 2);
327 std::string
glob_unescape(quickbook::string_view pattern
)
332 std::size_t pos
= pattern
.find("\\");
333 if (pos
== quickbook::string_view::npos
) {
334 result
.append(pattern
.data(), pattern
.size());
338 result
.append(pattern
.data(), pos
);
340 if (pos
< pattern
.size()) {
341 result
+= pattern
[pos
];
344 pattern
.remove_prefix(pos
);