(empty log message)
@@ -0,0 +1,693 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <string.h> | |
3 | +#include <stdlib.h> | |
4 | + | |
5 | +#define bool _Bool | |
6 | +#define true 1 | |
7 | +#define false 0 | |
8 | +#define u32 unsigned int | |
9 | +#define u8 unsigned char | |
10 | + | |
11 | +struct cs_path_info { | |
12 | + const char *name; | |
13 | + u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
14 | + u32 total_len; /* = strlen(name) */ | |
15 | + u32 const_len; /* = cs_const_part_length(name) */ | |
16 | +}; | |
17 | + | |
18 | +static void out_of_memory(void) | |
19 | +{ | |
20 | + fprintf(stderr, "Out of memory\n"); | |
21 | + exit(1); | |
22 | +} | |
23 | + | |
24 | +/* Copied from Linux kernel source code. */ | |
25 | +static unsigned int full_name_hash(const unsigned char *name, unsigned int len) | |
26 | +{ | |
27 | + unsigned long hash = 0; | |
28 | + while (len--) { | |
29 | + unsigned long c = *name++; | |
30 | + hash = (hash + (c << 4) + (c >> 4)) * 11; | |
31 | + } | |
32 | + return (unsigned int) hash; | |
33 | +} | |
34 | + | |
35 | +/** | |
36 | + * cs_pathcmp - strcmp() for "struct cs_path_info" structure. | |
37 | + * | |
38 | + * @a: Pointer to "struct cs_path_info". | |
39 | + * @b: Pointer to "struct cs_path_info". | |
40 | + * | |
41 | + * Returns true if @a != @b, false otherwise. | |
42 | + */ | |
43 | +static inline bool cs_pathcmp(const struct cs_path_info *a, | |
44 | + const struct cs_path_info *b) | |
45 | +{ | |
46 | + return a->hash != b->hash || strcmp(a->name, b->name); | |
47 | +} | |
48 | + | |
49 | +/** | |
50 | + * cs_const_part_length - Evaluate the initial length without a pattern in a token. | |
51 | + * | |
52 | + * @filename: The string to evaluate. Maybe NULL. | |
53 | + * | |
54 | + * Returns the initial length without a pattern in @filename. | |
55 | + */ | |
56 | +static int cs_const_part_length(const char *filename) | |
57 | +{ | |
58 | + char c; | |
59 | + int len = 0; | |
60 | + if (!filename) | |
61 | + return 0; | |
62 | + while (1) { | |
63 | + c = *filename++; | |
64 | + if (!c) | |
65 | + break; | |
66 | + if (c != '\\') { | |
67 | + len++; | |
68 | + continue; | |
69 | + } | |
70 | + c = *filename++; | |
71 | + switch (c) { | |
72 | + case '0': /* "\ooo" */ | |
73 | + case '1': | |
74 | + case '2': | |
75 | + case '3': | |
76 | + c = *filename++; | |
77 | + if (c < '0' || c > '7') | |
78 | + break; | |
79 | + c = *filename++; | |
80 | + if (c < '0' || c > '7') | |
81 | + break; | |
82 | + len += 4; | |
83 | + continue; | |
84 | + } | |
85 | + break; | |
86 | + } | |
87 | + return len; | |
88 | +} | |
89 | + | |
90 | +/** | |
91 | + * cs_fill_path_info - Fill in "struct cs_path_info" members. | |
92 | + * | |
93 | + * @ptr: Pointer to "struct cs_path_info" to fill in. | |
94 | + * | |
95 | + * Returns nothing. | |
96 | + * | |
97 | + * The caller sets "struct cs_path_info"->name. | |
98 | + */ | |
99 | +static void cs_fill_path_info(struct cs_path_info *ptr) | |
100 | +{ | |
101 | + const char *name = ptr->name; | |
102 | + const int len = strlen(name); | |
103 | + ptr->total_len = len; | |
104 | + ptr->const_len = cs_const_part_length(name); | |
105 | + ptr->hash = full_name_hash((const unsigned char *) name, len); | |
106 | +} | |
107 | + | |
108 | +/** | |
109 | + * cs_byte_range - Check whether the string is a \ooo style octal value. | |
110 | + * | |
111 | + * @str: Pointer to the string. | |
112 | + * | |
113 | + * Returns true if @str is a \ooo style octal value, false otherwise. | |
114 | + */ | |
115 | +static bool cs_byte_range(const char *str) | |
116 | +{ | |
117 | + return *str >= '0' && *str++ <= '3' && | |
118 | + *str >= '0' && *str++ <= '7' && | |
119 | + *str >= '0' && *str <= '7'; | |
120 | +} | |
121 | + | |
122 | +/** | |
123 | + * cs_decimal - Check whether the character is a decimal character. | |
124 | + * | |
125 | + * @c: The character to check. | |
126 | + * | |
127 | + * Returns true if @c is a decimal character, false otherwise. | |
128 | + */ | |
129 | +static bool cs_decimal(const char c) | |
130 | +{ | |
131 | + return c >= '0' && c <= '9'; | |
132 | +} | |
133 | + | |
134 | +/** | |
135 | + * cs_hexadecimal - Check whether the character is a hexadecimal character. | |
136 | + * | |
137 | + * @c: The character to check. | |
138 | + * | |
139 | + * Returns true if @c is a hexadecimal character, false otherwise. | |
140 | + */ | |
141 | +static bool cs_hexadecimal(const char c) | |
142 | +{ | |
143 | + return (c >= '0' && c <= '9') || | |
144 | + (c >= 'A' && c <= 'F') || | |
145 | + (c >= 'a' && c <= 'f'); | |
146 | +} | |
147 | + | |
148 | +/** | |
149 | + * cs_alphabet_char - Check whether the character is an alphabet. | |
150 | + * | |
151 | + * @c: The character to check. | |
152 | + * | |
153 | + * Returns true if @c is an alphabet character, false otherwise. | |
154 | + */ | |
155 | +static bool cs_alphabet_char(const char c) | |
156 | +{ | |
157 | + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
158 | +} | |
159 | + | |
160 | +/** | |
161 | + * cs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | |
162 | + * | |
163 | + * @filename: The start of string to check. | |
164 | + * @filename_end: The end of string to check. | |
165 | + * @pattern: The start of pattern to compare. | |
166 | + * @pattern_end: The end of pattern to compare. | |
167 | + * | |
168 | + * Returns true if @filename matches @pattern, false otherwise. | |
169 | + */ | |
170 | +static bool cs_file_matches_pattern2(const char *filename, | |
171 | + const char *filename_end, | |
172 | + const char *pattern, | |
173 | + const char *pattern_end) | |
174 | +{ | |
175 | + while (filename < filename_end && pattern < pattern_end) { | |
176 | + char c; | |
177 | + if (*pattern != '\\') { | |
178 | + if (*filename++ != *pattern++) | |
179 | + return false; | |
180 | + continue; | |
181 | + } | |
182 | + c = *filename; | |
183 | + pattern++; | |
184 | + switch (*pattern) { | |
185 | + int i; | |
186 | + int j; | |
187 | + case '?': | |
188 | + if (c == '/') { | |
189 | + return false; | |
190 | + } else if (c == '\\') { | |
191 | + if (cs_byte_range(filename + 1)) | |
192 | + filename += 3; | |
193 | + else | |
194 | + return false; | |
195 | + } | |
196 | + break; | |
197 | + case '+': | |
198 | + if (!cs_decimal(c)) | |
199 | + return false; | |
200 | + break; | |
201 | + case 'x': | |
202 | + if (!cs_hexadecimal(c)) | |
203 | + return false; | |
204 | + break; | |
205 | + case 'a': | |
206 | + if (!cs_alphabet_char(c)) | |
207 | + return false; | |
208 | + break; | |
209 | + case '0': | |
210 | + case '1': | |
211 | + case '2': | |
212 | + case '3': | |
213 | + if (c == '\\' && cs_byte_range(filename + 1) | |
214 | + && !strncmp(filename + 1, pattern, 3)) { | |
215 | + filename += 3; | |
216 | + pattern += 2; | |
217 | + break; | |
218 | + } | |
219 | + return false; /* Not matched. */ | |
220 | + case '*': | |
221 | + case '@': | |
222 | + for (i = 0; i <= filename_end - filename; i++) { | |
223 | + if (cs_file_matches_pattern2(filename + i, | |
224 | + filename_end, | |
225 | + pattern + 1, | |
226 | + pattern_end)) | |
227 | + return true; | |
228 | + c = filename[i]; | |
229 | + if (c == '.' && *pattern == '@') | |
230 | + break; | |
231 | + if (c != '\\') | |
232 | + continue; | |
233 | + if (cs_byte_range(filename + i + 1)) | |
234 | + i += 3; | |
235 | + else | |
236 | + break; /* Bad pattern. */ | |
237 | + } | |
238 | + return false; /* Not matched. */ | |
239 | + default: | |
240 | + j = 0; | |
241 | + c = *pattern; | |
242 | + if (c == '$') { | |
243 | + while (cs_decimal(filename[j])) | |
244 | + j++; | |
245 | + } else if (c == 'X') { | |
246 | + while (cs_hexadecimal(filename[j])) | |
247 | + j++; | |
248 | + } else if (c == 'A') { | |
249 | + while (cs_alphabet_char(filename[j])) | |
250 | + j++; | |
251 | + } | |
252 | + for (i = 1; i <= j; i++) { | |
253 | + if (cs_file_matches_pattern2(filename + i, | |
254 | + filename_end, | |
255 | + pattern + 1, | |
256 | + pattern_end)) | |
257 | + return true; | |
258 | + } | |
259 | + return false; /* Not matched or bad pattern. */ | |
260 | + } | |
261 | + filename++; | |
262 | + pattern++; | |
263 | + } | |
264 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
265 | + while (*pattern == '\\' && | |
266 | + (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | |
267 | + pattern += 2; | |
268 | + return filename == filename_end && pattern == pattern_end; | |
269 | +} | |
270 | + | |
271 | +/** | |
272 | + * cs_file_matches_pattern - Pattern matching without '/' character. | |
273 | + * | |
274 | + * @filename: The start of string to check. | |
275 | + * @filename_end: The end of string to check. | |
276 | + * @pattern: The start of pattern to compare. | |
277 | + * @pattern_end: The end of pattern to compare. | |
278 | + * | |
279 | + * Returns true if @filename matches @pattern, false otherwise. | |
280 | + */ | |
281 | +static bool cs_file_matches_pattern(const char *filename, | |
282 | + const char *filename_end, | |
283 | + const char *pattern, | |
284 | + const char *pattern_end) | |
285 | +{ | |
286 | + const char *pattern_start = pattern; | |
287 | + bool first = true; | |
288 | + bool result; | |
289 | + if (filename_end > filename && memchr(filename, '/', filename_end - filename)) { | |
290 | + printf("'"); | |
291 | + fwrite(filename, 1, filename_end - filename, stdout); | |
292 | + printf("', '"); | |
293 | + fwrite(pattern, 1, pattern_end - pattern, stdout); | |
294 | + printf("'\n"); | |
295 | + } | |
296 | + if (pattern_end > pattern && memchr(pattern, '/', pattern_end - pattern)) { | |
297 | + printf("'"); | |
298 | + fwrite(filename, 1, filename_end - filename, stdout); | |
299 | + printf("', '"); | |
300 | + fwrite(pattern, 1, pattern_end - pattern, stdout); | |
301 | + printf("'\n"); | |
302 | + } | |
303 | + while (pattern < pattern_end - 1) { | |
304 | + /* Split at "\-" pattern. */ | |
305 | + if (*pattern++ != '\\' || *pattern++ != '-') | |
306 | + continue; | |
307 | + result = cs_file_matches_pattern2(filename, filename_end, | |
308 | + pattern_start, pattern - 2); | |
309 | + if (first) | |
310 | + result = !result; | |
311 | + if (result) | |
312 | + return false; | |
313 | + first = false; | |
314 | + pattern_start = pattern; | |
315 | + } | |
316 | + result = cs_file_matches_pattern2(filename, filename_end, | |
317 | + pattern_start, pattern_end); | |
318 | + return first ? result : !result; | |
319 | +} | |
320 | + | |
321 | +/** | |
322 | + * cs_path_matches_pattern2 - Do pathname pattern matching. | |
323 | + * | |
324 | + * @f: The start of string to check. | |
325 | + * @p: The start of pattern to compare. | |
326 | + * | |
327 | + * Returns true if @f matches @p, false otherwise. | |
328 | + */ | |
329 | +static bool cs_path_matches_pattern2(const char *f, const char *p) | |
330 | +{ | |
331 | + const char *f_delimiter; | |
332 | + const char *p_delimiter; | |
333 | + while (*f && *p) { | |
334 | + f_delimiter = strchr(f + 1, '/'); | |
335 | + if (!f_delimiter) | |
336 | + f_delimiter = f + strlen(f); | |
337 | + p_delimiter = strchr(p + 1, '/'); | |
338 | + if (!p_delimiter) | |
339 | + p_delimiter = p + strlen(p); | |
340 | + if (*p == '/' && *(p + 1) == '\\') { | |
341 | + if (*(p + 2) == '(') { | |
342 | + /* Check zero repetition. */ | |
343 | + if (cs_path_matches_pattern2(f, p_delimiter)) | |
344 | + return true; | |
345 | + /* Check one or more repetition. */ | |
346 | + goto repetition; | |
347 | + } | |
348 | + if (*(p + 2) == '{') | |
349 | + goto repetition; | |
350 | + | |
351 | + } | |
352 | + if ((*f == '/' || *p == '/') && *f++ != *p++) | |
353 | + return false; | |
354 | + if (!cs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) | |
355 | + return false; | |
356 | + f = f_delimiter; | |
357 | + p = p_delimiter; | |
358 | + } | |
359 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
360 | + while (*p == '\\' && (*(p + 1) == '*' || *(p + 1) == '@')) | |
361 | + p += 2; | |
362 | + return !*f && !*p; | |
363 | +repetition: | |
364 | + do { | |
365 | + /* Compare current component with pattern. */ | |
366 | + if (!cs_file_matches_pattern(f + 1, f_delimiter, | |
367 | + p + 3, p_delimiter - 2)) | |
368 | + break; | |
369 | + /* Proceed to next component. */ | |
370 | + f = f_delimiter; | |
371 | + if (!*f) | |
372 | + break; | |
373 | + /* Continue comparison. */ | |
374 | + if (cs_path_matches_pattern2(f, p_delimiter)) | |
375 | + return true; | |
376 | + f_delimiter = strchr(f + 1, '/'); | |
377 | + } while (f_delimiter); | |
378 | + return false; /* Not matched. */ | |
379 | +} | |
380 | + | |
381 | +/** | |
382 | + * cs_path_matches_pattern - Check whether the given filename matches the given pattern. | |
383 | + * | |
384 | + * @filename: The filename to check. | |
385 | + * @pattern: The pattern to compare. | |
386 | + * | |
387 | + * Returns true if matches, false otherwise. | |
388 | + * | |
389 | + * The following patterns are available. | |
390 | + * \ooo Octal representation of a byte. | |
391 | + * \* Zero or more repetitions of characters other than '/'. | |
392 | + * \@ Zero or more repetitions of characters other than '/' or '.'. | |
393 | + * \? 1 byte character other than '/'. | |
394 | + * \$ One or more repetitions of decimal digits. | |
395 | + * \+ 1 decimal digit. | |
396 | + * \X One or more repetitions of hexadecimal digits. | |
397 | + * \x 1 hexadecimal digit. | |
398 | + * \A One or more repetitions of alphabet characters. | |
399 | + * \a 1 alphabet character. | |
400 | + * | |
401 | + * \- Subtraction operator. | |
402 | + * | |
403 | + * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | |
404 | + * /dir/dir/dir/ ). | |
405 | + * | |
406 | + * /\(dir\)/ '/' + 'Zero or more repetitions of dir/' (e.g. / /dir/ | |
407 | + * /dir/dir/ ). | |
408 | + */ | |
409 | +static bool cs_path_matches_pattern(const struct cs_path_info *filename, | |
410 | + const struct cs_path_info *pattern) | |
411 | +{ | |
412 | + const char *f = filename->name; | |
413 | + const char *p = pattern->name; | |
414 | + const int len = pattern->const_len; | |
415 | + /* If @pattern doesn't contain pattern, I can use strcmp(). */ | |
416 | + if (len == pattern->total_len) | |
417 | + return !cs_pathcmp(filename, pattern); | |
418 | + /* Compare the initial length without patterns. */ | |
419 | + if (len) { | |
420 | + if (strncmp(f, p, len)) | |
421 | + return false; | |
422 | + f += len - 1; | |
423 | + p += len - 1; | |
424 | + } | |
425 | + return cs_path_matches_pattern2(f, p); | |
426 | +} | |
427 | + | |
428 | +/** | |
429 | + * cs_correct_word - Check whether the given string follows the naming rules. | |
430 | + * | |
431 | + * @string: The string to check. | |
432 | + * @allow_pattern: True if allow use of patterns, false otherwise. | |
433 | + * | |
434 | + * Returns true if @string follows the naming rules, false otherwise. | |
435 | + */ | |
436 | +static bool cs_correct_word(const char *string, bool allow_pattern) | |
437 | +{ | |
438 | + u8 recursion = 20; | |
439 | + const char *const start = string; | |
440 | + u8 in_repetition = 0; | |
441 | + if (!*string) | |
442 | + goto out; | |
443 | + while (*string) { | |
444 | + unsigned char c = *string++; | |
445 | + if (in_repetition && c == '/') | |
446 | + goto out; | |
447 | + if (c <= ' ' || c >= 127) | |
448 | + goto out; | |
449 | + if (c != '\\') | |
450 | + continue; | |
451 | + c = *string++; | |
452 | + if (c >= '0' && c <= '3') { | |
453 | + unsigned char d; | |
454 | + unsigned char e; | |
455 | + d = *string++; | |
456 | + if (d < '0' || d > '7') | |
457 | + goto out; | |
458 | + e = *string++; | |
459 | + if (e < '0' || e > '7') | |
460 | + goto out; | |
461 | + c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
462 | + if (c <= ' ' || c >= 127 || c == '\\') | |
463 | + continue; | |
464 | + goto out; | |
465 | + } | |
466 | + if (!allow_pattern) | |
467 | + goto out; | |
468 | + switch (c) { | |
469 | + case '+': /* "\+" */ | |
470 | + case '?': /* "\?" */ | |
471 | + case 'x': /* "\x" */ | |
472 | + case 'a': /* "\a" */ | |
473 | + case '-': /* "\-" */ | |
474 | + continue; | |
475 | + } | |
476 | + /* Reject too deep wildcard that consumes too much stack. */ | |
477 | + if (!recursion--) | |
478 | + goto out; | |
479 | + switch (c) { | |
480 | + case '*': /* "\*" */ | |
481 | + case '@': /* "\@" */ | |
482 | + case '$': /* "\$" */ | |
483 | + case 'X': /* "\X" */ | |
484 | + case 'A': /* "\A" */ | |
485 | + continue; | |
486 | + case '{': /* "/\{" */ | |
487 | + if (string - 3 < start || *(string - 3) != '/') | |
488 | + goto out; | |
489 | + in_repetition = 1; | |
490 | + continue; | |
491 | + case '}': /* "\}/" */ | |
492 | + if (in_repetition != 1 || *string++ != '/') | |
493 | + goto out; | |
494 | + in_repetition = 0; | |
495 | + continue; | |
496 | + case '(': /* "/\(" */ | |
497 | + if (string - 3 < start || *(string - 3) != '/') | |
498 | + goto out; | |
499 | + in_repetition = 2; | |
500 | + continue; | |
501 | + case ')': /* "\)/" */ | |
502 | + if (in_repetition != 2 || *string++ != '/') | |
503 | + goto out; | |
504 | + in_repetition = 0; | |
505 | + continue; | |
506 | + } | |
507 | + goto out; | |
508 | + } | |
509 | + if (in_repetition) | |
510 | + goto out; | |
511 | + return true; | |
512 | +out: | |
513 | + return false; | |
514 | +} | |
515 | + | |
516 | +/** | |
517 | + * cs_get_name - Allocate memory for string data. | |
518 | + * | |
519 | + * @name: The string to save. | |
520 | + * | |
521 | + * Returns pointer to "struct cs_path_info" on success, NULL otherwise. | |
522 | + */ | |
523 | +static struct cs_path_info *cs_get_name(const char *name) | |
524 | +{ | |
525 | + struct cs_path_info *ptr = | |
526 | + (struct cs_path_info *) malloc(sizeof(struct cs_path_info)); | |
527 | + if (!ptr) | |
528 | + out_of_memory(); | |
529 | + ptr->name = strdup(name); | |
530 | + if (!ptr->name) | |
531 | + out_of_memory(); | |
532 | + cs_fill_path_info(ptr); | |
533 | + return ptr; | |
534 | +} | |
535 | + | |
536 | +/** | |
537 | + * cs_put_name - Free memory for string data. | |
538 | + * | |
539 | + * @name: Pointer to "struct cs_path_info". Maybe NULL. | |
540 | + * | |
541 | + * Returns nothing. | |
542 | + */ | |
543 | +static void cs_put_name(struct cs_path_info *name) | |
544 | +{ | |
545 | + if (name) { | |
546 | + free((void *) name->name); | |
547 | + free(name); | |
548 | + } | |
549 | +} | |
550 | + | |
551 | +/** | |
552 | + * cs_normalize_line - Format string. | |
553 | + * | |
554 | + * @buffer: The line to normalize. | |
555 | + * | |
556 | + * Returns nothing. | |
557 | + * | |
558 | + * Leading and trailing whitespaces are removed. | |
559 | + * Multiple whitespaces are packed into single space. | |
560 | + */ | |
561 | +static void cs_normalize_line(char *buffer) | |
562 | +{ | |
563 | + unsigned char *sp = (unsigned char *) buffer; | |
564 | + unsigned char *dp = (unsigned char *) buffer; | |
565 | + bool first = true; | |
566 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
567 | + sp++; | |
568 | + while (*sp) { | |
569 | + if (!first) | |
570 | + *dp++ = ' '; | |
571 | + first = false; | |
572 | + while (*sp > ' ' && *sp < 127) | |
573 | + *dp++ = *sp++; | |
574 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
575 | + sp++; | |
576 | + } | |
577 | + *dp = '\0'; | |
578 | +} | |
579 | + | |
580 | +static struct testcase { | |
581 | + const char *pathname; | |
582 | + const char *pattern; | |
583 | + _Bool match; | |
584 | +} testcases[] = { | |
585 | + { "/tmp/000", "/tmp/\\*", 1 }, | |
586 | + { "/tmp/000", "/tmp/\\@", 1 }, | |
587 | + { "/tmp/000", "/tmp/\\?\\?\\?", 1 }, | |
588 | + { "/tmp/000", "/tmp/\\*\\$\\@\\$", 1 }, | |
589 | + { "/tmp/000\\040111", "/tmp/\\*", 1 }, | |
590 | + { "/tmp/000\\040111", "/tmp/\\X", 0 }, | |
591 | + { "/tmp/000\\040111", "/tmp/\\$\\?\\$", 1 }, | |
592 | + { "/tmp/000111", "/tmp/\\(\\*\\)/\\*", 1 }, | |
593 | + { "/tmp/000/111", "/tmp/\\(\\*\\)/\\*", 1 }, | |
594 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\(\\*\\)/\\*", 1 }, | |
595 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\$\\}/\\$", 1 }, | |
596 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\a\\}/\\$", 0 }, | |
597 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\$\\-\\a\\}/\\$", 1 }, | |
598 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\x\\-\\a\\}/\\$", 1 }, | |
599 | + { "/tmp/\\001/\\002/\\040/^/$", "/tmp/\\{\\*\\-\\a\\}/\\?", 1 }, | |
600 | + { "/tmp/\\001/\\002/\\040/^/$", "/tmp/\\{\\*\\-\\a\\-\\x\\}/\\?", 1 }, | |
601 | + { "/tmp/$", "/tmp/\\*\\-\\a\\-\\x", 1 }, | |
602 | + { "/bin/true", "/bin/\\*", 1 }, | |
603 | + { "/bin/true", "/bin\\@\\*/\\*", 1 }, | |
604 | + { "/usr/local/", "/usr/\\*/", 1 }, | |
605 | + { "/usr/local/", "/usr/\\*\\*\\@\\*/", 1 }, | |
606 | + { "pipe:[12345]", "pipe:[\\$]", 1 }, | |
607 | + { "socket:[family=1:type=2:protocol=3]", "socket:[family=1:type=2:protocol=\\$]", 1 }, | |
608 | + { "http://tomoyo.osdn.jp/", "\\*/\\*/\\*/", 1 }, | |
609 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*", 1 }, | |
610 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*\\*\\@\\*\\@", 1 }, | |
611 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\@\\*/\\*\\@/\\*\\@\\*\\@\\*", 1 }, | |
612 | + { "http://tomoyo.osdn.jp/1.8/index.html", "http://\\{\\*\\}/\\@.html", 1 }, | |
613 | + { "http://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, | |
614 | + { "http://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, | |
615 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/security/ccsecurity/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
616 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/security/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
617 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
618 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x//ccs-patch///security//ccsecurity///?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\-.\\-..\\-\\*%\\*\\}/?root=tomoyo\\*\\*", 1 }, | |
619 | + { "/var/www/html/test/test/test/index.html", "/var/www/html/\\{test\\}/\\*.html", 1 }, | |
620 | + { "/etc/skel/", "/etc/\\{\\*\\}/\\*/", 0 }, | |
621 | + { "/etc/passwd", "/etc/\\{\\*\\}/\\*", 0 }, | |
622 | + { "/bin/true", "/bin/\\*/", 0 }, | |
623 | + { "/bin/", "/bin/\\*", 1 }, | |
624 | + { "/bin/", "/bin/\\@", 1 }, | |
625 | + { "/bin/", "/bin/\\@\\@", 1 }, | |
626 | + { "http://tomoyo.osdn.jp/", "\\*/\\*/\\*/\\?", 0 }, | |
627 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\@", 0 }, | |
628 | + { "http://tomoyo.osdn.jp/index.html", "http://\\*/\\@", 0 }, | |
629 | + { "socket:[family=1:type=2:protocol=3]", "/\\{\\*\\}/socket:[\\*]", 0 }, | |
630 | + { "/", "/\\(\\*\\)/\\*", 1 }, | |
631 | + { "/", "/\\{\\*\\}/\\*", 0 }, | |
632 | + { "/foo/", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 1 }, | |
633 | + { "/foo/", "/foo/\\{\\*\\}/\\*", 0 }, | |
634 | + { "/foo/bar/", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 1 }, | |
635 | + { "/foo/bar/", "/foo/\\{\\*\\}/\\*", 1 }, | |
636 | + { "/foo/bar", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/bar", 1 }, | |
637 | + { "/foo/bar", "/foo/\\{\\*\\}/bar", 0 }, | |
638 | + { "/foo/bar", "/foo/\\*/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 0 }, | |
639 | + { "/foo/bar/", "/foo/\\(\\*\\)/", 1 }, | |
640 | + { "/foo/bar/", "/foo/\\*/\\(\\*\\)/", 1 }, | |
641 | + { "/foo/bar/", "/foo/\\*/\\*/\\(\\*\\)/", 0 }, | |
642 | + { "/foo/bar/", "/foo/\\*/\\(\\*\\)/\\*/", 0 }, | |
643 | + { "/foo/", "/foo/\\(\\*\\)/", 1 }, | |
644 | + { "abc", "abc", 1 }, | |
645 | + { "abc", "\\A\\A\\A", 1 }, | |
646 | + { "abc", "\\X\\X\\X", 1 }, | |
647 | + { "abc", "\\*\\*\\*", 1 }, | |
648 | + { "abc", "\\@\\@\\@", 1 }, | |
649 | + { "abc", "\\a\\@\\x", 1 }, | |
650 | + { "abc", "\\?\\?\\?", 1 }, | |
651 | + { "abc", "\\?\\@\\?\\*", 1 }, | |
652 | + { "abc", "\\?\\@\\?\\*\\?", 1 }, | |
653 | + { "abc", "def", 0 }, | |
654 | + { "abc/def", "\\*/\\(\\X\\)/\\X", 1 }, | |
655 | + { "abc/def", "\\*/\\{\\X\\}/\\X", 0 }, | |
656 | + { "abc/def/012", "\\*/\\(\\X\\)/\\X", 1 }, | |
657 | + { "abc/def/012", "\\*/\\{\\X\\}/\\X", 1 }, | |
658 | + { "abc/def/012/345/6789", "\\*/\\(\\X\\)/\\X", 1 }, | |
659 | + { "abc/def/012/345/6789", "\\*/\\{\\X\\}/\\X", 1 }, | |
660 | + { "abc/345/012/def/6789", "\\*/\\(\\$\\)/\\X", 0 }, | |
661 | + { "abc/345/012/def/6789", "\\*/\\(\\*\\)/\\*/", 0 }, | |
662 | + { "abc/345/012/def/6789/////1//23///", "\\*/\\(\\*\\)/\\*/", 1 }, | |
663 | + { "abc/345/012/def/6789/////1//23//./", "\\*/\\(\\*\\)/\\?/", 1 }, | |
664 | + { "abc/345/012/def/6789//1//23//.", "\\*/\\(\\*\\)/\\?/", 0 }, | |
665 | + { "abc/345/012/def/6789//1//23//", "\\*/\\(\\*\\)/\\?", 0 }, | |
666 | + { "abc", "abc/\\*", 0 }, | |
667 | + { "abc/", "abc/\\*", 1 }, | |
668 | + { NULL, NULL, 0 }, | |
669 | +}; | |
670 | + | |
671 | +int main(int argc, char *argv[]) | |
672 | +{ | |
673 | + struct testcase *ptr; | |
674 | + struct cs_path_info *path; | |
675 | + struct cs_path_info *pattern; | |
676 | + for (ptr = testcases; ptr->pathname; ptr++) { | |
677 | + if (!cs_correct_word(ptr->pathname, 0)) { | |
678 | + printf("Bad path: %s\n", ptr->pathname); | |
679 | + continue; | |
680 | + } else if (!cs_correct_word(ptr->pattern, 1)) { | |
681 | + printf("Bad pattern: %s\n", ptr->pattern); | |
682 | + continue; | |
683 | + } | |
684 | + path = cs_get_name(ptr->pathname); | |
685 | + pattern = cs_get_name(ptr->pattern); | |
686 | + if (cs_path_matches_pattern(path, pattern) != ptr->match) | |
687 | + printf("Failed (\"%s\", \"%s\") == %d\n", | |
688 | + ptr->pathname, ptr->pattern, ptr->match); | |
689 | + cs_put_name(path); | |
690 | + cs_put_name(pattern); | |
691 | + } | |
692 | + return 0; | |
693 | +} |